[HCTF 2018]WarmUp
[HCTF 2018]WarmUp
题目来源:buuctf
题目类型:web
涉及考点:PHP代码审计、文件包含
1. 题目上来只有一张滑稽图片,查看源代码,发现有source.php文件
![](https://images.cnblogs.com/cnblogs_com/blogs/729425/galleries/2326707/o_230715134406_image-20230715201745123.png)
2. 我们进入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\" />";
}
?>
先介绍一下几个基本函数,方便我们后续解题:
highlight_file():对文件进行 PHP 语法高亮显示(对解题没什么影响)
isset():用于检测变量是否已设置并且非 NULL
is_string():用于检测变量是否是字符串
in_array(A,B):搜索B数组中是否包含A这个值
mb_substr(A,B,C):将A字符串从B截断到C,区间为[B,C),左闭右开;例如mb_substr("hello",0,2),则返回的值为"he"
mb_strpos(A,B):返回A字符串中出现B字符串的第一个位置;例如mb_strpos("abc","bc"),则返回值为1
urldecode(A):对A字符串进行url解码
empty():检查变量是否为空
$_REQUEST变量可用来收集通过GET和POST方法发送的表单数据
include可以将PHP文件的内容插入另一个PHP文件,include只生成警告,并且脚本会继续
A.B表示将A、B两字符串拼接
3. 接下来我们依次分析这段代码的各个部分
- 主要部分:
if (! empty($_REQUEST['file']) //表单数据是否非空
&& is_string($_REQUEST['file']) //表单数据是否是字符串
&& emmm::checkFile($_REQUEST['file']) //emmm类中checkFile方法返回是否为true
) {
include $_REQUEST['file']; //将表单数据中的PHP文件插入当前PHP文件(即回显到当前页面)
exit;
} else {
echo "<br><img src=\"https://i.loli.net/2018/11/01/5bdb0d93dc794.jpg\" />"; //页面回显一张图片
}
这段代码要求我们传入一段字符串给file,当file通过emmm类中checkFile方法之后,页面即可回显我们需要的信息
- emmm类:
class emmm
{
public static function checkFile(&$page)
{
$whitelist = ["source"=>"source.php","hint"=>"hint.php"];
//创建了一个关联数据,注意source和hint为键,source.php和hint.php为值
if (! isset($page) || !is_string($page)) { //若page变量未被定义或不是字符串
echo "you can't see it";
return false;
}
if (in_array($page, $whitelist)) { //若在whitelist数组中存在page的值
return true;
}
$_page = mb_substr(
$page,
0,
mb_strpos($page . '?', '?') //返回page变量中第一次出现'?'的位置,若没有出现则返回page的末尾位置
); //将page从0到截断处的内容赋给_page
if (in_array($_page, $whitelist)) { //若在whitelist数组中存在page的值
return true;
}
$_page = urldecode($page); //将page变量url解码后赋值给_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条件判断,我们依次分析:
1. 第一个 if 用于判断page是否为空,当我们没传入任何参数时,页面回显就是那张滑稽图片;
2. 第二个 if 用于判断page的值在不在白名单里面,白名单有 source.php 和 hint.php ,hint.php我们一会查看;
3. 第三个 if 是在page截断后赋值给 _page,再判断 _page的值在不在白名单里;
4. 第四个 if 是先将page进行url解码后赋值给 _page,再做截断和判断。
4. 分析完代码之后,我们先查看下hint.php(这个在代码中提示我们了)
![](https://images.cnblogs.com/cnblogs_com/blogs/729425/galleries/2326707/o_230715134406_image-20230715211917049.png)
5. 题目告诉我们flag在ffffllllaaaagggg中,我们开始构造payload
首先我们思考,当传入参数为source.php和hint.php时,页面均可正常回显,但并不是我们想要的信息。我们考虑向source.php里进行get传参。基本结构如:?file=source.php/ffffllllaaaagggg
但这样显然过不了第三个 if,我们在 source.php 后面加上 '?',构造如:?file=source.php?/ffffllllaaaagggg
,将该 payload 提交,可以通过checkFile方法,但页面无 flag 回显。因此我们再深入尝试,不断往目录下层搜索,最终得到的payload如下:
?file=source.php?../../../../../ffffllllaaaagggg
提交上去,最终得到了flag:
![](https://images.cnblogs.com/cnblogs_com/blogs/729425/galleries/2326707/o_230715134406_image-20230715213219661.png)
6. 存在的一些问题
虽然题目是写完了,但是肯定有小伙伴要问了,第四个 if 的作用在哪呢?确实,我们通过第三个 if 之后,flag就可以出来了,个人认为是出题人没考虑到这点,想用第四个 if 考一下两次url解码操作。
那我们这里考虑到第四个if,构造的payload如下:
?file=source.php%253f../../../../../ffffllllaaaagggg
%253f 即对 '?' 进行两次url编码,在参数传入服务端的时候解码一次,urldecode解码一次就会回到 '?'
flag{00498122-d651-489d-a7ac-a92c1a18882d}
日期:2023.7.15
作者:y0Zero