zoukankan      html  css  js  c++  java
  • Web_php_unserialize(序列化与反序列化)

    题目

    题目是这样的

    很明显是一道 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() 方法 .


    绕过正则表达式

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

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

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

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

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

    1. 先看 var_unserializer.c 文件 441 行

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

      注意这里区分大小写 !

    2. yy13

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

    3. yy17

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

    4. yy19

      我们来看 yy19 , 

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

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

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

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

    php 7.3.9 var_unserializer.c 文件 783 行

    yy17 已不再识别 " + " 号~


    绕过 __wakeup() 函数

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

    来看 var_unserializer.re 文件 371 行

    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 行

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

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

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

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

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


    构造 Exp

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

    Exp : TzorNDoiRGVtbyI6Mjp7czoxMDoiAERlbW8AZmlsZSI7czo4OiJmbDRnLnBocCI7fQ==

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


    一个注意点

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

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

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

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

    Private属性 : 数据类型:属性名长度:"0类名0属性名";数据类型:属性值长度:"属性值";
    
    Protected属性 : 数据类型:属性名长度:"0*0属性名";数据类型:属性值长度:"属性值";
    
    Public属性 : 数据类型:属性名长度:"属性名";数据类型:属性值长度:"属性值";

    本题中就有一个 Private 对象 , 会不会在复制粘贴时破坏了 " 0 " 这个特殊字符呢 ? 可以实验一下~

    1. 将序列化后的字符串直接存入文件

    2. 将序列化后的字符串复制粘贴存入文件

    3. vim 查看 a.txt 文件

    果然 , 输出到命令行的序列化字符串格式已经被破坏 , 因此必须要在脚本中直接构造出完整的 Exp

    转载于https://www.guildhab.top/?p=990

  • 相关阅读:
    轮播闪白效果
    轮播图效果
    打字游戏简约版
    js购物时的放大镜效果
    单例模式
    docker
    【spring】注解梳理
    【spring源码-1】BeanFactory + XMLBeanFactory
    【设计模式】
    【大数据Spark】PC单机Spark开发环境搭建
  • 原文地址:https://www.cnblogs.com/zesiar0/p/12698547.html
Copyright © 2011-2022 走看看