新聞中心
在信息安全和編程中,緩沖區(qū)溢出是一種異常,其中程序在將數(shù)據(jù)寫入緩沖區(qū)時(shí)會(huì)超出緩沖區(qū)邊界并覆蓋相鄰的內(nèi)存位置。緩沖區(qū)是留出的用于存儲(chǔ)數(shù)據(jù)的內(nèi)存區(qū)域,通常是在將數(shù)據(jù)從程序的一個(gè)部分移動(dòng)到另一部分或在程序之間移動(dòng)時(shí)使用的。如果假設(shè)所有輸入都小于特定大小,并且緩沖區(qū)被創(chuàng)建為該大小,則產(chǎn)生更多數(shù)據(jù)的異常事務(wù)可能導(dǎo)致其寫入緩沖區(qū)的末尾。

成都創(chuàng)新互聯(lián)公司主要從事成都網(wǎng)站設(shè)計(jì)、網(wǎng)站建設(shè)、網(wǎng)頁設(shè)計(jì)、企業(yè)做網(wǎng)站、公司建網(wǎng)站等業(yè)務(wù)。立足成都服務(wù)新區(qū),十余年網(wǎng)站建設(shè)經(jīng)驗(yàn),價(jià)格優(yōu)惠、服務(wù)專業(yè),歡迎來電咨詢建站服務(wù):13518219792
緩沖區(qū)溢出概念
緩沖區(qū)溢出是指當(dāng)計(jì)算機(jī)向緩沖區(qū)內(nèi)填充數(shù)據(jù)位數(shù)時(shí)超過了緩沖區(qū)本身的容量溢出的數(shù)據(jù)覆蓋在合法數(shù)據(jù)上,理想的情況是程序檢查數(shù)據(jù)長(zhǎng)度并不允許輸入超過緩沖區(qū)長(zhǎng)度的字符,但是絕大多數(shù)程序都會(huì)假設(shè)數(shù)據(jù)長(zhǎng)度總是與所分配的儲(chǔ)存空間相匹配,這就為緩沖區(qū)溢出埋下隱患,操作系統(tǒng)所使用的緩沖區(qū),又被稱為"堆棧"。在各個(gè)操作進(jìn)程之間,指令會(huì)被臨時(shí)儲(chǔ)存在"堆棧"當(dāng)中,"堆棧"也會(huì)出現(xiàn)緩沖區(qū)溢出。
緩沖區(qū)溢出攻擊之所以成為一種常見安全攻擊手段其原因在于緩沖區(qū)溢出漏洞太普遍了,并且易于實(shí)現(xiàn)。而且,緩沖區(qū)溢出成為遠(yuǎn)程攻擊的主要手段其原因在于緩沖區(qū)溢出漏洞給予了攻擊者他所想要的一切:植入并且執(zhí)行攻擊代碼。被植入的攻擊代碼以一定的權(quán)限運(yùn)行有緩沖區(qū)溢出漏洞的程序,從而得到被攻擊主機(jī)的控制權(quán)。
緩沖區(qū)溢出漏洞詳解
當(dāng)你使用諸如“C”或“C ++”之類的語言開發(fā)程序并使用gcc使用以下命令對(duì)其進(jìn)行編譯時(shí):
- gcc -o program program.c
你知道gcc如何將你的代碼從“C”轉(zhuǎn)換為計(jì)算機(jī)可執(zhí)行的機(jī)器語言嗎?簡(jiǎn)而言之,我們可以說該過程分3個(gè)步驟完成:
“C”中的代碼將轉(zhuǎn)換為匯編語言,該匯編語言是二進(jìn)制之后的最低級(jí)語言。此時(shí),匯編代碼被翻譯成二進(jìn)制。可執(zhí)行文件是“鏈接的”,換句話說,鏈接是由代碼使用的庫(kù)建立的。
現(xiàn)在讓我們看一下匯編器的基礎(chǔ)知識(shí),因?yàn)檫@種語言對(duì)于理解開發(fā)過程至關(guān)重要。
- section .text
- global _start
- _start:
- push rdx
- mov rdi, 0x4444444444444444 ; v_addr
- mov rsi, 0x5555555555555555 ; len
- mov rdx, 0x7 ; RWX
- mov rax, 10 ; mprotect0x80483dc
- syscall
- mov rcx, 0x2222222222222222
- mov rsi, 0x3333333333333333
- mov rdx, 0x6666666666666666 ; random_int
- mov rdi, rsi
- jmp _loop
- _loop:?
- cmp rcx, 0x0
- je _end
- lodsb
- not al
- xor al, dl
- stosb
- loop _loopon
- _end:
- pop rdx
- mov rax, 0x1111111111111111
- jmp rax
上面這段代碼的目的只是向你展示它的外觀。如你所見,代碼是由諸如push,mov,cmp等指令組成的。
- ; Note, in assembler everything behind a semicolon is considered as a comment
- call 0x80483dc; Call function at address 0x80483dc
- push 0x0; puts the value 0x0 on the stack
- pop ebx; put what is at the top of the stack in ebx
- mov eax, 0x1; puts 0x1 in eax
如你所見,并不復(fù)雜。eax或ebx這些就是我們所說的寄存器。在其中存儲(chǔ)一些值,例如地址,數(shù)字等。對(duì)于32位處理器,寄存器eax,ebx,ecx,edx,ebp,esp,eip和edi大小為8位。還有其他寄存器,但老實(shí)說,它們暫時(shí)對(duì)我們沒有任何意義。還有一件事,對(duì)于64位處理器,我們將使用相同的寄存器,只是它們將是16位而不是8位,并且“e”將替換為“r”,因此寄存器名稱將變?yōu)閞ax,rbx ,rcx,rdx,rbp,rsp,rip和rdi。
內(nèi)存段
.data-存儲(chǔ)全局變量的段;.bss-包含靜態(tài)變量;.text-包含我們的代碼,可能還不夠清楚,所以讓我們用一些代碼來說明一下:
在上面的示例中,我們可以看到以下內(nèi)容:
a和b在.bss中,c被放在堆棧上,而我們的函數(shù)主要在.text中。
讓我們看看如何反匯編程序:
我們將通過編譯上面的一小段代碼進(jìn)行測(cè)試。首先,打開你的終端,并簡(jiǎn)單地一個(gè)接一個(gè)地使用以下命令:
輸出內(nèi)容:
現(xiàn)在,我們已經(jīng)編譯了文件,我們將使用objdump對(duì)其進(jìn)行反編譯,objdump是大多數(shù)現(xiàn)代GNU / Linux發(fā)行版中提供的線性反匯編工具。
- objdump -M intel -d code
輸出內(nèi)容:
你所看到的一切都是正確的!從上面的屏幕截圖中,我們將重點(diǎn)放在“0000000000001119
讓我們繼續(xù)檢查一下我們程序的確切函數(shù):
推送rbp,將rbp放入堆棧;
mov rbp,rsp,將rsp放入rbp;
mov DWORD PTR [rbp-0x4],0xf將0xf(十六進(jìn)制為15)放入rbp;
mov eax,0x0將0放入eax;
pop rbp將什么放在棧頂中;
如你所見,沒有定義變量。我們甚至可以說也沒有變量名,但是最終我們?cè)趓bp中確實(shí)有15個(gè)。
讓我們看一下這段代碼:
輸出內(nèi)容:
從上面的輸出中,我們將了解每個(gè)會(huì)話的大小。到目前為止,你可以看到引用的bss列的大小為“8”。
現(xiàn)在讓我們看看如果通過添加新變量來修改代碼:
輸出內(nèi)容:
如你所見,bss從8個(gè)字節(jié)增加到12個(gè)字節(jié),我們可以輕松地認(rèn)為我們的全局變量存儲(chǔ)在bss中,因?yàn)樗闹荡_實(shí)增加了。
現(xiàn)在,我們將在主函數(shù)中包含一個(gè)靜態(tài)變量,以進(jìn)行另一項(xiàng)測(cè)試,然后看看會(huì)發(fā)生什么。
輸出內(nèi)容:
如你所見,變量沒有像前面的示例那樣存儲(chǔ)在bss中,而是存儲(chǔ)在從512字節(jié)變?yōu)?16字節(jié)的數(shù)據(jù)中??雌饋砗苡腥?,不是嗎?
進(jìn)行最終測(cè)試,以了解如果初始化全局變量會(huì)發(fā)生什么。
輸出內(nèi)容:
我們得到相同的結(jié)果,你知道為什么嗎?全局變量(如果已初始化)將被放入數(shù)據(jù)中。我認(rèn)為如果此時(shí)已經(jīng)開發(fā)了足夠的內(nèi)容,可以讓你了解變量的存儲(chǔ)位置和存儲(chǔ)方式。
內(nèi)存如何運(yùn)作?
現(xiàn)在,我們將不談?wù)撃愕奈锢韮?nèi)存,而是談?wù)揜AM及其如何由操作系統(tǒng)管理。在計(jì)算機(jī)上運(yùn)行的進(jìn)程需要內(nèi)存,而在計(jì)算機(jī)中,內(nèi)存量是有限的。
因此,進(jìn)程必須尋找可用的內(nèi)存才能工作。假設(shè)有多個(gè)進(jìn)程同時(shí)運(yùn)行。如果兩個(gè)進(jìn)程想要訪問相同的內(nèi)存區(qū)域,將會(huì)發(fā)生什么?而且,如果某個(gè)進(jìn)程寫入了一個(gè)內(nèi)存區(qū)域,那么另一個(gè)進(jìn)程將用其數(shù)據(jù)覆蓋該相同的內(nèi)存區(qū)域,那么第一個(gè)進(jìn)程將考慮找到其數(shù)據(jù),但它將找到第二個(gè)進(jìn)程的數(shù)據(jù)。這可能是一個(gè)很大的問題,不是嗎?
通過為每個(gè)進(jìn)程分配一定范圍的虛擬內(nèi)存,在32位系統(tǒng)上限制為4GB,在64位系統(tǒng)上限制為8 GB,這是操作系統(tǒng)的主要函數(shù)用來解決此問題的位置。
每個(gè)進(jìn)程將能夠使用所需的內(nèi)存地址,而不必?fù)?dān)心其他進(jìn)程,操作系統(tǒng)的內(nèi)核將設(shè)法鏈接虛擬內(nèi)存和實(shí)際內(nèi)存。
棧和堆
現(xiàn)在,我們將繼續(xù)進(jìn)行一些非常重要的事情,堆可以由程序員操縱。這是寫入動(dòng)態(tài)分配的內(nèi)存區(qū)域malloc()或calloc()的內(nèi)存部分。
此存儲(chǔ)區(qū)域沒有固定大小,它根據(jù)我們的要求增加或減少,我們可以通過分配或釋放算法保留或刪除塊以供將來使用。堆大小越大,內(nèi)存地址越大,并且它們與堆棧中的內(nèi)存地址越接近。與堆棧不同,除了物理內(nèi)存限制外,堆中變量的大小不受限制。
程序中的任何地方都可以使用指針訪問堆中存儲(chǔ)的變量,堆棧的大小也可變,但是堆棧的大小增加得越大,內(nèi)存地址減少的越多,從而接近堆的頂部。函數(shù)的堆??蚣苁嵌褩V械囊粋€(gè)存儲(chǔ)區(qū)域,在其中存儲(chǔ)了調(diào)用此函數(shù)所需的所有信息,該函數(shù)還有局部變量。
理解堆棧的概念
讓我們從LIFO開始講起,它并不代表任何復(fù)雜的事情,因?yàn)槲覀冎耙呀?jīng)看到過。 LIFO代表后進(jìn)先出。這就是說,放到棧上的最后一件事是我們要發(fā)布的第一個(gè)東西,尤其是通過pop和push看到的。
最終緩沖區(qū)溢出
最后,我們進(jìn)入開發(fā)部分。讓我們來看下面的一小段代碼。
該代碼看起來完全正常,在學(xué)習(xí)“C”語言時(shí)必須使用scanf函數(shù)。但是,如果我們看一下此示例中的堆棧,該怎么辦。
- [buffer (100)] [int a] [saved ebp] [saved eip]
你可能想知道保存的ebp和eip是什么?其實(shí),我們不在乎“保存的ebp”,我們感興趣的是“保存的eip”。你還記得eip包含什么嗎?下一條要執(zhí)行的指令的地址。如果我們更改此地址,我們可以執(zhí)行任何操作!
但是,如何更改此值?這很簡(jiǎn)單!你會(huì)發(fā)現(xiàn)scanf不會(huì)檢查接收到的字符數(shù)!讓我們用以下一段代碼來演示。
如果在執(zhí)行scanf時(shí)給出的值太大(例如,多個(gè)“A”),則會(huì)導(dǎo)致緩沖區(qū)溢出。因此,該程序?qū)⑾蛭覀兎祷胤侄五e(cuò)誤。但是,如果我們通過有效地址更改“A”值,則可以跳轉(zhuǎn)到任何位置,特別是在adminfunction()上。
緩沖區(qū)溢出和遠(yuǎn)程堆溢出的區(qū)別,以iOS Mail 客戶端MFMutable中的遠(yuǎn)程堆溢出為例。
在分析代碼流時(shí),我們確定了以下內(nèi)容:
1.以原始MIME格式下載電子郵件時(shí),會(huì)調(diào)用函數(shù)[MFDAMessageContentConsumer ConsumerData:length:format:mailMessage:],并且該函數(shù)也會(huì)多次調(diào)用,直到電子郵件以交換模式下載為止。它將創(chuàng)建一個(gè)新的NSMutableData對(duì)象,并為屬于同一電子郵件/ MIME消息的任何新流數(shù)據(jù)調(diào)用appendData:。對(duì)于其他協(xié)議(例如IMAP),它改用-[MFConnection readLineIntoData:],但邏輯和漏洞是相同的。
2.NSMutableData將閾值設(shè)置為0x200000字節(jié),如果數(shù)據(jù)大于0x200000字節(jié),它將把數(shù)據(jù)寫入文件,然后使用mmap系統(tǒng)調(diào)用將文件映射到設(shè)備內(nèi)存。閾值大小0x200000可以輕易增加,因此每次需要添加新數(shù)據(jù)時(shí),都會(huì)重新映射文件,并且文件大小以及mmap大小也會(huì)越來越大。
3.重新映射是在-[MFMutableData _mapMutableData:]內(nèi)部完成的,該漏洞位于此函數(shù)內(nèi)部。
易受攻擊的函數(shù)的偽代碼如下:-[MFMutableData _mapMutableData:] 在mmap系統(tǒng)調(diào)用失敗時(shí)調(diào)用函數(shù) MFMutableData__mapMutableData___block_invoke。
MFMutableData__mapMutableData___block_invoke的偽代碼如下,它分配一個(gè)大小為8的堆內(nèi)存,然后用分配的內(nèi)存替換data-> bytes指針。
在執(zhí)行-[MFMutableData _mapMutableData:]之后,進(jìn)程繼續(xù)執(zhí)行-[MFMutableData appendBytes:length:],從而在將數(shù)據(jù)復(fù)制到分配的內(nèi)存時(shí)導(dǎo)致堆溢出。
append_length是來自流式傳輸?shù)臄?shù)據(jù)塊的長(zhǎng)度,由于MALLOC_NANO是一個(gè)可預(yù)測(cè)的內(nèi)存區(qū)域,因此可以利用此漏洞。
攻擊者不需要耗盡最后一點(diǎn)內(nèi)存來導(dǎo)致mmap失敗,因?yàn)閙map需要一個(gè)連續(xù)的內(nèi)存區(qū)域。
根據(jù)mmap的操作說明,如果指定了MAP_ANON并且可用內(nèi)存不足,則mmap將失敗。
目標(biāo)是使mmap失敗,理想情況下,一封足夠大的郵件將不可避免地導(dǎo)致失敗。但是,我們認(rèn)為,可以使用其他可以耗盡資源的技巧來觸發(fā)漏洞。這些技巧可以通過多部分、RTF和其他格式來實(shí)現(xiàn),我們會(huì)在稍后再介紹。
另一個(gè)影響可利用性的重要因素是硬件規(guī)格:iPhone 6擁有1GB內(nèi)存;iPhone 7有2GB內(nèi)存;iPhone X有3GB內(nèi)存;
較舊的設(shè)備具有較小的物理RAM和較小的虛擬內(nèi)存空間,因此沒有必要耗盡所有的RAM來觸發(fā)這個(gè)漏洞,當(dāng)mmap在可用的虛擬內(nèi)存空間中找不到給定大小的連續(xù)內(nèi)存時(shí),它就會(huì)失敗。
我們已經(jīng)確定,MacOS不會(huì)同時(shí)受到這兩個(gè)漏洞的攻擊。
在iOS 12中,觸發(fā)漏洞更容易,因?yàn)閿?shù)據(jù)流傳輸是在同一過程中完成的,因?yàn)槟J(rèn)郵件應(yīng)用程序(MobileMail)會(huì)處理更多的資源,從而耗盡分配的虛擬內(nèi)存空間(尤其是UI),而在iOS 13中,MobileMail將數(shù)據(jù)流傳遞到后臺(tái)進(jìn)程(即郵件)。它把資源集中在解析電子郵件上,從而降低了虛擬內(nèi)存空間意外耗盡的風(fēng)險(xiǎn)。
由于MobileMail / maild并未明確設(shè)置電子郵件大小的最大限制,因此可以設(shè)置自定義電子郵件服務(wù)器并發(fā)送包含幾GB純文本的電子郵件。 iOS MIME /消息庫(kù)在流式傳輸數(shù)據(jù)時(shí)將數(shù)據(jù)平均分成大約0x100000字節(jié),因此完全可以不下載整個(gè)電子郵件。
請(qǐng)注意,這只是如何觸發(fā)此漏洞的一個(gè)示例。攻擊者無需發(fā)送此類電子郵件即可觸發(fā)此漏洞,并且采用多部分、RTF或其他格式的其他技巧也可以使用標(biāo)準(zhǔn)大小的電子郵件實(shí)現(xiàn)相同的目標(biāo)。
目前,蘋果修復(fù)了iOS 13.4.5 beta中的兩個(gè)漏洞,如以下屏幕截圖所示:
為了緩解這些漏洞,你可以使用最新版的Beta。如果無法使用Beta版,不過要禁用郵件應(yīng)用程序,并使用不易受攻擊的Outlook,Edison Mail或Gmail。
網(wǎng)站名稱:什么是緩沖區(qū)溢出以及如何利用漏洞
網(wǎng)站路徑:http://fisionsoft.com.cn/article/copohog.html


咨詢
建站咨詢
