SECCON Beginners CTF 2018 Activation & crackme
はじめに
ちょうど帰省していて,少しだけ時間があったのでぼっちで参加してみました.チーム名は,テレビでFoxチャンネルのリスナーって海外ドラマが流れていたのと,途中でANAのCMが流れていたので"foxana"です.適当です.
本当の初心者しか参加してはいけないのかと思ってたら割といろんな人が参加していたので,メンバーに声かけていつものチームで参加すればよかったかなぁと思いました.
スコアは1014ptで72位でした.Writeupはいろんな人が書いてくれると思うので,僕はReversingのWarmup以外の2問を簡単に書いておきます.
Activation
問題文
この問題の FLAG は ctf4b{アクティベーションコード} です。
$ file Activation.exe Activation.exe: PE32 executable (GUI) Intel 80386 Mono/.Net assembly, for MS Windows
.Net assemblyなのでdnspyでデコンパイルしてみます.
僕の環境だとdriveinfoのところでflagがtrueになってくれなくて(CDドライブがないから?),そもそもアクティベーションコードを入力するところまでいかなかったので,赤くなっている部分にブレークポイントをしかけて無理やりflagをtrueにしました.
重要なのは選択しているif文のところです.ここで,入力したアクティベーションコードが正しいかのチェックをしています. 実行してここでステップインすることで,Cという関数の中でなにをやっているのかを見ていきます.
AESで暗号化しているっぽいので,適当な場所で止めてIVとKeyの値を控えます.
また,これを E2AA8B78-798D-49BF-B9E7-13D334768E86.F()
と比較しているのでこれも何が入っているのか調べます.
あとは復号するスクリプトを書くだけです. E3c0Iefcc2yUB5gvPWge1vHQK+TBuUYzST7hT+VrPDhjBt0HCAo5FLohfs/t2Vf5
が E2AA8B78-798D-49BF-B9E7-13D334768E86.F()
の中身です.
デコンパイルしたC#コードをそのまま使ったので,実行環境がない人は
とか使うといいかもです.
以上より,フラグは ctf4b{ae03c6f3f9c13e6ee678a92fc2e2dcc5}
です.
crackme
問題文
バイナリを解析して、入力値を求めてください。
$ file crackme crackme: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=7e64cdb9686f4ec7c55701b9bbf8b69417da0c46, stripped
コマンドライン引数としてフラグを与えて,それが正しいかどうか検証しています.
$ ./crackme usage: ./crackme <FLAG>% $ ./crackme hoge Try again...%
objdumpで逆アセンブルしてみると,mainで2つの関数を読んでいることがわかります.一つは
400978: e8 09 fe ff ff call 400786 <exit@plt+0x2c6>
で,もう1つは
40098f: e8 42 fc ff ff call 4005d6 <exit@plt+0x116>
です.
あとは,具体的な処理を追う為にgdbで実行していきます.
この2つの関数は入力値検証関数で,それぞれ前の16バイト,後ろの16バイトを検証しています. 一つ目の関数を見てみると,検証するための値として,以下の16バイトの値をスタックに置いています.
0032| 0x7fffffffc8f0 --> 0x3f7b64683e8c9e9c 0040| 0x7fffffffc8f8 --> 0x641e73557f0a6350
入力値に対して特定の処理を施した結果がこれらと等しいかどうかを1バイトずつ検証していきます.入力値に対してどのような処理をするのかというと,以下のような処理を4回繰り返すことで16バイトの値それぞれに対して検証しています.これ↓は1周目の値です.meと名前を付けましたが,この変数はスタック上に保存されている値で,これを変化させながら計算処理に使用しています.
# first 入力値の1バイト目とme(0xff)とのxor 0x9cとの比較 # second me(0xff)と0x15のxor -> me(0xea) 入力値の2バイト目とme(0xea)とのxor 0x9eとの比較 # third me(0xea)と0x20のor -> me(0xea) 入力値の3バイト目とme(0xea)とのxor 0x8cとの比較 # fourth me(0xea)と0xfのand -> me(0xa) 入力値の4バイト目とme(0xa)とのxor 0x3eとの比較
どんな処理をしているのかがわかったので,あとは2つ目の検証関数も見ていくだけですが,2つ目の検証関数の処理は少々複雑(他の人のwriteupを見る限り大したことやってない…?なにを見ていたのか…)で,z3で書くのが辛かったので,angrを使ってみました.
以下がangrを使って書いたスクリプトです.
コマンドライン引数で渡すタイプのangrの書き方がわからなくて,hamaさんのブログを参考にしました.
(参考というかほぼ丸パクリですね...)
重要なのはfindの部分とavoidの部分で,findはmain関数にて”正しい値か正しくない値かで最後の出力を分岐しているところ”があるので,このアドレスを入れています.avoidに書いた9つの値は,1つは”mainにある間違った値が入力されたときに通る箇所”,もう4つは”検証関数1にて,間違った値が入力されたときに通るアドレス”,残りの4つは”検証関数2にて,間違った値が入力されたときに通る箇所”です.
これを実行するとフラグが出てきます.
$ python solve_angr.py WARNING | 2018-05-27 14:39:50,238 | angr.analyses.disassembly_utils | Your verison of capstone does not support MIPS instruction groups. WARNING | 2018-05-27 14:39:51,556 | angr.factory | factory.path_group() is deprecated! Please use factory.simgr() instead. Deprecation warning: Use eval(expr, cast_to=str) instead of any_str 'ctf4b{D0_y0u_l!k3_x86_4ssembly?}'
よってフラグは, ctf4b{D0_y0u_l!k3_x86_4ssembly?}
です.
検証関数1だけの処理を書いて諦めたz3バージョンも載せておきます.このスクリプト,なんとフラグが半分だけ求まります.