前言

buuctf里web的第一道题,比较怀念,依然不会写。

正文

打开题目,是一个滑稽图
在这里插入图片描述
看不出什么,查看网页源代码,发现了source.php
在这里插入图片描述
访问source.php,发现以下source.php中的代码,
有一说一,我也看不懂是什么意思,看别人的题解知道,这是index.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\" />";
}
?>

发现hint.php,看一下,提示flag在/ffffllllaaaagggg里面,咱也不知道怎么访问,就跑去看看大佬的题解
在这里插入图片描述
看大佬题解知道接下来要分析代码,咱也不会,就看看大佬的分析过程

代码分析

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\" />";
}

整体利用的漏洞就是代码最后的include函数,利用文件包含漏洞
因此,最后的if条件语句是关键,即需要满足 if(true && true && true) ,才会执行include函数,否则输出滑稽图。
! empty($_REQUEST[‘file’]满足true简单
is_string($_REQUEST[‘file’]满足true简单
emmm::checkFile($_REQUEST[‘file’]满足true,需要执行emmm类中的checkFile函数,使得该函数最终返回true才可以

整体细节详解(checkFile函数的目标就是返回true):

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\" />";
}

1、定义了一个类emm

2、在类emm中定义了一个checkFile()函数

3、不执行函数,先判断下面的if语句:if (true && true && true)才能执行include函数,所
以需要满足如下要求:
(1)$_REQUEST['file']不为空,!empty($_REQUEST['file']才会返回true
(2)$_REQUEST['file']是字符串,is_string($_REQUEST['file']才会返回true;
(3)checkFile($_REQUEST['file']返回true,emmm::checkFile($_REQUEST['file']才
会返回true
因此,满足这三个条件,最后才能执行include函数

4、接下来看看checkFile()函数中的内容
(1)第一个if不能return flase,因为这里不能执行{}中的语句,因为不管return true或者return false,都会
终止当前函数的执行,所以需要满足if (false || false)才能继续执行下面的代码,即,$page存在并且是字符串;
(2)第2个语句可以执行,返回true
那么,就需要$whitelist中存在$page
注:in_array函数是检查数组中是否存在某个值(找到true;找不false),特别注意这是在数组的键值中找,不包括键

5、这样的话我们就可以构造payload
(1)测试payload1:
http://111.198.29.45:56708/index.php?file=source.php
执行了source.php,输出了里面的内容

(2)测试payload2:
http://111.198.29.45:56708/index.php?file=source.php../../../../../ffffllllaaaagggg
我想得到ffffllllaaaagggg时就出问题了,因为ffffllllaaaagggg不在$whitelist数组的键值中,并且
继续执行代码后,下面$_page=source.php../../../../../../../ffffllllaaaagggg,第3if语句false
urldecode后$_page=source.php../../../../../../../ffffllllaaaagggg,mb_substr后还是这个,第4
if语句还是false,最后输出you can't see it,还return false,这还玩啥

该如何解决?我需要include ffffllllaaaagggg文件,而且需要使用../,怎样绕过?
注意到mb_strpos($page . '?', '?')没,我们构造一个?即可

(3)测试payload3:
http://111.198.29.45:56708/index.php?file=source.php?../../../../../../ffffllllaaaagggg
构造?的话,第2if语句就不能返回true了,第3if语句一样,也不能执行return语句,第4if语句需要满足if
(true),因为需要执行{}中的内容,最后使得checkFile()函数返回的布尔类型为true

(4)payload3执行流程:此时file=source.php?../../../../../../ffffllllaaaagggg
1if返回false
2if返回false
$_page=source.php
3if返回true,退出checkFile函数,此时核心代码中已满足if(true&&true&&true),即执行include函数
最后include(source.php?../../../../../../ffffllllaaaagggg)

分析得到payload如下

http://xxx/index.php?file=source.php%253f../../../../../ffffllllaaaagggg

访问即可得到flag。

执行流程:

1if返回false
2if返回false
$_page=source.php%3f../../../../../ffffllllaaaagggg
3if返回false
urldecode执行后,$_page=source.php?../../../../../ffffllllaaaagggg
执行mb_substr后$_page=source.php
return true
下面核心代码执行同理
最后include(source.php%253f../../../../../ffffllllaaaagggg)

注意

(1)只要函数中return执行了,就会立即结束函数的执行,继续执行函数外的代码;
(2)||表示任意||两边只要有一边是true,整体就返回true;
(3)in_array函数是检查数组中是否存在某个值(找到true;找不false),特别注意这是在数组的键值中找,不包括键;
(4)mb_strpos查找目标首次出现的位置,从0开始;
(5)mb_substr返回字符串,特别注意的是:mb_strpos获取的数字,在mb_substr不是从0开始,而是代表返回的长度。

最后说一句

其实看了题解也没太看懂,存着,有空多看看,总有一天会看懂的。