CVE-2016-7124漏洞复现

CVE-2016-7124漏洞复现

实验环境

操作机:Windows 10

服务器:apache 2.4.39

PHP版本:5.2.17nts

漏洞影响版本

PHP5 < 5.6.25

PHP7 < 7.0.10

漏洞产生原因

如果类中存在__wakeup方法,调用 unserilize() 方法前则先调用__wakeup方法,当序列化字符串中表示对象属性个数的值大于 真实的属性个数时会跳过__wakeup的执行

漏洞复现

编写测试脚本

<?php
header("Content-Type: text/html; charset=utf-8");
/*
__construct: 在创建对象时候初始化对象,一般用于对变量赋初值。
__destruct: 和构造函数相反,当对象所在函数调用完毕后执行。
__toString:当对象被当做一个字符串使用时调用。
__sleep:序列化对象之前就调用此方法(其返回需要一个数组)
__wakeup:反序列化恢复对象之前调用该方法
__call:当调用对象中不存在的方法会自动调用该方法。
__get:在调用私有属性的时候会自动执行
__isset()在不可访问的属性上调用isset()或empty()触发
__unset()在不可访问的属性上使用unset()时触发
*/

class cve_2016_7124{
   
    public $name = "__wakeup()rg";
    public function __wakeup(){
   
        echo "this is __wakeup<br/>";
    }
    public function __destruct(){
   
        echo "this is __destruct<br/>";
    }
}
//$a = new cve_2016_7124();
//echo serialize($a);
//O:13:"cve_2016_7124":1:{s:4:"name";s:12:"__wakeup()rg";}

$a = $_GET['a'];
$a = str_replace("\\","",$a);//通过浏览器传入的数据,特殊符号会自动转义,所以这里使用str_replace过滤掉\
var_dump(unserialize($a));

?>

image-20210222195438631

当表示对象属性个数的值正确时,__wakeup函数正常执行

image-20210222195703750

当表示对象属性个数的值大于真实个数时,__wakeup函数不执行

参考

PHP中序列化与反序列化

CVE-2016-7124漏洞复现

PHP序列化_serialize_格式详解

PHP序列化_serialize_格式详解

1.前言
PHP (从 PHP 3.05 开始)为保存对象提供了一组序列化和反序列化的函数:serialize、unserialize。不过在 PHP 手册中对这两个函数的说明仅限于如何使用,而对序列化结果的格式却没做任何说明。因此,这对在其他语言中实现 PHP 方式的序列化来说,就比较麻烦了。虽然以前也搜集了一些其他语言实现的 PHP 序列化的程序,不过这些实现都不完全,当序列化或反序列化一些比较复杂的对象时,就会出错了。于是我决定写一份关于 PHP 序列化格式详解的文档(也就是这一篇文档),以便在编写其他语言实现的 php 序列化程序时能有一个比较完整的参考。这篇文章中所写的内容是我通过编写程序测试和阅读 PHP 源代码得到的,所以,我不能 100% 保证所有的内容都是正确的,不过我会尽量保证我所写下的内容的正确性,对于我还不太清楚的地方,我会在文中明确指出,也希望大家能够给予补充和完善。
2.概述
PHP 序列化后的内容是简单的文本格式,但是对字母大小写和空白(空格、回车、换行等)敏感,而且字符串是按照字节(或者说是 8 位的字符)计算的,因此,更合适的说法是 PHP 序列化后的内容是字节流格式。因此用其他语言实现时,如果所实现的语言中的字符串不是字节储存格式,而是 Unicode 储存格式的话,序列化后的内容不适合保存为字符串,而应保存为字节流对象或者字节数组,否则在与 PHP 进行数据交换时会产生错误。
PHP 对不同类型的数据用不同的字母进行标示,Yahoo 开发网站提供的 Using Serialized PHP with Yahoo! Web Services 一文中给出所有的字母标示及其含义:
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
N 表示的是 NULL,而 b、d、i、s 表示的是四种标量类型,目前其它语言所实现的 PHP 序列化程序基本上都实现了对这些类型的序列化和反序列化,不过有一些实现中对 s (字符串)的实现存在问题。
a、O 属于最常用的复合类型,大部分其他语言的实现都很好的实现了对 a 的序列化和反序列化,但对 O 只实现了 PHP4 中对象序列化格式,而没有提供对 PHP 5 中扩展的对象序列化格式的支持。
r、R 分别表示对象引用和指针引用,这两个也比较有用,在序列化比较复杂的数组和对象时就会产生带有这两个标示的数据,后面我们将详细讲解这两个标示,目前这两个标示尚没有发现有其他语言的实现。
C 是 PHP5 中引入的,它表示自定义的对象序列化方式,尽管这对于其它语言来说是没有必要实现的,因为很少会用到它,但是后面还是会对它进行详细讲解的。
U 是 PHP6 中才引入的,它表示 Unicode 编码的字符串。因为 PHP6 中提供了 Unicode 方式保存字符串的能力,因此它提供了这种序列化字符串的格式,不过这个类型 PHP5、PHP4 都不支持,而这两个版本目前是主流,因此在其它语言实现该类型时,不推荐用它来进行序列化,不过可以实现它的反序列化过程。在后面我也会对它的格式进行说明。
最后还有一个 o,这也是我唯一还没弄清楚的一个数据类型标示。这个标示在 PHP3 中被引入用来序列化对象,但是到了 PHP4 以后就被 O 取代了。在 PHP3 的源代码中可以看到对 o 的序列化和反序列化与数组 a 基本上是一样的。但是在 PHP4、PHP5 和 PHP6 的源代码中序列化部分里都找不到它的影子,但是在这几个版本的反序列化程序源代码中却都有对它的处理,不过把它处理成什么我还没弄清楚。因此对它暂时不再作更多说明了。
3.NULL 和标量类型的序列化
NULL 和标量类型的序列化是最简单的,也是构成符合类型序列化的基础。这部分内容相信许多 PHP 开发者都已经熟知。如果您感觉已经掌握了这部分内容,可以直接跳过这一章。
3.1.NULL 的序列化
在 PHP 中,NULL 被序列化为:
N; 3.2.boolean 型数据的序列化
boolean 型数据被序列化为:
b:;
其中 为 0 或 1,当 boolean 型数据为 false 时, 为 0,否则为 1。
3.3.integer 型数据的序列化
integer 型数据(整数)被序列化为:
i:;
其中 为一个整型数,范围为:-2147483648 到 2147483647。数字前可以有正负号,如果被序列化的数字超过这个范围,则会被序列化为浮点数类型而不是整型。如果序列化后的数字超过这个范围(PHP 本身序列化时不会发生这个问题),则反序列化时,将不会返回期望的数值。
3.4.double 型数据的序列化
double 型数据(浮点数)被序列化为:
d:;
其中 为一个浮点数,其范围与 PHP 中浮点数的范围一样。可以表示成整数形式、浮点数形式和科学技术法形式。如果序列化无穷大数,则 为 INF,如果序列化负无穷大,则 为 -INF。序列化后的数字范围超过 PHP 能表示的最大值,则反序列化时返回无穷大(INF),如果序列化后的数字范围超过 PHP 所能表示的最小精度,则反序列化时返回 0。
3.5.string 型数据的序列化
string 型数据(字符串)被序列化为:
s::"";
其中 是 的长度, 是非负整数,数字前可以带有正号(+)。 为字符串值,这里的每个字符都是单字节字符,其范围与 ASCII 码的 0 - 255 的字符相对应。每个字符都表示原字符含义,没有转义字符, 两边的引号("")是必须的,但不计算在 当中。这里的 相当于一个字节流,而 是这个字节流的字节个数。
4.简单复合类型的序列化
PHP 中的复合类型有数组(array)和对象(object)两种,本章主要介绍在简单情况下这两种类型数据的序列化格式。关于嵌套定义的复合类型和自定义序列化方式的对象的序列化格式将在后面的章节详细讨论。
4.1.数组的序列化
数组(array)通常被序列化为:
a::{<key 1><value 1><key 2><value 2>…}
其中 表示数组元素的个数,<key 1>、<key 2>…… 表示数组下标,<value 1>、<value 2>…… 表示与下标相对应的数组元素的值。
下标的类型只能是整型或者字符串型,序列化后的格式跟整型和字符串型数据序列化后的格式相同。
数组元素值可以是任意类型,其序列化后的格式与其所对应的类型序列化后的格式相同。
4.2.对象的序列化
对象(object)通常被序列化为:
O::""::{<field name 1><field value 1><field name 2><field value 2>…}
其中 表示对象的类名 的字符串长度。 表示对象中的字段1个数。这些字段包括在对象所在类及其祖先类中用 var、public、protected 和 private 声明的字段,但是不包括 static 和 const 声明的静态字段。也就是说只有实例(instance)字段。
<filed name 1>、<filed name 2>……表示每个字段的字段名,而 <filed value 1>、<filed value 2>…… 则表示与字段名所对应的字段值。
字段名是字符串型,序列化后格式与字符串型数据序列化后的格式相同。
字段值可以是任意类型,其序列化后的

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值