自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

如何通過CVE-2015-7547(GLIBC getaddrinfo)漏洞繞過ASLR

安全 漏洞
在這項研究中,我們使用了Linux創(chuàng)建進程時的特性來繞過ASLR。這種技術(shù)同時也可以用于其他內(nèi)存損壞漏洞的利用。因此,用戶應(yīng)始終嘗試通過及時部署軟件修補程序和更新來保護服務(wù)器。以保證我們在潛在威脅行動者行動前領(lǐng)先一步。

0x01 前言

2016年2月16日,Google披露了一個重要的緩沖區(qū)溢出漏洞,該漏洞在GLIBC庫中的getaddrinfo函數(shù)中觸發(fā)。同時他們還提供了一份PoC?;诖?,在本文中,我們將展示如何通過CVE-2015-7547繞過ASLR。

0x02 漏洞描述

getaddrinfo()函數(shù)的作用是通過查詢DNS服務(wù)將主機名和服務(wù)解析為addrinfo結(jié)構(gòu)體。

在getaddrinfo()函數(shù)實現(xiàn)中,使用了alloca()函數(shù)(在堆棧上分配緩沖區(qū))對DNS進行響應(yīng)。開始的時候,該函數(shù)首先分配一段??臻g用于DNS響應(yīng), 如果響應(yīng)時間過長,它將重新分配一個堆緩沖區(qū)用于響應(yīng)。但由于更新代碼將緩沖區(qū)為新分配的堆緩沖區(qū)后,舊的堆棧緩沖區(qū)未及時釋放,仍舊再使用。 這個懸空指針便造成了一個經(jīng)典的緩沖區(qū)溢出。

ASLR? ASLR!

在上述情況下,可以通過這個漏洞覆蓋getaddrinfo()函數(shù)的返回地址,但是我們應(yīng)該將返回地址覆蓋到哪里呢?在啟用ASLR的系統(tǒng)中,模塊地址是隨機的。因此,攻擊者不能將攻擊流地址設(shè)置為預(yù)先設(shè)定的地址。

fork()

fork()是Linux中創(chuàng)建新進程的方法。一個典型的fork使用方法如下圖所示:

fork出的子進程和父進程使用的相同的指令段,他們只有pid不相同,而pid是fork函數(shù)返回給子進程的。和windows下的代碼復(fù)用的區(qū)別在于,這里意味著子進程與其父進程共享許多特性--具有相同的寄存器狀態(tài),堆棧和內(nèi)存布局。

0x03 程序流程示例

考慮一個服務(wù)器應(yīng)用程序,其運行模式如下:

1. 客戶端遠程連接到應(yīng)用程序。

2. 應(yīng)用程序自己fork一個子進程用于響應(yīng)客戶端請求

3. 在處理客戶端請求的過程中,子進程使用"getaddrinfo()"函數(shù)解析主機名。 同時,它向其DNS服務(wù)器發(fā)送DNS請求。

4. DNS服務(wù)器對DNS請求做出一個合法響應(yīng)。

5. 子進程啟動與已被解析的主機的連接。

每次主進程進行響應(yīng)處理時,它都會自己fork一個子進程。根據(jù)前面的描述,這意味著所有子進程將共享相同的內(nèi)存布局--包括加載模塊的地址。 這種場景對于許多服務(wù)(例如HTTP代理,電子郵件服務(wù)器或DNS服務(wù)器)是非常常見的。

0x04 攻擊流程示例

在實施攻擊的過程中,我們假設(shè)攻擊者具有能夠響應(yīng)受害者的任意DNS請求的能力。實現(xiàn)這種情形完全可以通過ARP欺騙、DNS欺騙完成。攻擊場景如下圖:

1. 一個攻擊者構(gòu)造一個請求發(fā)送給受害服務(wù)器

2. 為了響應(yīng)攻擊者的請求,受害服務(wù)器的守護進程fork出一個子進程

3. 子進程處理請求時,發(fā)起一個DNS請求

4. 攻擊者回復(fù)一個惡意的DNS響應(yīng),該響應(yīng)將子進程的返回地址覆蓋,在這里,我們將其設(shè)置未0x12121212

5. 攻擊者獲得子進程用connect()函數(shù)發(fā)起的TCP回連

如果0x12121212確實是getaddrinfo()的正確返回地址,那么該進程將正常運行,并通過connect()發(fā)起tcp連接。

如果不是這種情況,并且攻擊者將返回地址寫為其他任何地址,則應(yīng)用程序?qū)⒂捎趦?nèi)存段錯誤或執(zhí)行無效的指令而崩潰。

這種方式可以作為判斷一個地址是否為getaddrinfo()的返回地址的一種方法,原因在于如果地址正確,那么一個TCP連接將會成功建立。由于模塊的基址在不同的子進程中是沒有隨機化(前面提到的公用內(nèi)存布局),于是這個地址在所有的子進程中可以通用。一個攻擊者可以使用這種方式去遍歷每一個可能的地址,知道正確建立TCP連接而獲得正確的地址。

然而,采用這種方式進行基址定位需要猜解2的64次方的數(shù)量的地址,這并沒有太大的現(xiàn)實意義。

逐字節(jié)的逼近

不過,攻擊者可以每次只覆蓋一個字節(jié)。 例如,假設(shè)getaddrinfo的返回地址為0x00007fff01020304:

我們首先只覆蓋getaddrinfo()函數(shù)的返回地址最低有效位(LSB)的一字節(jié)。這里用0x00進行覆蓋。由于在假設(shè)中g(shù)etaddrinfo()的返回地址為0x00007fff01020304,將最低位覆蓋為0x00,那么這里返回地址就會變?yōu)?x00007fff01020300,由于該地址是非法地址,函數(shù)返回后程序就會崩潰。于是我們繼續(xù)重復(fù)上述操作,并且每次重復(fù)是LSB只加1(即第一次0x00,第二次0x01,第三次0x02,...),當(dāng)我們將LSB增加到0x04時,getaddrinfo函數(shù)的返回地址為正確的返回地址0x00007fff01020304,此時程序不會崩潰,建立tcp連接。于是最低位的值便確定了。

接下來,我們重復(fù)上述整個操作,通過覆蓋返回地址的兩個字節(jié)(0x04 0x00)來枚舉下一個字節(jié),我們將返回地址的第一個字節(jié)設(shè)置為剛剛猜解出來的正確字節(jié)(0x04),于是我們只需要采取相同辦法猜解第二個字節(jié)即可。猜解成功的標(biāo)志和第一個字節(jié)一樣,建立正確的連接。

接下來是第三個字節(jié),第四個字節(jié)。。。

通過這種逐字節(jié)逼近的方法,我們最多只需要進行8*2^8次(每個字節(jié)最多2^8次猜解,總共8字節(jié))嘗試便可以得到正確的返回地址,這種方式在幾秒內(nèi)便可得到結(jié)果。

0x05 查找可利用的應(yīng)用程序

http://codesearch.debian.net是一個包含超過18,000 Debian包的索引的網(wǎng)站。我們通過該網(wǎng)站查找所有調(diào)用 fork()和getaddrinfo()函數(shù)的應(yīng)用程序(這些程序都是有可能進行利用的),發(fā)現(xiàn)超過1300個潛在的可利用的應(yīng)用程序。 然后,進一步的我們需要檢查每個應(yīng)用程序的源代碼,檢查其流程是否適合我們的需要。

0x06 Tinyproxy

Tinyproxy是Linux下一個小型的http代理軟件,通過審計,發(fā)現(xiàn)該應(yīng)用程序的執(zhí)行流程符合上述分析的執(zhí)行流程。當(dāng)它在響應(yīng)HTTP連接請求時,會fork出一個子進程,然后調(diào)用getaddrinfo()函數(shù)來檢索所請求的網(wǎng)站的IP地址。 然后使用connect()函數(shù)連接該主機獲取網(wǎng)站內(nèi)容。

1. 堆棧任意指針泄露

在下面的代碼塊中我們遇到了第一崩潰點:

rbx寄存器首先被覆蓋,然后執(zhí)行"mov BYTE PTR [rbx],sil"指令,該指令可釋放對rbx指向的地址的指針。 rbx原先是指向棧上的,也就是說,如果我們采用逐字節(jié)逼近的方法,枚舉其值使得該程序在堆棧上泄漏一個地址。

下圖(output of /proc/PID/maps)顯示了堆棧的邊界。 正如圖中所示,它的初始大小總是大于0x1000字節(jié)。

寄存器rbx指向的地址必須是可寫的,否則會引發(fā)分段錯誤導(dǎo)致程序崩潰。然而它的缺陷在于,無論在哪個地址寫入“sil”值,只要它是可寫地址,程序流將正確地繼續(xù),這意味著對于rbx的低12位設(shè)置什么值根本無關(guān)緊要,因為由于堆棧的緣故它總是可讀可寫的。

所以我們只要在堆棧范圍內(nèi)泄露了任意一個指針。當(dāng)我們必須精確定位時,堆棧變量指向的地址并不會對程序流產(chǎn)生什么影響。

2. 泄露?;?/strong>

由于應(yīng)用程序的流程總是相同的,所以棧的大小總是相同的。 這意味著我們可以依賴于?;返竭@些變量,結(jié)構(gòu)體和緩沖區(qū)的偏移量進行定位。而且在這樣的情況下,我們往往依賴于這樣的常量偏移。所以首先應(yīng)該得到?;贰?/p>

由于我們已經(jīng)得到一個在??臻g內(nèi)的地址,所以泄露?;繁让杜e任意地址更簡單。而且我們知道它擁有兩個屬性,一是?;放c頁面邊界(0x1000)對齊,二是棧基址將是棧后面的第一個不可讀的地址。

讓我們假設(shè)堆?;吩?x00007fffed008000。我們利用已經(jīng)泄漏的任意堆棧地址,并將其對齊到頁邊界得到一個新的對齊地址,例如0x00007fffed000140對齊到0x00007fffed000000。然后,我們枚舉堆棧基址,從這個對齊的地址開始進行覆蓋,并在每次嘗試之后遞增0x1000(頁大小)。在我們發(fā)送請求之后,等待一段時間,并檢查服務(wù)器是否嘗試連接到我們解析的IP。如果是,這意味著我們還沒有達到堆?;?。如果發(fā)生超時,說明服務(wù)器發(fā)生崩潰,我們達到了目標(biāo),獲得了堆?;?。

3. 堆棧偏移

在從getaddrinfo返回之前,程序會執(zhí)行以下檢查:

注意以紅色突出顯示的塊。如果我們到達它并且傳遞一個無效的堆指針作為參數(shù),應(yīng)用程序崩潰,因為它試圖釋放(free()函數(shù))一個無效的堆塊。如果要繞過這個free()函數(shù),r14和rdi必須相等。 r14指向原來的__alloca()函數(shù)堆棧緩沖區(qū)。 由于堆棧基址先前泄漏,并且__alloca()緩沖區(qū)與堆棧基址的偏移量應(yīng)該是常量,因此我們不應(yīng)該遇到任何問題。 然而,我們發(fā)現(xiàn)偏移在每次運行時略有不同。 為什么?

這涉及到內(nèi)核代碼對ASLR的處理問題,如下linux內(nèi)核代碼所示:

  1. /arch/x86/kernel/process.c 

觀察上述內(nèi)核代碼,可以看到,如果ASLR被啟用,每次堆棧分配時SP(堆棧指針)將減少一個隨機數(shù)。這意味著在每次不同的運行時,在rsp和堆棧之間將存在一個隨機增量。

幸運的是,這個增量非常小。我們可以輕松地枚舉這個隨機偏移。

看看上面的IDA代碼片段,我們可以發(fā)現(xiàn),如果rdi等于r14,程序?qū)⒉粫\行到釋放rdi的那個分支。 因此,我們可以使用我們之前得到的堆?;?,結(jié)合預(yù)先計算(即,如果該值于?;穼R程序?qū)⒎祷?),然后嘗試所有其他2^9種可能性,便可得到此增量。

4. 泄漏LIBC模塊地址

這部分的實現(xiàn)是超級簡單的,因為我們可以使用前面提到的技術(shù)(byte-by-byte approach 逐字節(jié)逼近)來枚舉返回地址的每個字節(jié)從而得到它。

5. 代碼執(zhí)行

剩下要做的就是構(gòu)造一個ROP鏈,這是非常容易和直接。我們知道system()函數(shù)在libc的基址偏移的某個確定的位置,所以我們只需設(shè)置它的參數(shù),并使用ROP調(diào)用它。

0x07 結(jié)論

在這項研究中,我們使用了Linux創(chuàng)建進程時的特性來繞過ASLR。這種技術(shù)同時也可以用于其他內(nèi)存損壞漏洞的利用。因此,用戶應(yīng)始終嘗試通過及時部署軟件修補程序和更新來保護服務(wù)器。以保證我們在潛在威脅行動者行動前領(lǐng)先一步。

責(zé)任編輯:武曉燕 來源: paloaltonetworks.com
相關(guān)推薦

2021-10-29 11:52:16

谷歌漏洞TPM

2015-01-29 11:47:35

2020-09-22 12:07:13

攻擊

2025-03-28 07:15:00

漏洞網(wǎng)絡(luò)攻擊網(wǎng)絡(luò)安全

2015-07-13 09:18:20

2015-01-23 16:57:09

2015-09-18 09:33:03

2021-02-07 14:39:22

Falco漏洞安全工具

2015-06-03 10:21:44

漏洞溢出漏洞9patch

2015-02-04 11:20:28

2022-06-14 09:00:21

漏洞補丁

2023-11-20 11:24:24

2021-01-29 10:29:17

漏洞中間人攻擊網(wǎng)絡(luò)攻擊

2021-10-08 15:53:26

漏洞PoC漏洞攝像頭

2015-01-29 09:15:57

2009-12-18 13:31:18

Cisco路由器配置口

2015-05-13 19:30:21

2021-06-17 11:05:46

漏洞Linux發(fā)行版Polkit auth

2021-02-09 10:31:04

漏洞webWordPress F

2024-07-05 07:53:08

點贊
收藏

51CTO技術(shù)棧公眾號