php反序列化漏洞,也叫php对象注入
php反序列化漏洞学习
合上吧,没有新姿势。
序列化与反序列化
php中有两个函数serialize()和unserialize()
serialize()
把复杂的数据类型压缩到一个字符串中 数据类型可以是数组,字符串,对象等。测试代码如下
1 |
|
序列化打印的结果为
1 | O:4:"Test":3:{s:10:" Test flag";s:3:"fff";s:7:" * test";s:3:"kkk";s:5:"test1";s:3:"aaa";} |
嗯,如果你发现这个问题,那么说明你认真地思考了,这其实涉及到了 PHP 的属性的访问权限问题,序列化为了能把整个类对象的各种信息完完整整的压缩,格式化,必然也会将属性的权限序列化进去,我们发现我定义的类的属性有三种 private protected 和 默认的 public(写不写都一样),其中
(1)Puiblic 权限:
他的序列化规规矩矩,按照我们常规的思路,该是几个字符就是几个字符,你看test1
属性
(2)Private 权限:
该权限是私有权限,也就是说只能 test类使用,在序列化的时候一定要在 private 属性前面加上自己的名字,但是好像长度还是不对,还少了两个,怎么回事?
这样,我们将其序列化的结果存入一个文件中,我们使用hexdump
看一下内部的结构
我们看到 test 的前后出现了两个 %00 ,也就是空白符,在私有属性序列化的时候格式是
1 | %00类名%00属性名 |
(3)Protected 权限:
我们看 hexdump 的结果
如图所示:
格式为
1 | %00*%00属性名 |
unserialize()
恢复原先被序列化的变量,测试代码如下:
1 |
|
反序列化的打印结果如下
1 | xianyu Object ( [test] => 123 ) |
反序列化的数据本质上来说是没有危害的
用户可控数据进行反序列化是存在危害的
可以看到,反序列化的危害,关键还是在于可控或不可控。
PHP(反)序列化有关的常用魔法函数
__construct()
创建一个新的类时,自动调用该方法
__destruct()
当一个类被销毁时自动调用该方法
__toString()
当把一个类当作字符串使用时就会自动调用该方法
__sleep()
当调用serialize()
函数时,PHP 将试图在序列动作之前调用该对象的成员函数 __sleep()。这样就允许对象在被序列化之前做任何清除操作
__wakeup
当使用 unserialize() 恢复对象时, 将调用 __wakeup() 成员函数
unserialize() 对单一的已序列化的变量进行操作,将其转换回 PHP 的值。返回的是转换之后的值,可为 integer、float、string、array 或 object。如果传递的字符串不可解序列化,则返回 FALSE。
__invoke
当把一个类当作函数使用时,就会自动调用该方法
仅在php5.30+
支持
更多关于php魔术方法的使用请参考php魔术方法
php Session序列化
简介
PHP 内置了多种处理器用于存取 $_SESSION 数据时会对数据进行序列化和反序列化。
处理器 | 对应的存储格式 |
---|---|
php | 键名 + 竖线 + 经过 serialize() 函数反序列处理的值 |
php_binary | 键名的长度对应的 ASCII 字符 + 键名 + 经过 serialize() 函数反序列处理的值 |
php_serialize (php>=5.5.4) | 经过 serialize() 函数反序列处理的数组 |
关键点在于,如果脚本中设置的序列化处理器与php.ini设置的不同,或者两个脚本注册session使用的序列化处理器不同,那么就会出现安全问题。原因是未正确处理|
,如果以php_serilize方式存入,比如我们构造出|
伪造的序列化值存入,但之后解析又是用的php处理器的话,那么将会反序列化伪造的数据(|
之前当作键名,|
之后当作键值)。(php5.6.13版本以前是第一个变量解析错误注销第一个变量,然后解析第二个变量,但是5.6.13以后如果第一个变量错误,直接销毁整个session)。
那么我们通过什么方式将数据注入到session中呢?
一方面,开发者本身将用户可控的数据传进了session,比如joomla等;
另一方面,可通过配置不当可造成session被控。当session.upload_progress.enabled打开时,php会记录上传文件的进度,在上传时会将其信息保存在$_SESSION中。详情见https://bugs.php.net/bug.php?id=71101
实际利用
session.auto_start=On
1
2
3
4
ini_set('session.serialize_handler', 'php');
session_start();
$_SESSION['a'] = $_GET['a'];由于session.auto_start是打开的,所以它会先以默认的
serializer handlers
存入,但在读取时却是以php
序列化的方式,我们可以注入|
,来使得后面任意伪造的序列化字符串,以此来利用反序列化漏洞。session.auto_start=Off
当两个脚本的序列化处理器不同就会有问题出现
test1.php
1 |
|
test2.php
1 |
|
构造好链接:
1 | http://127.0.0.1/test1.php?a=|O:9:"xianyu123":1:{s:2:"hi";s:9:"xianyu123";} |
然后访问test2.php
,就会执行代码,输出xianyu123