2016年8月29日月曜日

katagaitaiCTF#5 関西medに参加しました

本日(08月28日)に大阪で開催されたkatagaitai CTF勉強会(med)に参加してきました。

内容は午前はCryptography, 午後はPwnでした。Cryptographyはハッシュ関数に関する攻撃をやりました。問題はhack you 2014のhashmeと、ebCTF 2013のmd5collidingを中心に勉強しました。どうも講師の方がこのwriteupを読む可能性があるようなので、今回はしっかり書こうと思います。ちなみに一番前の中央に座っていました。[ ^o^]
katagaitaiCTFの5周年おめでとうございます。

午前 - Cryptography

Length extension attackやハッシュ衝突系の問題は昔勉強したことがあったので、今回のCryptographyはあまり資料を見なくても解けました。

hashme

ユーザー名を登録すると、「login=[username]&role=anonymous」で登録されます。ログインする際にroleにadministratorが入っていればフラグが表示されます。しかし、ログインするには登録時に生成される認証コードが必要です。認証コードは
s = "login=[username]&role=anonymous"
base64( xor( s + hashme(SALT + s), KEY ) )
で生成されます。
ただし、SALTとKEYはサーバー側にあり、不明です。(ローカルで実行する際はSALT="HOGE", KEY="\x01\x02\x03\x04"にしました)
ここでhashmeという独自実装のハッシュ関数がありますが、md5のように32バイトのhexdigestを出力するハッシュ関数で、ハッシュでごちゃごちゃにした各4バイトのA, B, C, DをB, A, D, Cの順にくっつけて出力しています。

(1)KEYを取得
とりあえずKEYは単純なxorで使われており、入出力はどちらも取得できるので、まずKEYを計算します。方法として、認証コード(cert)をbase64デコードし、それと"login=[username]&role=anonymous"をxorすれば、KEYの最初の方から復号化できます。さらに、[username]の長さを長くすると、それだけKEYを長く復号化できます。xorではKEYを繰り返してxorしているので、同じパターンの繰り返しが出た時点でそこまでが実際のKEY値です。

(2)length extension attackを実行
本日まではhashpumpしか使えなかったのですが、今回の講義を通して原理が分かったので実装することができました。ハッシュ関数は、ある時点で(A, B, C, D)が計算され、それをもとに次の(A, B, C, D)を計算する、という動作を繰り返しています。したがって、最終的に出力された(A, B, C, D)を使って、次の(A, B, C, D)を計算することで、hashme(A)の結果だけからhashme(A+B)が作れるということです。ただし、(A, B, C, D)の計算内には現在のバイトインデックスも含まれるので、Aの長さも必要です。今回の場合、 s = "login=[username]&role=anonymous"のときのhashme(SALT + s)が取得できるので、それをもとにhashme(SALT + s + "&role=administrator")を作ります。
しかし、SALTの長さは分からないので、ここは1から順番に仮定して、成功するまで繰り返します。

これらを実装したpython2のコードを載せます。ただし、pwntoolsというライブラリを使用しています。

今まで完全に闇の世界だったlength extension attackの原理が理解できたときは、かなり感動しました。

md5colliding

これ系も実は昔やったことがあり、そのときfastcollを使ったのも覚えていますが、2つまでしか同じハッシュを持つファイルが作れないなぁと思っていてそのままでした。今回の講義で少しステップアップした使い方ができるようになったかな。
さて、この問題は、指定された5つの文字列をそれぞれ出力するようなexeファイルを5つ作る、という問題です。ただし、5つのファイルは全てmd5が同じである必要があり、sha1が異なる必要があります。
fastcollというツールを使うと、
$ fastcoll A -o B C
でA+paddingという内容で、異なるpadding、同じmd5ハッシュ値を持つBとCが出力されます。ここから3つ以上同じハッシュを持つファイルの作り方が分からなかったのですが、
$ fastcoll B -o D E
としてCとD(もしくはE)との差分(追加分)をCに付けたせば良いようです。(なぜなら、hashmeで言ったように、サイズさえ一致すれば、それより前のバイナリは後のハッシュ値に影響しないからです。)
次にexeが異なる出力を持つ必要があるのですが、argv[0][0]などハッシュ値に影響しない部分を利用すれば良いようです。(もしファイル名が自動で割り当てられる場合はどんな方法がベストかな...?) とにかく今回はargv[0]で1から5までの出力を振り分けるのですが、Windows起動が面倒だったのでx86用のelfでやりました。
実行ファイル用のコードは次のようにしました。(linuxでは"./main"のように実行するのでargv[0][0]ではなくargv[0][2]を判定に使っています。) 実行ファイル用のCコードと、衝突生成用のpython2のコードを載せておきます。

同じmd5ハッシュ、異なるsha1ハッシュが出力され、出力内容も指定通りでした。コードは5個に限らず自由な個数だけ衝突ファイルを生成できるように工夫しています。

parlor

この問題はそもそも問題文を理解しておらず、時間内には解けませんでしたが、家でやったのでwriteupしておきます。
入力に対して条件を通過すれば賭け金だけ増え、失敗すれば賭け金が消えるゲームです。条件というのが
md5(hoge + input) % odds == 0
であることです。ちなみに問題文ではもうちょっと分りにくかったです。また、この「+」は文字列の結合で、hogeは16進数形式のダイジェストなのですが、バイナリとして結合されます。ここで数値加算してみたり文字列そのまま結合してみたりして時間取られました。さらに、inputには改行コードが最後に付いてくるっぽいのでそこにも注意。
この問題でユーザーが指定できるのはlog_2(odds)の整数とinputのバイナリです。hogeはランダムでサーバー側に保存されています。oddsの指数は100まで指定できるので、最大で2**100まで設定可能です。一回成功すればそれを繰り返せば良さそうなのですが、一度送ったinputは二度と送れません。

(1)md5(hoge + x)を知りたい
md5(hoge + x)が求まれば、md5(hoge + x + y)も求まります。しかし、md5(hoge + x)は最大でも下位100bitまでしか残りません。ということでbrute force attackで残りの28bitを探すことにしました。
...と書きながらコードを書いて実行したらあまりの重さにパソコンが止まったので今日はここまで。

午後 - Pwn

なんか凄いことやってるなぁって感じでした。
1つ1つのプロセスで何やってるのかは分かりましたし、資料にソースコードや構造体、サンドボックスの命令などが書かれていたので良かったものの、実際にソースコードも無い状態であれを解けと言われたら確実に捨てます。

tp

最後まで分からなかったこと(自分の書いた攻撃コードに関する疑問):
  • fastbinsに繋がったりbinsに繋がったり。その辺の設計もうろ覚えでした。 
  • 0x1337という数が分かりませんでした。idなら10とか20とかでもいいはず?
  • shellstormのshellcodeが動きませんでしたが、あれはseccompが原因だったのかな?
  • どうやってあんなに長いアセンブリを読む気になれるのか。

でも、やっぱりヒープ苦手だなと実感しました。もっと勉強してきます。

(2)習得したこと:
  • malloc, seccompなどの設計が少し分かりました。
  • main_arena以外にthread_arenaなるものがあるとは......
  • gdbでアタッチしたりダンプしたりするのが便利でした。
  • DynamicROP(?)の復習になりました。
最終的にflag1は取得できたので良かったですが、flag2については手も足も出ない状態です。特にヒープのところの動作がいまいち分かってないので解説みたいなのは書けませんが、書いた攻撃コードを載せておきます。(Ubuntuで書いたので日本語で書けませんでした。)

最初にひっかかったのはremote_readで、payloadを書き込んですぐにsendしなければならないのですが、remote_read内にrecvがあったので、次のステップに進めないままrecvで止まっていました。次にひっかかったのはpayloadのROP gadgetで、「pop esi; ret」を使ってるつもりが「pop esi; rep ...」みたいなのを使ってました。目grepが足りない。最後にひっかかったのがshellcodeで、shellstormからコピーしたものは動きませんでした。たぶんseccompのせいかな?
個人的にtpはflag1だけを集中的に説明しても良かったんじゃないかなとも思います。(そうするとプロが困るのかも。)

さいごに

今回はCryptography, Pwnいずれも前回のmedに比べるとかなり成長した状態で挑戦できたと思っています。(前のmedは1つも解けなかった。)
いつもkatagaitaiCTFではたくさんの知識を習得させてもらっています。講師のみなさん、NRIさんなど、運営してくださっている方々、本当にありがとうございます。
今後も参加したいと思っているので、よろしくお願いします!

2016年8月13日土曜日

katagaitaiCTF#6 関西easyに参加しました

本日(08月13日)に大阪で開催されたkatagaitai CTF勉強会(easy)に参加してきました。

内容は午前はWeb, 午後はReversingでした。WebはHack.lu CTF 2014のHotCows Datingという問題で、ReversingはCSAW CTF 2013のcrackmeという問題をそれぞれ解きました。正直Webの方はBurpの設定が上手くいかずにあまり付いていけませんでした。一応フラグは取得できたもののイマイチ「なんで?」というところが多いので、今回はcrackmeの方を中心にWriteUpを書こうと思います。

crackmeは32bitのLinux用バイナリで、とりあえずWindowsを起動してIDA Pro Freeで解析を開始しました。bindやaccpetなどから単体でサーバーとして動作するプログラムだと分かったので、とりあえずポート番号だけメモして動作しました。crackmeへ接続すると、パスフレーズを聞かれます。パスワードが合っていないと終了してしまいます。あまりIDAを使ったことの無い私は文字列検索で文字列を送信(表示)している部分を見つけ、その周辺から解析を始めました。その結果、明らかに分岐している部分があったので、その上の関数にverifyという名前を付けて関数を見ます。ちょっと前に変数名とコメントを付ける機能を知ってからはじゃんじゃん使ってるので解説が始まる前には解析が終わってたと思います。verify関数(仮)をC言語にすると次のようになりました。

int verify(char* input)
{
  char chr = input[0];
  int hash = 0x539;
  char *pass = input;
  int tmp;
 
  for(; *pass != 0; pass++) {
    tmp = chr + (hash << 5);
    chr = *(pass + 1);
    hash += tmp;
  }
 
  return hash;
}

あらためて見るといかにC言語が苦手かが分かります......
さて、とりあえず自分で解こうと思って6桁くらいまでのブルートフォースを書きましたが一向に終わらないので別の方針を立てることにしました。
そこで、入力を「A」「AA」「AAA」「AAAA」...のように試すと、徐々にハッシュ値が増加していることが分かりました。(4バイトを超えたらまた小さくなる感じでした。)
入力が長く、ASCII値が大きいほどハッシュ値も大きくなるので、二分探索(手動)で0xEF2E3558に徐々に近づけていきました。(でも5分程度で下2桁以外は揃いました。)
6桁でハッシュ値が答えに近づき、最後の方は揃わなそうだったので、最後の1文字だけブルートフォースアタックしました。その結果「aSA6>6」で通りそうだったので送ったらフラグが表示されました。

勉強会が終わった今では頭の悪い解き方です。はい。

ということで、勉強会ではkleeとz3pyを使う方法をやったので、その内勉強会中ずっとやってたz3による解法についてもWriteUpします。z3pyは条件式に当てはまるような入力を見つけてくれるモジュールです。文字列を探す方法は分からなかったのでIntVectorでやってましたが実行できず。スライドを読むとBitVecなら上手くいくそうなので時間がかかりましたが書いてみました。

実行すると入力条件に当てはまるような文字列を見つけて表示してくれます。講師の方の書かれたコードでは40秒程度かかりましたが、このコードでは1秒前後で実行できました。私は講師の方のコードをリストにして初期化に入力、hashともにBitVecを使っただけなのですが、なぜここまで違いが出たのかは全く分かりません......

easyとはいえ、今回の勉強会を通して知らなかったことをたくさん学ぶことができたので、大変有意義な1日でした。主催者やNRI Secureの皆様、ありがとうございます!次のmedも参加する予定なのでよろしくお願いします。