今日は Daily AlpacaHack の Day 4 である Reversing (rev) の問題を解きました!一応revは「コンパイルされたバイナリが与えられて、それを元に隠されたフラグを見つける」という問題であることは知っていましたが、実際にやってみるとめちゃくちゃ難しかったです。でも今まで (crypto, web) の中では一番好きな分野でした!
問題
与えられているもの
-
challengeというコンパイルされたバイナリファイル -
challenge.cというC言語のソースコード (半年ぶりくらいにC言語触った...)
最初はソースコードを読んでフラグがどのような形式で保存されているかを考えていたのですが、ヒントをみたら「実際のrevではソースコードは与えられず、この問題も実はバイナリファイルだけから解ける」と書いてあったので、AIにその都度便利なバイナリ解析コマンドを教えてもらいながらソースコード話でフラグを探すことにしました!
聞かれていること
バイナリのどこかに含まれている、Alpaca{flag} という形式の文字列を探すこと。Alpacaというラッパーが分かっているのは大きくて、これを元に文字列検索のようなことをしていくらしいです。
解く
自分はバイナリ解析用のコマンドをまだ何も知らないので、取り組み方としては、「こういうことがしたいな」と思ったらその都度それを達成できるコマンドがあるかをAIに聞くという方法でした。
まず、基本となるよく使うコマンドの一覧を教えてもらいました:
| コマンド名 | 説明 | 得られる情報 | 使用例 | 出力例 |
|---|---|---|---|---|
| file | ファイルの基本情報を確認 | 形式・アーキテクチャなど | file challenge |
challenge: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked |
| strings | バイナリ内の文字列を抽出 | 「文字列みたいなもの」のリスト | strings challenge | Enter flag:, Wrong length, ... |
| xxd | バイナリを16進数で表示 | 16進数の値・対応するASCII文字 | xxd challenge | 00001160: 488d 3da9 2e00 0048 8d35 a22e 0000 4829 H.=....H.5....H) |
| grep | データから入力のパターンを探す | 見つかったパターンのバイトオフセット | `grep -i "Fkwfdf" challenge | 4614:Fkwfdf |
次にメインの解いた流れですが、こんな感じです
1. (これは少しズルでソースコードから得た知識ですが) バイナリ内ではフラグが「xorエンコーディング」されていることが分かりました。これはどういうことかというと、フラグの文字列一つ一つのcharに対して、$^ 7 = ^ 000001111$ のようなxor演算を適用しているということです
フラグは絶対にAlpaca{...}で囲まれているため、"Alpaca" ^ 7 = "Fkwfdf" という文字列を探せば良いのだと分かりました
これは、Pythonの「ワンライナー」というのを用いて、コマンドラインで直接以下を実行して求めました。
python3 -c "print(''.join(chr(ord(c) ^ 7) for c in 'Alpaca'))"
2. 次に、この文字列を strings コマンドを使ってバイナリファイルの中から探します
strings ./challenge | grep "Fkwfdf"
出力は以下でした
Fkwfdf|kH
実際にこの文字列 ("Alpaca") が存在することが分かったので、フラグはこの辺りにありそうです!
3. grep を用いてこの文字列の出現するバイトオフセットを調べます
grep -aob "Fkwfdf" challenge
出力は
4614:Fkwfdf
ということで、4614バイト目くらいからこの文字列が始まることが分かりました。
4. ここで、dd という「どこからどれくらいの長さ」を指定しつつファイル内のデータを抜き出せる(コピーする)コマンドをAIに教えてもらったので、これを用いて4614バイト目から100バイト分のバイナリデータを取得し、これに xxd を適用することで周辺のASCII文字を取得しました
dd if=challenge bs=1 skip=4614 count=100 2>/dev/null | xxd
結果、ここら辺には Fkwfdf|kH.E.H.|krdl~z.H.E.H.E. というような文字列があることが分かりました。(H.E.H. みたいな部分は機械語というらしく、何らかの理由で紛れ込んでいて答えには関係ないらしいですが深くは分かりません)
5. 最後に、この文字列に再度 Python で 「^ 7」の xor 演算を適用し、元のフラグの文字列に戻してあげます (xorは2回適用すると元の値に戻るという性質がある)
python3 -c "print(''.join(chr(ord(c) ^ 7) for c in 'Fkwfdf|kH.E.H.|krdl~z.H.E.H.E.'))"
出力は
Alpaca{lO)B)O){lucky})O)B)O)B)
でした。変な文字 (機械語?) が紛れ込んでいて最初は混乱したのですが、もう疲れていたのでとりあえず答えっぽい lucky を提出してみたらあっていたので、とりあえず解けたということにします。
まとめ
今日はここまでです。revの問題は初めてでしたが、結構ハマりそうな予感がします。特に、中身の見えないバイナリファイルを、いろいろなコマンドの出力結果を観察しながら解剖していく感じが楽しいです (大学受験でやった化学の構造決定と似た雰囲気を感じます)
明日の Daily AlpacaHack は難易度 Hard らしいので、できる限り頑張ってみたいと思います!
