RHG全称Robot Hacking Game,一般是指对二进制漏洞的自动发现(CrashMe)和利用(ExpMe,PopCalc)的比赛(也就是CTF中常见的PWN类型题目),通常分为自动化漏洞挖掘和自动化漏洞利用两个方向。
漏洞一般是是二进制文件的内存损坏漏洞(堆栈溢出,格式化字符串漏洞,UAF等),今年的BCTF还举办了条件竞争(Race Condition)和自动堆风水的比赛。目前的比赛中的题目基本上是运行在Linux上的X86架构程序(也就是我们PWN入门的那种),32位和64位都有。
比赛的主办方会提供一套基于HTTP请求的API,包括获取题目,提交Crash,提交flag等。通常会进行多轮比赛,每轮持续十分钟到半小时不等,每轮提供不同的题目。
本人在这方面水平有限,仅以此文抛砖引玉,蹭一下网安奥运会的热度,有不足之处还请各位不吝赐教。
目前百度的BCTF的平台(https://anquan.baidu.com/bctf/#/zh-CN/home)还在开放,可以在上面下载到一些题目,以及BCTF的题目API(https://anquan.baidu.com/bctf/#/en/challenge),可以作为开发参考。
对于自动化漏洞挖掘方面的比赛,一般会提供一个二进制文件和一个基本的输入文件,参赛者需要使用模糊测试(fuzz testing)技术来使用题目提供的种子输入文件(seed)对目标程序进行模糊测试,在测试过程中成功触发崩溃(Crash)后,通过比赛提供的API提交触发崩溃的输入来得分。
目前的自动化漏洞挖掘技术通常是指模糊测试技术,另外还有一些如数据流分析,污点分析等技术也可以对二进制文件进行一定程度上的漏洞挖掘,但是通常还是使用效果更好的模糊测试技术。
关于模糊测试这部分,网上有很多的技术文档,具体原理就不细说了,总之就是通过对输入进行变异来发现崩溃,目前最出名的模糊测试的框架是谷歌开发的AFL,AFL经过多年发展,由社区开发了AFL++,WinAFL,AFLGO等多个修改版本(https://github.com/Microsvuln/Awesome-AFL),另外还有一些如libFuzzer等模糊测试框架,参考(https://github.com/secfigo/Awesome-Fuzzing)。
参加这种类型的比赛主要就是做一个足够高效和稳健的AFL套皮工具,跟比赛平台交互下载题目,启动AFL跑Crash,然后拿去提交得分。
在GitHub看到一个开源Repo(https://github.com/0xaww/RHG-AutoPwn-Robot),是针对BCTF的自动化漏洞挖掘比赛的bot,README写的很详细,可以作为开发参考。
对于自动化利用(Automatic Exploit Generation)的比赛,通常只会提供一个二进制文件,与我们平时CTF中的PWN题目基本一致,需要我们的机器人自动对题目进行分析,确定漏洞类型,进行漏洞利用,然后利用API获取flag并提交得分。
通过 file
, checksec
等命令来获取目标文件的基本信息,包括架构,机器字长和动态/静态链接,开启的保护。
如果题目使用了静态链接,我们还需要恢复一部分题目的符号(系统函数名),因为只有拿到这些函数名(如 memcpy
, printf
)才能方便符号执行框架去匹配我们写好的漏洞模式。
我采用的策略是,程序在静态链接时,会将编译器版本的字符串一同编译进去,所以使用 stringfile|grep ubuntu
可以看到程序编译时使用的系统版本,如图使用的是 ubuntu16.04
进行编译的:
那么我们知道 ubuntu16.04
使用的是 glibc2.23
,我们就可以针对这个版本的 libc.so
去生成相应的函数签名。
具体来说,以 system
函数为例,我们可以把多个2.23版本的 libc.so
拿出来,提取他们的 system
函数的字节码,然后只保留序列和字节码相同的部分,构造出一个函数签名,拿这个签名去匹配静态链接的程序,匹配成功的概率很高。
根据目标程序的漏洞类型,一般分为以下几种:
栈溢出
格式化字符串漏洞
对于以上两种漏洞,比较好用的工具有zeratool,使用了angr进行符号执行检测漏洞,实际上使用了一种基于模式的漏洞分析利用策略来检测栈溢出漏洞和格式化字符串漏洞。
zeratool针对不同类型的漏洞利用angr来探测漏洞类型,比如对于栈溢出漏洞会去检测 memcpy
, strcpy
等函数的参数,然后计算缓冲区是否足够;对于格式化字符串漏洞则会检测 printf
等函数,判断格式参数是否可控。
堆溢出,UAF等与堆管理器相关的漏洞
跟踪 malloc
, free
等内存申请释放的函数,然后以一定的操作序列去排布堆块。
之前的BCTF的堆风水相关的题目是这样的:
先进行一些堆块分配(分配10个0x100的堆块),然后输入操作数1和2,根据操作数来执行不同操作:
要求两次操作结束后,分配的两个目标堆块的距离等于给定值。
直接给后门
还有一类题目是类似于逆向,输入特定的key经过一系列检查后,直接返回 system("/bin/sh")
。
利用主要是靠符号执行去探索能够到达漏洞点的输入,然后根据得到的输入去构造利用。总结就是需要针对不同漏洞,根据不同的保护和触发条件来构造利用,目前的比赛的题目都是相对简单的漏洞,所以可以针对每种情况分别写一个利用策略。
直接给后门
没什么好说的,拿angr梭哈就好了,参考zeratool的 winFunctionDetector.py
,探测到后门函数之后用angr去探索到目标函数的路径即可。
格式化字符串
首先是探测到漏洞,找到格式化字符串的函数,然后根据保护去构造利用,由于是自动化利用,所以要尽量追求利用的稳定性和普适性,我的策略是在NX保护未开启或者存在bss段输入的程序上,通过修改返回地址到shellcode上来进行利用,相对更加稳定。如果开启了一些保护就需要进行泄露了,这块也可以参考zeratool的策略。
栈溢出
栈溢出利用需要根据保护和溢出点来区分,总之就是尽量保证利用在一次触发漏洞就能完成,让利用具有更高稳定性。可以针对 ret2text
, ret2shellcode
, ret2libc
等分别写一套模式。
一些开源的自动化利用工具和资源:
https://github.com/ChrisTheCoolHut/Zeratool
https://github.com/SCUBSRGroup/Automatic-Exploit-Generation
https://github.com/xct/ropstar
https://github.com/YSc21/aegg
https://github.com/SmllXzBZ/AEGPaper
可以看出核心其实还是利用符号执行去探索到达目标漏洞点的路径,目前比较流行的符号执行框架有angr,s2e等。有的比赛题目会对符号执行框架本身的缺陷进行一些针对,比如加入多个无限循环条件或者多次跳转,让符号执行的代价无限增加(路径爆炸),所以在执行策略上也应该有所取舍,比如可以结合unicorn等模拟框架来对程序进行一定程度上的模拟执行,规避路径爆炸问题,或者对使用资源进行限制,在占用内存达到一定量的时候就停止路径探测等等。
本文跟根据作者参加过的几次RHG竞赛的经验来浅谈一下竞赛规则和基本策略,关于符号执行技术这块之后大概会专门出一期文章来写一下,然后就是蹭一下网安奥运会的热度,给有需要的战队做一个参考。
目前国内的RHG比赛还是刚起步,相关赛制存在一些漏洞,比如我们写好的机器人如果是跑在本地的,通过API来与服务器交互,那么完全可以使用API把题目下载下来,然后进行一些“人工”智能做题并提交,在比赛方是难以检测的(如之前的BCTF的RHG比赛,今年的没参加不知道有没有改进)。
去年的陇剑杯RHG比赛为了解决这个问题,给每个参赛队伍提供了一台专用服务器,机器人跑在专用服务器上,比赛期间选手无法对机器人进行操作,访问题目的各种接口也只能由专用服务器来访问,一定程度上缓解了这种人工做题的问题。
https://firmianay.gitbooks.io/ctf-all-in-one/content/doc/5_advanced.html
https://github.com/ksluckow/awesome-symbolic-execution
https://triton-library.github.io/
http://klee.github.io/
http://s2e.systems/docs/s2e-env.html
https://firmianay.gitbook.io/ctf-all-in-one/5advanced/5.3symbolicexecution/5.3.4s2e