Index
RSA の q だけが getPrime(32) で生成されている。n = p * q に 32-bit の小さい素因数が混ざるため、n はそのまま分解できる。
> sage -c "print(factor(28576441902831457590427748668696557474043630372677632241672926869035931925834848020457746529998588578832822195110996090327253913633985879817257290686867076047319213))"
2563568311 * 11147134944761554899103193302305006326021647555260965402717106245676295507085025759806151126198169243492736478560331248503739955004454679349973167415575483
分解後は通常の RSA 復号でよい。phi = (p - 1) * (q - 1) を計算し、d = e^{-1} mod phi から c^d mod n を戻す。
/login は username と password を直接 SQL に埋め込んでいるのでSQLiを考える。
返却に user[0] が表示されるため、UNION-based SQLi で1列目に見たい値を置けば結果を画面に出せる。
欲しいtable/column 名は固定ではないが、SQLite では sqlite_schema から schema を取得できる。まず secret_% の CREATE TABLE 文を読んで table/column 名を特定し、その名前を使って中身を取り出す。
' UNION SELECT sql, name FROM sqlite_schema WHERE name LIKE 'secret_%'--
' UNION SELECT {column_name}, '' FROM {table_name}--
chall.sh は入力を [[ "$GUESS" -eq "$SECRET" ]] で比較している。
当てたところでflagは得られないのでコマンド実行できないか考える。
Bash の -eq は実際には両辺を評価する。配列添字などを使うことでコマンドを実行でき、socat の設定により stderr 側の出力も接続へ返ってくることを利用する。
a[$(cat /flag.txt >&2)]
vuln() は stack 上の alpaca_functions[3] を、入力した choice で境界チェックなしに呼び出す。choice は正方向の OOB で saved RBP や saved return address 側を関数ポインタとして読める。遠くないはずなので適当に5 を選ぶとそのまま win() に流れシェルが取れた。
chal.sh は root で flag.txt を作った後、すぐに rm * で chal.sh と flag.txt を削除してから shell を起動する。ファイル名としては消えているため、普通に cat flag.txt しても読めない。
ただし bash chal.sh の親プロセスは、実行中の script を fd 255 として開いたまま残している。/proc/$PPID/fd/255 を読むと、unlink 済みのフラグの書かれた chal.sh 本体を参照できる。