HCTF2018 WarmUp1代码审计:include分割符
HCTF2018 Warmup1 代码审计
第一步:查看源码
一张大脸,先检查源码:发现:

直接访问这个文件,发现一堆代码,需要代码审计:
第二步,开始审计
1 |
|
这道题总结出来的经验:
先不管前面的类,直接看后面的怎么包含file。
因为我们的逻辑很清楚:
要想得到下一步的线索,就必须利用输入的payloads得到更多的回显,而要得到回显,就是要成功通过前面checkFile()的检查,使得我们传入的被$_REQUEST参数能够被include包含,从而取得回显。
在类后面要被include的条件是:
file的内容是非空的,
file要是字符串
在emm类里面继承过来的file文件需要经过处理之后返回为true
关键是checkFile要为true。那我们就来看checkFile里面怎么让file检查为true.
第一步,参数传入,以获得线索
这里我们就先看了前几个代码块,先看看要满足他们的条件,我们需要传入怎样的参数。
这也算是做题的经验了,因为我们不可能一步到位直接得到flag所在文件的名称,先要对前面的函数进行试探输入payloads得到线索。
1 | $whitelist = ["source"=>"source.php","hint"=>"hint.php"]; |
($page在这里指的是checkFile()这个函数里面的参数)
首先定义了一个数组,其中有source.php和hint.php,如果没有检查到page或者page不为字符串,那么就会返回为false:
有返回,那这个checkFile函数也就不会再继续下去了,并且也会返回false,导致我们的$file参数无法被include读取。
这里有个重要的知识点:
即if括号内部的布尔值对程序整体运行的影响,以及return对整体程序的影响,具体见我分类分在php类别里的博客。
由于hint.php在白名单中,完全符合整个checkFile()返回true的条件。
那我们先试一下hint.php,显示:

第二步,思考如何利用后续代码块特性绕过。
这里我们肯定不方便直接抓取这个文件,(这里的直接抓取就是指直接把ffffllllaaaagggg引入REQUEST里面的file参数内,因为这样肯定会被checkFile过滤掉并且返回false,无法被include读取)
并且根据这道题后面的条件来看,我们要读取这个文件就要使用上include函数,所以我们要继续往后看,看还需要绕过哪些条件。
1 | $_page = mb_substr( |
解读代码:
这里进行了一个代码的截取,操作,是我们绕过checkFile()函数的关键
这里substr截取代码时的第三个参数是截取代码的长度,第二个参数是开始的长度
而巧妙之处就在于:
1 | mb_strpos($_page,'?','?') |
这段代码将page手动加了一个?然后探测?的位置,而在主流编程语言中,字符串位置的参数都是从0开始的,例如我要探测:
1 | mmk&nina? |
这里?的位置在参数8的位置,而截取的时候,长度是8,所以刚好把?前面的截取走了,并没有包含?
第三步,配置payloads,开始绕过
所以利用这里$_page截取?前面的字符,并只将这部分截取的字符给函数检查的特性,我们可以在payloads的?前写为:
1 | /?file=hint.php? |
这样$_page就会只截取到hint.php,并且它确实在白名单内,我们得以成功绕过
重点又来了:
include有个特性:
在它读取的文件部分,我们可以用“/”符号把文件分成几个部分:
1 | include(example1.php\/example2.php) |
include会挨个读取文件,即使第一个文件不存在,它的读取也不会就此停止
所以:我们可以在hint.php?后面先用”/“符号进行分=分割,再输入我们想读取的文件,flag
这里太坑爹了,flag长成:ffffllllaaaagggg,这居然是在暗示我们,flag在上数四级文件夹的目录下,难绷。
所以最终的payloads为:
1 | /?file=hint.php?/../../../../ffffllllaaaagggg |
?前面是为了绕过checkFile()的检查,后面则是我们的文件目标
而include在读取的时候因为前面hint.php?没法读取,就会读取”/“后的文件,从而顺利得到flag。
大功告成,若有不足,希望dalao指出(