Index

5/31 login-bonus-3

generate_password(char **password) がローカル変数 secret[32] のアドレスを呼び出し元へ返しており、関数を抜けた時点で password はスタック上の壊れた領域を指す。次の auth() では input[256] が同じスタック領域を再利用するので、password が実質的に input の後半を指す形になる。

先頭16バイトに任意のパスワード、直後にNUL、さらに password が指すオフセットにも同じ16バイトを置く。strlen(input) == 16 を満たしつつ strcmp(input, password) も一致するため認証を通過できる。

6/1 Half-Year Recap

半年分のDaily AlpacaHackを振り返る記念問題でフラグが明示されている。

6/2 Cache Me If You Can

Flaskの /flag は初回だけダミーを返し、その後は本物のフラグを返す。一方で前段のNginxが200応答を長期間キャッシュしており、素直に /flag へアクセスすると初回のダミーが固定されてしまう。

キャッシュキーはURI全体なので、/flag?a=1 のように未使用のクエリを付けると別キーになる。バックエンド側ではすでに初回フラグが折れているため、この新しいキーで本物のフラグが返る。

6/3 vm1

Node.jsの vm.runInNewContext をサンドボックスとして使っているが、node:vm はセキュリティ境界ではない。グローバルオブジェクトからconstructor chainをたどるとホスト側の Function を作れてしまう。

this.constructor.constructor('return process.env.FLAG')() のように実行し、ホストプロセスの環境変数を直接読む。

6/4 Small e

RSAで e = 5、かつ平文が短くパディングもない。平文整数を m とすると、今回のサイズでは m^5 が法 n を超えないため、暗号文は剰余ではなく単なる5乗になっている。

したがって暗号文 c の整数5乗根を取れば m が復元できる。

6/5 RPS GAME

shuffle(items)sorted(items, key=lambda _: random.getrandbits(1)) で実装されている。キーが0/1しかなく安定ソートなので、先頭要素が先頭に残る確率が高く、完全なシャッフルにはなっていない。

相手の手のリストはラウンド間で引き継がれるため、前回見えた先頭手に勝つ手を次に出し続ければ勝率が600/1000を超える。初手だけ初期配置の先頭に勝つ手を選び、以降は観測結果を追従する。

6/6 Flag for localhost