BUUCTF[HCTF 2018]WarmUp 1题解
BUUCTF[HCTF 2018]WarmUp 1题解
- 分析
- 解题过程
- 代码审计
- 主体函数
- CHECK函数:
- 构造payload
- 总结
分析
启动靶机,进入网址,是一张滑稽的表情包:
程序化F12
查看源码:
发现注释内容,访问
url:/source.php
得到下面的源码:
<?phphighlight_file(__FILE__);class emmm{public static function checkFile(&$page){$whitelist = ["source"=>"source.php","hint"=>"hint.php"];if (! isset($page) || !is_string($page)) {echo "you can't see it";return false;}if (in_array($page, $whitelist)) {return true;}$_page = mb_substr($page,0,mb_strpos($page . '?', '?'));if (in_array($_page, $whitelist)) {return true;}$_page = urldecode($page);$_page = mb_substr($_page,0,mb_strpos($_page . '?', '?'));if (in_array($_page, $whitelist)) {return true;}echo "you can't see it";return false;}}if (! empty($_REQUEST['file'])&& is_string($_REQUEST['file'])&& emmm::checkFile($_REQUEST['file'])) {include $_REQUEST['file'];exit;} else {echo "<br><img src=\"https://i.loli.net/2018/11/01/5bdb0d93dc794.jpg\" />";}
?>
OK,这时候我们可以知道这是一道PHP代码审计题,接下来就是公式化构造payload时间啦!
解题过程
代码审计
主体函数
我们来到了代码审计阶段,先从主体开始:
if (! empty($_REQUEST['file'])&& is_string($_REQUEST['file'])&& emmm::checkFile($_REQUEST['file'])) {include $_REQUEST['file'];exit;} else {echo "<br><img src=\"https://i.loli.net/2018/11/01/5bdb0d93dc794.jpg\" />";}
首先:
$_REQUEST
是一个关联数组,包含 $_GET
、 $_POST
和 $_COOKIE
的内容
$_REQUEST['file']
表示从用户请求中获取名为 “file” 的参数值
用途:获取表单数据、URL 参数、Cookie 值等用户输入
if
判断了以下几个条件:
- file的值是否不为空
- file的值是否为字符串
- emmm::checkFile(file)的回显是否为1
当上面的所有答案都为是,执行:
include $_REQUEST['file'];
反之,输出滑稽表情包。
接下来我们的任务就是解析emmm::checkFile()函数,以绕过waf条件,访问文件。
CHECK函数:
class emmm{public static function checkFile(&$page){$whitelist = ["source"=>"source.php","hint"=>"hint.php"];if (! isset($page) || !is_string($page)) {echo "you can't see it";return false;}if (in_array($page, $whitelist)) {return true;}$_page = mb_substr($page,0,mb_strpos($page . '?', '?'));if (in_array($_page, $whitelist)) {return true;}$_page = urldecode($page);$_page = mb_substr($_page,0,mb_strpos($_page . '?', '?'));if (in_array($_page, $whitelist)) {return true;}echo "you can't see it";return false;}}
一步一步分析:
$whitelist = ["source"=>"source.php","hint"=>"hint.php"];
这是一个关联数组,关联数组的主要特点是键值对数组。
键(key):“source” 和 “hint” - 这些是白名单标识符;
值(value):“source.php” 和 “hint.php” - 实际允许访问的文件名;
这里我们发现了另一个文件hint.php,是一个提示,访问网址显示:
hint.php
提示了flag的文件名。
我们接着分析代码:
if (! isset($page) || !is_string($page)) {echo "you can't see it";return false;}
上面这段代码这段代码判断参数是否为NULL,是否为字符串。
if (in_array($page, $whitelist)) {return true;}
上面这段代码判断参数是否在白名单中(即判断参数的值和数组的值是否相同)。
$_page = mb_substr($page,0,mb_strpos($page . '?', '?'));if (in_array($_page, $whitelist)) {return true;}
mb_strpos($page . '?', '?')
:
若$page
包含 ?
:返回第一个?
的位置;
若$page
不含?
:返回字符串长度。
mb_substr($page, 0, mb_strpos($page . '?', '?'))
:
$page
:原始输入;
0
:起始位置;
mb_strpos($page . '?', '?')
:从 mb_strpos()
获取的长度。
这个函数截取了参数从最开始直到第一个?
之间的子串,?之后的子串被丢弃。
请注意这段代码,我们考虑是否能够利用函数的特性,在?之后构造一段代码
之后的代码是:解码url,再次判断参数是否在白名单中。
构造payload
/?file=hint.php?/../../../../ffffllllaaaagggg
或
/?file=source.php?/../../../../ffffllllaaaagggg
构造思想主要是在?
后加入/
,../
表示上一级目录,然后一级一级的搜索
所以实际上,payload是一层一层实验出来的。
比如最开始是:
/?file=hint.php?/../ffffllllaaaagggg
发现网页为空,说明该目录中没有目标文件,那么就再尝试上一级目录。
顺便提一下:在原有的payload基础上在加几层都不会影响文件显示,说明这个文件在网站的根目录下
最后的flag是:flag{966bef86-ebda-43d8-8703-8e6a57b211de}
总结
本题考察的是PHP代码阅读能力和常见的目录遍历能力。
本题涉及到的PHP函数整理如下:
mb_strpos($page . '?', '?')
: 返回第一次出现?的数组下标
mb_substr($page, 0, mb_strpos($page . '?', '?'))
:截取第一个字符到?出现之前的字符(不包括?)
这道题的提示还是比较明显的,难度不是很大,但是对于最后?/../../
的构造原理是否有普适性,笔者不解,总的来说也是一道适合入门的题目。