[HCTF 2018]WarmUp
[HCTF 2018]WarmUp
初次访问,这个滑稽应该是代表了作者幸灾乐祸的表情
一开始以为flag隐写在图片里,下载图片后查看里面的内容没有找到flag
查看网页源代码
得到提示:<!--source.php-->
,访问source.php
代码审计
|
|
初步审计,看到代码里出现了个hint.php
,先访问看看
告知flag在ffffllllaaaagggg
,也就是要想办法看到ffffllllaaaagggg
里的内容
先看看emmm类
外的代码
|
|
这题应该是想让我们用文件包含代码include $_REQUEST['file']
去查看ffffllllaaaagggg
里的内容
假设ffffllllaaaagggg
文件和source.php
在相同目录下,构造payload:?file=ffffllllaaaagggg
就可以包含到ffffllllaaaagggg
文件
假设ffffllllaaaagggg
文件在source.php
所在的目录的父级目录(当前目录的上一级目录)下,则构造payload:?file=../ffffllllaaaagggg
就可以包含到ffffllllaaaagggg
文件
但是想要让代码执行到include $_REQUEST['file']
,得满足if
条件判断:
'file'
参数的值不为空 (满足√)'file'
参数的值为字符串 (满足√)- 将
'file'
参数的值传入emmm类
的checkFile
函数后返回真 (暂且未知?)
所以接下来的重点是审计emmm类
的checkFile
函数:
|
|
以上代码中出现的函数名:
通过审计可以得知,checkFile
函数里有三个地方可以返回true
若是要通过第一个地方来返回true,通过URL传进来的file
参数的值只能为source.php
或者hint.php
即:source.php?file=hint.php
或source.php?file=hint.php
,后面是不能跟其他的东西的,很显然是不可以通过第一个地方来返回true,毕竟我们是要去访问ffffllllaaaagggg
文件
若是要通过第二个地方来返回true,通过URL传进来的file
参数的值的格式必须为:source.php?xxxx
或者hint.php?xxxx
当$page
变量的值为source.php?xxxx
或者hint.php?xxxx
,代码会执行到:
|
|
截取$page
变量的值的第一个?
前的字符串给$_page
变量,$_page
变量的值为source.php
或者hint.php
,在白名单内,返回true
所以payload为:
?file=source.php?/../ffffllllaaaagggg
?file=source.php?/../../ffffllllaaaagggg
?file=source.php?/../../../ffffllllaaaagggg
- ……
(把source.php
替换成hint.php
也可以)
在绕过白名单的情况下不断尝试往上级目录包含ffffllllaaaagggg
文件
最终确定payload为:http://09b122ae-c857-4cde-b0c1-0619a97418a6.node4.buuoj.cn:81/source.php?file=source.php?/../../../../ffffllllaaaagggg
可能会有疑惑:在路径前加个
source.php?
难道不会出错吗?服务器会把
source.php?
看作是与当前页面处于同级目录下的一个叫做"source.php?“的目录,如果路径就单单是这个目录,因为目录不存在,就会无法确定而出错。如果路径是
source.php?/..
:相当于是当前目录下的一个"source.php?“目录下的上级目录,尽管"source.php?“目录不存在,但source.php?/..
一定就是当前目录了嘛(有点绕)所以
source.php?/../../../../ffffllllaaaagggg
和../../../ffffllllaaaagggg
的效果是一样的对此我特意做了个实验来验证,放在本文的最下方
当然,也可以从第三处入手来返回true,总的思路差不多
与第二处相比,第三处就多了条代码:$_page = urldecode($page)
,也就是会先把$page
的值通过URL解码后再截取第一个?
前的字符串到白名单里比较
|
|
因为使用超全局变量$_REQUEST
已经被解码了,所以要先把payload通过URL编码两次
最终确定payload为:http://09b122ae-c857-4cde-b0c1-0619a97418a6.node4.buuoj.cn:81/source.php?file=source.php%253F/../../../../ffffllllaaaagggg
(也可以把/
编码一次,但不可以编码两次)
有的人可能有疑惑:为什么不把后面的
/
也编码编码两次?全都编码两次的结果是:
source.php%253F%252F..%252F..%252F..%252F..%252Fffffllllaaaagggg
执行
emmm::checkFile($_REQUEST['file'])
的$_REQUEST['file']
接收参数时自动解码一次:source.php%3F%2F..%2F..%2F..%2F..%2Fffffllllaaaagggg
执行
$_page = urldecode($page)
时再解码一次:source.php?/../../../../ffffllllaaaagggg
这时返回true,开始执行
include $_REQUEST['file']
,但是执行include $_REQUEST['file']
的$_REQUEST['file']
接收到的参数是只解码过一次的:source.php%3F%2F..%2F..%2F..%2F..%2Fffffllllaaaagggg
,服务器会把它直接当作一个文件名
验证实验:
- 服务器:CentOS 7
首先在网站的根目录/www/admin/localhost_80/wwwroot
下有创建一个index.php
作为网站首页,wwwroot
目录下只有以下内容:
|
|
index.php
里的内容:
|
|
接着在/www/admin
下创建一个flag.php
|
|
以上可知flag.php
在index.php
的上上级目录下,正常用?file=../../flag.php
可以包含到flag.php
用?file=xxx/../../../flag.php
同样也可以包含到flag.php
由此可知,尽管"xxx"是不存在的,但服务器仍会把"xxx"当作是一个目录,xxx/..
的意思是"xxx"目录的上级目录,相当于就是当前目录,所以xxx/../../../flag.php
和../../flag.php
是相同路径
参考文章
当我站在传授他人的角度思考问题时
才能真正领悟并融会贯通
博客受益者是未来那个遗忘某个技术的自己