这半年大大小小的各种安全比赛做了好多,总结一下各种比赛中exploit题的解题思路吧。
首先,将exploit题分成两个步骤:
- 发现溢出点
- 利用溢出点
下面就从这两点出发,分别进行详细阐述。
发现溢出点
这个部分主要就是用两种方法:
- 直接拖进IDA里分析程序
- fuzzing
直接分析
比较简单的程序可以用这种方式。简答是指程序内部逻辑比较简单,直观表现就是程序文件比较小。一般来说,这种题目都是在exploit关的前面几道题。
由于溢出的本质是程序把用户输入的「数据」当做「代码」执行了,那么溢出点一定发生在程序接收数据或者处理数据的过程中。所以在分析时,可以通过输入表找到接收数据的函数,直接从接收完数据开始分析。比赛中遇到的函数有:
- recv() 大部分网络程序都会使用这个函数接收数据,如CSAW2013
- scanf() 比较少,但是也有过,如XDCTF线上赛exploit第一题
一般来说,在接收数据时就发生溢出的情况比较少,大部分都是发生在处理数据时。这时候可以找经常出现溢出的函数,比如strcpy,strcat,memcpy等。不过部分情况下,编译器会将这类函数优化为mov-cmp-jnz的汇编语言段,因此还是需要人工分析。
总之,可以直接分析的程序一般都比较简单,就不多说了。
fuzzing
相对复杂的程序就不能直接分析了,这时候我们就需要用到fuzzing。一般比赛中的程序不会太复杂,接收数据的途径也比较少,所以fuzzing这类程序还是比较简单的,大部分都不需要使用fuzzing工具,手动找几个特定点就能fuzzing到溢出点了。原则就是见到输入就给超长字符串,一般试几次就能找到了,比如XDCTF2013决赛的FTPServer,还有XDCTF2013线上赛第六题webio.exe。
通过fuzzing使得程序崩溃后,还需要找到确切的溢出点。这时候需要看是什么标志导致程序崩溃,比如FTPServer的LIST命令、Webio.exe的Host字段,在IDA里寻找相应的字符串,然后通过交叉引用功能,可以迅速找到处理这段数据的地方。之后再慢慢调试,直到找到发生溢出的函数。
利用溢出点
从比赛情况来看,对溢出利用技巧的要求还是比较少的。仅有ISCC2013线上赛溢出部分最后一个题需要bypass DEP,其他比赛大多集中于绕过栈地址随机化。当然这也可能是由于我做比赛不够多,所以才有这样的印象。
绕过栈地址随机化,目前看到的方法如下:
- JMP/CALL reg跳板,这种方法是应用最多的。XDCTF2013的线上赛的一、三、六是这种方法。
- 栈中找buf地址,然后用retn的gadget跳。如XDCTF2013线下赛的FTPServer
- CSAW2013 Exploition300中的方法。