寻找溢出点
打开CCPROXY后利用CMD控制台使用telnet命令连接主机127.0.0.1;使用ping命令寻找溢出点,观察到当ping后字符串长度达到1011时不产生溢出,而在长度达到1012时产生溢出,如下图:
确定EIP并修改为JMP ESP值
通过OD的“查找”“所有参考文本字串”进入字符串查找界面。点击右键“查找文本”后输入需要查找的字符串信息“Host not found”进行查找,如下图:
按下Enter后进入“Host not found”所在语句,并在此处设置断点。关闭代理,重新利用OD进行CCPROXY的加载,并利用CMD执行“telnet 127.0.0.1”后进入CCPROXY的控制台,ping一个可以产生的长1012的字符串后,观察到OD成功断下。按F8单步步过执行至“retn”语句,观察到此时ESP值为0x0111 66F0,如下图:
为使得成功执行shellcode,因此需要修改地址0x0111 66F0中内容为JMP ESP的地址,使得retn命令执行后,可以成功跳转至JMP ESP语句,以保证shellcode的执行。
点击右键“查找”“命令”搜索JMP ESP指令,如下图:
观察到在该程序自有模块中,不存在JMP ESP指令,因此尝试在程序调用的其他模块中进行命令的查找。点击右键“查看”进入“模块kernel32”进行命令的查找,如下图:
在地址0x7C87 4413处查找到JMP ESP命令,如下图:
修改retn返回地址ESP地址0x0111 66F0中内容为JMP ESP所在地址0x7C87 4413,如下图:
按F7单步步入,进入到JMP ESP处执行,如下图:
但由于该地址不好转换成字符串,即ASCII最大到0x7F,而低0x20位不可键入,因此此时有两种方法进行转换:
- 第一, 在编写shellcode时,使用“\x”转义符,使得该地址按照十六进制形式输入;
- 第二, 重新在调用到的模块中寻找JMP ESP命令,使得其地址满足每个都在0x20到0x7F之间,以保证可以直接使用字符串输入,而不借用“\x”转义符。
以下提供第二种方案的可行性证明,在“WININET”0x766B3823处找到,如下图:
该地址恰巧属于0x20至0x7F之间,可转换为字符串“#8kv”输入。
编写并生成添加用户的Shellcode
添加用户需要调用“system()”函数,而该函数封装在“msvcrt.dll”文件中,同样在OD的模块中查找,观察到CCPROXY在执行过程中调用了“msvcrt.dll”文件,如下图:
因此直接在VC6中建立一个Win32 Console Application,并编写shellcode,如下:
在VC中调试,点击右键“Go To Disassembly”进入反汇编界面,如下图:
需要注意的是,由于CCPROXY已经加载了system()函数所需的“msvcrt.dll”模块,因此只需要调用system()函数的汇编部分代码即可,即从0x0040 103C处开始,到0x0040 10A3处结束。根据此信息,我们在Memory中查找对应的十六进制数,以便作为shellcode隐藏在ping命令中,保证JMP ESP后执行shellcode,如下图:
将该部分数据copy至UltralEdit中并用列编辑模式去掉没用的信息,仅留下shellcode对应十六进制数部分,并使用转义字符“\x”使其保持十六进制格式输入,如下图:
调试发现,在执行过程中,EBP为0x0111 6700,输入ping命令的字符串信息地址为0x0111 621C,在栈中查看,发现恰好对应,继续向下查找发现对应填充字符“aaaa…aaa”,由于执行JMP ESP后,将回到EBP即0x0111 6700处继续向下执行代码,因此查看栈中该处信息,观察到其上便是我们所构造的ping字符串,如下图:
分析跳转后所在位置执行的恰好为ping命令中第4位至第1012位的内容,因此,我们在这区间添加shellcode,以保证其正确执行。
根据上述描述,最终构建的ping命令字符串为:4个随意字符做填充使用,十六进制版本的shellcode,无用字符一直填充至第1012位,第1013位至第1016位为JMP ESP命令地址转换成的字符串,即“#8kv”。
编写程序完成攻击
攻击代码如下:
打开CCPROXY,然后直接运行编写好的攻击代码,在OD中进行查看,观察到,JMP ESP执行结束后将执行编写好的shellcode,如下图:
执行完赋值命令后system()参数变为“net user ****LIU /add”,如下图:
输入“net user”进行shellcode执行验证,shellcode成功执行,如下图:
实现远程控制shellcode的编写
利用CCPROXY的溢出漏洞来实现远程控制,就shellcode目的本身而言,希望完成自动实现允许该主机可被远程控制的设置修改。搜索到WMIC提供了修改设置的可能,而其同时提供了命令行接口,因此我们可以直接使用命令行完成,即调用system()函数来完成DOS命令的控制。命令“wmic PATH win32_terminalservicesetting WHERE (__Class!="") CALL SetAllowTSConnections 1”可以完成开启允许远程控制的设置。
将该部分shellcode写入程序,仅更改shell部分,其余代码保持与添加用户代码的一致性,得到攻击代码,利用OD调试并执行shellcode,进入CMD界面使用“net start”命令进行查看,观察到,成功打开远程控制,如下图:
还有一些其他方式,也可以实现远程控制,但介于ping所能利用的shellcode空间大小仅有1008位,且其他方式需要进行的验证很多,在此不适用于攻击利用。但同时CCPROXY也提供了其他访问方式,如HTTP代理的溢出漏洞出现在第4056位,因此可利用的shellcode空间将更大。以下将对其他远程控制方案进行介绍:
- 第一, 通过“mstsc”命令打开远程控制,这时会要求输入被控制端的IP地址,同时要求进行用户名和密码的验证;当然成功的条件还包括被控制端允许被远程控制;
- 第二, “nbtstat”命令可以得到远程主机的NEYBIOS信息,包括用户名、所属工作组、MAC地址等新信息,这将有利于增大攻击成功概率;
- 第三, “net view”命令可以查看远程主机的共享资源;
- 第四, “net use”命令可以将远程主机的共享文件映射到本地,并可以通过建立IPC连接来上传文件,以此来进行进一步操作;
- 第五, “net start”命令用于开启远程主机的相关网络服务;与其配套的“net stop”命令可以停止一些限制性的服务,以达到更好的攻击效果;
- 第六, “telnet”命令下,可以使用“open IP”来达到远程登陆的效果。
根据上述方法,将多种攻击方式和可利用的命令、漏洞结合起来,将能更好的实现远程控制与攻击操作。
探究溢出产生的原因
由于当产生溢出时,出现了调用ntdll.dll中的 KiUserExceptionDispatcher()函数来进行异常处理,并调用MassageBoxA弹出窗口来提醒异常原因。因此利用OD查看“Host not found”位置为0x0043037E。打开IDA,进入该部分函数所在位置即函数“sub_430300”进行分析,如下:
观察到该架构中进入“Host not found”字符串部分前,经历了两个if-else函数,而且均输入else部分。按下F5进行反编译代码分析,如下图:
观察到,字符串“Host not found”所在函数存在sprintf()函数和strcat()函数,sprintf()操作为,以参数name的形式,将用户输入的ping命令字符串送到buf中,而由于字符串大于buf长度而产生溢出;strcat()操作为将buf的字符串追加在v4+8末尾,当v4空间不够时溢出;因此存在两中溢出可能。
进行分析得知,v4+8为提示字符串“Host not found: ”,其后追加ping命令后正常输出,如下图:
可见,sprintf()的引用是在CCPROXY自身中而并非引用“msvcrt.dll”中,因此这也是没有API没有拦截到溢出的原因。故当执行返回result操作后,原有溢出导致的EIP变化,最终影响了程序的执行过程,从而导致了异常处理函数的调用。
遇到的难点与问题分析
问题1:
实验过程中发现每次溢出点的位置都不确定,笔者遇到溢出点的变化从刚开始的1012处溢出,到1009处溢出,如下图:
因此决定根据栈中的内容来确定溢出点和retn返回的具体位置究竟要填充至多少位,选择“aaaaa…aaa”作为填充字符串,结尾使用“FFE4”作为标志,用OD进行调试后发现,当执行至retn命令时,ESP中存储的内容不是完整的标志位,而在栈中检查,发现少使用了3个字符进行填充,如下图:
增加3个字符后,成功使得定位标志恰好占用返回地址位,因而确定了溢出后可利用的是ping中字符串的第1013位至1016位,如下图:
问题2:
由于数据填充的末尾需要时JMP ESP命令所在地址,而由于该地址最好由ping命令直接输入而不是修改,因此需要找到符合ASCII码可用字符输入范围内的JMP ESP地址。在“右键”“查看”“WININET”模块中找到了一个符合该标准的JMP ESP命令,如下图:
如图所示,该地址0x766B3823所对应的ASCII字符为“vk8#”;又由于栈从高地址向低地址生长,因此在ping命令后的EIP对应位置应为字符串“#8kv”。观察到,当使用该字符串对ping名利进行结尾填充时,执行至retn时,将要跳转的地址为0x766B3823,如下图:
后发现直接使用转义字符“\x”也可以完成,这样就不用特意去寻找在ASCII码范围内的JMP ESP地址了。
问题3:
起初,shellcode编写过程中,对栈空间进行了“sub esp, 15H”的预留操作,但在shellcode执行过程中,尽管进行了system()的调用,仍然不能添加用户。错误的原因在于,第一,此时esp占用了起始net位置,将导致数据的覆盖;第二,最关键的地方在于esp是按照4字节对齐的,因此不符合标准。
问题4:VC构建过程中的报错
- fatal error C1083: 无法打开包括文件:“stdafx…h”: No such file or directory
解决办法:对工程文件设置后无果,直接删除不使用该预编译文件即可。 - “unresolved external symbol __imp__WSACleanup@0”
解决办法:产生一些列关于WinSock2的报错原因是,ws2_32.lib这个lib没有link上去,增加“#pragma comment(lib, “Ws2_32.lib”)”即可。 - unhandled exception in .exe :0xC0000005:Access Violation.
解决办法:编译通过,但运行就出现该问题,可能是指针存在错误,但笔者保证指针没有错误,因此直接重启VC,成功解决。