目录
public、private、protected序列化后的区别
什么是php序列化?
所用到的两个函数
serialize() //函数用于序列化对象或数组,并返回一个字符串。
unserialize() //函数用于将通过 serialize() 函数序列化后的对象或数组进行反序列化,并返回原始的对象结构。
serialize()实例
数组序列化实例
将一组数组序列化后,获得一串字符串
<?php
highlight_file(__FILE__);
$a = array('a' => 'Apple' ,'b' => 'banana' , 'c' => 'Coconut');
$s = serialize($a);
echo $s;
//a:3:{s:1:"a";s:5:"Apple";s:1:"b";s:6:"banana";s:1:"c";s:7:"Coconut";}
?>
对象序列化实例
将一个对象序列化后,获得一串字符串
<?php
highlight_file(__FILE__);
class test_class{ //创建类
var $test = '123'; //在类中新建一个test变量
}
$class1 = new test_class; //创建对象
$class_str = serialize($class1);//序列化对象
echo $class_str;//输出序列化后的字符串
//O:10:"test_class":1:{s:4:"test";s:3:"123";}
?>
unserialize()实例
数组反序列化实例
将一个标准格式的序列化字符串反序列化,会获得以下数组
<?php
highlight_file(__FILE__);
$str = 'a:3:{s:1:"a";s:5:"Apple";s:1:"b";s:6:"banana";s:1:"c";s:7:"Coconut";} ';
$un_str=unserialize($str);
var_dump($un_str);
?>
对象反序列化实例
将一个标准格式的序列化字符串反序列化,会获得一个新的对象
<?php
highlight_file(__FILE__);
$str = 'O:10:"test_class":1:{s:4:"test";s:3:"123";}';
$un_str=unserialize($str);
var_dump($un_str);
?>
序列化字符串格式解析
O:10:"test_class":1:{s:4:"test";s:3:"123";}
对象类型:类名称长度:类名:类中变量个数:{变量:变量名长度:变量名; 变量类型:变量内容长度:变量内容}
a:3:{s:1:"a";s:5:"Apple";s:1:"b";s:6:"banana";s:1:"c";s:7:"Coconut";}
数组类型:数组个数:{变量:变量名长度:变量名;变量类型:变量内容长度:变量内容;.....}
- 序列化类型存在两种:
- 对象类型
- 数组类型
- 可以看出,大括号内,两个分号(;)为一组,两个分号代表着一个变量中的内容。
- {s:4:"test";s:3:"123";}
变量类型
不同的存储类型,序列化后会变换成不同的字母
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
public、private、protected序列化后的区别
- public(公共的):在本类内部、外部类、子类都可以访问
- private(私人的):只有本类内部可以使用
- protected(受保护的):只有本类或子类或父类中可以访问
依照上面3个修饰符序列化后,获得以下结果:
O:10:"test_class":3:{
s:6:"test_1";s:3:"str";
s:18:"test_classtest_3";i:123;
s:9:"*test_2";i:123;
}
- public:序列化后基本无变化
- private:序列化后,会在变量名前加上类的名字,变量名长度也会增加
- protected:序列化后,变量名前会多了个 * ,但为什么变量名长度会变成9个字节?
- 其实protected属性序列化的时候格式是%00*%00成员名
魔术方法
在利用对PHP反序列化进行利用时,经常需要通过反序列化中的魔术方法,检查方法里有无敏感操作来进行利用。
PHP 将所有以 __(两个下划线)开头的类方法保留为魔术方法。
常见的魔术方法
__construct()//创建对象时触发
__destruct() //对象被销毁时触发
__call() //在对象上下文中调用不可访问的方法时触发
__callStatic() //在静态上下文中调用不可访问的方法时触发
__get() //用于从不可访问的属性读取数据
__set() //用于将数据写入不可访问的属性
__isset() //在不可访问的属性上调用isset()或empty()触发
__unset() //在不可访问的属性上使用unset()时触发
__invoke() //当脚本尝试将对象调用为函数时触发
__sleep() //此功能可以用于清理对象,并返回一个包含对象中所有应被序列化的变量名称的数组。
__wakeup() //经常用在反序列化操作中,例如重新建立数据库连接,或执行其它初始化操作。
方法调用实例
__sleep()
serialize() 函数会检查类中是否存在一个魔术方法 __sleep()。如果存在,该方法会先被调用,然后才执行序列化操作。此功能可以用于清理对象,并返回一个包含对象中所有应被序列化的变量名称的数组。如果该方法未返回任何内容,则 NULL 被序列化,并产生一个 E_NOTICE 级别的错误。
__wakeup()
unserialize() 会检查是否存在一个 __wakeup() 方法。如果存在,则会先调用 __wakeup 方法,预先准备对象需要的资源。
__toString()
__toString() 方法用于一个类被当成字符串时应怎样回应。例如 echo $obj; 应该显示些什么。此方法必须返回一个字符串,否则将发出一条 E_RECOVERABLE_ERROR 级别的致命错误。
反序列化漏洞原理
当反序列化 unserialize() 的参数可控时,我们可以构造序列化字符串,从而修改对象内部的变量甚至函数。
案例:
<?php
error_reporting(0);
show_source(__FILE__);
class c{
public $str = 111;
function __wakeup(){
echo $this->str;
}
}
$str = $_GET['str'];
if (isset($str)){
unserialize($str);
}
?>
当我们可以控制反序列化时,我们将可以修改类中变量的内容
poc:
?str=O:1:"c":1:{s:3:"str";s:3:"123";}
当然不会那么简单,只修改值的内容而已,我们还能打个xss
poc:
?str=O:1:"c":1:{s:3:"str";s:24:"<script>alert()</script>";}
当然这个只是一个简单的案例,下一次再讲解php反序列化进阶知识。