关于反序列化漏洞的初探
前置知识
面向对象编程
简单介绍
在PHP中存在两个方法,分别是serialize()和unserialize()这两个。
示例
1 |
|
然后下面是输出结果。
1 | O:6:"person":2:{s:4:"name";s:5:"south";s:3:"age";i:18;} |
可以很明显的看到其实是序列化其实是类似于把一个对象进行了Json格式化【只是长得像,实质格式完全不同。
一个小知识点
对象的私有成员变量,类名称前会加上*****。
序列化结果解析
1 | O: // 类型: |
序列化结果类型详解
1 | a - array |
漏洞原理
PHP中引起反序列化漏洞的主要原因是PHP的魔术方法【magic function,由双下划线开头的函数如**__construct()**等会在某些情况下会自动调用。
一个重要结论
反序列化漏洞的产生原因是用户输入的数据具有危险性,而并非是反序列化本身。
看看官方文档对魔术方法的介绍。
1 | __construct(), __destruct(), __call(), __callStatic(), __get(), __set(), __isset(), __unset(), __sleep(), __wakeup(), __toString(), __invoke(), __set_state(), __clone() 和 __debugInfo() 等方法在PHP中被称为"魔术方法"(Magic methods)。在命名自己的类方法时不能使用这些方法名,除非是想使用其魔术功能。 |
举个例子,看看**__construct()**这个函数。
1 | PHP5允行开发者在一个类中定义一个方法作为构造函数。具有构造函数的类会在每次创建新对象时先调用此方法,所以非常适合在使用对象之前做一些初始化工作。 |
一个例子
1 |
|
这段代码运行后得到的最终结果是age=18,很明显的推断就是同官方文档所说,在创建一个对象的时候PHP自动执行了对象内的**__construct()**函数。
常见的魔术方法
1 | __wakeup() // 使用unserialize时触发 |
需要重点注意的函数
命令执行
1 | exec() |
文件操作
1 | file_put_contents() |
如果在跟进程序过程中发现这些函数就要打起精神,一旦这些函数的参数我们能够控制,就有可能出现高危漏洞。
一些例子
简单例子
1 |
|
此处如果构造一个对象的command参数是一段恶意代码并序列化后传参进入,就能造成一个有效攻击。
1 |
|
Payload如下。
1 | O:5:"Hello":1:{s:7:"command";s:13:"php hello.txt";} |
就能访问到目录下的flag文件了。
welcome to bugkuctf
今年黑盾有一题也跟这个一样,解法写在另一篇讲述文件包含的文章中了。
1 | // index.php 部分代码 |
1 | // hint.php |
index.php传入两个参数,一个是file,一个是password,再看hint.php,构造了一个类,其中的**__tostring()这个方法在index.php的echo \(password;**时会被调用,那么解题思路就是对**file**传参**hint.php**,使**index.php**达成**include(hint.php);**这一个指令,然后**password**传入一个**\)file成员变量,使得__tostring()方法中的file_get_contents(\(this->file);**指令可以读取**\)file成员变量所传入的文件名。而此处的文件名明显是flag.php**了
构造的类如下。
1 |
|
最终Payload如下。
1 | file=hint.php&password=O:4:"Flag":1:{s:4:"file";s:8:"flag.php";} |
PHP
.git源码泄漏就不多说了。
1 | // index.php部分代码 |
包含了class.php和waf.php。
1 | // class.php |
可以看出Blog类中有一个**__destruct()方法,起作用是当一个对象销毁时调用system这个指令,而在最后有一句unset(\(b)**的销毁对象指令,且可控的部分只有**\)this->file,后面被.php给闭合,故此需要使用|管道符来进行操作,其作用于此做个笔记,就是把左边命令的输出数据作为右边命令的输入数据,先执行左边的命令,再执行右边的命令,类似的操作比如在Linux下寻找对应的软件包rpm -qa|grep nginx,先列出所有的软件包,然后寻找Nginx**。
1 | // waf.php |
并没有过滤什么,那么大致构造的Payload如下。
1 | ?tip=php://input&tips=O:4:"Blog":1:{s:4:"file";s:30:"Flag.php|cat ../templates/Flag";} |
并没有成功,git查看历史版本的waf。
1 |
|
于是最终Payload如下。
1 | ?tip=php://input&tips=O:4:"Blog":1:{s:4:"file";s:34:"Flag.php|ca''t$IFS./templates/Flag";} |