このWebサイトは/index.phpと/mypage.phpの2つのページから成り立っています。
/index.phpへPOSTリクエストを送る事でログインが出来ます。ゲストとしてログインするにはパスワードは必要ありません。管理者としてログインするためにパスワードが必要で、これは現状知ることは出来ません。ログインに成功すると、cookieを渡されます。このcookieはguest
かadmin
を暗号化したものと、暗号文がWebサイト自身によって発行されたことを保証するTagと呼ばれる情報です。
/mypage.phpではcookieの情報に基づいて、ゲストまたは管理者向けのマイページを表示します。管理者向けのマイページを表示させることが出来ればflagが手に入り、そのためにはadminの認証情報を持ったcookieが必要です。この問題で目指すのは、adminとしての有効な認証情報を持ったcookieを設定して/mypage.phpにアクセスし、管理者パスワード(Flag)を得ることです。
cookieの暗号化で用いられている方式は、aes-128-gcm
であり、これは鍵と初期化ベクトルから生成したデータと、平文とのxorをとっています。そのため、暗号文とある値a
のXORをとることで、平文とa
のxorをとったものを暗号化したデータを得ることが出来ます。
guest
を暗号化したものを得ることが出来るので、これと'guest' xor 'admin'
のxorをとることによって、復号するとadmin
になる暗号文を手に入れることが出来ます。
このように、暗号文から平文を改ざんすることが容易であるため、aes-128-gcm
にはハッシュを使って改ざんを検知する仕組みがあります。ここで使われているcookieではtag
として与えられているので、改ざんしたcookieを有効な物として認識させるためには、tag
も適切に変更しなくてはいけません。
一般的にこの値は、秘密鍵を知らないと計算することはできません。しかし、PHPのopenssl_decrypt()
関数は、内部でtagの長さをチェックしません。このため、tagの長さが求める長さであることを、関数の呼び出し側で確認する必要がありますが、このWebサイトではこれを怠っています。そのため、短いtagが正しいtagの先頭部分と一致していた場合に復号に成功してしまいます。
暗号文を改ざんしたcookieに付けるtagの長さを1byteにすれば、高々256通りのtagを総当たりで試す事によって必ずログインに成功し、Flagを手に入れることが出来ます。
TSGCTF{Deadlock_has_been_broken_with_Authentication_bypass!_Now,_repair_website_to_reject_rewritten_CookiE.}