問題の概要
出題ページには、問題ページへのリンクとソースコードが配布されていた。
問題ページをみると、「ガチャ」があった。
ソースコードを併せてみてみると、ボタンを押すとランダムに艦隊(の名前)がゲットできるというものだ。
ゲットした艦隊(の名前)の情報は、クッキーで管理されているようだが、クッキーの改竄を防ぐためにハッシュ関数で
署名をして、ページアクセス時にそのクッキーの値を検証している。
ハッシュ関数に使っているソルトがフラグとなっているようだ。
足掻き
最近自力で解けていないため、これは自力で解きたいと思い、1 日かけた。
結論をいうとダメでした…
php の関数を調べまくり、ハッシュ関数のソルトが固定であり、
平文とハッシュ関数の種類と暗号文がわかっているんだから、ソルトの値を得ることはできないのかという発想で色々調べた。
そうして 1 日が立った後 writeup をみると、伸長攻撃 というものがあるらしい。
調べてみても、漠然としかわからん。(英語読むのまだ慣れない)
英語で length extention attack というらしい。
今回はあたったサイトの情報や本でハッシュ関数と伸長攻撃のことを調べたので、簡単にまとめることにした。
伸長攻撃
伸長攻撃とは、ハッシュ関数 H と入力文字列 m || k (|| は文字列を連結させるやつ)でできたハッシュ値 H(m || k)があるとする。
この時、k がわからなくても、m とハッシュ値 H(m || k)と k の文字長がわかるなら、入力に m* を付け足したハッシュ値 H(m || k || m* )を求められるというもの。
ハッシュ関数には反復型ハッシュ関数という種類がある。
これに分類されるハッシュ関数は、MD5, SHA1, SHA256, SHA512 があり、
これらに対して伸長攻撃は有効になり得る。
この攻撃が成立するのは、要は入力を分割して、端から圧縮関数にいれるという操作を繰り返すのだが、
出力が次の入力となるために、順に計算していけば出力がわかってしまう。
重要なところは、ハッシュ関数ではまず入力を nbit で区切る。(64byte ごと?)
(入力が nbit の整数倍にみたない場合はパディングして整数倍にする)
その後、その入力 m_0 を定数 L や初期ベクトル h と一緒にして圧縮関数に通す。
その後、その出力を次の h として、L, h, m_1 を圧縮関数に通す。
これを繰り返して、最後の出力がハッシュ値として出力される。
感想
これは以下の点で認証がよくなかったと思う。
- salt が固定。
- 入力をそのままハッシュ関数に入れている。
ハッシュ関数に入れる前に、入力をある程度バリデーションすれば伸長攻撃は防げるんじゃないだろか。
例えば、入力の長さを制限するだけでも有効そうだ。
ハッシュ関数の勉強になった。
まだ謎が残っている点で、hashpump を使って exploit を書いたのだが、
入力データはこういう結果になった。
3%80%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%c0,10
この、%80と%c0はなんだろうか
(調べた結果 80 は utf-8 で Padding Character のようだが(機能は不明)、%c0 の意味が全然わからない。)
参考になった github リポジトリ
(https://github.com/cbornstein/python-length-extension)[https://github.com/cbornstein/python-length-extension]