一次性进群,长期免费索取教程,没有付费教程。
教程列表见微信公众号底部菜单
进微信群回复公众号:微信群;QQ群:460500587
微信公众号:计算机与网络安全
ID:Computer-network
一、逆向分析揭开蜜罐中神秘工具的面纱
在虚拟机里搭建了一个蜜罐环境,已经是千疮百孔,估计被各种人做成了“肉鸡”,虽然设置了快照但是一直没有还原过,一直想看看能不能拿到有价值的exploit。有一个黑客在这个蜜罐里摆了一个工具包,还设置了密码,这个工具包是付费的,首先想到了0day,也许这个黑客掌握0day呢,于是果断把工具包拿了出来逆向分析了一下。
工具包里是一个需要输入密码的exe,不多说直接放到OllyDbg里跑一下看看能不能得到密码,程序入口直接是Pushad,按F8键单步执行几步之后就直接返回了,于是首先想到的是加壳,放到PEiD里看一下(见图1)。
图1 PEiD显示加壳情况
果然是加壳了,还好是UPX壳,现在脱UPX壳的脱壳机可以轻易搜索和下载,就不讲手动脱壳的过程了,将UPX壳脱掉之后再用PEiD跑一下看看(见图2)。
图2 UPX脱壳后PEiD显示情况
EP段的值是.code说明UPX壳已经正常脱掉,将脱壳后的程序放到OllyDbg里跑起来,刚开始这个程序只是从寄存器读取了一些参数,直接按F9键让程序完全跑起来。
经过反汇编调试,可以看到这个程序跑起来后在C盘下的一个隐藏的目录里创建了名为1.tmp的文件夹(见图3),同时创建了一个名为01.bat的脚本,然后它将这个脚本运行了起来。接下来我们到C盘下的这个目录里看看这个脚本的内容(见图4)。
图3 隐蔽目录下创建的脚本
图4 隐蔽目录下脚本内容
简单看一下这个脚本的内容,其中%var% == "520."的if语句中暴露了这个程序的密码,in (ips.txt)do (start 02.bat %%i)说明当密码判断成功后,将对ips.txt中保存的IP地址执行02.bat脚本。现在马上打开02.bat脚本,看看脚本的内容(见图5)。
图5 跟踪生成的02.bat脚本
从图5可以看到这个脚本在执行cs.exe文件,%1是它的参数,nc就是常见的nc链接工具。直接输入520.密码,来看看这个工具的作用(见图6)。
图6 工具追踪
至此,这个所谓的收费工具的真面目全部显示了出来,原来就是emm大牛在2008年写的MS08067的exploit,这个收费工具现在网上随处可见,其实就是写了个批量的脚本,并没有什么0day。
二、从CrackMe到逆向破解技术
CrackMe其实是一些小程序,公开给别人尝试破解,编写CrackMe的人可能是程序员、黑客等,他们可能是为了验证自己的程序安全性,也可能为了挑战其他黑客的水平,最终形成了破解技术中CrackMe的独特文化。CrackMe简称CM,也存在CM竞赛形式,多见于一些论坛。
接下来,通过对一个CrackMe破解过程的分析,加深您对破解技术的理解,该CrackMe如图7所示。
图7 来自XX论坛发布的Crackme-160.chm
首先运行一次CrackMe,是一个普通的序列号程序。
这个CrackMe的作者有点偷懒了,上面那行相当于输入,下面那行是需要算的序列号,如图8所示。
图8 程序界面
首先用PEiD检查一下,结果如图9所示。
图9 PEiD查询结果
从图9中可以看到,这是一个没有壳的程序,用VC++ 6.0编译。
首先要找到计算序列号的部分,既然没有壳我们就可以直接用OD载入。
这个程序下断点的方法很多,这里举几个例子。
1、查找参考字符串(见图10)
图10 查找参考字符串
从图10可以看到“User Name must have at least 5 characters.”“Correcrt way to go, You Get It.”“Correct!”这几个比较可疑的字符串,对每个附近的代码简单跟踪分析一下就能找到关键跳转的位置。
经分析可知,计算序列号的代码在这部分,如图11所示。
图11 反汇编代码(序列号计算部分)
2、API断点
尝试使用GetDlgItemTextA、GetMessageA、GetMessageW等API,随意输入一个name,单击Check验证,程序成功在GetMessageA处断下。然后按Ctrl+F9组合键返回,找到程序领空内调用的代码,向下寻找验证部分,或者按Alt+K组合键打开堆栈调用窗口(见图12)。
图12 调用堆栈窗口
从图12中可以看到程序领空内调用堆栈的函数地址为00401556,双击跟入,结果如图13所示。
图13 跟踪代码
从图13中可以发现编译器罗列的API的入口,接下来可以按Ctrl+R组合键查找调用这段代码的参考(见图14)。
图14 查找参考
分别跟入两个call语句,分析附近代码可知在第2个call语句下面的是验证序列号的程序。
找到计算序列号的代码之后,我们在计算处上方及中途下断点,如图15所示。
图15 断点位置
简单阅读代码可以发现,程序用循环来计算序列号,假设程序需要根据每位输入的字符来计算(其实的确是这样),所以可以把断点下在循环的首部,然后输入有规律的数据(比如这里输入的是abcdefg,之所以要这样做是为了找到程序在哪里是否读入每位字符),单击Check按钮让程序断下,分析代码后可以得到这些关键位置(见图16)。
图16 关键变量注释
接着,根据分析出的地址对应的变量写出注册机的计算代码(这里选择使用C/C++来编写注册机,因为C/C++提供了_asm这一关键字,所以可以方便地用汇编来实现,简化了编写注册机的难度)。
通过上面的代码可以看出,输出的key还不是最终的密码,可以用循环最后一次保存的密码来验证计算的结果正确与否(这里用来测试的name是12345678),结果如图17所示。
图17 测试注册机(失败)
-1786453832的十六进制双字的值就是9584E0B8,所以到这里的计算是正确的。
那么为什么计算的结果不是正确的密码呢,继续F8单步调试程序,如图18所示。
图18 计算序列号后代码
在执行完call<jmp.&MFC42.#2818>语句后,ecx和eax值均被改变了,而且ecx存储的值经测试就是name对应的正确密码。由此可以确定call<jmp.&MFC42.#2818>内的代码计算出了密码。
通过OD提供的call的函数名可以看出来,这是一个MFC封装的函数,因为缺少MFC42.DLL的符号表,所以OD没有把对应的函数名告诉我们。
这里可以使用IDA Pro自动下载所需的符号表(也可以手动搜索.pdb文件)来找到对应函数,把CrackMe扔到IDA Pro中,跳转到0x00401642的位置,如图19所示。
图19 加载符号表后的代码注释
由此可见这个函数是CString::Format函数,压入的参数分别是call前3个push中的前2个(第3个push的是返回地址,VC 6.0编译出的程序调用函数时先用ecx保存地址,然后压入堆栈)。
其实有使用MFC经验的程序员应该早已猜出来call要实现的功能,因为call前压入了一个常量参数“%lu”是16进制数的标识。
于是,在注册机代码下添加如下代码:
CString str;
str.Format(_T("%lu"),key);
std:wcout<<LPCTSTR(str);
测试结果如图20所示。
图20 验证注册机
可以看到,得到了正确密码,破解成功。
三、多语言配合完成exploit
接下来将通过对一个程序的逆向分析,讲解C语言、汇编语言、CMD命令等如何协同合作,来完成从漏洞挖掘到分析,再到写入恶意代码,最后完成对一个目标计算机的exploit过程。
有一个朋友写了一个有漏洞的程序,模拟的是一个网络服务。大家都知道,在运行一些网络服务的时候,会在计算机中开启一个端口,用这个端口来监听同样在互联网上执行这个网络服务的其他计算机发来的交互信息,就好像3389、445这些端口一样,由于这个程序存在漏洞,正好可以拿这个程序,在虚拟机中配置的环境下运行一下看看(见图21)。
图21 Server程序运行情况
程序成功运行起来了,它其实是一个模拟的网络服务,既然是网络服务,可以看看端口的开放情况,在CMD下用netstat -an查看结果如图22所示。
图22 Server程序运行后端口情况
果然程序开放了7777端口,这个正在监听的端口其实就是这个正在运行的网络服务开启的。接下来看看这个程序的功能,先正向反汇编一下,看看程序是如何运行的。用IDA Pro打开程序,直接按F5查看伪代码(见图23)。
图23 Server程序反汇编伪代码
从图23可以看到,其实就是用常规的Socket建立套接字进行交流,用WSAStartup初始化,然后畸形了一些bind、listen等对IP端口的监听,但是这里需要关注一些重点的操作(见图24)。
图24 Server程序反汇编分析
图24这里htons后面的0x1E61u,如果将十六进制转换成十进制就是7777,其实这里就是对端口的复制,也就相当于C语言中的sockaddr.sin_port操作(见图25)。
图25 Server程序反汇编分析
紧接着可以看到recv了,这是Socket方法中最为重要的一个函数,按照MSDN中的描述,v7是套接字名称,v17是接收的数组,512是数组大小,这个程序会监听传到这个端口上的数组,接下来要关注一下这个数组了(见图26)。
图26 Server程序反汇编分析
sub_401000这个函数调用了v17这个数组,直接跟进这个函数里看看代码(见图27)。
图27 Server程序漏洞定位
第一个看到的就是strcpy函数,可以初步认为漏洞发生在这里,漏洞挖掘最为重要的是对strcpy敏感,因为大多数的栈溢出都会发生在这个函数中。假设复制目标的buff大小为8Byte,而对于a1的大小并没有进行严格检查,就会造成栈溢出,这里不多介绍,经过这一段的分析,可以初步考虑POC程序怎么写,也就是说,要建立一个与这个IP、端口的通信,然后发送一个畸形字符串到这个IP和这个端口上,如果程序崩溃说明漏洞真的存在。写的程序的关键算法如图28所示。
图28 构造Client程序
其实很简单,通过建立套接字与目标IP端口实现连接,然后通过memset对一个1024byte大小的Exploit_buffer赋值,这个Exploit_buffer就是要发送的字符串,最后通过send函数将字符串发过去,在编译之后输入命令发送这个字符串。这里要说明一下,在虚拟机中可以用另外一个虚拟机对这个虚拟机进行连接,只要能ping通就可以,这里为了省事,就直接向127.0.0.1本地发送,如图29所示。
图29 Client程序连接测试
从图29可以看到这个字符串连接成功,那么看看在接收端发生了什么(见图30),记得要挂载WinDbg(见图31)。
图30 Server程序接收畸形字符串
图31 WinDbg跟踪漏洞触发位置
接收端收到了一大串A,即我们构造的Exploit_buffer中的内容,同时WinDbg又跳了出来,程序崩溃了。EIP指向41414141,这是一片无效的内存地址,而41正是A的ASCII码(十六进制),漏洞果然存在,我们调用kb可以看看被破坏的栈中的内容(见图32)。
图32 WinDbg查看漏洞触发时内存空间情况
至此可以确定,超长串的字符确实可以造成漏洞的触发,问题函数就是sub_401000。接下来要确定溢出点在什么位置,只有找到了确定的地址,才可以正确地构造返回地址及覆盖shellcode,这里省去了在栈中调试的过程,其实就是用memory查看内存覆盖的情况,根据当前esp的地址0012fbb8以及字符串起始的位置来计算长度究竟是多少,最后可以知道漏洞的位置在字符串的第201个字,也就是最开始的v17[201]开始出了问题,于是将POC的目标位置改成其他字符,来确定漏洞是否在那个地方被触发(如图33所示)。
图33 从POC到exploit
这里将Exploit_buffer的第200和201位置的元素改成cccc,这里用0xcc是因为不知道怎么输入cc对应ASCII码的内容,用0xcc表示效果相同,此时再次将这个程序发送过去,WinDbg又跳了出来,这次显示的结果如图34所示。
图34 RET地址的定位
为什么是4141cccc呢?这与栈的操作有关,其实就是低地址向高地址排列,就好像输入01020304时,到栈内就成了04030201。至此,可以确定栈溢出发生时的问题了,其实真实情况如图35所示。
图35 栈溢出时的情况
继续查看栈内的内容(见图36)。
图36 栈内内容
至此可以完全地确定这次exploit应该如何完成了。这里将这个返回地址修改成jmp esp地址,然后将esp也就是0012fbbc后的内容改成我们的shellcode(见图37),就能完成这次exploit了(见图38)。
图37 shellcode的构造
图38 exploit完成
上面的shellcode部分是在exploit-db上找的,其功能是在目标计算机上建立一个名为broK3n的用户,该用户具有Administrator权限。当然,这个shellcode也可以换成其他的,正如刚才所讲,将对应的返回地址修改成1245fa7f,到了栈里就成了7ffa4512,这是一个通用的jmp esp跳转地址,到Windows 7下仍然可以使用,屡试不爽。接下来覆盖shellcode,由于shellcode大小是194字,前面是204字,总大小是512字,不会超过字符串的布置大小。在发送过去之后,接收端显示的是图39所示的内容。
图39 Server接收exploit
打开计算机的用户列表,如图40所示。
图40 漏洞发生后远程添加Administrator用户
BroK3n已经出现在计算机的用户列表中了。当然,这些shellcode还能完成反弹shell、下载执行指定程序等内容。
到这里,对一台计算机的exploit完成了。这个程序非常简单,假如计算机中在运行某些运营商的某些网络服务,而那个网络服务存在一个远程执行代码的漏洞,那么黑客就可以通过上面的这些操作完成对计算机的控制,其后果是很严重的。只是将黑客所有复杂的操作简化下来,精简之后就是上面所讲的内容。虽然如今的计算机存在DEP、UAC等防护,即使如此,有些黑客大牛们仍然能利用各种高深的技巧绕过防护。
四、写在最后
从整个分析中可以知道,计算机的攻击与防护需要熟练使用各种工具软件,通过使用汇编和CMD命令可以了解漏洞是如何产生的,不但使用C语言可以完成对exploit的编写工作,而且使用Python和Ruby也能完成相同的功能。攻击与防护工作绝不是使用单一语言、单个工具软件就能完成的工作,必须要经过长期的学习与研究。
在学习的过程中会发现,与内存打交道其实并不是一件枯燥的事情,想象一下在广袤的内存空间中驰骋,在自由控制指针、控制程序的时候,是不是有种一切尽在掌控的感觉,个中乐趣只有随着逐渐地深入研究之后才能体会。
黑客攻防这条路并没有想象中那么难走,关键在于乐趣。如果您真心想学习逆向分析、漏洞挖掘与漏洞分析,并能写出exploit程序,从而完成一次缓冲区溢出攻击。第一,要能耐得住寂寞。当您花数小时沉浸在内存空间的时候,就是一个编程“修行”的过程,同时也会发现沉浸其中是多么地其乐无穷。第二,就是要多学习、多交流。黑客绝对不是只会拿着工具的“脚本小子”,无论是web、应用、移动或者其他,都需要静心学习。心浮气躁只能浪费自己的时间,很多前辈们都经历过弯路,多跟技术大牛交流可以事半功倍,少走弯路。第三,是要保持兴趣。兴趣永远是最好的老师,保持对事物的兴趣可以让自己坚持,坚持,再坚持。
微信公众号:计算机与网络安全
ID:Computer-network
【推荐书籍】