Web_php_unserialize(序列化与反序列化)

摘要:
您可以参考PHP反序列反序列化的一个小特性。我还在标题中下载了一个版本的PHP源代码,以验证phpinfo的信息是否在标题中泄漏。您可以使用dirsearch扫描它。这里,标题的环境是PHP5.310。首先看var_unserializer第441行中序列化字符串的第一位。c文件是“O”,所以跳转到yy13。注意这里区分大小写!
题目

题目是这样的

Web_php_unserialize(序列化与反序列化)第1张

很明显是一道 PHP 反序列化的题目 , 直接来看题目给出的流程

  1. 首先判断当前是否存在 GET 参数 " var " , 若存在则对其进行 Base64 解码后存入 $var 变量 . 若不存在则输出当前页面源码

  2. 对 $var 进行一个正则过滤 , 若通过正则过滤 , 则对其进行反序列化操作 , 否则响应提示信息 .

题目中给出一个 Demo 类 , 需要注意一下其中三个魔术方法

  • __wakeup()

    该方法是PHP反序列化时执行的第一个方法 , unserialize()会先检查是否存在 __wakeup() 方法 , 若存在则会先调用该方法 , 来预先准备对象需要的资源( 比如重新建立数据库连接 , 执行其他初始化操作等等 )

  • __construct()

    与其它 OOP( 面向对象 ) 语言类似 , PHP中也存在构造方法 , 具有构造方法的类会在每次创建新对象前调用此方法 ,该方法常用于完成一些初始化工作 .

  • __destruct()

    析构方法 , 当 某个对象的所有引用都被删除 或者 当对象被显式销毁 时 , 析构函数会被执行 .

有关 PHP 其它魔术方法的内容可以参考 PHP 官方文档

有关 PHP 反序列化漏洞的内容可以参考 PHP 反序列化漏洞


解题思路

回到题目中 , 看一看有哪些注意点

  1. unserialize() 方法的参数来源于 GET 请求

    虽然该请求获取的值经过一系列处理 ,包括一个Base64解码和一个正则过滤 , 但至少能确定该参数值是用户可控的 . 事实上这个正则过滤是可以绕过的 .

  2. unserialize() 的 __wakeup() 方法

    在反序列化时 , PHP 会先执行 __wakeup() 函数 . 本题中 __wakeup() 函数的作用为 : 将 $file 变量强制赋值为 index.php , 而题目又提示 flag 在 fl4g.php 中 , 因此这又牵扯到一个老问题了 : 如何绕过 __wakeup() 函数

然后就可以拿到 Flag 了 , 本题其实也就考了两个点 : 如何绕过正则表达式 以及 如何绕过 __wakeup() 方法 .


绕过正则表达式

先来看下序列化后字符串的内容是怎么样的 .

Web_php_unserialize(序列化与反序列化)第2张

Web_php_unserialize(序列化与反序列化)第3张

而正则匹配的规则是: 在不区分大小写的情况下 , 若字符串出现 "o:数字" 或者 "c:数字' 这样的格式 , 那么就被过滤 .

很明显 , 因为 serialize() 的参数为 object ,因此参数类型肯定为对象 " O " , 又因为序列化字符串的格式为 参数格式:参数名长度 , 因此 " O:4 " 这样的字符串肯定无法通过正则匹配

那么怎么办呢 ? 你可以参考 php反序列unserialize的一个小特性 , 我自己也下载了一份题目中版本的 PHP 源码来验证

题目中泄漏了 phpinfo 信息 , 可以用 dirsearch 扫到, 这里就交代下题目的环境为 PHP 5.3.10

  1. 先看 var_unserializer.c 文件 441 行

    Web_php_unserialize(序列化与反序列化)第4张

    序列化字符串的第一位为 " O " , 因此这里跳转到 yy13 .

    注意这里区分大小写 !

  2. yy13

    Web_php_unserialize(序列化与反序列化)第5张

    yy13 会判断下一位的字符是否为 " : " , 若是就跳转到 yy17 , 若不是就跳转到 yy3 , 这里会跳转到 yy17

  3. yy17

    Web_php_unserialize(序列化与反序列化)第6张

    yy17 会判断下一位是否为数字 , 若为数字就跳转到 yy20 , 若为 " + " 号就跳转到 yy19

  4. yy19

    我们来看 yy19 , Web_php_unserialize(序列化与反序列化)第7张

    yy19会判断下一位是否为数字 , 若为数字就跳转到 yy20 , 否则跳转到 yy18

问题来了! 当反序列化操作读取到 " : " 号时 , 下面不管是 " 数字 " 还是 " +数字 " , 都会跳转到 yy20 , 而正则匹配的规则能过滤 O:4 , 却不会过滤 O:+4.

因此 , 我们可以利用 O:+4 这样的写法来绕过正则过滤 .

值得一提的是 : 该利用方式仅能在 PHP 5 中复现 , 在 PHP7 中 , yy17 的规则被修改了 , 因此 " + " 号无法再被利用了

php 7.3.9 var_unserializer.c 文件 783 行

Web_php_unserialize(序列化与反序列化)第8张

yy17 已不再识别 " + " 号~


绕过 __wakeup() 函数

这也是一个老问题了 , 具体可以参考 CVE-2016-7124

来看 var_unserializer.re 文件 371 行

Web_php_unserialize(序列化与反序列化)第9张

object_common2() 函数使用 call_user_function_ex(CG(function_table), rval, &fname, &retval_ptr, 0, 0, 1, NULL TSRMLS_CC) 来调用 __wakeup() 函数 , 但在执行 call_user_function_ex() 函数前 , 需要通过一个条件判断 .

process_nested_data() 函数用于对象的属性检查 , 那么这个对象是何时创建的呢 ? 来看 var_unserializer.re 文件第 359 行

Web_php_unserialize(序列化与反序列化)第10张

在 object_common1() 中 , 调用 object_init_ex(*rval, ce) 函数创建并返回了该对象 .

流程也就是这样的 : 创建对象之后 , 对对象的属性检查 , 若属性检查通过 , 就调用 __wakeup() 方法

若对象属性检查不通过 , 则会跳出 object_common2() 函数 , 不再调用 __wakeup() 函数 . 由于对象及其属性在 object_common1() 中已经被创建 , 因此这里对象将会被销毁 , 从而触发析构函数__destruct() .

因此这里我们仅需要破坏对象属性检查就可以绕过 __wakeup() 函数 , 最简单的方法就是增大对象属性的个数 , 使其饭序列化异常 .

PHP 7 中这部分代码被修改 ,无法再用该方式绕过 __wakeup() 方法


构造 Exp

现在两个考点都已经解决了 , 构造 Exp 变得非常简单

Web_php_unserialize(序列化与反序列化)第11张

Exp : TzorNDoiRGVtbyI6Mjp7czoxMDoiAERlbW8AZmlsZSI7czo4OiJmbDRnLnBocCI7fQ==

然后就拿到了 Exp , 将其作为 var 变量的参数提交即可拿到 Flag

Web_php_unserialize(序列化与反序列化)第12张


一个注意点

这里说一个需要注意的点 :

最开始的我是先把序列化后的字符串输出 , 然后手工添加 " + " 号和破坏对象属性 , 最后再对其 Base64 编码后提交 , 但是始终拿不到 Flag

翻看了一会儿以前的笔记 , 突然发现了这个知识点

不同属性的对象序列化后字符格式是不一样的

Private属性 : 数据类型:属性名长度:"

免责声明:内容来源于网络,仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇MDK内的KEEP关键字以及$$Base $$LimitWindows API常识下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Hadoop 序列化

摘自:http://blog.csdn.net/zhang0558/article/details/53444533 序列化和反序列化以及hadoop数据类型 1.什么是序列化和反序列化   序列化就是把内存中的对象,转换成字节序列(或其他数据传输协议)以便于存储(持久化)和网络传输。   反序列化就是将收到 字节序列(或其他数据传输协议)或者是硬盘的持久...

C# 序列化与反序列化之DataContract与xml对子类进行序列化的解决方案

C# 序列化与反序列化之DataContract与xml对子类进行序列化的解决方案 1、DataContract继承对子类进行序列化的解决方案 第一种是在 [DataContract, KnownType(typeof(继承的子类))]添加 KnownType(typeof(继承的子类))即可,第二种是在序列化的时候,添加类型 DataContractSe...

简单对象访问协议(SOAP)初级指南[转]

这篇文章带你全面回顾对象远程进程调用(ORPC)技术的历程,以帮助你理解SOAP技术的基础,以及它克服存在技术(如CORBA和DCOM)的许多缺陷的方法。随后讲述详细的SOAP编码规则,并把焦点放在SOAP是怎样映射到存在的ORPC概念上的。   引言:   当我在1984年开始把计算作为我的职业的时候,大多数程序员并不关心网络协议。但是在九十年代网络变得...

.NET插件技术-应用程序热升级

今天说一说.NET 中的插件技术,即 应用程序热升级。在很多情况下、我们希望用户对应用程序的升级是无感知的,并且尽可能不打断用户操作的。 虽然在Web 或者 WebAPI上,由于多点的存在可以逐个停用单点进行系统升级,而不影响整个服务。但是 客户端却不能这样做,毕竟用户一直在使用着。 那么有没有一种方式,可以在用户无感知的情况下(即、不停止进程的情况下)对...

推荐:Java性能优化系列集锦

Java性能问题一直困扰着广大程序员,由于平台复杂性,要定位问题,找出其根源确实很难。随着10多年Java平台的改进以及新出现的多核多处理器,Java软件的性能和扩展性已经今非昔比了。现代JVM持续演进,内建了更为成熟的优化技术、运行时技术和垃圾收集器。与此同时,底层的硬件平台和操作系统也在演化。 目录: 一、Java性能优化系列之一--设计优化 二、J...

kafka producer serializer序列化(六)

生产者需要将要发送的数据转换成字节数组才能通过网络发送给kafka,对于一些简单的数据,kafka自带了一些序列化工具, 如:StringSerializer Double Long Integer Byte,它们都实现了  Serializer 接口,但是如果你要发送的数据是一个对象 Persion,那么就需要自定义序列化才能将数据发送给kafka...