php反序列化漏洞

  对php反序列化的基本了解,不光php存在反序列化漏洞

1.反序列化漏洞

  在各类语言中,将对象的状态信息转换为可存储或可传输的过程就是序列化,反之就是反序列化,主要是为了方便对象的传输,通过文件、网络等方式将序列化后的字符串进行传输,最终通过反序列化可以获取之前的对象。

  很多语言都存在序列化函数,如Python、Java、PHP、.NET等。在CTF中经常看到PHP反序列化的身影,原因在于PHP提供了丰富的魔术方法,加上自动加载类的使用,为构写EXP提供了便利。

2.什么序列化和反序列化

  在传递变量的过程中,有可能遇到变量值要跨脚本文件传递的过程。在一个脚本文件中如果想要调用之前脚本的一个变量,但是之前一个脚本已经执行完毕,所有的变量和内容被释放掉了,如何操作?

  serialize和unserialize就是为解决这个问题存在的,serialize(序列化)可以将变量转换为字符串,并且在转换的过程中可以保存当前变量的值unserialize(反序列化)可以将serialize生成的字符串转换回变量

下面是这两种函数的简单解释:

序列化:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php
//一个类
class User
{
//类的数据
public $age = 0;
public $name = '';
//输出数据
public function printdata()
{
echo 'User '.$this->name.' is '.$this->age.' years old.<br />';
}
}
//创建一个对象
$usr = new User();
//设置数据
$usr->age = 18;
$usr->name = 'vergilben';
//输出数据
$usr->printdata();
//输出序列化后的数据
echo serialize($usr)
?>

结果如下

Dl9AnH.jpg

“O”表示对象,“4”表示对象名长度为4,“User”为序列化的对象名称,“2”表示对象中存在2个属性。

“{}”里面是参数的key和value

“s”表示字符串,“3”表示长度,“age”为是属性名称;“i”是interger对象,“18”是value

反序列化:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php

//一个类
class User
{
//类的数据
public $age = 0;
public $name = '';
//输出数据
public function printdata()
{
echo 'User '.$this->name.' is '.$this->age.' years old.<br />';
}
}
//重建对象
$usr = unserialize('O:4:"User":2:{s:3:"age";i:18;s:4:"name";s:9:"vergilben";}');
//输出数据
$usr->printdata();
?>

结果如下:

Dl9Fje.jpg

以上可以看到被序列化的结果转换成了正常语句。

3.PHP反序列化后的其他数据格式

类型 表达
布尔值(bool) b:value —> b:0
整数型(int) i:value —> i:1
字符串型(str) s:length:”value”; —>s:4:”aaaa”
数组型(array) a:\:{key,value pairs}; —> a:1:{i:1;s:1:”a”}
对象型(object) O:\:
NULL型 N

4.如何利用反序列化进行攻击

  PHP中存在魔术方法,即PHP自动调用,但是存在一些调用条件,比如,__destruct是对象被销毁的时候进行调用,通常PHP在程序块执行结束时进行垃圾回收,这将进行对象销毁,然后自动触发__destruct魔术方法,如果魔术方法存在一些恶意代码,即可完成攻击。

常见魔术方法调用条件:

函数名 何时调用
__construct() 当一个对象创建时触发
__destruct() 当一个对象被销毁时触发
__toString() 把对象当作字符串使用时触发
__wakeup() 反序列化恢复对象前调用
__sleep() 序列化对象前调用(其返回需要是一个数组)
__get() 从不可访问的属性读取数据
__call() 在对象上下文中调用不可访问的方法时触发
__callStatic() 在静态上下文中调用不可访问的方法时触发