F12
查看源码,界面如下,可以看到右侧代码框内有一行绿色的耀眼文字。应该是线索?/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\" />";
}
?>
$whitelist = ["source"=>"source.php","hint"=>"hint.php"];
source.php
改成 hint.php
即可,然后可以看到一串字符,虽然不是答案,但是我们得到了一个线索,flag 在 ffffllllaaaagggg
里面。flag not here, and flag in 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\" />";
}
$_REQUEST['file']
不认识是什么,先称他为 ¥! empty()
)is_string()
)checkFile()
函数且返回 true
(emmm::checkFile()
)echo
);而如果程序满足三大条件,程序就会 include
他。include
干嘛用,但是可以打开这个链接 https://i.loli.net/2018/11/01/5bdb0d93dc794.jpg ,发现这就是网页上显示的大黄猥琐斜眼笑脸,显然,¥现在不满足三大条件,而要让网页发生点别的,就得让¥满足,我们先从 checkFile()
函数入手。第一个代码块
if (! isset($page) || !is_string($page)) {
echo "you can't see it";
return false;
}
依然是 if语句 的判断:如果 $page
满足
其中之一,则输出 "you can't see it"
并返回 false
。大问题,我们不能让他执行。
第二个代码块
if (in_array($page, $whitelist)) {
return true;
}
依然是 if语句 的判断:如果 page 存在于 whitelist 也就是一开始定义得白名单数组中,就返回 true。这个可以让他执行。
第三个代码块
$_page = mb_substr(
$page,
0,
mb_strpos($page . '?', '?')
);
这里给 $_page
赋了个什么值:
mb_substr()
函数负责获取部分字符串;
mb_strpos()
函数负责查找字符串在另一个字符串中首次出现的位置;
在 PHP
中,字符串后的 '.'
为并置运算符,表示连接两个字符串;
因此,mb_strpos
返回的是 $page
字符串中的字符 '?'
之前的所有字符串,又因为字符串末尾置了一个 '?'
,若原字符串中不含 '?'
则会返回原字符串。
第四个代码块
if (in_array($_page, $whitelist)) {
return true;
}
这里重复了第二个代码块的内容,但是这次判断的是 $_page
,若存在于白名单中则返回 true
。也可以让他执行。
第五个
$_page = urldecode($page);
urldecode()
函数负责解码 URL 编码的字符串,这里将解码后的代码赋给了 $_page
。
第六个代码块
$_page = mb_substr(
$_page,
0,
mb_strpos($_page . '?', '?')
);
这里重复了第三个代码块的内容,但是这次截取的是 $_page
自己的内容。
最后一部分
if (in_array($_page, $whitelist)) {
return true;
}
echo "you can't see it";
return false;
程序再次检查修改后的 $_page
是否在白名单中,若在,则返回 true
。若不在,则输出 "you can't see it"
并且返回 false
。
也就是说,若要 checkFile()
函数返回 true
,则必须控制 $page
参数不为空且为字符串,然后在三次判断是否在白名单中的任意一次让他存在于白名单中即可。而此时,白名单中只有两个成员:source.php
和 hint.php
。
所以 $page
是什么呢?
emmm::checkFile($_REQUEST['file'])
从这段代码可以看出,page 就是 _REQUEST['file'] ,而
file
的内容需要有什么? 在之前我们尝试访问白名单文件时,得到了一个提示:flag in ffffllllaaaagggg
,而我们的最终目的,就是让这个参数被 include
进去。这里利用的其实就是 文件包含漏洞 ,通过 include
包含并打开 ffffllllaaaagggg
即可。因此给 file
赋的字符串必须为包含文件 ffffllllaaaagggg
的路径名。
接下来我们需要让 checkFile()
返回为 true
,由于必须包含 ffffllllaaaagggg
,因此第一个白名单判断肯定无法使用,而第二个 if语句可以。因为第二次判断之前,程序对字符串进行了截取,我们只需保证 ?
之前为白名单成员即可。
当然,也可以用第三个if语句,不过因为调用了
urldecode(page)
对连接进行了二次解码,因此后面需要用%253f
(‘?’ 的二次编码)代替白名单元素后面的?
。
因此,file
可以取值为:
file=hint.php?ffffllllaaaagggg
或者是:
file=source.php?ffffllllaaaagggg
但是,由于我们需要通过 include
打开文件,但是我们需要的文件并不一定在当前目录下,因此,如果打开失败,可在文件名前加 ../
用来转到上一层目录查找。比如:
file=source.php?../ffffllllaaaagggg
仍无法找到可自行增加 ../
继续向上层目录查找即可。
通过在 url
后以 ?
引导来添加参数,而且不同参数可用 &
分隔。即可以在地址栏输入以下内容则传参成功:
https://靶机地址?file=hint.php?ffffllllaaaagggg
一般用 payload
来表示后面这段参数:file=hint.php?ffffllllaaaagggg
将刚才得到的 payload
输入到浏览器地址栏之后,记得加 ?
。可以看到浏览器显示空页面,因为文件并不在当前目录下,因此我们需要添加 ../
来继续查找,可能会需要多个。
在添加五个 ../
之后,终于得到了我们的 Flag
!
所以,最终的 payload
为:
file=hint.php?../../../../../ffffllllaaaagggg
或者:
file=source.php?../../../../../ffffllllaaaagggg
当然还有:
file=source.php%253f../../../../../ffffllllaaaagggg
自此,夺旗成功。
flag
。<!DOCTYPE html>
<html>
<style>
.slickButton3 {
margin-right:20px;
margin-left:20px;
margin-top:20px;
margin-bottom:20px;
color: white;
font-weight: bold;
padding: 10px;
border: solid 1px black;
background: #111111;
cursor: pointer;
transition: box-shadow 0.5s;
-webkit-transition: box-shadow 0.5s;
}
.slickButton3:hover {
box-shadow:4px 4px 8px #00FFFF;
}
img {
position:absolute;
left:20px;
top:0px;
}
p {
cursor: default;
}
.input{
border: 1px solid #ccc;
padding: 7px 0px;
border-radius: 3px;
padding-left:5px;
-webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075);
box-shadow: inset 0 1px 1px rgba(0,0,0,.075);
-webkit-transition: border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;
-o-transition: border-color ease-in-out .15s,box-shadow ease-in-out .15s;
transition: border-color ease-in-out .15s,box-shadow ease-in-out .15s
}
.input:hover{
border-color: #808000;
box-shadow: 0px 0px 8px #7CFC00;
}
</style>
<head>
<meta charset="UTF-8">
<title>用户登陆</title>
</head>
<body background="./image/background.jpg" style="background-repeat:no-repeat ;background-size:100% 100%; background-attachment: fixed;" >
<form action="check.php" method="GET">
<div>
</br></br></br></br>
<p style="font-family:arial;color:white;font-size:20px;text-align:center;font-family:KaiTi;">我是cl4y,是一个WEB开发程序员,最近我做了一个网站,快来看看它有多精湛叭!</p>
</br></br></br></br></br></br></br>
<p style="font-family:arial;color:white;font-size:20px;text-align:center;">用户名:</p>
<div align="center"><input type="text" name="username" style="text-align:center;" class="input" /></div>
<p style="font-family:arial;color:white;font-size:20px;text-align:center;">密 码:</p>
<div align="center"><input type="text" name="password" style="text-align:center;" class="input" /></div>
<div align="center">
<input type="submit" value="登录" class="slickButton3">
</div>
</div>
</form>
<div style="position: absolute;bottom: 0;width: 99%;"><p align="center" style="font:italic 15px Georgia,serif;color:white;"> Syclover @ cl4y</p></div>
</body>
</html>
<style>
标签部分可以不看,他在 html
中负责定义页面样式,对解题没有帮助。需要注意的是注意下面 <body>
标签内,有一个占用很大部分的 <form>
标签。<form>
用于创建表单,也就是说,我们的登录信息应该会通过这里传递。<form>
标签内有两个参数,他们表示用 GET
方法提交到 check.php
页面。<form action="check.php" method="GET">
method
负责设置提交的方法,一共有两种方法:POST
方法 和 GET
方法。
POST
方法
GET
方法
1
和 123
,可以在地址栏中看到我们提交的数据,并且可以发现,我们所在的页面就是 check.php
。且参数名分别为 username
和 password
。SQL注入
,那么,什么是 SQL注入
呢?SQL注入即是指web应用程序对用户输入数据的合法性没有判断或过滤不严,攻击者可以在web应用程序中事先定义好的查询语句的结尾上添加额外的SQL语句,在管理员不知情的情况下实现非法操作,以此来实现欺骗数据库服务器执行非授权的任意查询,从而进一步得到相应的数据信息。 —— 百度百科
select * from user where username='&username&'and password='&password&'
where
用于规定选择的标准。
&username&
和 &password&
分别为用户传入的用户名和密码,如果能在数据库中查询到则返回真值并通过验证。因此,其实只需要让 username='&username&'and password='&password&'
语句为真即可。
' or 1
这样的字符串,那么查询语句就会变成这个样子:select * from user where username='' or 1'and password='&password&'
#
将后面的代码注释掉,就能正确返回真值了。用户名改为 ' or 1#
于是我们得到如下代码:select * from user where username='' or 1#'and password='&password&'
当然,也可以用类似的方法修改密码。
' or 1#
,由于密码不能为空,因此顺便输入一个密码。登录即可。也可以直接修改
url
,但是空格需要使用+
表示。
可以看到 '
和 #
被自动替换为了转义码 %27
和 %23
,我们在输入时也可以直接使用转义码输入。
自此,夺旗成功。
什么都看不到,所以还是直接查看源码吧。源码好长一段,大部分都是界面设计代码,但是主题代码里有一段瞩目的注释:
<!--
$cat=$_GET['cat'];
echo $cat;
if($cat=='dog'){
echo 'Syc{cat_cat_cat_cat}';
}
-->
_GET 变量用于收集来自 method="get" 的表单中的值。if 语句判断表单中
url
后加上 ?cat=dog
,回车查看。貌似,答案出来了……
自此,夺旗成功。
?file=flag.php
。
flag.php
文件即可。查询资料得知,php://filter
与包含函数结合时,php://filter
流会被当作php文件执行。
php://filter
是一种元封装器, 设计用于数据流打开时的筛选过滤应用。 这对于一体式(all-in-one)的文件函数非常有用,类似 readfile()
、 file()
和 file_get_contents()
, 在数据流内容读取之前没有机会应用其他过滤器。php://filter
目标使用以下的参数作为它路径的一部分。 复合过滤链能够在一个路径上指定。详细使用这些参数可以参考具体范例。
—— PHP手册
php://filter
需要加上读取代码,比如 read=convert.base64-encode
,用 base64
编码输出,不然会直接当做php代码执行,而无法查看源代码内容。
综上,构造Payload
:
?file=php://filter/read=convert.base64-encode/resource=flag.php
将上述 Payload
加入URL中,得到如下 base64
编码的字符:
PD9waHAKZWNobyAiQ2FuIHlvdSBmaW5kIG91dCB0aGUgZmxhZz8iOwovL2ZsYWd7MGFkNTg1NDAtZDc0ZC00MWU4LWJkYjQtMDlmNmUxZTNiZjAxfQo=
将 base64
字符解密,得到源代码,其中包含 Flag
:
<?php
echo "Can you find out the flag?";
//flag{0ad58540-d74d-41e8-bdb4-09f6e1e3bf01}
自此,夺旗成功。