Szarny.io

There should be one-- and preferably only one --obvious way to do it.

ksnctf 31. KanGacha の writeup

問題リンク

ksnctf - 31 KanGacha

問題文からの調査

問題文には,問題へのリンクとそのソースコードがありました.

リンク先は,[Gacha]ボタンを押すと戦艦(?)の名前が出てきて,[Clear]ボタンを押すとリストが初期化されるというシンプルなアプリケーションでした.
ソースコードでは,いきなりFLAGがありますがもちろん隠されており,以下にガチャのプログラムが書かれていました.まず,ここから調べていきたいと思います.

ソースコードの調査

if (isset($_COOKIE['ship']) and
    isset($_COOKIE['signature']) and
    hash('sha512', $salt.$_COOKIE['ship']) === $_COOKIE['signature'])
    $ship = explode(',', $_COOKIE['ship']);
else
    $ship = array();

まず,この部分では,

  1. Cookieにshipがセットされているか
  2. Cookieにsignatureがセットされているか
  3. sha512(salt+cookieのshipの値)とcookieのsignatureの値が一致しているか

の三点を調べ,

  • すべて真のとき,Cookieのshipの値をコロンでパースしてship配列に代入
  • それ以外のとき,ship配列を新たに生成

しています.

if ($_POST['submit'] === 'Gacha')
    {
        //  Yamato is ultra rare
        $ship[] = mt_rand(0, count($shipname)-2);

        $s = implode(',', $ship);
        $sign = hash('sha512', $salt.$s);

        setcookie('ship', $s);
        setcookie('signature', $sign);
    }

次のこの部分は,Gachaボタンが押された時に動作し,

  1. ship配列の末尾に0~9の整数値を代入
  2. ship配列の各要素をコロンで結合し,sに代入
  3. sha512(salt+s)の値をsignに代入
  4. Cookieのshipにsを,signatureにsignをそれぞれ代入

の4点を行っています.

if ($_POST['submit'] === 'Clear')
    {
        setcookie('ship', '', 0);
        setcookie('signature', '', 0);
    }

最後のこの部分は,Clearボタンが押された時に動作し,Cookieの初期化が行われます.

とにかく,ultra rareのYamatoが出ると,Flagが手に入るようです.
表示部分のソースを見るとわかりますが,ship配列に入っている数値を添え字としてshipname配列を出力しています.が,その数値を決定している乱数が 0 ~ shipnameの要素数-2 (Nagato~Hyuga)なので普通にやってると出るわけありません.
アプリケーション上にフォームとかがあるわけではないので,こちら側がいじれるのはCookieだけです.よって,本問ではCookieをどうにかすることによって,無理やりYamatoを当てたいと思います.

以上より,本問を突破する方法が見えてきます.つまり,

  1. ship配列に数値"10"を代入させること
  2. その上で,sha512(salt+cookieのshipの値)とcookieのsignatureの値を一致させること

の両方ができれば,Yamatoが画面上に表示されると考えられます.

解法

ソルトとハッシュに対する攻撃的なキーワードでサーチしたところ,以下のような投稿が見つかりました.
ソルトとハッシュ関数だけでパスワードをハッシュ化するのが微妙な理由 - Qiita

要約すると,

salt ソルト(本問のFlag)
data データ(本問におけるship配列をパースしたもの)
hash ハッシュ関数(本問ではsha512)

とするとき,
 hash(salt+data) の結果が分かっているならば,
 hash(salt+data) = hash(salt+data') となる任意のデータ data' が,salt が不明なままでも求められるらしいです.

そこで,伸長攻撃を行ってくれる,hashpumpを使います.

root@kali:~# hashpump
Input Signature: 489009674f199404993dffeba9dfb5d138414173104d9c1f0d69ab147211f513d07a7020d1c1eda22808efe5a6dc3c7c26bab25ea6813021ddffe8c7d636944d
Input Data: 9
Input Key Length: 21
Input Data to Add: ,10
930472e5483c2f10a18f8a2873d50850809f6f90a88b98f453a67ee896a69c86d4bfac8877f8ae63d48ab5acb7079d389588f6675495ae83c956a95788ab0d9c
9\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xb0,10

これで,signatureの値とshipの値が分かったので,開発者コンソールとかでCookieの値をこれらで上書きしてガチャを回せばYamatoが出るはずです!(shipの値はパーセントエンコーディングする必要があります)

f:id:Szarny:20170826190258p:plain

おわりに

ソルトつけてれば安心みたいな記事をよく目にしますが,こんな攻撃があったんですね.
スクリプト組んでやろうかなと思いましたが,パラメタ打ってパーセントエンコーディングしてCookie塗り替えるだけなんでいいかと思って手動でやりましたが,パーセントエンコーディングの部分でちょっとミスがあって時間かかっちゃったんで,今考えると組んだ方が早かったかもしれません(;^_^A