SECCON 2014 横浜大会 Write-up(バイナリ後半組)

SECCON 2014 横浜大会に出た。バイナリ予選の後半組で予選を通過したが、翌日のクイズ大会で初戦敗退(´・ω・`)
バイナリ予選は前半組と後半組に分けられ、それぞれ5問の問題が1問ずつ出題され、最初に解いた人から抜けていく方式。1人が前に出てプロジェクタに画面を写す代わりに、問題がn分早く配られるという形式だった。最も短いnを提示した人が前に出るオークション形式。他の人が解いている様子を見るのはなかなか面白かった。こういう形式なので、問題は簡単。以下Write-up。

1問目


テニスゲーム。100点先取でゲーム終了でフラグが表示される。ゲームスピードが遅いので100点は待てない。タイトルからVMを解析する面倒な問題かと思ったけど、そんなことはなかった。デバッガで動かしていると、点数が入ったときにプログラムが落ちる。IsDebuggerPresentで検索すると、下記のようなコードがある。

00AB3A39  |> \FF15 94E0BC00 |CALL DWORD PTR DS:[<&KERNEL32.IsDebugge>; [IsDebuggerPresent
00AB3A3F  |.  85C0          |TEST EAX,EAX
00AB3A41  |.  0F85 A2000000 |JNZ ScriptGa.00AB3AE9
00AB3A47  |.  8B85 A4FEFFFF |MOV EAX,DWORD PTR SS:[EBP-15C]
00AB3A4D  |.  83F8 64       |CMP EAX,64
00AB3A50  |.  7D 25         |JGE SHORT ScriptGa.00AB3A77
00AB3A52  |.  83FF 64       |CMP EDI,64
00AB3A55  |.  7D 20         |JGE SHORT ScriptGa.00AB3A77
00AB3A57  |.  833D 9467F500>|CMP DWORD PTR DS:[F56794],0
00AB3A5E  |.^ 0F85 0CFEFFFF \JNZ ScriptGa.00AB3870

都合の良いことに、点数をチェックしているコードも見える。00AB3A41のJNZをNOPにして、00AB3A4Dと00AB3A52の定数を1にすると、点数が入った瞬間にゲームが終了する。その状態でウィンドウを閉じると、キーが表示される。

PlayGame

2問目

00201000 >/$ 55             PUSH EBP
00201001  |. 8BEC           MOV EBP,ESP
00201003  |. 57             PUSH EDI
00201004  |. 8B7D 08        MOV EDI,DWORD PTR SS:[EBP+8]
00201007  |. 85FF           TEST EDI,EDI
00201009  |. 7E 0D          JLE SHORT 00201018
0020100B  |. 83FF 02        CMP EDI,2
0020100E  |. 7F 08          JG SHORT 00201018
00201010  |. B8 01000000    MOV EAX,1
00201015  |. 5F             POP EDI
00201016  |. 5D             POP EBP
00201017  |. C3             RETN
00201018  |> 8D47 FE        LEA EAX,DWORD PTR DS:[EDI-2]
0020101B  |. 56             PUSH ESI
0020101C  |. 50             PUSH EAX
0020101D  |. E8 DEFFFFFF    CALL 00201000
00201022  |. 4F             DEC EDI
00201023  |. 57             PUSH EDI
00201024  |. 8BF0           MOV ESI,EAX
00201026  |. E8 D5FFFFFF    CALL 00201000
0020102B  |. 83C4 08        ADD ESP,8
0020102E  |. 03C6           ADD EAX,ESI
00201030  |. 5E             POP ESI
00201031  |. 5F             POP EDI
00201032  |. 5D             POP EBP
00201033  \. C3             RETN
00201034     CC             INT3
00201040 >/$ 6A 2E          PUSH 2E
00201042  |. E8 B9FFFFFF    CALL 00201000
00201047  |. 6A 2D          PUSH 2D
00201049  |. 8BC8           MOV ECX,EAX
0020104B  |. E8 B0FFFFFF    CALL 00201000
00201050  |. 03C8           ADD ECX,EAX
00201052  |. 51             PUSH ECX
00201053  |. 68 F4202000    PUSH OFFSET format = "Key: %lu"
00201058  |. FF15 A0202000  CALL DWORD PTR DS:[<&MSVCR100.printf>]
0020105E  |. 83C4 10        ADD ESP,10
00201061  |. 33C0           XOR EAX,EAX
00201063  \. C3             RETN

何が出力される?という問題。Immunity Debuggerで適当なプログラムを開いて、このプログラムを貼り付けて動かせば良い。エントリポイントが先頭ではなく00201040ということと、00201035-0020103Fが抜けているということに注意。

2971215073

3問目

これが解けて決勝に進めた。
暗号化するプログラムと暗号文が与えられて、暗号化前の文を答える問題。プログラムを読むと入力を並び替えるプログラムだった。逆算すれば良い。

A = "5wonKRJB7y pyTLD90ss VNF=2utrXPH4via QIA6xnseSKC8zro:UME-1titWOG\0"

D = [""]*256
T = [
0x3A, 0x32, 0x2A, 0x22, 0x1A, 0x12, 0x0A, 0x02, 0x3C, 0x34, 0x2C, 0x24, 0x1C, 0x14, 0x0C, 0x04,
0x3E, 0x36, 0x2E, 0x26, 0x1E, 0x16, 0x0E, 0x06, 0x40, 0x38, 0x30, 0x28, 0x20, 0x18, 0x10, 0x08,
0x39, 0x31, 0x29, 0x21, 0x19, 0x11, 0x09, 0x01, 0x3B, 0x33, 0x2B, 0x23, 0x1B, 0x13, 0x0B, 0x03,
0x3D, 0x35, 0x2D, 0x25, 0x1D, 0x15, 0x0D, 0x05, 0x3F, 0x37, 0x2F, 0x27, 0x1F, 0x17, 0x0F, 0x07,
]

for i in range(64):
    D[T[i]] = A[i]

print "".join(D)
ABCDEFGHIJKLMNOPQRSTUVWX Key: transposition rstuvwxyz012456789-=

ちなみに、隣の人は暗号文をさらに何度も暗号化して解いていた。なるほど。この操作は置換群だから、有限回の操作で元に戻るし、作問者がそこまで考えていなければ、その回数はそんなに多くなさそう。

transposition

4問目

与えられたプログラムを動かすと答えが出るけど、ものすごく時間がかかるので、なんとかしろ、という問題。プログラムを解析すると処理は以下の通り。

int c = 0;
int n = 1;
while (true)
{
    int t = 0;
    for (int i=1; i<=n; i++)
        if (n%i==0)
            t++;
    if (t==2)
        c++;
    n++;
    if (c>=10000000)
        break;
}
n--;
printf("Key: %d", n);

10000000番目の素数を求めるプログラム。

179424673

5問目

ボーナス問題らしい。前に出た人が解いてしまったので、他の参加者への問題の配付は無し。引数に2014を与えると、

Key: Enjoy_2014

と表示するプログラムだった。

Enjoy_2014