Index
main は long array[0x100] に対して index >= 0x100 だけをチェックしており、負数は弾いていない。
普通の負数添字では stack の下側へ書くだけで戻りアドレスには届かないが、x86-64 の実効アドレス計算は64bitでwrapする。戻りアドレス相当の添字は 259 なので、259 - 2^61 のような巨大な負数を入れると、比較上は負数で通りつつ、index * 8 のアドレス計算では 259 * 8 と同じ位置に回り込む。
この位置に win() のアドレスを書けば saved return address を直接差し替えられる。canary には触らないため、そのまま関数を抜けて win() に到達する。
chal.c は長さ19の入力を取り、固定初期状態 0xACE1 の16-bit LFSRを1文字ごとに1回進める。各文字は state & 0x7F と XOR され、その結果が enc[i] と一致するかだけを見ている。
変換は1 byte XORなので逆向きに解く必要はなく、検証と同じ順番でLFSRを進めながら enc[i] ^ (state & 0x7F) を計算すれば元の文字がそのまま戻る。LFSRのtapもソース中に固定で書かれているため、全19文字を順に復元できる。
compose には外から接続できる public と、port公開されていないランダム名のserviceがあり、どちらも同じimageで socat が sh を起動している。flagは hidden 側の FLAG 環境変数にだけ入っている。
公開shellに入ると SOCAT_SOCKADDR から自分の Docker bridge IP が分かる。サービス名はDNSで当てられなくても、同じ /24 内の 1337/tcp を短いtimeoutでscanすれば hidden service のIPに到達できる。見つかった各shellへ echo "$FLAG" を送ると、publicでは空、hidden側だけ値が返る。
入力はASCII 6文字以下で、eval(code) されるだけ。戻り値は表示されないので、直接 ALPACA_FLAG を評価しても読めない。
6文字で副作用を起こせる組み込みとして help() が使える。これを評価すると Python の対話helpに入るため、その後の help> プロンプトでは長さ制限なしで調べたい名前を入力できる。__main__ を指定すると、実行中の jail.py module のDATAとして global 変数が表示される。
chal.sage は first primes p_i とflag文字 c_i から prod(p_i*x - ord(c_i)) を作り、展開済みの多項式だけを出力している。したがって有理数体上で多項式を因数分解すれば、各一次因子から ord(c_i) / p_i に対応するrootが得られる。
注意点は、ord(c_i) が p_i で割り切れる位置では因子が正規化され、係数だけを見ても元のprimeが残らないこと。rootの集合を取り出し、既知のprime列に対して p_i * root が整数かつflag形式に合うものを順に割り当てると、全文字を復元できる。
frontend は受け取ったURLをそのまま curl --silent --show-error --insecure <url> へ渡す。shellは介さないのでコマンド注入ではないが、curlがHTTP以外のprotocolも扱える点が攻撃面になる。