👏

picoCTF2021 writeup Web Exploitation

2021/03/31に公開

Zenn初投稿です。
2021/3/17~31の期間で開催されたpicoCTF2021のwriteupです。
ソロで参加して獲得ポイントが2980pts / 11750pts、全体だと239位 / 6215、国内の学生だと9位 / 83 でした。問題数多すぎて単に時間が足りずにこぼした問題もあるのですが、そこそこ頑張ったかなという感じです。

大会終了後は常設CTFとなっているのでぜひ解いてみてください。
https://play.picoctf.org/practice?originalEvent=34&page=1

Web Exploitationカテゴリの中で解けた問題を解説していきます。
別カテゴリはこちら↓
(作成中)

Web Exploitation

GET aHEAD (20pts)

問題のページを開くと、背景色を赤か青に変えられるボタンが置いてあります。
HTMLの実装を覗いてみるとChoose Redを押すとGETリクエスト、Choose Blueを押すとPOSTリクエストが飛んでおり、Hint1のMaybe you have more than 2 choicesと合わせて考えると、HTTPメソッドで色の切り替えを行っているような雰囲気があります。
タイトルの言う通り、HTTPメソッドのHEADでリクエストを行ってみるとFlagが返ってきます。
curl http://mercury.picoctf.net:45028/index.php --head

Cookies (40pts)

クッキーを検索できるページのようです。クッキーモンスターが喜びそうですね。
プレースホルダにあるsnickerdoodleをそのまま検索してみると、検索にヒットするようでI love snickerdoodle cookies!と返ってきます。
Webに携わる人がCookieと言われたら食べる方より先にセッション管理する方が出てくるのは自明なのでCookieを見てみます。私はChromeの拡張機能のEditThisCookieを使っています。
Cookieの中にnameという値が保存されていて、この数字を変更することで画面に表示するCookieの出し分けを行っているようです。この値を0から順番になめていけばそのうちFlagが出てきます。

Scavenger Hunt (50pts)

シンプルな問題ページが与えられます。
とりあえずHTMLを見てみると、Flagの1つ目の断片が見つかります。
あと怪しいのは<head>の中にあるmycss.cssmyjs.jsですね。

cssの方に2つ目の断片がありますが、jsの方は次の断片のヒントのようです。
How can I keep Google from indexing my website?
とのことで、Googleがサイトをクロールするときに見るものといえばrobots.txtです。

/robots.txtを見ると、3つ目の断片と次のヒントが出てきます。
I think this is an apache server... can you Access the next flag?

Apache Serverであることと、Accessが大文字で強調されていることに気づけば、アクセス制御に使う.htaccessのことかなと察しが付きます。

/.htaccessを見てみると、4つ目の断片と最後のヒントが出ます。
I love making websites on my Mac, I can Store a lot of information there.
Macを使っていることと、Storeが大文字になっていることに気づけば.DS_Storeのことかなとわかります。(これ以外に説明のしようがない……)

/.DS_Storeを見れば最後の断片が出てくるので順番に繋げばFlagになります。

It is my Birthday (100pts)

問題文を読むと、md5が同じだが違う内容を持つPDFファイルを上げればよさそうです。
md5が一致する2つのファイルは適当にGoogleで検索すれば手に入るので(https://www.mscs.dal.ca/~selinger/md5collision/) 、ダウンロードしてきたファイル名の末尾に.pdfをつけてアップロードすればクリアです。

Who are you? (100pts)

期間中に何をやっても解けず、終了後に他の方のWriteupを読んでやっとできました。

アクセスするとOnly people who use the official PicoBrowser are allowed on this site!と言われてしまいます。
PicoBrowserを使っていることを装わなければならないようですが、これはヘッダーのユーザーエージェントを変更すればいいだけです。
その後も何かとアクセスを拒否されてしまうので一覧形式で書きます。

alert header key value
Only people who use the official PicoBrowser are allowed on this site! User-Agent PicoBrowser
I don't trust users visiting from another site. Referer http://mercury.picoctf.net:36622/
Sorry, this site only worked in 2018. Date 2018/1/1 00:00:00
I don't trust users who can be tracked. DNT 1
This website is only for people from Sweden. X-Forwarded-For 185.195.92.41 (スウェーデンの政府機関?のホームページのIP)
You're in Sweden but you don't speak Swedish? Accept-Language sv-SE

Some Assembly Required 1 (70pts)

これはWeb問というよりほぼBinaryかReverse問では……?と思いながら解いていました。Web Assemblyはジャンル分けが難しいですね。

問題のページを開くとEnter flag:という文字列とともに入力欄とボタンがあり、文字列を入力するとFLAGが正しいかを教えてくれるみたいです。

Submitボタンを押したときに、onclickonButtonPress()関数が発火しています。
ChromeのDeveloper ToolのSourcesタブからjsを読んでみると、何やら難読化されていますが実際の判定はWeb Assembly上で行っている雰囲気を感じ取れます。
雰囲気がわからなくても一つずつ丁寧にほぐしていけば、input欄のvalueをWebAssemblyのcheck_flag関数に通していることがわかるので興味のある方は解読してみてください。(私も全部易読化したのですがどこかに捨て書いたのか見つかりませんでした)

なので同じくSourcesタブからwasmを見てみましょう。(最初、ここからdisassembleした状態で見られるのに気づきませんでした)

ざっと見るとstrcmp, check_flag, copy_charの3つの関数があります。
そして一番下のdataというところにFLAGが書いてあります。
直接この値と、入力された値を比較しているだけのようです。

wasm初めて読んだのですが割と読みやすいですね。

Some Assembly Required 2 (110pts)

Some Assembly Required 1 と似ていますが、先程のようにFlagがそのまま書いてあるわけではなさそうです。
Some Assembly Required 1 のアセンブリと見比べてみると、copy_char関数がちょっと伸びていそうです。
差分を確認してみると、

block $label0
	local.get $var5
	i32.eqz
	br_if $label0
	local.get $var4
	i32.load offset=12
	local.set $var6
	i32.const 8
	local.set $var7
	local.get $var6
	local.get $var7
	i32.xor
	local.set $var8
	local.get $var4
	local.get $var8
	i32.store offset=12
end $label0

あたりが気になります。

wasmの命令表とにらめっこしながら解読してみると、入力された一文字がNULL文字ではなかったら、8とxorを取った文字を返すことがわかります。

ということで、下の方に書いてある文字列をCyberChefなどを使って8とxorを取ってみるとFlagっぽい文字列が出ます。一部完全に復号できていませんが、picoCTF{hogehoge}という形式から考えれば問題はないかと思います。

Web Gauntlet2 (170pt)

or and true false union like = > < ; -- /* */ admin
が使えないように制限された状態でのSQLインジェクションです。

制限がない状態であれば、
user = "admin';--", pass = "hoge"
として、パスワード認証をスキップしたいところですが、残念ながら; -- adminが制限されているのでいつもの手法では不可能です。

問題は、

  1. admin をどう入力するか
  2. パスワードをどうスキップするか

の2点です。

  1. adminをどう入力するか

に関してはad'||'minとすることで文字列を結合させればいいだけです。

  1. パスワードをどうスキップするか

は、要はuserを入力し終えたら読み込みを止めてもらえばいいわけです。
読み込みを止めるには;で止める以外にもヌル文字で止める方法があります。

そこで、POSTMAN等のRESTクライアントで直接値をPOSTします。
user = "ad'||'min%00", pass = "hoge"

そうすると、「おめでとう、filter.phpにアクセスして(意訳)」と言われるので素直に従うとFLAGをもらえます。

Web Gauntlet 3 (300pts)

Web Gauntlet 2 の更に制限がきついバージョンです。
ただWeb Gauntlet 2 を非想定解で解いてしまったのか、全く同じ方法で通ってしまいました。

Discussion