GrimmCon CTF writeup
12/30に行われたGrimmCon CTFに参加できませんでしたが、writeupを見ず解けたもののwriteupを残しておきます。 (というか書いた時点では参加者のwriteupが見当たらなかったです)
Web
Lottery [misc-ish web, easy]
Did you win the lottery? Find out online!
Investigation
- 問題ページにアクセスすると数字を入力して当選かどうか調べるアプリが稼働しています
- 問題がeasyカテゴリーかつ桁数も多そうなので、一先ずブルートフォースではないと考えます
- ソースを見ると
<!-- proudly developed in GNU nano, the omnipotent IDE -->
という一文が書いてあることに気づきます。Web問にnano?と思い調べると、nanoはファイルを編集した後、backupファイルとして<ファイル名>~
という名前のファイルを作成することが分かりました- というわけで
index.php~
にアクセスするとindex.php~のソースが見れます
- というわけで
Exploit
- flagが出るのは
preg_match("/^94519372$/")
にマッチする数値、つまり94519372
だと思ったのですが、入力してもflagが出てこなかったです😅- よーーーーーーーーーーく見ると、最後の
$
が全角です!なので94519372$
が正解でしたw
- よーーーーーーーーーーく見ると、最後の
flag{fb163d62a1670e6fcbb8b03a3daf31ab}
- easyなのにsolveが少なかったのは多分これが理由。全角に馴染みがある自分でさえ気づかなかったから、まして海外のプレイヤーは...😂
Syringe [ UNION-based SQLi, easy]
Doctors love their databases! Here is a library of words and semantics relating to medical words, like "syringe", or "x-ray", or "injection". Find whatever you need, just by searching for it!
Investigation
- 化学系の単語が格納されたDBから単語を検索できるようです。
- 問題はシリンジ、注射器の意味→SQL injection
- ソースを見るとコメントに
<!-- if ( isset($_GET["debug"])){ echo($sql_query); } -->
という一文があるのでSQLのクエリを可視化できる
Exploit
使っているクエリは
SELECT * FROM semantics WHERE name LIKE "%<input>%";
%"
を入れるとLIKE句を抜けることができる。その後UNION-based SQLiへ移行- group_concat関数でtable_nameをダンプするとき、テーブル名が多すぎて途中で見切れてしまうのでLIMIT句で出力を調整する
hoge%" union select table_name from information_schema.tables limit 100 -- -
テーブル名がダンプできた。flagテーブルがあるのでcolumnダンプする。flagカラムがあるようなので↓でフィニッシュ
hoge%" union select flag from flag -- -
flag{f2a5006b1b07cc08362772807322ef62}
fruitify [GraphQL injection?, medium]
Come grab a tasty freshly made juice, they are delicious
Investigation
- 問題サーバにアクセスするとフルーツジュース?のサイトが表示されます
色々観察すると、WebアプリがReactか何かでフロントエンドが構築され、webpackを使っていることも分かったのでdevtoolからcomponentを見てみます
- するとGraphQLを使っていることが分かりました!これはもうGQLiしかないとアタリをつけます
GQLiはやったことないのでこのサイトを参考にGraphQL Playgroundをインストールし、APIエンドポイントである
http://challenge.ctf.games:32355/graphql
を設定して自動でGraphQLの情報を調べます
GraphQL Playgroundというツールをインストールし、APIのエンドポイントであるURLを指定すると、自動的にインスペクションクエリを送信した上で、スキーマを可視化してくれます。
Exploit
- flagというスキーマ?があるので送ってみます
query UserQuery { flag }
するとflagが帰ってきました。
{ "data": { "flag": "flag{5e4e716b08873b04ed7ee8c2d88a5a2e}" } }
Key To The Castle [ jwk spoofing, medium]
Are you the king of the castle? Did you get locked out?
Investigation
- 問題ページにアクセスするとログイン画面があり、admin以外の任意の名前でログインできます
- Cookieを見てみるとJWTがセッションとして使われていたので、jwt.ioでデコードしてみます
JWTのheaderがデカすぎてスクショに収まり切りませんでしたが、重要なポイントは以下の3つです
- RS256で署名している
- headerの部分にjwkが含まれている(本来はjkuなどでjwks.jsonに書かれるべきものです)
- payloadのguestをadminに書き換えればよさそう
よって、この問題のゴールは自前のJWKを用意し、usernameをadminに書き換えた後自分の用意したRSAの公開鍵と秘密鍵で署名することです。
- 実はこのGrimmCon CTFの主催は半年ほど前に同じJWKS Spoofingの問題を出題しており、今回も似たような手法で解けるとアタリをつけて取り組みました。
Exploit
- まずmkjwk.orgで自前のJWKを生成します
- KeySizeは2048、Key UseはSignature、AlgorithmはRS256、そしてKey IDはkidのことなのでJWT中に記載されている
key-1
を使います
- KeySizeは2048、Key UseはSignature、AlgorithmはRS256、そしてKey IDはkidのことなのでJWT中に記載されている
- 画面左のオレンジのボタンで自前のJWKをコピーしたら、8gwifi.orgでJWK-to-PEMを使って自前の公開鍵と秘密鍵を生成しましょう
これで生成した公開鍵と秘密鍵をjwt.ioでデコードしたJWTのSignatureの部分に貼り付け、headerのjwk中のn
をmkjwk.orgで生成したJWKに含まれているnに置き換えればJWTを自由に改ざんできます
nと 公開鍵&秘密鍵
あとはjwt.ioの左側で表示されているJWTをセットしてアクセスすればadminとしてログインできるのでflagが表示されます
Bake The World [ UNION-based SQLi with double percent encoding, medium]
We had a vulnerability in a legacy service. We implemented a proxy to sanitize a parameter.
Investigation
- 問題ページにアクセスするとflaskで稼働しているブログが表示されます
バックエンドはwerkzeug 1.0.1というpythonのWSGI utility libraryが使われているらしい
- 以前見た問題に、このバックエンドのwerkzeugの特定のverでCVEのPoCを使ってファイルを読み出すものがあったので、1.0.1のCVEを探す
- デバッグモードがオンだと
/console
にアクセスしてOSコマンドインジェクションができるっぽいが、/console
にはアクセスできず。 - 問題文の
We implemented a proxy to sanitize a parameter
は/console
に向けてのものではなさそう
- デバッグモードがオンだと
- 以前見た問題に、このバックエンドのwerkzeugの特定のverでCVEのPoCを使ってファイルを読み出すものがあったので、1.0.1のCVEを探す
ふりだしに戻る。個々の記事へアクセスするとURLが
http://challenge.ctf.games:31848/post/1
となり、この/post/XXX
のXXXをいじるとエラー画面が出る
ここでSSTIができるのでは?と思ったがそれすらも不可。問題文のsanitizeはSSTIに対するものでもないらしい
ここで半ば諦めていたところ、
/post/XXX
のXXXを6'
にするとシングルクォートがフィルタリングされていることに気づく
- ダブルクォートなどはフィルタリングされていないので不審に思い、どうにかシングルクォートをしこめないか考えていると、ダブルパーセントエンコーディングを使うことを思いつく
%27
→%2527
ビンゴ!どうやら問題文のproxyでサニタイズしているのはシングルクォートだったようです。
あとはSQLiteのSQLiを組み立てるだけです。
- 色々試したところ、UNION-based SQLiでカラム数は9個でした。
http://challenge.ctf.games:31848/post/6%2527%20union%20select%201,group_concat(flag),3,4,5,6,7,8,9 from flag%20--%20-
感想
- 最近のCTFでは連敗続きだったので、久々に「解けた」という感覚を思い出せてよかったです!
- GrimmCon CTFは主催者の1人がJohn Hammond氏ということで、半年前のNahamCon CTFと同じ良問揃いだろうと思って解きましたが、程よい難易度で流石だと思いました
- このCTFで今年のCTF仕舞いです!良いお年を!