Link
Embed
Share
Beginning
This slide
Copy link URL
Copy link URL
Copy iframe embed code
Copy iframe embed code
Copy javascript embed code
Copy javascript embed code
Share
Tweet
Share
Tweet
Slide 1
Slide 1 text
PE(exe)ファイルの Rev問を解いてみよう <公開版> Ciruela(@__Xcyba17her_)
Slide 2
Slide 2 text
目次 問題解説 Binary 2 (NeverLAN CTF 2019) Crack me (InnoCTF 2019) Left 5 Seconds (Victor of Cyberists) Gacha (Victor of Cyberists) 完全版ではksnctfの3問のWriteupと,問題ファイルやソルバを 用意しています.色々と考慮すべきことがあるので,完全版の 配布はTwitter DMにより個人的に行うことにします. 完全版からksnctfの部分のwriteupを抜いただけなので,ツー ルのある機能の説明が省かれているにもかかわらず,その後の 問題で既知の機能として扱われている可能性があります. 2
Slide 3
Slide 3 text
Binary 2 (NeverLAN CTF 2019) 3
Slide 4
Slide 4 text
どんな問題? 認証システムのユーザーネームとパスワードを忘れてしまった… Employee_Payroll.exe(32bit)というファイルが渡される 実行すると,UsernameとPasswordが要求される 4
Slide 5
Slide 5 text
ファイルの種類を識別する WindowsではTrIDNetが便利(シグネチャデータが大きいが我慢) 画像から,exeファイルの可能性が高い 5
Slide 6
Slide 6 text
dnSpyで逆コンパイル dnSpyは,.NET製かつシンボル情報が多ければバイナリから ソースコードに復元する超優秀なツール exeファイルはまずこいつに突っ込んでみる! exeファイルが32bitか64bitかで32bit版と64bit版のdnSpyを使 い分ける必要がある 6
Slide 7
Slide 7 text
怪しいbtnLogin_Click関数 “OK”ボタンを押すと実行されると予想 UsernameとPasswordが正し ければ実行される 文字列を作った後,それを ウィンドウで表示する ログイン試行が3回を超えたら 実行される… UsernameとPasswordのいずれ かが誤っていれば実行される … 7
Slide 8
Slide 8 text
checkUsername関数とcheckPassword関数 直接文字列を比較している… Usernameは”admin” Passwordは ”dGhpc19pc19ub3RfdGhlX2ZsYWdfeW91X3NlZWtfa2VlcF9sb29raW5n” 8
Slide 9
Slide 9 text
見つけたUsernameとPasswordを入れる flag{ST0RING_STAT1C_PA55WORDS_1N_FIL3S_1S_N0T_S3CUR3} 2ページ前のthis.r1~this.r53にはflagが平文で入っていた 9
Slide 10
Slide 10 text
Crack me (InnoCTF 2019) 10
Slide 11
Slide 11 text
Crack_me.exe(32bit)が渡される “Enter the key:”と表示され,何か入力するとウィンドウが閉 じる 11 どんな問題?
Slide 12
Slide 12 text
dnSpyを使えない セクション情報などしか表示されない 12
Slide 13
Slide 13 text
IDA Freewareで眺める 文字入力関数のcinやscanfを探す Importsタブ → Ctrl+Fで検索機能を表示 → cinで検索 1件だけ表示されるので,ダブルクリックでその場所へ移動 13
Slide 14
Slide 14 text
IDA Freewareで眺める cinはsub_403630関数で呼ばれている “sub_403630”の部分をダブルクリック 14
Slide 15
Slide 15 text
IDA Freewareで眺める “Enter the key:”の表示と入力の処理がある ここがメインの処理のようだ 15
Slide 16
Slide 16 text
IDA Freewareで眺める cinの入力処理後,分岐でsub_4032F0関数が呼ばれることが多い 入力後の文字列の検証でダメだった場合の処理と予想 16
Slide 17
Slide 17 text
IDA Freewareで眺める 17 “Invalid key!”と表示 exit(0)のような処理 sub_4032F0関数は検証でダメだった場合の処理でよさそう “Invalid Key”の表示やexit(0)的な処理がある
Slide 18
Slide 18 text
IDA Freewareで眺める sub_4032F0関数に名前”wrong_exit”と名付けておく sub_4032F0をクリック→Nキーを押す→Nameに”wrong_exit”と入力 18
Slide 19
Slide 19 text
IDA Freewareで眺める 一つ目の分岐を見る sub_403310関数の返り値が0xFFFFFFFF(=-1)以外ならよさそうだが… 19
Slide 20
Slide 20 text
IDA Freewareで変数に名前をつける [ebp+var_28]は,cinの引数であり,入力文字列へのポインタが 入っていると考えられる var_28に”input_string”と名前をつけてみる var_28をクリック → Nキーを押す → 名前を付ける 20
Slide 21
Slide 21 text
IDA Freewareで眺める 読むのが非常に面倒くさい… さらに関数を呼んでいる デバッグしてみよう Strという第一引数に”_”を 指定していることから入力に おける”_”に関する関数と 予想 デバッグ時には入力文字列に “_”を含ませてみる 21
Slide 22
Slide 22 text
(x64dbgに付属する)x32dbgを使う x64dbgとx32dbgは,x32とx64のPEファイルをデバッグできる Ollydbgは32bitしかデバッグできないのでこちらのほうが便利 Ollydbgよりカラフルでよさそう(小並感) 22
Slide 23
Slide 23 text
x32/x64dbgで関数を探す CPUタブ内で左クリック → 検索 → 全モジュールを検索 → 外部 関数呼び出し → 関数名cinで検索 → 結果をダブルクリックし て移動 23
Slide 24
Slide 24 text
x32/x64dbgでグラフ表示 CPUタブ内で左クリック→グラフ 関数全体が表示されない場合は,左クリック→全体図 24
Slide 25
Slide 25 text
x32/x64dbgでブレークポイントを設置 左の灰色の点をクリックするとブレークポイントを設置できる IDAでsub_403310関数に相当する関数を探し,その直後にブレーク ポイントを置く IDAでsub_403310関数に相当する関数の返り値をeaxの値から調べ ることができる 25
Slide 26
Slide 26 text
sub_403310の返り値を検証する “_”の入力の有無や位置を変えて返り値(eax)の変化を調べる “_”を入れないとeaxは0xffffffff(=-1)に,入れると”_”のイン デックスがeaxに入ることがわかる 関数直前には入力文字列へのポインタがecxに格納されるため,入 力文字列も扱う関数だと予想できる sub_403310は,指定文字列から指定文字か文字列を検索しイン デックスを返し,見つからなければ-1を返す,strstrのような 関数だと予想できる 分岐前に返り値(eax)が0xffffffffならwrong_exitに移動するこ とになっていた 入力文字列に”_”が含まれていればwrong_exitに移動しない! 26
Slide 27
Slide 27 text
x32/x64dbgで関数に名前を付ける sub_403310に”sub_strstr”という名前をつけてみる 該当命令で左クリック→ラベル→ラベル00E53310を選択し,名前 を付ける 27
Slide 28
Slide 28 text
2つめの分岐を調べる またもやsub_strstrを呼んでいる ebp+var_30には以前のsub_strstrで見つかった”_”のインデッ クス(厳密にはアドレス)が入っており,それに1を加えたものを 引数として渡している つまり入力文字列にはアンダーバー’_’が2つ以上必要 28
Slide 29
Slide 29 text
3つめの分岐を調べる sub_4033F0の返り値(eax)が0x0fであればよい 入力にアンダーバー’_’が2つ入るようにしながら,入力文字列 を変えてみる eaxには入力文字列の長さが入ることが分かる sub_4033F0の機能はstrlenと同じ 入力すべき文字列は15文字 29
Slide 30
Slide 30 text
4つめの分岐を調べる sub_402850の返り値(eax)が指すアドレスに入っている1バイト の値が0x72(=‘r’)であればよい cmp命令の前にブレークポイントを置き,eaxの値を調べる eaxは入力文字列の先頭のアドレス 入力する文字列の1文字目は’r’ 30
Slide 31
Slide 31 text
5つめの分岐を調べる エスパーできる 1文字目の’r’に平文で見 れる”everse”が続くので はないか 実際に1文字目から ”reverse”とすると exit_wrongに分岐しない 31
Slide 32
Slide 32 text
6つめの分岐を調べる sub_402850は4つめの分岐に出てきた 4つめの分岐のsub_402850では,0を引数とし,返り値は入力文字 列の1文字目のアドレスだった 今回は8を引数としていて,今回は返り値が入力文字列の9文字目の アドレスであると予想する.すなわち,9文字目が’i’(=0x69)であ ると予想する 32
Slide 33
Slide 33 text
7つめの分岐を調べる 9文字目のASCIIコードと10文字目のASCIIコードを加えると 0xdcになる 9文字目は’i’(=0x69)だった → 10文字目は’s’(=0x73) 33
Slide 34
Slide 34 text
8つめの分岐を調べる これまでの情報から,1文字目 から11文字目は確定し, ”reverse_is_????” と考えられる “fine”が見えるので, “reverse_is_fine”かと 思いきや通らない 34
Slide 35
Slide 35 text
8つめの分岐を調べる ごめんなさい,これは完全に勘です(m´・ω・`)m 実は,sub_403090を通るとeaxには入力文字列の12~15文字目を前に3 だけシフトした文字列が入る 例えば,入力の12~15文字目が”abcd”だと返り値eaxには”^_`a”が 入る では,入力の12~15文字目を前に3だけシフトして”fine”だったら いい,すなわち”ilqh”だったらいいのではと考えた これでだめだったら諦めるか地道に読んでいくつもりだった 35
Slide 36
Slide 36 text
偶然の正解 ”reverse_is_ilqh”が正解だった しかし,フラグ文字列では’e’が’3’に改変されていた 恐らく最後の分岐以降がその処理 36
Slide 37
Slide 37 text
Left 5 Seconds (Victor of Cyberists) 37
Slide 38
Slide 38 text
どんな問題? left_five_seconds.exeとGameLogic.dllが渡される 1から30のマスを5秒以内で順にクリックする マウスで30回すべてのマスをクリックしていくブルートフォースの ようなスクリプトを書いても解けそう 38
Slide 39
Slide 39 text
dnSpyが使える クリア条件が見つからないが,時間計測とそれに応じexitする Form1.Timer1_Tick関数が見つかる exitしてしまう条件の500という値は5.00秒を示していそう この値を大きくすれば1~30までクリックする十分な時間を確保できそう 39
Slide 40
Slide 40 text
dnSpyでプログラムを書き換える dnSpyでは,逆コンパイルしたソースコードを書き換えて再コン パイルすることができる! if (this.time_count == 500)をif (this.time_count == 50000)に書き換えたい ソースコード上で左クリック → Edit Method → 該当部分を書き換 える → 右下のCompileボタン → ソースコード画面のFile → save All… → left_five_seconds_edited.exeなどと保存 40
Slide 41
Slide 41 text
うまく起動しない left_five_seconds_edited.exeを起動してみる “Invalid Hash!”と表示されて起動しない ハッシュ値の検証をして改造の検知をしている? 41
Slide 42
Slide 42 text
どこでハッシュ値の検証を行っているか left_five_minutes.exeとGameLogic.dllの両方で”Invalid Hash!”という文字列を探す IDAでView → Open subviews → stringsでバイナリ内の文字列が 表示される 42
Slide 43
Slide 43 text
どこでハッシュ値の検証を行っているか GameLogic.dllで”Invalid Hash!”が見つかる それに加え,”77777f298dba696637adfadf59a5630f”という文 字列が見つかる left_five_seconds.exeのハッシュ値とこれを比較し,合ってい なければ起動しない可能性がある 43
Slide 44
Slide 44 text
Windowsで,ハッシュ値を算出する HashSumやHashCalcが使える left_five_minutes.exeのMD5ハッシュ値 が”77777f298dba696637adfadf59a5630f”に一致 GameLogic.dllのMD5ハッシュ値照合用の文字 列”77777f298dba696637adfadf59a5630f”を, left_five_seconds_edited.exeのMD5ハッシュ値に変えてしまえ ばハッシュ値による改造検知を通過できそう 44
Slide 45
Slide 45 text
ハッシュ値の検査を回避する left_five_seconds_edited.exeのハッシュ値を算出する f97402713388f91420ad583e679df8b9 45
Slide 46
Slide 46 text
ハッシュ値の検査を回避する GameLogic.dllのハッシュ値検査用文字列を改ざんする Stirlingで改ざんする 検索・移動 → 検索 → 検索文字列入力 (データ種別は文字列・検 索範囲はデータ全体) 46
Slide 47
Slide 47 text
ハッシュ値の検査を回避する 右のASCII文字列からハッシュ値検査用文字列を変更する 書き換えたら,ファイル → 上書き保存 47
Slide 48
Slide 48 text
ハッシュ値の検査を回避する left_five_minutes_edited.exeを起動する ハッシュ値検査を回避できている 残り時間を示す文字列が0:00になっても終了しない 1~30をクリックし終わるとフラグが表示される 48
Slide 49
Slide 49 text
別解 Form1.Button_Clickといういかにもボタンが押されたら呼ば れそうな関数がある さて,プログラムが終了してしまう条件は,time_countが500 を超えてしまうことだった つまり,Form1.Button_Clickにブレークポイントを置いて実 行し,ボタンを押す → プログラムがブレークポイントで一時停 止する → time_countの値を編集して0に戻す → プログラムを 進めるというのを繰り返せば解ける time_countはunsignedの変数のようであり,負数には設定できな い.つまり,プログラムを動かしてボタンを押すまでを5秒でやら なければいけないので少し不安があり,そして30回もやるのはめん どくさい 49
Slide 50
Slide 50 text
Gacha (Victor of Cyberists) 50
Slide 51
Slide 51 text
どんな問題? Gacha.exe(64bit)と使用画像のフォルダが渡される 12回のガチャで12種類の誕生石を集めなければならない 51
Slide 52
Slide 52 text
dnSpyで開ける しかし,難読化処理されておりほぼコードを読めない 52
Slide 53
Slide 53 text
IDAで眺める 難読化データばかりで処理がわからない 難読化手法も不明 53
Slide 54
Slide 54 text
CheatEngineを使ってみる CheatEngineは,ゲームチーター御用達?のオープンソースソ フトウェアであり,条件を指定してメモリアドレスの検索を 行ったり,メモリの書き換えを行うことができる Gacha.exeを起動してからCheatEngineを起動し,左上のコン ピュータのアイコンをクリック,プロセスの一覧からGachaを選択 し,Openをクリック 54
Slide 55
Slide 55 text
未知数検索 未知数検索とは,ゲーム内でのアクションごとに値が変化した メモリアドレスを絞っていき,重要な機能を持つアドレスを特 定すること Gacha.exeにおいては,ガチャを引くごとに残り回数が減るた め,残り回数や資金を格納しているアドレスを特定できる可能 性がある アドレスを特定したら,あとは内容を好きなように書き換える 55
Slide 56
Slide 56 text
未知数検索 まずScan Typeを”Unknown Initial Value”,Value Type を”4 Bytes”にし,First Scanをクリック 以降は,ガチャを引いた後,Scan Typeを”Changed Value”と してNext Scanをすることを繰り返す 残念ながらこの方法では絞り込めない 56
Slide 57
Slide 57 text
特定の値の配列の検索 Gachaでは,これまでに出た誕生石を1,出ていない誕生石を0と する12要素の配列を作成し,12種類の誕生石が全て排出された か判断している可能性がある 例えば,1月,4月,6月の誕生石が既出であったら,内容が [1,0,0,1,0,1,0,0,0,0,0,0]となっている配列が存在する可能性 がある 配列の検索にあたり,現在2,3,4,5,9,11,12の誕生石が排出されて いる場合を例にやりかたを示す 57
Slide 58
Slide 58 text
特定の値の配列の検索(1byte単位) 配列の各要素が1バイトである場合と4バイトである場合の2通り で検索してみる.まずは1バイト単位で検索 New Scan → Value Type: Array of Byte → Valueのテキス トボックスに”00 01 01 01 01 00 00 00 01 00 01 01”を入 力 → 全てのメモリを調べるためにWritable, Executable, CopyOnWriteのチェックを全て■にする → First Scan しかし結果は1件もない 58
Slide 59
Slide 59 text
特定の値の配列の検索(4byte単位) New Scan → Value Type: Grouped → Valueのテキストボッ クスに”4:0 4:1 4:1 4:1 4:1 4:0 4:0 4:0 4:1 4:0 4:1 4:1”を入力(4:0は4バイトの値が0の意) → 全てのメモリを調べ るためにWritable, Executable, CopyOnWriteのチェックを 全て■にする → First Scan 1件だけ結果が出る 59
Slide 60
Slide 60 text
CheatEngineでメモリを書き換える 結果のアドレスをダブルクリック → Valueが0である各アドレ スのValueをダブルクリックして1を入力 60
Slide 61
Slide 61 text
CheatEngineでメモリを書き換える 次にガチャを引くと,フラグが表示される 本来ならサーバ側に処理を隠すし,わかりやすく連続したアドレスに格納し ないが,それだと初心者は絶対に解けないのでこうしたとのこと 61
Slide 62
Slide 62 text
解説終了! お疲れさまでした(o^―^o) 62