Reversing問題を3問取り組んで、1問のみ解けました。残りは、最後の最後が分からず断念したものが1問、時間切れが1問でした。
babycmp
フラグを引数に入力するため、argv[1]
に入力文字が含まれます。ファイルはx86-64のELFなので、sizeof(char*) = 8
であり、呼び出し規約より第2引数はRSIレジスタです。
main関数で[rsi+8]
と(mov rbp,rsi
より同等な)[rbp+8]
に注目すると、1文字ずつWelcome to SECCON 2022
とXORしていました。main+DB
以降、XORした文字列と変数の値とを比較していたため、それをXORしたところフラグが出ました。
1a = [0x04, 0x20, 0x2F, 0x20, 0x20, 0x23, 0x1E, 0x59, 0x44, 0x1A,
2 0x7F, 0x35, 0x75, 0x36, 0x2D, 0x2B, 0x11, 0x17, 0x5A, 0x03,
3 0x6D, 0x50, 0x36, 0x07, 0x15, 0x3C, 0x09, 0x01, 0x04, 0x47,
4 0x2B, 0x36, 0x41, 0x0A, 0x38]
5
6out = ""
7for n, s in enumerate(a):
8 out += chr(ord(("Welcome to SECCON 2022"*2)[n]) ^ s)
9
10print(out)
SECCON{y0u_f0und_7h3_baby_flag_YaY}
eguite
テキストボックスにFlagを入力してCHECKボタンをクリックすると正解か否かを表示してくれるファイルが2つ渡されます。(片方はPE、もう片方はELFで動きは同じとのことです)
IDAで見ると大量のマングル名が出ますが、GUIでマングル付きということはデマングル後が「onClick」か「on_click」のサブルーチンがあるだろうと推測して探してみると、eguite::Crackme::onclick
を発見しました。
中身を見てみると、はじめにSECCON{
と}
、そして括弧の中身の13文字目、20文字目、27文字目に-
が含まれているかをチェックしていました。
その後ろに、from_str_radix
で文字列を16進数の数値として扱う箇所があり、その結果をlea
命令とxor
命令で演算した結果がハードコードされた数値と一致しているかを確認していました。
-
で区切られる数値を左からa, b, c, d(すなわちSECCON{a-b-c-d}
)とした場合、式は次のとおりです。
b + a = 0x8B228BF35F6A
c + b = 0xE78241
d + c = 0xFA4C1A9F
a + d = 0x8B238557F7C8
c XOR b XOR d = 0xF9686F4D
これが解けずに諦めました。
eldercmp
__detect_debugger
において、sigaction
で0x0B
(SIGSEGV)のシグナルをtrampoline
というサブルーチンに変更しています。
arch_prctl
でARCH_SET_CPUID
を無効にするため、main
にあるcpuid
命令でSIGSEGVが発生し、trampoline
に移ります。
その後、heart
というサブルーチンに移り、hlt
などの命令でSIGSEGVを呼んでtrampoline
に移る作業を繰り返しています。
この時に例外を発生した命令を参照しているようで、ソフトウェアブレークポイントによる0xcc
が含まれているとexit
するようです。
そうでない場合、trampoline
はSIGSEGVを発生した次の命令にジャンプするため、結局のところheart
の中身を見ればエンコード方法がわかります。
なお、XORされていて分かりにくいですが、heart+9C8
がCorrect!
をputsする基本ブロックです。
エンコード方法を基に、エンコード後がheart+91B
のハードコードされた値になるような入力を求めれば良さそうです。
が、デコードする方法を考えているうちに時間切れでした。