Index
chal.cu は入力の先頭64byteを16個の int として読み、4x4行列 A にしている。GPU kernelの中身は分岐のない行列演算で、最終的に C' = 12 * A * B + 29 * C を計算し、固定のtarget行列と比較している。
したがってCUDA実行環境は不要で、公開されている定数から逆算できる。まず TARGET - 29 * C を12で割って A * B を得る。次に4x4行列 B を有理数上で逆行列にして右から掛ければ A が復元できる。
復元された各要素はすべて32bit整数になる。これをプログラムと同じlittle-endianの int として64byteに直列化すると読むべき入力になる。
jail.py は入力に .. が含まれていないことだけを確認し、os.path.join("/app", filename) で作ったpathが通常ファイルなら cat している。パストラバーサル文字列は弾かれるが、絶対パスを渡すケースは考慮されていない。
Pythonの os.path.join は後続要素が絶対パスの場合、前の /app を捨ててその絶対パスを返す。そのため入力に /flag.txt を指定すると、.. を使わずに /app 外の /flag.txt を参照できる。
chall.py はRSA暗号文を作ったあと、法として通常の (p - 1) * (q - 1) ではなく (p + 1) * (q + 1) を使っている。その値も出力されているため、bad_phi = n + p + q + 1 から p + q を得られる。
p + q と n = p * q が分かれば、二次方程式 x^2 - (p+q)x + n = 0 の判別式から p, q を復元できる。これで本来のCarmichael値も計算できる。
出力されている flag は元平文ではなく、暗号文を誤った指数で復号した値である。誤った復号は真の剰余群上では元平文をある指数で累乗した値になっているので、その有効指数を真のCarmichael値上で逆にして累乗し直すと元の平文に戻せる。
username がHTMLエスケープされずに <h1> 内へ展開されるため、タグを閉じれば任意の要素を挿入できる。CSPはnonce付きinline scriptだけを許可しているが、nonce生成が secrets.token_bytes() の呼び出しではなく str(secrets.token_bytes) になっているため、プロセス中で固定の関数reprをbase64化した値になる。
通常ページを一度取得すると、style や既存 script の nonce 属性からその固定nonceを読める。これを使って、username に同じnonce付きの script を埋め込む。
Admin botは対象ページのドメインにFLAG Cookieを設定してから指定pathを訪問する。埋め込んだscriptは有効nonceにより実行され、document.cookie を外部URLへのトップレベル遷移に含めることでCookieを回収できる。
配布物は .NET のWindows Formsアプリで、ボタン押下時に IsValidFlag が入力文字列を検証していた。メタデータ上のメソッドRVAからILを読むと、まず入力長が固定長と一致するかを確認している。
検証ループでは、入力の各文字を定数 0x75 とxorし、その結果を静的RVAフィールドに置かれた34バイトの配列と比較していた。したがってGUIを動かす必要はなく、この配列をバイナリから取り出せばよい。