日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > linux >内容正文

linux

linux堆上的内存可执行吗,pwn的艺术浅谈(二):linux堆相关

發(fā)布時(shí)間:2023/11/30 linux 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 linux堆上的内存可执行吗,pwn的艺术浅谈(二):linux堆相关 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

這是linux pwn系列的第二篇文章,前面一篇文章我們已經(jīng)介紹了棧的基本結(jié)構(gòu)和棧溢出的利用方式,堆漏洞的成因和利用方法與棧比起來更加復(fù)雜,為此,我們這篇文章以shellphish的how2heap為例,主要介紹linux堆的相關(guān)數(shù)據(jù)結(jié)構(gòu)和堆漏洞的利用方式,供大家參考。

0.前置知識(shí)

0.0 編譯+patch方法

how2heap源碼地址https://github.com/shellphish/how2heap,為了方便調(diào)試編譯時(shí)使用gcc -g -fno-pie xx.c –o xx。這里先介紹一種linux下patch文件加載指定版本libc的方法,patchelf –set-interpreter 設(shè)置elf啟動(dòng)時(shí)使用指定ld.so(elf文件在啟動(dòng)時(shí)ld.so查找并加載程序所需的動(dòng)態(tài)鏈接對(duì)象,加載完畢后啟動(dòng)程序,不同libc版本需要不同的加載器,不同版本libc和加載器下載地址https://github.com/5N1p3R0010/libc-ld.so),然后patchelf –set-rpath :/設(shè)置elf啟動(dòng)時(shí)加載指定libc。編譯+patch示例

0.1 linux堆管理簡圖及源碼地址

0.2 linux堆的數(shù)據(jù)結(jié)構(gòu)

0.2.1堆塊數(shù)據(jù)結(jié)構(gòu)

首先介紹下linux下堆的基本數(shù)據(jù)結(jié)構(gòu)。

各字段含義如下:

0.mchunk_prev_size。當(dāng)當(dāng)前堆物理相鄰的前一個(gè)堆為空閑狀態(tài)時(shí)mchunk_prev_size記錄前一個(gè)空閑堆的大小,當(dāng)當(dāng)前堆物理相鄰的前一個(gè)堆為占用狀態(tài)時(shí)mchunk_prev_size可用于存儲(chǔ)前一個(gè)堆的數(shù)據(jù)。

1.mchunk_size,記錄當(dāng)前堆包含堆頭的大小,堆的大小在申請(qǐng)時(shí)會(huì)進(jìn)行對(duì)齊,對(duì)齊后堆的大小為2*size_t的整數(shù)倍,size_t為機(jī)器字長。mchunk_size的低三比特位對(duì)堆的大小沒有影響,ptmalloc用它來記錄當(dāng)前堆的狀態(tài),三個(gè)比特位從高到低依次:

NON_MAIN_ARENA,記錄當(dāng)前堆是否不屬于主線程,1 表示不屬于,0 表示屬于。

IS_MAPPED,記錄當(dāng)前堆是否是由 mmap 分配的。

PREV_INUSE,記錄前一個(gè)堆是否被分配。

2.fd、bk,堆處于分配狀態(tài)時(shí),堆結(jié)構(gòu)體偏移fd的位置存儲(chǔ)數(shù)據(jù);堆處于空閑狀態(tài)時(shí),fd、bk分別記錄物理相鄰的前一空閑堆、物理相鄰的后一空閑堆,即用于對(duì)應(yīng)空閑鏈表的管理

3.fd_nextsize、bk_nextsize,large chunk處于空閑狀態(tài)時(shí)使用,分別用于記錄前一個(gè)與當(dāng)前堆大小不同的第一個(gè)空閑堆、后一個(gè)與當(dāng)前堆大小不同的第一個(gè)空閑堆

0.2.2 空閑鏈表

理解ptmalloc堆漏洞利用的另一個(gè)比較重要的結(jié)構(gòu)體是bin,為了節(jié)省內(nèi)存分配開銷,用戶釋放掉的內(nèi)存并不會(huì)馬上返還給系統(tǒng),而是保存在相應(yīng)的空閑鏈表中以便后續(xù)分配使用。Ptmalloc使用的空閑鏈表bin有四種,fastbin、samllbin、largebin、unsortedbin ,一個(gè)好的內(nèi)存分配器應(yīng)該是內(nèi)存碎片少、且能在較低算法復(fù)雜度和較少內(nèi)存分配次數(shù)的情況下滿足用戶使用內(nèi)存(申請(qǐng)和釋放)的需求,四種bin的實(shí)現(xiàn)就體現(xiàn)了這種思想。

為了減少內(nèi)存碎片,ptmalloc在釋放當(dāng)前堆cur_chunk時(shí)會(huì)檢測(cè)cur_chunk的prev_inuse位(標(biāo)識(shí)物理相鄰前一個(gè)堆(物理低地址)是否處于空閑狀態(tài))和cur_chunk的物理相鄰下一個(gè)堆是否是top_chunk、物理相鄰下一個(gè)堆的prev_inuse位。若cur_chunk的prev_inuse位為0則合并后向堆并將后向堆的地址作為新的合并后的堆的起始地址;若cur_chunk的物理相鄰下一個(gè)堆的prev_inuse位為0則進(jìn)行前向合并并將cur_chunk的地址作為新的合并后的堆的起始地址。若待釋放的cur_chunk的物理相鄰下一個(gè)堆為top_chunk則將cur_chunk和top_chunk合并,并將cur_chunk的地址作為新的top_chunk起點(diǎn)。

Ptmalloc堆的一些參數(shù)

0) fastbin

fastbin是保存一些較小堆(32位系統(tǒng)默認(rèn)不超過64字節(jié),64位系統(tǒng)默認(rèn)不超過128字節(jié))的單鏈表結(jié)構(gòu)。由于fastbin中相同index鏈接的都是相同大小的堆,ptmalloc認(rèn)為不同位置的相同大小的堆沒有區(qū)別,因此fastbin使用lifo的方法實(shí)現(xiàn),即新釋放的堆被鏈接到fastbin的頭部,從fastbin中申請(qǐng)堆也是從頭部取,這樣就省去了一次遍歷單鏈表的過程。fastbin的內(nèi)存分配策略是exact fit,即只釋放fastbin中跟申請(qǐng)內(nèi)存大小恰好相等的堆。

1) smallbin

smallbin中包含62個(gè)循環(huán)雙向鏈表,鏈表中chunk的大小與index的關(guān)系是2* size_t* index。由于smallbin是循環(huán)雙向鏈表,所以它的實(shí)現(xiàn)方法是fifo;smallbin的內(nèi)存分配策略是exact fit。

從實(shí)現(xiàn)中可以看出smallbin鏈接的chunk中包含一部分fastbin大小的堆,fastbin范圍的堆是有可能被鏈入其他鏈表的。當(dāng)用戶申請(qǐng)smallbin大小的堆而smallbin又沒有初始化或者申請(qǐng)大于smallbin最大大小的堆時(shí),fastbin中的堆根據(jù)prev_inuse位進(jìn)行合并后會(huì)進(jìn)入如上unsortedbin的處理流程,符合smallbin或largebin范圍的堆會(huì)被鏈入相應(yīng)的鏈表。

2) largebin

largebin包含63個(gè)循環(huán)雙向鏈表,每個(gè)鏈表鏈接的都是一定范圍大小的堆,鏈表中堆的大小按從大到小排序,堆結(jié)構(gòu)體中的fd_nextsize和bk_nextsize字段標(biāo)識(shí)鏈表中相鄰largechunk的大小,即fd_nextsize標(biāo)識(shí)比它小的堆塊、bk_nextsize標(biāo)識(shí)比它大的堆塊。

對(duì)于相同大小的堆,釋放的堆插入到bin頭部,通過fd、bk與其他的堆鏈接形成循環(huán)雙向鏈表。

Largebin的分配策略是best fit,即最終取出的堆是符合申請(qǐng)內(nèi)存的最小堆(記為chunk)。若取出的chunk比申請(qǐng)內(nèi)存大至少minsize,則分割chunk并取合適大小的剩余堆做為last remainder;若取出的chunk比申請(qǐng)內(nèi)存不大于minsize,則不分割chunk直接返回做為用戶申請(qǐng)內(nèi)存塊。

3) unsortedbin

unsortedbin可以視為空閑chunk回歸其所屬bin之前的緩沖區(qū),分配策略是exact fit。可能會(huì)被鏈入unsortedbin的堆塊是申請(qǐng)largebin大小堆塊切割后的last remainder;釋放不屬于fastbin大小且不與topchunk緊鄰的堆塊時(shí)會(huì)被先鏈入unsortedbin;在特定情況下將fastbin內(nèi)的堆合并后會(huì)進(jìn)入unsortedbin的處理流程(特定情況為申請(qǐng)fastbin范圍堆fastbin為空;申請(qǐng)非fastbin范圍smallbin的堆但smallbin未初始化;申請(qǐng)largechunk)

1.how2heap調(diào)試

1.0 First_fit

這個(gè)程序闡釋了glibc分配內(nèi)存的一個(gè)策略:first fit,即從空閑表中取出的堆是第一個(gè)滿足申請(qǐng)內(nèi)存大小的堆(fastbin、smallbin exact fit,largebin best fit)

Shellphish給出的例子中先申請(qǐng)了0×512和0×256大小的兩個(gè)堆,然后釋放掉0×512大小的堆(申請(qǐng)0×256大小的堆的作用是避免釋放不是mmap分配的堆a(bǔ)的時(shí)候合并到topchunk),實(shí)例中再次申請(qǐng)0×500大小的堆由于largebin的best fit分配策略glibc會(huì)分割堆后返回堆a(bǔ),即堆c等價(jià)于堆a(bǔ),這時(shí)我們輸出堆a(bǔ)的內(nèi)容即輸出修改后的堆c的內(nèi)容。

glibc的first fit分配策略可用于use after free(uaf,釋放后重用)的利用,即修改新分配堆的內(nèi)容等價(jià)于修改被釋放的堆,uaf一般是由于釋放堆后指針未置零造成的,不過在uaf的利用過程中我們一般使新分配的堆的大小等于被釋放的堆的大小。

1.1 fastbin_dup

fastbin下doublefree的一個(gè)示例(未加tcache機(jī)制)。

Shellphish給出的例子中先申請(qǐng)了3個(gè)0×8大小的堆(同樣地申請(qǐng)c的原因是避免合并到topchunk),然后釋放a(此時(shí)再次釋放a構(gòu)成doublefree雙重釋放,但是由于glibc在釋放fastbin大小的堆時(shí)檢查且僅檢查fastbin頭部的堆和要釋放的堆是否相等,若相等則報(bào)錯(cuò)),為了繞過glibc在釋放堆時(shí)對(duì)bin頭結(jié)點(diǎn)的檢查,我們free(b),此時(shí)fastbin如下(b=0×602020,a=0×602000;由于fastbin是單鏈表且LIFO,后釋放的b被插入到鏈表頭)

然后我們?cè)俅蝔ree(a),由于此時(shí)bin頭結(jié)點(diǎn)指向b,所以對(duì)頭結(jié)點(diǎn)的檢查被繞過,free(a)之后

可以看到此時(shí)fastbin中有兩個(gè)a,如果此時(shí)我們申請(qǐng)三個(gè)0×8大小的堆,則依次從fastbin頭部取得到a、b、a三個(gè)堆。

1.2 fastbin_dup_into_stack

fastbin下doublefree的利用示例(未開啟tcache機(jī)制)。主要思路是在doublefree時(shí)我們有一次修改一個(gè)存在于fastbin鏈表的堆的機(jī)會(huì),然后通過偽造堆的內(nèi)容可以使得fastbin鏈入偽造的堆,再次申請(qǐng)內(nèi)存可以得到偽造地址處的堆。

示例中先申請(qǐng)了3個(gè)0×8大小的堆,然后通過free(a)、free(b)、free(a)構(gòu)成一次doublefree(原理同fastbin_dup),此時(shí)fastbin的連接狀態(tài)是a->b->a。再次申請(qǐng)兩個(gè)0×8大小的堆,由于fastbin的lifo,此時(shí)fastbin中只剩a,且此時(shí)堆a(bǔ)存在于fastbin和用戶申請(qǐng)的堆中,即我們可以控制一個(gè)存在于fastbin的堆的內(nèi)容。容易想到的一種利用方式是偽造fastbin鏈表的內(nèi)容,進(jìn)而達(dá)到在偽造地址處申請(qǐng)堆的效果。

示例中在棧中偽造了一個(gè)0×20大小的堆(偽造堆頭如下圖選中部分,其中a=0×405000,&stack_var=0x00007fffffffdfb0),此時(shí)堆a(bǔ)的fd指向&stack_var,即fastbin:a->stack_var,此時(shí)第二次申請(qǐng)不超過0×18大小的堆(64位系統(tǒng),跟申請(qǐng)堆時(shí)字節(jié)對(duì)齊有關(guān),返回的堆的大小會(huì)被轉(zhuǎn)化成滿足條件的最小2*size_sz的倍數(shù),最大0×10+8,8字節(jié)可占用下一個(gè)堆的prev_size)即可返回棧地址處的偽造堆。

1.3 fastbin_dup_consolidate

fastbin attack構(gòu)成doublefree的一個(gè)示例。原理是利用申請(qǐng)一次largebin大小的堆會(huì)將fastbin的堆進(jìn)行合并進(jìn)入unsortedbin的處理流程,此時(shí)再次free fastbin中的堆會(huì)繞過free時(shí)對(duì)fastbin鏈表頭節(jié)點(diǎn)的檢查進(jìn)而構(gòu)成一次doublefree。

從下圖free的流程中我們可以看出free時(shí)只會(huì)檢查釋放fastbin大小的堆時(shí)被釋放的堆是否和fastbin的頭結(jié)點(diǎn)是否一致,而在申請(qǐng)0×400的largechunk時(shí),fastbin鏈表非空,fastbin中的堆會(huì)進(jìn)行合并并且進(jìn)入unsortedbin的處理流程,在unsortedbin的處理流程中符合fastbin大小的堆會(huì)被放入smallbin,這樣就繞過了free時(shí)對(duì)fastbin頭結(jié)點(diǎn)的檢查,從而可以構(gòu)成一次對(duì)fastbin大小的堆的doublefree。

1.4 unsafe_unlink

堆可以溢出到下一個(gè)堆的size域且存在一個(gè)指向堆的指針時(shí)堆溢出的一種利用方式。

Unsafe unlink利用的前提是可以溢出到下一個(gè)堆的size域,利用的大致思路是在chunk0構(gòu)造fakechunk且fakechunk可以繞過unlink雙向鏈表斷鏈的檢查,修改chunk1的pre_size使之等于fakechunk的大小,修改chunk1中size域的prev inuse位為0以便free(chunk1)時(shí)檢查前后向堆是否空閑時(shí)(這里是后向堆,即物理低地址)觸發(fā)unlink雙向鏈表斷鏈構(gòu)成一次任意地址寫。下面看一下unlink的具體細(xì)節(jié)和原理。

示例中首先申請(qǐng)了兩個(gè)0×80大小的堆chunk0和chunk1(非fastbin大小,因?yàn)閒astbin大小的堆為了避免合并pre_inuse總是為1),然后在chunk0中構(gòu)造fake_chunk

需要注意的是,我們構(gòu)造的fake chunk的起點(diǎn)是chunk0的數(shù)據(jù)部分即fd,fake chunk的prev size和size域正常賦值即可(最新的libc加入了cur_chunk’size=next_chunk’s prev_size),fake chunk中關(guān)鍵的部分是fake data,這一部分要繞過unlink雙向鏈表斷鏈的檢查,即fd->bk=p&&bk->fd=p

chunk的結(jié)構(gòu)體如下

所以由結(jié)構(gòu)體的尋址方式可得

(fd->bk=fd+3* size_t)=p

(bk->fd=bk+2* size_t)=p

所以可得

fd=p-3* size_t

bk=p-2* size_t

即fakechunk中fd和bk域如上構(gòu)造即可繞過unlink雙向鏈表的斷鏈檢查。

構(gòu)造完fakechunk還需要修改下chunk1的prevsize和size的數(shù)據(jù),

首先是prevsize要修改成fakechunk的大小(包含堆頭,原因是glibc尋找下一個(gè)堆的宏如下,即將當(dāng)前堆偏移size的數(shù)據(jù)視為下一個(gè)堆)

chunk1 size部分的inuse位要置0,即標(biāo)識(shí)物理相鄰低地址堆為空閑狀態(tài)(這也是unlink無法使用fastbin大小的堆的原因,fastbin大小的堆為了減少堆合并的次數(shù)inuse位總是置1)

最后構(gòu)造的fakechunk+chunk1部分?jǐn)?shù)據(jù)如下,chunk0堆頭0×405000,fakechunk堆頭0×405010,chunk1堆頭0×405090,圖中選中部分為fakechunk

其中fakechunk的fd要使用指向堆節(jié)點(diǎn)的指針(如指向該節(jié)點(diǎn)的全局變量,非堆地址)的原因是unlink源碼中傳入的第二個(gè)參數(shù)是struct malloc_chunk * p。下面分析下unsafeunlink是如何導(dǎo)致任意地址寫的。閱讀源碼可以發(fā)現(xiàn)smallbin范圍內(nèi)非fastbin范圍的堆在unlink時(shí)只檢查了雙向鏈表的完整性,然后執(zhí)行了雙向鏈表摘除節(jié)點(diǎn)的操作。

斷鏈的過程

fd->bk=bk 即(fd->bk=p)=(bk=p-2* size_t)

bk->fd=fd 即(bk->fd=p)=(fd=p-3* size_t)

最終相當(dāng)于

p=p-3* size_t

即獲得了兩個(gè)相等的指針(struct malloc_chunk * p),試想如果此時(shí)我們可以修改一個(gè)指針指向的地址同時(shí)可以修改另一個(gè)指針指向的內(nèi)容不就可以構(gòu)成一次任意地址寫了嗎?巧的是(;p)我們恰好可以達(dá)到這樣的效果。

此時(shí)我們修改fake_chunk[3]為要寫的地址,修改fake_chunk[0]為要寫的地址的內(nèi)容即可。原因是fake_chunk[3]-3*size_t=fake_chunk,這里相當(dāng)于給fake_chunk指向一個(gè)新的地址;fake_chunk[0]訪問的是&fake_chunk[0]地址處的值,即上一步修改的地址處的內(nèi)容。這樣就構(gòu)成了一次任意地址寫^.^

1.5 house_of_spirit

利用fastbin范圍的堆釋放時(shí)粗糙的檢查可以在任意地址處偽造fastbin范圍fakechunk進(jìn)而返回fakechunk的一種利用方式。思路是在指定地址處偽造fastbin范圍的fakechunk,釋放掉偽造的fakechunk,再次申請(qǐng)釋放掉的fakechunk大小的堆即可得到fakechunk。

其中fastbin范圍的堆釋放時(shí)的檢查如下圖所示,

我們構(gòu)造的fakechunk只需要繞過free時(shí)的檢查即可:

0.2*size_sz

1.偽造的fakechunk不能是fastbin的頭結(jié)點(diǎn),即不能直接構(gòu)成doublefree

利用house of spirit可以得到fakechunk處的堆,同時(shí)如果我們有fakechunk處寫的權(quán)限利用fastbinattack即可劫持控制流。

1.6 poison_null_byte

由于glibc在返回用戶申請(qǐng)的堆時(shí)不恰當(dāng)?shù)母露训膒resize域和錯(cuò)誤的計(jì)算nextchunk的位置可以導(dǎo)致一次堆重疊。

方法是先申請(qǐng)堆然后釋放掉中間位置的一個(gè)堆bchunk(假設(shè)堆的大小都如圖所示),假設(shè)存在一個(gè)off by null的漏洞,由于前一個(gè)堆是占用狀態(tài)時(shí)prevsize域用來存儲(chǔ)前一個(gè)堆的數(shù)據(jù),這樣我們可以從achunk溢出到bchunk的size域最低位將其置0。

此時(shí)申請(qǐng)一個(gè)0×100大小的堆會(huì)返回釋放掉的bchunk位置的堆。原因是在申請(qǐng)一個(gè)smallbin且非fastbin范圍的堆時(shí)會(huì)檢查smallbin是否為空,本例中smallbin為空則執(zhí)行smallbin的初始化過程,即將可能的fastbin中的堆進(jìn)行合并進(jìn)入unsortedbin的處理流程,申請(qǐng)的堆的大小是smallbin范圍,此時(shí)會(huì)取largebin頭結(jié)點(diǎn)的一個(gè)堆進(jìn)行切割返回(同樣地為了減少內(nèi)存碎片,largebin的堆從大到小排序)。這里largebin中只含一個(gè)0×200大小的堆,則直接對(duì)其進(jìn)行切割然后返回給用戶。

然后再次申請(qǐng)一個(gè)0×80大小的堆。原因是0×100+0×80+兩個(gè)堆頭=0×200使之結(jié)束的位置正好落于cchunk

這時(shí)free(b1)、free(c)釋放掉兩個(gè)堆,由于nextchunk即cchunk的preinuse為0會(huì)觸發(fā)前向合并(向物理高地址)過程。原因是fake了一個(gè)cchunk的presize,系統(tǒng)修改的是我們的fake presize,即下圖的0xf0,系統(tǒng)依然認(rèn)為bchunk的位置有一個(gè)0×210的fakechunk。

此時(shí)再次申請(qǐng)一個(gè)0×300大小的堆,由于合并后bchunk和cchunk的大小為0×300,系統(tǒng)會(huì)返回合并后的bchunk。又由于此時(shí)b2chunk沒有被釋放處于占用態(tài),b2chunk位于合并的bchunk內(nèi),此時(shí)構(gòu)成一次堆重疊。

1.7 house_of_lore

利用偽造smallbin鏈表來最終達(dá)到一次任意地址分配內(nèi)存的效果。前提是可以在要分配的地址處偽造堆(修改結(jié)構(gòu)體中fd、bk的指向),且可以修改victim堆(被釋放的smallbin堆)的bk指針。

方法是在要分配的內(nèi)存地址(如棧地址)處構(gòu)造一個(gè)fake smallbin chunk鏈,使之如下圖所示。

然后申請(qǐng)一個(gè)堆防止釋放victim的時(shí)候合并到topchunk,釋放掉victim,此例中victim會(huì)進(jìn)入fastbin鏈表。

再次申請(qǐng)一個(gè)largechunk,觸發(fā)fastbin的合并過程并使fastbin的堆進(jìn)入unsortedbin的處理流程,victim處于smallbin的范圍最終被鏈入smallbin頭結(jié)點(diǎn)。而由于我們事先構(gòu)造了如上的fake smallbin鏈,此時(shí)smallbin的鏈接情況是smallbin:victim->stack_buf1->stack_buf2。

由于smallbin的exact fit和fifo策略,此時(shí)申請(qǐng)一個(gè)victim大小的堆會(huì)直接返回bin結(jié)點(diǎn)bk指向的victim(bin的結(jié)構(gòu)體是mchunkptr*),然后斷鏈并修改bin的bk指針指向victim的bk節(jié)點(diǎn)即stack_buf1。glibc取smallbin的chunk源碼如下。

此時(shí)stack_buf1的結(jié)構(gòu)如下(其中0x7fffffffdfb0=stack_buf1,0x7ffff7dd4b98=smallbin,0x7fffffffdf90=stack_buf2),即此時(shí)smallbin:stack_buf1->stack_buf2

這樣此時(shí)再申請(qǐng)一個(gè)victim大小的堆直接取smallbin的bk指向的stack_buf1即得到相應(yīng)地址處的堆,達(dá)到了任意地址分配內(nèi)存的效果。

1.8 overlapping_chunks

通過修改一個(gè)位于空閑鏈表的堆的size域可以構(gòu)成一次堆重疊

過程如上。修改位于bin的p2的size域,修改后p2結(jié)構(gòu)如下(p2=0×405110,選中部分為p2 data部分)

此時(shí)申請(qǐng)一個(gè)修改后的p2 size的堆會(huì)得到從p2位置起始的fake size大小的堆p4,如下圖

1.9 overlapping_chunks_2

通過堆溢出修改下一個(gè)占用態(tài)堆的size域構(gòu)成一次堆重疊

shellphish給出的示例中先free掉p4(我個(gè)人感覺這一步是沒有必要的,shellphish可能是出于演示的目的考慮?因?yàn)樯院罂梢钥吹轿覀兛梢杂^察到p5的prevsize在free(p2)后會(huì)發(fā)生變化,如果有小伙伴看到這里可以一起交流,snip3r[at]163.com)。free p4后p5的prevsize為3f0

然后修改p2的size域?yàn)閜2+p3+標(biāo)志位,釋放掉。此時(shí)glibc會(huì)認(rèn)為p2的size域的大小包圍的堆是要被釋放的,會(huì)錯(cuò)誤的修改p5的prevsize值。free p2后p5的prevsize為bd0

此時(shí)由于物理相鄰的前向堆p4處于空閑態(tài),fake p2會(huì)和p4合并鏈入largebin。然后申請(qǐng)2000大小的largechunk會(huì)將上述合并后的堆切割后返回p2起始的堆,從而構(gòu)成一次堆重疊。

1.10 house_of_force

利用topchunk分配內(nèi)存的特點(diǎn)可以通過一次溢出覆蓋topchunk的size域得到一次任意地址分配內(nèi)存的效果。

首先通過一次堆溢出覆蓋topchunk的size域?yàn)橐粋€(gè)超大的整數(shù)(如-1),避免申請(qǐng)內(nèi)存時(shí)進(jìn)入mmap流程。

然后申請(qǐng)一個(gè)evilsize大小的堆改變topchunk的位置。evilsize的計(jì)算如下,這么計(jì)算的原因是當(dāng)bin都為空時(shí)會(huì)從topchunk處取堆

修改topchunk到目標(biāo)地址后在申請(qǐng)一次堆即可對(duì)目標(biāo)地址處的內(nèi)存進(jìn)行改寫。

1.11 unsorted_bin_into_stack

通過修改位于unsortedbin的堆的size域和bk指針指向目標(biāo)fakechunk,在目標(biāo)地址構(gòu)造fakechunk(構(gòu)造size和bk指針。我們也可以不修改victim的size,malloc兩次得到目標(biāo)地址的fakechunk;原理都是構(gòu)造fake unsortedbin鏈表)可以得到一次任意地址申請(qǐng)內(nèi)存的機(jī)會(huì)。

其中如果要偽造victim的size的話要滿足check 2*SIZE_SZ (> 16 on x64) && < av->system_mem

通過溢出修改位于unsortedbin的victim的size和bk,并構(gòu)造fakechunk,最終構(gòu)造出如下fake smallbin鏈表

在下一次申請(qǐng)內(nèi)存時(shí)glibc遍歷unsortedbin找到exact fit的堆塊并返回,最終可以得到目標(biāo)地址處的偽造堆。

1.12 unsorted_bin_attack

通過偽造unsortbin鏈表進(jìn)行unsortedbin attack泄露信息(libc基址)的一種方法。

方法是構(gòu)造如下fake unsortedbin鏈表,

這樣在申請(qǐng)得到victim后會(huì)將victim斷鏈,從而target_addr fake chunk的fd會(huì)指向相應(yīng)的bin,進(jìn)而可以泄露libc基址。(當(dāng)然也可以泄露bk之類位置的其他信息,如果有的話;p)

1.13 large_bin_attack

利用malloc進(jìn)行unsortedbin處理時(shí)插入largebin通過修改largebin鏈表上的堆的bk、bk_nextsize均可以得到任意地址寫的機(jī)會(huì)。

首先要申請(qǐng)如上圖3個(gè)堆和相應(yīng)的為了避免合并到topchunk的barrier(只申請(qǐng)barrier3應(yīng)該就夠用了,shellphish這么寫可能是在之后復(fù)雜的申請(qǐng)釋放中不在考慮合并到topchunk的情況),其中p1要保證是smallbin且非fastbin范圍(且保證在后續(xù)申請(qǐng)堆時(shí)堆大小夠用),p2、p3要保證是largebin范圍。

(1)然后依次釋放p1、p2,由于非fastbin范圍的堆在釋放后會(huì)首先鏈入unsortedbin,此時(shí)unsortedbin的情況是。(簡單說就是unsortedbin:p2->p1,其中各個(gè)指針的指向如圖)

(2)此時(shí)申請(qǐng)一個(gè)0×90大小的堆,從glibc的源碼中可以看到遍歷unsortedbin的過程是從bin頭結(jié)點(diǎn)的bk指針開始遍歷。這樣取到的第一個(gè)堆是0×320大小的p1,p1滿足0×90的申請(qǐng),glibc會(huì)從p1中分割出0×90的大小,然后繼續(xù)遍歷unsortedbin直至遍歷結(jié)束;此時(shí)得到鏈表的第二個(gè)堆0×400大小的p2,p2非smallbin范圍且largebin為空,被鏈入largebin

此時(shí)unsortbin:(p1-0×90),largebin:p2.

(3)然后釋放0×400大小的p3,p3非fastbin范圍被鏈入unsortedbin頭結(jié)點(diǎn)(fd指向p3)。

(4)此時(shí)利用溢出或其他手段修改largebin中的p2的bk、bk_nextsize(或、且)和size。可以看到p2修改前的size為0×411,shellphish把它修改成了0x3f1,這樣做是因?yàn)閘argebin中鏈接的一定范圍的堆是從大到小降序排列的,修改后0×400大小的p3被鏈入largebin時(shí)會(huì)被鏈入頭結(jié)點(diǎn)。

在做好以上的準(zhǔn)備工作后再次申請(qǐng)一個(gè)0×90大小的堆,同(2)過程依然由p2分割得到堆,由于p3>修改后的p2的size,p3被鏈入largebin頭結(jié)點(diǎn)。鏈入的過程類似unlink,類似的我們得到了一次任意地址寫的機(jī)會(huì)。

1.14 house_of_einherjar

利用一次off by null修改下一個(gè)占用態(tài)chunk的prev_inuse位,同時(shí)修改下一個(gè)下一個(gè)占用態(tài)chunk的prev_size值,利用top chunk和后向合并(物理低地址)機(jī)制得到一次任意地址分配內(nèi)存的機(jī)會(huì)。這種off by null利用的前提是可以在目標(biāo)地址處(最終分配內(nèi)存的地址處)構(gòu)造fakechunk。

利用的方法是在目標(biāo)地址處構(gòu)造fakechunk,由于稍后會(huì)看到fakechunk處會(huì)觸發(fā)unink,為了繞過雙向鏈表完整性的檢測(cè)fd、和bk均可置為fakechunk。其中設(shè)置fakechunk的prev_size和size的值是可以但沒必要的。

由于占用態(tài)的堆prev_size會(huì)用來存儲(chǔ)前一個(gè)堆的數(shù)據(jù),所以天然的prev_size域可以修改;當(dāng)存在off by null時(shí)可以將下一個(gè)占用態(tài)堆的prev inuse置0。我們修改a的prev_size為fake_size,b的prev_inuse為0。這時(shí)我們釋放掉b,由于b和topchunk緊鄰,b會(huì)和topchunk合并;同時(shí)由于b的prev_inuse為0會(huì)觸發(fā)后向合并(物理低地址),glibc尋找下一個(gè)空閑堆的方式是chunk_at_offset(p, -((long) prevsize)),即將當(dāng)前位置偏移prev_size的位置視為nextchunk,這樣(b+b.prev_size)得到下一個(gè)堆位于fakechunk,合并到topchunk并最終得到新的topchunk起點(diǎn)為fakechunk。此時(shí)再次申請(qǐng)堆從topchunk處取即可得到target處的fakechunk。

這樣通過反推target=b_chunk_header-fake_size得到fake_size=b_chunk_header-target。

2.總結(jié)

本文到這里就結(jié)束了,linux pwn基礎(chǔ)知識(shí)的介紹到這里也就結(jié)束了,但是glibc還在不斷更新,堆管理一些細(xì)節(jié)也在不斷微調(diào),一些新的提高性能的機(jī)制如tcache也開始應(yīng)用于新版本的libc,關(guān)于不斷更新的新版本libc的漏洞利用方式的探索還遠(yuǎn)遠(yuǎn)沒有結(jié)束。

總結(jié)

以上是生活随笔為你收集整理的linux堆上的内存可执行吗,pwn的艺术浅谈(二):linux堆相关的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。