一、源码审计
进入靶场,发现有一个表单和源码展示链接。
先看源码,进行审计。
include 'config.php'; // FLAG is defined in config.php
if (preg_match('/config\.php\/*$/i', $_SERVER['PHP_SELF']))
{
exit("I don't know what you are thinking, but I won't let you read it :)");
}
if (isset($_GET['source']))
{
highlight_file(basename($_SERVER['PHP_SELF']));
exit();
}
//------------------分界线------------------
$secret = bin2hex(random_bytes(64));
if (isset($_POST['guess']))
{
$guess = (string) $_POST['guess'];
if (hash_equals($secret, $guess))
{
$message = 'Congratulations! The flag is: ' . FLAG;
}
else
{
$message = 'Wrong.';
}
}
$_SERVER['PHP_SELF']
存储的是当前运行脚本的路径,源码分界线以上是通过获取$_SERVER['PHP_SELF']
中的值来高亮源码的功能,但似乎能进行SSRF,先看分界线以下。
通过生成一个64字节的随机数并转为十六进制数作为密钥,要求用户输入的guess
参数与密钥能通过hash_equals()
的比较,如果通过则展示flag。
hash_equals
函数目前没有什么漏洞,它甚至能防止时序攻击。那么我们只能通过尝试改变$_SERVER['PHP_SELF']
来进行SSRF。
假设我们有方法操作$_SERVER['PHP_SELF']
,将其赋值为config.php
,不过preg_match()
函数的匹配成功条件是以config.php
结尾的字符串。注意到basename()
函数,这个函数有一个漏洞不再赘述,利用这个我们可以绕过preg_match()
的死亡exit。
那么如何操作$_SERVER['PHP_SELF']
中的值?
Apeach解析问题
这个问题不会出现在Nginx(1.15.11)上,我测试了Apeach(2.4.39),$_SERVER['PHP_SELF']
中的值取决于url。
测试代码:
//test.php
echo $_SERVER['PHP_SELF'];
- 访问
http://127.0.0.1/test.php
- Output:
/test.php
- 访问
http://127.0.0.1/test.php/flag.php
- Output:
/test.php/flag.php
- 访问
http://127.0.0.1/test.php/flag.php?source
- Output:
/test.php/flag.php
这个特性保证了我们正常访问了test.php
也能操作$_SERVER['PHP_SELF']
的值。这使得在这个题中我们可以改变highlight_file()
的文件,实现SSRF。
二、payload构造
访问http://xxx.xxx/index.php/config.php/%ff?source
原理解释:
index.php: 访问index.php
/%ff: 绕过preg_match,$_SERVER['PHP_SELF']
中的值为/index.php/config.php/%ff
?source: 触发高亮
首先通过/%ff
绕过preg_match()
,basename()
将/index.php/config.php/%ff
处理为config.php
,然后被highlight_file()
读取源码。