PHP反序列化
序列化基础知识
序列化(Serialization)是将对象的状态信息(属性)转换为可以存储或传输的形式的过程。简单来说,序列化就是将对象转换为数组或字符串等格式,而反序列化就是将序列化后的参数换源成实例化的对象
php的一些概念
类是对象的抽象,而对象是类的具体实例,类是抽象的,不占用内存,而对象是具体的,占用存储空间
函数与方法的区别:面向对象的语言中叫方法,面向过程的语言中叫函数
成员变量和属性的概念是一样的
调用函数
在PHP中将数据序列化和反序列化会用到两个函数
serialize 将对象或者数组序列化成有序的字符串,并将字符串返回
unserialize 将字符串还原成原来的对象
序列化的作用:
①方便存储,把对象的字节序列永久放在磁盘中,需要时可以随时调用,大大节省磁盘占用空间。
②方便传输,在传输过程中可以直接传输字节序列,而不是对象,这可以大大提高传输速率。
在PHP中,序列化和反序列化一般用做缓存,比如session缓存,cookie等。
不同的数据类型序列化
用下面的代码例子来演示
<?php
class TEST {
public $data;
private $data2;
protected $data3 = "blbl";
public function __construct($data, $data2)
{
$this->data = $data;
$this->data2 = $data2;
$this->data3;
}
}
$number = 34;
$double = 6.66;
$str = 'user';
$bool = true;
$null = NULL;
$arr = array('benben','dazhuang',123);
$test = new TEST('uu', 123);
echo serialize($number)."\n";
echo serialize($double)."\n";
echo serialize($str)."\n";
echo serialize($bool)."\n";
echo serialize($null)."\n";
echo serialize($arr)."\n";
echo serialize($test)."\n";
?>
运行结果
i:34;
d:6.6600000000000001;
s:4:"user";
b:1;
N;
a:3:{i:0;s:6:"benben";i:1;s:8:"dazhuang";i:2;i:123;}
O:4:"TEST":3:{s:4:"data";s:2:"uu";s:11:" TEST data2";i:123;s:8:" * data3";s:4:"blbl";}
| 类型 | 结构 |
|---|---|
| integer | i:value; |
| double | d:value; |
| string | s:strlen:"value"; |
| Boolean | b:value;(保存1或0) |
| Null | N; |
| Array | a:size: |
| Object | O:size(类名的长度):"类名":size: |
注意:
修饰符(public、private、protected)不同时,序列化后的结果也不同
公有属性序列化时,变量名不需更改
私有属性序列化时,变量名前需要加”%00类名%00”
保护属性序列化时,变量名前加”%00*%00”,所以会多三个字符
PHP反序列化漏洞原理
代码中的 unserialize() 接收的参数可控,服务器未对用户输入的序列化字符串进行检测,导致攻击者可以控制反序列化过程,从而导致代码执行,SQL注入,目录遍历等不可控后果
魔术方法
PHP中把以两个下划线__开头的方法称为魔术方法
php常见的魔术方法
- __construct(),构造函数,当实例化对象的时候会自动调用,最先会执行的一个方法
- __destruct(),析构函数,当对象被销毁时会被自动调用
- __sleep(),执行serialize()时,先会调用这个函数
- __wakeup(),执行unserialize()时,先会调用这个函数
- __toString(),把对象当作字符串使用时,会被自动调用
- __invoke(),把对象当做函数使用时,会被自动调用
- __call(),调用不存在的方法的名称和参数时会触发
- __callStatic(),用静态方式中调用一个不可访问方法时调用
- __get(),获得一个类的成员变量时调用
- __set(),设置一个类的成员变量时调用
- __isset(),当对不可访问属性调用isset()或empty()时调用
- __unset(),当对不可访问属性调用unset()时被调用
- __set_state(),调用var_export()导出类时,此静态方法会被调用
- __clone(),当对象复制完成时调用
- __autoload(),尝试加载未定义的类
- __debugInfo(),打印所需调试信息
PHP魔术方法详解
https://www.cnblogs.com/bcxc/articles/17033498.html
原生类
原生类顾名思义就是php自带的类,这些原生类中内置一些魔术方法,当我们在代码中,找不到可以利用的类时候,可以考虑使用php中自带的原生类
- 注意
不同的php版本环境,自带的原生类也会不一样
利用技巧
- 从代码中找到触发的魔术方法,根据魔术方法获取对应的原生类
- 再利用原生类的内置的魔术方法,达到我们想要的目的
获取原生类代码
<?php
$classes = get_declared_classes();
foreach ($classes as $class) {
$methods = get_class_methods($class);
foreach ($methods as $method) {
if (in_array($method, array(
'__destruct',
'__toString',
'__wakeup',
'__call',
'__callStatic',
'__get',
'__set',
'__isset',
'__unset',
'__invoke',
'__set_state'
))) {
print $class . '::' . $method . "\n";
}
}
}
常见用途
1.目录遍历
2.文件读取
3.构造XSS
4.Error绕过
5.SSRF
6.获取注释内容
利用方法详情
PHP原生类利用:php反序列化字符串逃逸_kkkl
字符逃逸
这个原理比较简单,但解释起来比较复杂,最好需要亲手操作一下,网上有很多师傅写的已经很细了,我就不在这里赘述了
参考文章:https://www.cnblogs.com/kkkkl/p/16898932.html
__wakeup绕过(CVE-2016-7124)
利用版本
PHP5 < 5.6.25、 PHP7 < 7.0.10
原理
当序列化字符串中表示对象属性个数的值大于真实的属性个数时会跳过__wakeup 的执行
最后结果要urlencode的原因
如果不进行url编码,最后输出的结果是片段的,不是全部的,会有类似截断导致结果异常,所以需要进行url编码

浙公网安备 33010602011771号