1 进入链接
进入链接,直接看到一张滑稽
F12查看源代码,发现source.php
访问,出现源码
<?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 代码审计
① class emmm
$whitelist = ["source"=>"source.php","hint"=>"hint.php"];
首先是一个emmm类,出现了另一个地址/hint.php,直接访问,发现如下:
提示flag在ffffllllaaaagggg里面
然后有四个if语句:
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; }
第一个if判断$page是否是字符串,不是则返回false
第二个if判断$page是否存在于$whitelist中,是则返回true
截取$page中"?"前部分
第三个if判断$page是否存在于$whitelist中,是则返回true
将$page解码,再截取"?"的部分
第四个if判断再次处理后的$page是否存在于$whitelist中,是则返回true
②类外
<?php highlight_file(__FILE__); class emmm { …… } 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" />"; } ?>
余下代码说明需要满足三个条件:1、值为非空;2、值是字符串;3、值能通过checkfile(),否则的话就打印3 滑稽
3 解决方案
①目录穿越漏洞
根据include的特性
构造payload:source.php?file=hint.php?/../../../../../../ffffllllaaaagggg (中间的目录层数是试出来的。)
②url二次解码
可以将?经过两次url编码,在服务器端提取参数时解码一次,checkFile函数中解码一次,仍会解码为'?',仍可通过第四个if语句校验
构造payload:source.php?file=hint.php%253f/../../../../../../ffffllllaaaagggg