0x00 基础知识
1 Windows环境
选取wmplayer.exe程序运行时的内存布局示意,包括栈,堆,加载模块(DLLs)和可执行文件本身。
Win32进程空间布局示意。
1.2 入口点(Entrypoint)
运行一个EXE的时候,会先根据IAT表加载相应的DLL,并且用GetProcAddress得到API的真实地址。也就是说EXE运行后,DLL的EP将是第一个被调用的地方,而EXE本身的EP应该是最后被调用的,但它是EXE本身代码的入口。
不同系统平台下反汇编结果。本节程序选取《逆向工程核心原理》一书中的HelloWorld.exe进行调试示意。
Windows Server 2008下反汇编结果(存在地址随机化,相同的exe程序,载入调试器后入口地址与XP下不同)。
Windows XP下反汇编结果。
F7 Step into。
004027A1地址处的RETN指令,用于返回到函数调用者的下一条指令(弹出ESP内容到EIP,然后跳转,0012FFC0处的值是004011A5,小端排序),一般是被调用函数的最后一句,即返回004011A5(JMP 0040104F)
EIP值为004011A5,ESP指针继续向下移动,ESP从0012FFC0指向0012FFC4(ESP+4)。
不同的开发工具生成的启动函数不同。同一种开发工具,产生的启动函数也随版本的不同而不同。
Call 00402524
1.3 函数调用步骤示意
过程描述 | 汇编代码 |
---|---|
参数入栈 返回地址入栈 代码区跳转 栈帧调整 |
push 参数3 push 参数2 push 参数1 call (相当于push+jmp) push ebp mov ebp esp sub esp,xxx |
1.4 函数返回步骤示意
过程描述 | 汇编代码 |
---|---|
栈回收 把ESP所指向内容弹出到EBP(相当于保存的上一栈的EBP弹出) 返回地址弹入EIP并Jump |
ADD ESP,XXX POP EBP RETN (相对于POP+JUMP) |
函数栈帧示意。
1.5 栈溢出基础知识
1.5.1 原理示意
栈溢出就是缓冲区溢出的一种。 由于缓冲区溢出而使得有用的存储单元被改写,往往会引发不可预料的后果。程序在运行过程中,为了临时存取数据的需要,一般都要分配一些内存空间,通常称这些空间为缓冲区。如果向缓冲区中写入超过其本身长度的数据,导致缓冲区无法容纳,就会造成缓冲区以外的存储单元被改写,这种现象就称为缓冲区溢出。栈溢出原理示意入下图。
1.5.2 程序调试
调试带有字符串拷贝的简单程序示意。程序可以用Dev-C++ 4.9.9.2编译,小巧简单。程序代码如下。
#!cpp
#include <string.h>
void do_something(char *Buffer)
{
char MyVar[128];
strcpy(MyVar,Buffer);
}
int main (int argc, char **argv)
{
do_something(argv[1]);
}
调用函数,CALL function.00401290。
函数返回后的下一条执行地址压栈,在栈中可以看到004012EA,函数跳转到00401290。
进入子函数,PUSH EBP,EBP入栈。
MOV EBP ESP 改变栈底,让EBP指向ESP,EBP的内容(0x0022FF78)指向了前一个栈帧,所以[EBP+4]=004012EA。
开辟栈存储空间:SUB ESP,0x98
根据调试截图可知,如果[Buffer]的大小大于0x98字节,strcpy()函数将会覆盖保存的EBP(saved EBP)和保存的EIP(saved EIP),覆盖过程示意如下。
0x01 漏洞调试
2.1 漏洞测试环境
程序描述 | 备注 |
---|---|
Easy RM to MP3 Converter 2.7.3.700 | 存在栈溢出漏洞的软件 |
Windows XP Pro SP3 En MSDN VL(不打任何补丁,关闭DEP) | 模拟受害端 |
Kali-linux-1.0.9-i386 | 模拟攻击端 |
Windbg 6.12.0002.633 X86 | |
Python 2.7.7 | |
Immunity Debugger 1.85 |
2.2 Easy RM to MP3 Converter栈溢出调试过程
利用Perl和Python可以生成不同的m3u文件、POC进行测试。
打开Easy RM to MP3 Converter,加载具有10000个字符A的crash.m3u无效文件,我们发现目标软件捕获了该错误,跳出友好提示。
程序抛出一个错误,但是看起来这个错误被程序异常处理例程捕捉到了,程序并没崩掉。
调整字符个数,继续运行,目标软件在20000和30000之间可以崩溃掉。很明显,EIP 0x41414141是crash.m3u中的数据,说明程序返回地址被覆盖,EIP跳转到0x41414141,但找不到可执行的指令,所以报错。同时,从图中可以看出程序的EIP也可以被我们填充成一个指向恶意代码的地址。
如果使用二分法。用25000个A和5000个B填充m3u文件,如果EIP变为41414141 (AAAA)。那么返回地址就位于20000到25000之间。
如果EIP变为42424242 (BBBB)那么返回地址就位于25000到30000之间。
使用25000A+5000B,可以看到EIP为42424242(BBBB),所以返回地址位于25000到30000之间了。
根据调试信息,返回返回地址为42424242,说明ESP指向的返回地址已经弹出到EIP,则在内存中,栈顶指针ESP会指向EIP的下一个位置,如上图所示。
查看esp中的数据 d esp。
寻找存放shellcode的地址空间原理。
根据函数调用的堆栈平衡原理,缓冲区溢出之后,ESP应该停留在函数(这里假设为:XXCopy)调用之前所在位置上。也就是说,覆盖完EIP之后继续填充的数据都将保存在ESP所指地址中。
我们用BBBB重写了EIP和可以看到ESP所指的缓冲区。在我们调整脚本之前,需要精确的定位出来返回地址在缓冲区的位置,因为如果填充的都是AAAABBBB之类的,区分度不够高。调用metasploit中自带的工具。
[email protected]:/opt/metasploit/apps/pro/msf3/tools# ./pattern_create.rb 5000
重写EIP前面需要覆盖的缓冲区长度。创建一个文件,填充25000+1069个A,再加4个B,EIP应该就会被重写成为42424242。
d esp
26061+4+4 = 26069
Buffer | EBP | EIP | ESP 指向位置 |
---|---|---|---|
A(x 26061) | AAAA | BBBB | CCCCCCCCCCCCCCCCCCCCC |
414141414141414141414141…41 | 41414141 | 4242424242 | |
26061 bytes | 4bytes | 4bytes |
当函数返回,BBBB被置入EIP中(pop ebp,retn),所以流程尝试到地址0x42424242(BBBB)执行。找内存空间存放我们的shellcode。
为了让应用程序崩溃,我们已经向内存中写入了26069 个A,我们已经向保存的EIP存储空间写入了一个新的值(函数返回执行时,RET将弹出并跳转到这个值),我们已经写了一堆的字符C。当应用程序崩溃时,可以查看所有这些寄存器(D ESP,D EAX,D EBX,D EBP,…)。如果你能在这些寄存器中的一个,看到缓冲区里的值(无论是A,还是C),那么你或许可以用shellcode取代它们的值,并跳转到该位置。在我们的例子中,我们可以看到,ESP似乎指向我们的C,所以理想情况下,我们会用实际的shellcode取代C,告诉EIP跳转到ESP的地址。
直接跳到一个内存地址不是一个好的方法(000ff730包含了字符串终止符(NULL: 00) …所以你看到来自缓冲区第一部分的字母A…我们无法到达重写EIP后我们的数据了….另一方面,在Exploit使用内存地址直接跳转是非常不可靠的…因为内存地址会因为系统版本,语言等的不同而不同)
windbg中输入a,然后再输入jmp esp ,报错,直接回车,返回命令输入界面。然后u jmp esp之前的地址。
在地址7c90120e,你可以看到ffe4。这是操作码JMP ESP
现在,我们需要在这些加载的DLL中的某一个,找到这个操作码(opcode)。
查看WinDbg窗口,可以容易找到属于Easy RM to MP3应用程序的DLL。
如果我们能够在这些DLL中找到一个操作码,那么我们就可以在Windows平台上制作可靠的漏洞利用程序。
如果我们使用属于操作系统的DLL,那么我们可能会发现,漏洞利用程序在其他版本的操作系统上无法正常工作。
因此,我们在C:\Program Files\Easy RM to MP3 Converter\MSRMCcodec02.dll
中搜索操作码。
此DLL在地址01c20000和020ed000之间加载。搜索操作码FF E4。
s 01c20000 020ed000 ff e4
当选择一个地址时,寻找空字节是很重要的。
你应该尽量避免使用地址中含有空字节。空字节将成为一个字符串结束符,那么缓冲区数据其余的部分将变得不可用。
s 70000000 l fffffff ff e4
因为我们希望把我们的shellcode放入ESP中(在覆盖的EIP之后放置载荷payload),从列表中选出的JMP ESP地址空间中不能有NULL字节。
空字节作为一个字符串结束,因此所有在它后面的内容会被忽略。
我们在覆盖的EIP之后放置我们的shellcode,这个地址不能包含空字节。
第一个地址起作用的地址是0x01ddf23a。
输入命令可以验证这个地址是否含有jmp esp(在地址01ccf23a处反汇编指令)
u 01ddf23a
0x02 漏洞利用
3.1 弹计算器
如果我们用0x01ccf23a 覆盖EIP,那么 jmp esp将会被执行。esp 包含了shellcode,所以我们就有了一个可用的exploit。首先我们可以用带有“NOP & break”的shellcode测试下。用Windbg调试,软件打开m3u文件。
再次运行程序,用windbg附加,运行,打开新的m3u文件。程序将会在地址000ff745暂停。那么说明jmp esp起作用了。esp开始于 000ff730, 它含有NOPs 指令直到 000ff744。
现在添加真实的shellcode,并开发exploit。再次打开,弹出计算器。
3.2 绑定端口
利用msfpaload生成shellcode,绑定某端口,例如8888。
Shellcode执行成功后,如果没有设置过防火墙,windows防火墙弹出拦截提示,unblock允许。
netstat –ano 查看网络连接情况,可以看到,打开了8888端口,查看进程号PID为388,程序为EasyRMtoMP3Converter.exe。
在Windows防火墙打开的情况下,ping不通,如果防火墙允许8888端口通信,telnet可以连接。
查看网络状态,与windows机器上显示一致。
0x03 问题说明
描述 | 备注 |
---|---|
Metasploit每次生成不同的shellcode的不同输出。所以,如果你看自己的机器上每次看到不同的shellcode,则不必惊慌。 | 需要去掉坏字符 \x00\x0a\x0d\x1a |
使用相同的命令,不同的系统,默认的编码器可能不同 | msfpayload windows/shell_bind_tcp LPORT=8888 R | msfencode -b '\x00\x0a\x0d\x1a' -t perl |
Kali系统中metasploit默认所使用的编码器与BackTrack不一致。 | 参考文献上使用的是Backtrack系统,其metasploit使用的默认编码器是x86/shikata_ga_nai,而Kali在不指定参数的情况下,使用的默认编码器是cmd/powershell_base64 |
Backtrack默认编码器x86/shikata_ga_nai Backtrack系统metasploit所生成的shellcode size 368 |
|
Kali默认编码器cmd/powershell_base64 Kali系统metasploit所生成的shellcode size 985 |
|
使用Kali默认编码器生成的shellcode,程序执行后崩溃。 | |
为了生成可绑定端口的的shellcode,在Kali系统中需要指定编码器 msfencode -e x86/shikata_ga_nai |
参考文献
1) https://www.google.com
2) https://www.corelan.be/index.php/2009/07/19/exploit-writing-tutorial-part-1-stack-based-overflows/
3) https://www.corelan.be/index.php/forum/exploit-writing-debuggers/error-when-executed-u-unassemble-followed-by-the-address-that-was-shown-before-entering-jmp-esp/
4) http://blog.csdn.net/yuzl32/article/details/6126592
5) http://extreme-security.blogspot.com/2013/02/stack-overflows-part-2-executing.html
6) http://cstriker1407.info/blog/a-reading-notes-of-the-devils-training-camp-msfpayload-using-the-tool-and-free-to-kill/
7) http://www.securitysift.com/windows-exploit-development-part-1-basics/
8) 《逆向工程核心原理》