picoCTF2021 writeup Web Exploitation
Zenn初投稿です。
2021/3/17~31の期間で開催されたpicoCTF2021のwriteupです。
ソロで参加して獲得ポイントが2980pts / 11750pts、全体だと239位 / 6215、国内の学生だと9位 / 83 でした。問題数多すぎて単に時間が足りずにこぼした問題もあるのですが、そこそこ頑張ったかなという感じです。
大会終了後は常設CTFとなっているのでぜひ解いてみてください。
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.css
とmyjs.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ボタンを押したときに、onclick
でonButtonPress()
関数が発火しています。
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
が制限されているのでいつもの手法では不可能です。
問題は、
- admin をどう入力するか
- パスワードをどうスキップするか
の2点です。
- adminをどう入力するか
に関してはad'||'min
とすることで文字列を結合させればいいだけです。
- パスワードをどうスキップするか
は、要はuserを入力し終えたら読み込みを止めてもらえばいいわけです。
読み込みを止めるには;
で止める以外にもヌル文字で止める方法があります。
そこで、POSTMAN等のRESTクライアントで直接値をPOSTします。
user = "ad'||'min%00", pass = "hoge"
そうすると、「おめでとう、filter.phpにアクセスして(意訳)」と言われるので素直に従うとFLAGをもらえます。
Web Gauntlet 3 (300pts)
Web Gauntlet 2 の更に制限がきついバージョンです。
ただWeb Gauntlet 2 を非想定解で解いてしまったのか、全く同じ方法で通ってしまいました。
Discussion