読者です 読者をやめる 読者になる 読者になる

Catt13ya's Diary

Welcome, Thank you for visiting this page.

CTF for ビギナーズ 2016 東京 write up

CTF for ビギナーズ 東京 に参加してきた。応募は400人強で参加者は100人近くいたらしい。CTF大人気。
講義は講師の方々がわかりやすく説明してくれて、特にForensic(Network)はMactimeなどの知らなかった知識が増えたので大変良かった。
f:id:nk06c-c-gw:20161022224205j:plain

CTF

演習はおよそ1時間半で行われた。結果は1510点で3位だった。
f:id:nk06c-c-gw:20161022224436p:plain

正直全問は覚えてないので、binaryをメインにいくつかwriteupを書くことにした。

bin100(Plain)

$ file bin100 
bin100: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=9e1c3d61ee4092e980ceda5cbfc414accd5669cd, stripped

32bitのELF ファイルで実行すると Enjoy CTF! と出力される。stringsコマンドを使ってみる。

$ strings bin100 | grep ctf4b
ctf4b{57r1n65_c0mm4nd_f1nd_fl46}

FLAGが確認できた。grepコマンドは指定したキーワードを検索するコマンドで、出力結果から自分がほしい情報だけを抜き出す際によく使われる。CTFのみならずLinuxではlogの確認などでもお世話になるので覚えておいて損はないと思う。

bin200-1(Calc)

file bin200_1 
bin200_1: ELF 32-bit LSB  executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=8d898459500ece4b577adc3d97e5c881543c648b, not stripped

アセンブラのテキストファイルが渡されていて、計算値が直接FALGとなる問題だった(たぶん)。実行しても何も表示されない。

まず、プログラムのアセンブラを得るにはobjdumpコマンドを用いる。

$ objdump -M intel -d bin200_1 

...
080483ed <calc>:
 80483ed:   55                      push   ebp
 80483ee:   89 e5                   mov    ebp,esp
 80483f0:   83 ec 10                sub    esp,0x10
 80483f3:   8b 45 0c                mov    eax,DWORD PTR [ebp+0xc]
 80483f6:   8b 55 08                mov    edx,DWORD PTR [ebp+0x8]
 80483f9:   01 d0                   add    eax,edx
 80483fb:   89 45 fc                mov    DWORD PTR [ebp-0x4],eax
 80483fe:   8b 45 10                mov    eax,DWORD PTR [ebp+0x10]
 8048401:   0f af 45 0c             imul   eax,DWORD PTR [ebp+0xc]
 8048405:   89 45 08                mov    DWORD PTR [ebp+0x8],eax
 8048408:   8b 45 08                mov    eax,DWORD PTR [ebp+0x8]
 804840b:   99                      cdq    
 804840c:   f7 7d fc                idiv   DWORD PTR [ebp-0x4]
 804840f:   89 45 fc                mov    DWORD PTR [ebp-0x4],eax
 8048412:   8b 45 10                mov    eax,DWORD PTR [ebp+0x10]
 8048415:   8b 55 08                mov    edx,DWORD PTR [ebp+0x8]
 8048418:   01 d0                   add    eax,edx
 804841a:   89 45 0c                mov    DWORD PTR [ebp+0xc],eax
 804841d:   8b 45 0c                mov    eax,DWORD PTR [ebp+0xc]
 8048420:   01 45 fc                add    DWORD PTR [ebp-0x4],eax
 8048423:   8b 45 fc                mov    eax,DWORD PTR [ebp-0x4]
 8048426:   c9                      leave  
 8048427:   c3                      ret   
...

-M でIntel記法を指定している。これを静的解析で順に追っていってもいいのだが、時間がかかるので動的解析を行う。gdbはELF binaryのデバッガでCTFのバイナリ解析では必須。今回配布されていたVMにもインストールされていたので競技中はこれを使った。(実際はpedaも組み込まれていて純粋なgdbではないが。)

$ gdb -q bin200_1 
Reading symbols from bin200_1...(no debugging symbols found)...done.
gdb-peda$

-q はライセンス関係を表示させないための引数。これで解析を進める。gdbの使い方についてはこのページがとてもわかりやすい。

gdb-peda$ start

 [----------------------------------registers-----------------------------------]
EAX: 0x1 
EBX: 0xf7fca000 --> 0x1aada8 
ECX: 0x5008119d 
EDX: 0xffffdb04 --> 0xf7fca000 --> 0x1aada8 
ESI: 0x0 
EDI: 0x0 
EBP: 0xffffdad8 --> 0x0 
ESP: 0xffffdabc --> 0xf7e524ad (<__cxa_atexit+29>:   test   eax,eax)
EIP: 0x804842e (<main+6>:   nop)
EFLAGS: 0x292 (carry parity ADJUST zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x8048428 <main>:  push   ebp
   0x8048429 <main+1>:   mov    ebp,esp
   0x804842b <main+3>:   sub    esp,0x1c
=> 0x804842e <main+6>:  nop
   0x804842f <main+7>:   nop
   0x8048430 <main+8>:   mov    DWORD PTR [ebp-0xc],0x14
   0x8048437 <main+15>:  mov    DWORD PTR [ebp-0x8],0x8
   0x804843e <main+22>:  mov    DWORD PTR [ebp-0x4],0xfffffffd
[------------------------------------stack-------------------------------------]
0000| 0xffffdabc --> 0xf7e524ad (<__cxa_atexit+29>: test   eax,eax)
0004| 0xffffdac0 --> 0xf7fca3c4 --> 0xf7fcb1e0 --> 0x0 
0008| 0xffffdac4 --> 0xf7ffd000 --> 0x20f30 
0012| 0xffffdac8 --> 0x804848b (<__libc_csu_init+11>:   add    ebx,0x1b75)
0016| 0xffffdacc --> 0xf7fca000 --> 0x1aada8 
0020| 0xffffdad0 --> 0x8048480 (<__libc_csu_init>:   push   ebp)
0024| 0xffffdad4 --> 0x0 
0028| 0xffffdad8 --> 0x0 
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value

Temporary breakpoint 1, 0x0804842e in main ()
gdb-peda$ 

startと打つとこんな感じで表示されるはず。上の方にあるregistersは今日の講義でも解説があった各種レジスタが保持する値を表示している。codeの部分では現在実行中の命令とその前後の命令が表示される。stackは文字通りstackの中に格納されている情報を示す。
命令を進めるにはni、関数の中に入るのであればsiを用いる。

 [----------------------------------registers-----------------------------------]
EAX: 0x14 
EBX: 0xf7fca000 --> 0x1aada8 
ECX: 0x5008119d 
EDX: 0xffffdb04 --> 0xf7fca000 --> 0x1aada8 
ESI: 0x0 
EDI: 0x0 
EBP: 0xffffdad8 --> 0x0 
ESP: 0xffffdabc --> 0x14 
EIP: 0x8048459 (<main+49>:  call   0x80483ed <calc>)
EFLAGS: 0x292 (carry parity ADJUST zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x804844f <main+39>:  mov    DWORD PTR [esp+0x4],eax
   0x8048453 <main+43>:  mov    eax,DWORD PTR [ebp-0xc]
   0x8048456 <main+46>:  mov    DWORD PTR [esp],eax
=> 0x8048459 <main+49>: call   0x80483ed <calc>
   0x804845e <main+54>:  mov    ds:0x804a020,eax
   0x8048463 <main+59>:  mov    eax,ds:0x804a020
   0x8048468 <main+64>:  nop
   0x8048469 <main+65>:  nop
Guessed arguments:
arg[0]: 0x14 
arg[1]: 0x8 
arg[2]: 0xfffffffd 
[------------------------------------stack-------------------------------------]
0000| 0xffffdabc --> 0x14 
0004| 0xffffdac0 --> 0x8 
0008| 0xffffdac4 --> 0xfffffffd 
0012| 0xffffdac8 --> 0x804848b (<__libc_csu_init+11>:   add    ebx,0x1b75)
0016| 0xffffdacc --> 0x14 
0020| 0xffffdad0 --> 0x8 
0024| 0xffffdad4 --> 0xfffffffd 
0028| 0xffffdad8 --> 0x0 
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x08048459 in main ()
gdb-peda$ 

これはcalc関数が呼ばれる直前の状態。C言語風に書くと calc(0x14, 0x8, 0xfffffffd) といった感じだろうか。この後calc関数によって計算が行われ、計算結果がeaxレジスタに格納される。

 [----------------------------------registers-----------------------------------]
EAX: 0xffffffe5 
EBX: 0xf7fca000 --> 0x1aada8 
ECX: 0x5008119d 
EDX: 0xffffffe8 
ESI: 0x0 
EDI: 0x0 
EBP: 0xffffdad8 --> 0x0 
ESP: 0xffffdabc --> 0xffffffe8 
EIP: 0x804845e (<main+54>:  mov    ds:0x804a020,eax)
EFLAGS: 0x282 (carry parity adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x8048453 <main+43>:  mov    eax,DWORD PTR [ebp-0xc]
   0x8048456 <main+46>:  mov    DWORD PTR [esp],eax
   0x8048459 <main+49>:  call   0x80483ed <calc>
=> 0x804845e <main+54>: mov    ds:0x804a020,eax
   0x8048463 <main+59>:  mov    eax,ds:0x804a020
   0x8048468 <main+64>:  nop
   0x8048469 <main+65>:  nop
   0x804846a <main+66>:  mov    eax,0x0

eaxレジスタ0xffffffe5が格納されている。今回はこれがそのままFLAGとなるので、10進数に変換してからサーバに送信した。

ctf4b{-27}

負数の変換は対象値から1引いたもののbit反転をとれば良い。pythonで書くとこんな感じ。

val = (0xffffffe5 - 1) ^ 0xffffffff

※追記
純粋に静的解析のみで解くのであれば、main関数の

8048430: c7 45 f4 14 00 00 00    mov    DWORD PTR [ebp-0xc],0x14
 8048437:   c7 45 f8 08 00 00 00    mov    DWORD PTR [ebp-0x8],0x8
 804843e:   c7 45 fc fd ff ff ff    mov    DWORD PTR [ebp-0x4],0xfffffffd
 8048445:   8b 45 fc                mov    eax,DWORD PTR [ebp-0x4]
 8048448:   89 44 24 08             mov    DWORD PTR [esp+0x8],eax
 804844c:   8b 45 f8                mov    eax,DWORD PTR [ebp-0x8]
 804844f:   89 44 24 04             mov    DWORD PTR [esp+0x4],eax
 8048453:   8b 45 f4                mov    eax,DWORD PTR [ebp-0xc]
 8048456:   89 04 24                mov    DWORD PTR [esp],eax
 8048459:   e8 8f ff ff ff          call   80483ed <calc>

の部分とcalc関数の

080483ed <calc>:
 80483ed:   55                      push   ebp
 80483ee:   89 e5                   mov    ebp,esp
 80483f0:   83 ec 10                sub    esp,0x10
 80483f3:   8b 45 0c                mov    eax,DWORD PTR [ebp+0xc]
 80483f6:   8b 55 08                mov    edx,DWORD PTR [ebp+0x8]
 80483f9:   01 d0                   add    eax,edx
 80483fb:   89 45 fc                mov    DWORD PTR [ebp-0x4],eax
 80483fe:   8b 45 10                mov    eax,DWORD PTR [ebp+0x10]
 8048401:   0f af 45 0c             imul   eax,DWORD PTR [ebp+0xc]
 8048405:   89 45 08                mov    DWORD PTR [ebp+0x8],eax
 8048408:   8b 45 08                mov    eax,DWORD PTR [ebp+0x8]
 804840b:   99                      cdq    
 804840c:   f7 7d fc                idiv   DWORD PTR [ebp-0x4]
 804840f:   89 45 fc                mov    DWORD PTR [ebp-0x4],eax
 8048412:   8b 45 10                mov    eax,DWORD PTR [ebp+0x10]
 8048415:   8b 55 08                mov    edx,DWORD PTR [ebp+0x8]
 8048418:   01 d0                   add    eax,edx
 804841a:   89 45 0c                mov    DWORD PTR [ebp+0xc],eax
 804841d:   8b 45 0c                mov    eax,DWORD PTR [ebp+0xc]
 8048420:   01 45 fc                add    DWORD PTR [ebp-0x4],eax
 8048423:   8b 45 fc                mov    eax,DWORD PTR [ebp-0x4]
 8048426:   c9                      leave  
 8048427:   c3                      ret  

の部分から、引数をX, Yなどで置き換えて計算することになりそう。

bin200-2(Forget)

競技中時間がなかったのでかなりデタラメな解き方をしてしまった問題。

$ file bin200_2 
bin200_2: ELF 32-bit LSB  executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=94bcd8c6433917ae9b1bc5b202ae166a09e9b8cd, stripped

strippedなバイナリは解析しづらいのでfile打ったときは苦笑した。実行すると

./bin200_2 
Find FLAG!

ctf4b{`Sorry. I forgot to display the FLAG. This message is not FLAG.`}

と出力される。ひとまずgdbで追ってみる。

[-------------------------------------code-------------------------------------]
   0x8048504:   sub    esp,0x10
   0x8048507:   mov    DWORD PTR [esp],0x80485f7
   0x804850e:   call   0x8048340 <puts@plt>
=> 0x8048513:  call   0x80484e5
   0x8048518:   mov    eax,0x0
   0x804851d:   leave  
   0x804851e:   ret    
   0x804851f:   nop
No argument

putsを呼んだ後、関数が呼ばれている。どんな動作をするかはまだ不明。siで関数の中に入る。

[-------------------------------------code-------------------------------------]
   0x80484e8:   sub    esp,0x18
   0x80484eb:   mov    DWORD PTR [esp],0x80485f1
   0x80484f2:   call   0x8048330 <printf@plt>
=> 0x80484f7:  call   0x80484cc
   0x80484fc:   leave  
   0x80484fd:   ret    
   0x80484fe:   push   ebp
   0x80484ff:   mov    ebp,esp
No argument

printfで"ctf4b"が出力された後、また別の関数が呼ばれている。

[-------------------------------------code-------------------------------------]
   0x80484e8:   sub    esp,0x18
   0x80484eb:   mov    DWORD PTR [esp],0x80485f1
   0x80484f2:   call   0x8048330 <printf@plt>
=> 0x80484f7:  call   0x80484cc
   0x80484fc:   leave  
=> 0x80484d9: call   0x8048370 <putchar@plt>
   0x80484de:   call   0x80484b3

ctf4b{の出力後に入る関数(0x0804847d 周辺)に注目する。

gdb-peda$ context code 30
[-------------------------------------code-------------------------------------]

   ...(略)...

=> 0x804847d:    push   ebp
   0x804847e:   mov    ebp,esp
   0x8048480:   sub    esp,0x28
   0x8048483:   mov    Y,0x0
   0x804848a:   jmp    0x804849d ; -----------------------
                                                          |
   0x804848c:   mov    eax,Y                              |
   0x804848f:   mov    eax,DWORD PTR [eax*4+0x804a040]    |
   0x8048496:   mov    X,eax                              |
   0x8048499:   add    Y,0x1                              |
   0x804849d:   mov    eax,Y ;  <-------------------------
   0x80484a0:   cmp    eax,0x19
   0x80484a3:   jbe    0x804848c

見やすいように少し整形した。stackは多分こんな感じ。

stack
Y = (ebp - 0x10)
X = (ebp - 0xc)
(ebp - 0x8)
(ebp - 0x4)
(ebp)

これを見ると、講義でやったfor文の形に似ていることがわかる(講義資料rev, 実行制御:ループ処理参照)。Yは0で初期化され、0x0804849dで$eaxに代入される。その後0x19と比較され、0x0804848cに飛ばされる。次の処理は

eax = 0; // Y == 0だから
eax = address[0 * 4 + 0x804a040]にある値を代入;
x = eax;
Y += 1;
eax = Y; // このときY == 1
eaxと0x19を比較...

以降、Yがインクリメントされているのでaddress[Y * 4 + 0x804a040]にある値を順番にXに代入していくことになる。
となれば気になるのは0x0804a040になにがあるかである。

gdb-peda$ x/100s 0x804a040
0x804a040:  "1"
0x804a042:  ""
0x804a043:  ""
0x804a044:  "n"
0x804a046:  ""
0x804a047:  ""
0x804a048:  "7"
0x804a04a:  ""
0x804a04b:  ""
0x804a04c:  "_"
0x804a04e:  ""
0x804a04f:  ""
0x804a050:  "v"
0x804a052:  ""
0x804a053:  ""
0x804a054:  "4"
0x804a056:  ""
0x804a057:  ""
0x804a058:  "l"
0x804a05a:  ""
0x804a05b:  ""
0x804a05c:  "u"
0x804a05e:  ""
0x804a05f:  ""
0x804a060:  "3"
0x804a062:  ""
0x804a063:  ""
0x804a064:  "_"
0x804a066:  ""
0x804a067:  ""
0x804a068:  "3"
0x804a06a:  ""
0x804a06b:  ""
0x804a06c:  "q"
0x804a06e:  ""
0x804a06f:  ""
0x804a070:  "u"
0x804a072:  ""
0x804a073:  ""
0x804a074:  "4"
0x804a076:  ""
0x804a077:  ""
0x804a078:  "l"
0x804a07a:  ""
0x804a07b:  ""
0x804a07c:  "_"
0x804a07e:  ""
0x804a07f:  ""
0x804a080:  "c"
0x804a082:  ""
0x804a083:  ""
0x804a084:  "h"
0x804a086:  ""
0x804a087:  ""
0x804a088:  "4"
0x804a08a:  ""
0x804a08b:  ""
0x804a08c:  "r"
0x804a08e:  ""
0x804a08f:  ""
0x804a090:  "4"
0x804a092:  ""
0x804a093:  ""
0x804a094:  "c"
0x804a096:  ""
0x804a097:  ""
0x804a098:  "7"
0x804a09a:  ""
0x804a09b:  ""
0x804a09c:  "3"
0x804a09e:  ""
0x804a09f:  ""
0x804a0a0:  "r"
0x804a0a2:  ""
0x804a0a3:  ""
0x804a0a4:  ""
0x804a0a5:  ""
0x804a0a6:  ""
0x804a0a7:  ""
0x804a0a8:  ""
0x804a0a9:  ""
0x804a0aa:  ""
0x804a0ab:  ""
0x804a0ac:  ""
0x804a0ad:  ""
0x804a0ae:  ""
0x804a0af:  ""
0x804a0b0:  ""
0x804a0b1:  ""
0x804a0b2:  ""
0x804a0b3:  ""
0x804a0b4:  ""
0x804a0b5:  ""
0x804a0b6:  ""
0x804a0b7:  ""
0x804a0b8:  ""
0x804a0b9:  ""
0x804a0ba:  ""
0x804a0bb:  ""
0x804a0bc:  ""

表示された文字をつなげると、 1n7_valu3_3qual_ch4r4c73rとなる。これがFLAG。というわけで ctf4b{1n7_valu3_3qual_ch4r4c73r}

帰宅後、IDAを使うともっとスムーズにたどり着けることがわかった。
f:id:nk06c-c-gw:20161022223658p:plain

ちなみに、今回のような文字列の中に数字が紛れ込むようなものはLeetというらしい。CTFではよく見かける。

bin300(Crypt)

$ ./bin300 
Please input flag... hoge
Wrong flag...

32bitのelf binaryで入力を受け付ける。入力値が正しくないと上記のように出力されるようだ。
以下アセンブラの抜粋(整形済み):

0804851d <main>:

    ...
 804853e:   8d 44 24 1c             lea    eax,[esp+0x1c]
 8048542:   89 44 24 04             mov    DWORD PTR [esp+0x4],eax
 8048546:   c7 04 24 c6 86 04 08    mov    DWORD PTR [esp],0x80486c6
 804854d:   e8 be fe ff ff          call   8048410 <__isoc99_scanf@plt>

 8048552:   c7 44 24 08 42 00 00    mov    DWORD PTR [esp+0x8],0x42
 804855a:   c7 44 24 04 1e 00 00    mov    DWORD PTR [esp+0x4],0x1e
 8048562:   c7 04 24 30 a0 04 08    mov    DWORD PTR [esp],0x804a030
 8048569:   e8 6a 00 00 00          call   80485d8 <decrypt>

 804856e:   c7 44 24 08 b7 ff ff    mov    DWORD PTR [esp+0x8],0xffffffb7
 8048576:   c7 44 24 04 1e 00 00    mov    DWORD PTR [esp+0x4],0x1e
 804857e:   c7 04 24 30 a0 04 08    mov    DWORD PTR [esp],0x804a030
 8048585:   e8 4e 00 00 00          call   80485d8 <decrypt>
 
804858a:    c7 44 24 04 30 a0 04    mov    DWORD PTR [esp+0x4],0x804a030
 8048592:   8d 44 24 1c             lea    eax,[esp+0x1c]
 8048596:   89 04 24                mov    DWORD PTR [esp],eax
 8048599:   e8 12 fe ff ff          call   80483b0 <strcmp@plt>
 804859e:   85 c0                   test   eax,eax
 80485a0:   75 16                   jne    80485b8 <main+0x9b>

80485a2:    8d 44 24 1c             lea    eax,[esp+0x1c]
 80485a6:   89 44 24 04             mov    DWORD PTR [esp+0x4],eax
 80485aa:   c7 04 24 cb 86 04 08    mov    DWORD PTR [esp],0x80486cb
 80485b1:   e8 0a fe ff ff          call   80483c0 <printf@plt>
 80485b6:   eb 0c                   jmp    80485c4 <main+0xa7>
 80485b8:   c7 04 24 e3 86 04 08    mov    DWORD PTR [esp],0x80486e3
 80485bf:   e8 1c fe ff ff          call   80483e0 <puts@plt>

    ...

080485d8 <decrypt>:
 80485d8:   55                      push   ebp
 80485d9:   89 e5                   mov    ebp,esp
 80485db:   83 ec 14                sub    esp,0x14
 80485de:   8b 45 10                mov    eax,arg[2]
 80485e1:   88 45 ec                mov    Y,al
 80485e4:   c7 45 fc 00 00 00 00    mov    X,0x0
 80485eb:   eb 1c                   jmp    8048609 <decrypt+0x31>
 
 80485ed:   8b 55 fc                mov    edx,X
 80485f0:   8b 45 08                mov    eax,flag_address
 80485f3:   01 c2                   add    edx,eax
 
 80485f5:   8b 4d fc                mov    ecx,X
 80485f8:   8b 45 08                mov    eax,flag_adress
 80485fb:   01 c8                   add    eax,ecx

 80485fd:   0f b6 00                movzx  eax,BYTE PTR [eax]
 8048600:   32 45 ec                xor    al,Y

 8048603:   88 02                   mov    BYTE PTR [edx],al
 8048605:   83 45 fc 01             add    X,0x1

 8048609:   8b 45 fc                mov    eax,X
 804860c:   3b 45 0c                cmp    eax,flag_length
 804860f:   7c dc                   jl     80485ed <decrypt+0x15>
 
 8048611:   c9                      leave  
 8048612:   c3                      ret    

main関数内の0x0804854dでscanfにより標準入力を受付ており、後にstrcmpしている。scanf後、decrypt関数が以下のように呼ばれている。

decrypt(0x0804a030, 0x1e, 0x42);
decrypt(0x0804a030, 0x1e, 0xffffffb7);

0x0804a030について調べる。

readelf -S bin300
There are 30 section headers, starting at offset 0x1180:

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .interp           PROGBITS        08048154 000154 000013 00   A  0   0  1
  [ 2] .note.ABI-tag     NOTE            08048168 000168 000020 00   A  0   0  4
  [ 3] .note.gnu.build-i NOTE            08048188 000188 000024 00   A  0   0  4
  [ 4] .gnu.hash         GNU_HASH        080481ac 0001ac 000020 04   A  5   0  4
  [ 5] .dynsym           DYNSYM          080481cc 0001cc 000090 10   A  6   1  4
  [ 6] .dynstr           STRTAB          0804825c 00025c 00008c 00   A  0   0  1
  [ 7] .gnu.version      VERSYM          080482e8 0002e8 000012 02   A  5   0  2
  [ 8] .gnu.version_r    VERNEED         080482fc 0002fc 000040 00   A  6   1  4
  [ 9] .rel.dyn          REL             0804833c 00033c 000008 08   A  5   0  4
  [10] .rel.plt          REL             08048344 000344 000038 08   A  5  12  4
  [11] .init             PROGBITS        0804837c 00037c 000023 00  AX  0   0  4
  [12] .plt              PROGBITS        080483a0 0003a0 000080 04  AX  0   0 16
  [13] .text             PROGBITS        08048420 000420 000272 00  AX  0   0 16
  [14] .fini             PROGBITS        08048694 000694 000014 00  AX  0   0  4
  [15] .rodata           PROGBITS        080486a8 0006a8 000049 00   A  0   0  4
  [16] .eh_frame_hdr     PROGBITS        080486f4 0006f4 000034 00   A  0   0  4
  [17] .eh_frame         PROGBITS        08048728 000728 0000d0 00   A  0   0  4
  [18] .init_array       INIT_ARRAY      08049f08 000f08 000004 00  WA  0   0  4
  [19] .fini_array       FINI_ARRAY      08049f0c 000f0c 000004 00  WA  0   0  4
  [20] .jcr              PROGBITS        08049f10 000f10 000004 00  WA  0   0  4
  [21] .dynamic          DYNAMIC         08049f14 000f14 0000e8 08  WA  6   0  4
  [22] .got              PROGBITS        08049ffc 000ffc 000004 04  WA  0   0  4
  [23] .got.plt          PROGBITS        0804a000 001000 000028 04  WA  0   0  4
  [24] .data             PROGBITS        0804a028 001028 000026 00  WA  0   0  4
  [25] .bss              NOBITS          0804a04e 00104e 000002 00  WA  0   0  1
  [26] .comment          PROGBITS        00000000 00104e 00002b 01  MS  0   0  1
  [27] .shstrtab         STRTAB          00000000 001079 000106 00      0   0  1
  [28] .symtab           SYMTAB          00000000 001630 000490 10     29  45  4
  [29] .strtab           STRTAB          00000000 001ac0 0002b8 00      0   0  1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
  L (link order), O (extra OS processing required), G (group), T (TLS),
  C (compressed), x (unknown), o (OS specific), E (exclude),
  p (processor specific)

上記から、0x0804a030は.dataセクションにあることがわかる。.dataセクションはプログラム実行時に、初期値を与えられた変数が格納される領域。objdumpで実際に.dataを見てみる。

$ objdump -M intel -j .data -d bin300

bin300:     file format elf32-i386


Disassembly of section .data:

0804a028 <__data_start>:
 804a028:   00 00                   add    BYTE PTR [eax],al
    ...

0804a02c <__dso_handle>:
 804a02c:   00 00 00 00                                         ....

0804a030 <flag>:
 804a030:   96 81 93 c1 97 8e 8d 9a 87 aa 8d 9a 87 aa 86 9c     ................
 804a040:   98 85 99 90 aa 96 87 8c 85 81 d4 d4 88 f5           ..............

これにより、0x0804a030はflagが格納されたアドレスであり、flagの長さが30であることがわかった。更に、decrypt関数について調べてみる。decrypt内の変数と引数は次のように書き換えた。

変更前 変更後
$ebp-0x14 Y local val
$ebp-0x4 X local val
$ebp+0x8 flag adress arg[0]
$ebp+0xc flag length arg[1]
$ebp+0x10 arg[2] arg[2]

decrypt関数の処理をプログラムになおすと多分こんな感じ。xorでは下位8bitのみが影響していることに注意。

Y = arg[2];
for (X = 0; X < 0x1e; X++){
    edx = flag_adress + X;
    eax = flag_adress + X;  
    eax = (eax & 0xff) ^ Y;
    [edx] = eax;
}

以上のことから、.dataセクションのflagから、それぞれxorをとりflagを逆算する。

# python

flag = [
    0x96, 0x81, 0x93, 0xc1, 0x97,
    0x8e, 0x8d, 0x9a, 0x87, 0xaa, 
    0x8d, 0x9a, 0x87, 0xaa, 0x86,
    0x9c, 0x98, 0x85, 0x99, 0x90,
    0xaa, 0x96, 0x87, 0x8c, 0x85,
    0x81, 0xd4, 0xd4, 0x88, 0xf5
]
num = 0x42
num2 = 0xffffffb7

def decrypt(num):
    for i in range(len(flag)):
        flag[i] ^= (num & 0xff)

decrypt(num)
decrypt(num2)

for i in range(len(flag)):
    print(chr(flag[i]), end="")
print()

実行結果:

ctf4b{xor_xor_simple_crypt!!}

※おそらく運営側が意図した解法はこのような手順だと思われる。しかし、実はgdbで一通り動かすだけでflagが確認できてしまう。
f:id:nk06c-c-gw:20161023150437p:plain


とりあえずここまで。ミスがあったらごめんなさい、なにかあればコメント欄等にお願いします。