Sky's blog

Summary of serialization attacks & Part 2

字数统计: 1,574阅读时长: 6 min
2019/05/05 Share

前言

之前写了一篇介绍序列化概念和两种常见攻击:1.魔法方法,2.session序列化引擎。
本篇文章继续深入,介绍另外方法:原生类序列化问题。

原生类同名函数

问题引入

什么是原生类同名函数攻击漏洞呢?我们不妨看如下代码:

1
2
3
4
5
6
7
8
9
10
class UploadFile {

function upload($fakename, $content) {
..... // 你什么也不能做
}

function open($fakename, $realname) {
..... // 你什么也不能做
}
}

假设有这样一个上传类,但是因为有.htaccess文件的控制,上传文件夹被限制的很死,我们很难上传我们的一句话文件。唯一的突破口是利用类中的函数或者漏洞,删除.htaccess文件,否则即便上传了一句话文件,也不能被解析。

问题探索

但是纵观类中函数,没有一个具有删除或者覆盖功能,此时应该如何操作呢?此时便应该考虑一下是否有原生类具有同名函数。比如此处的open函数,我们可以通过php代码进行搜索:

1
2
3
4
5
6
7
8
<?php
foreach (get_declared_classes() as $class) {
foreach (get_class_methods($class) as $method) {
if ($method == "open")
echo "$class->$method\n";
}
}
?>

不难得到如下结果

1
2
3
4
SQLite3->open
SessionHandler->open
XMLReader->open
ZipArchive->open

发现有4个php原生类带有open方法,我们查阅每个方法的实现。

方法实现探究

对于SQLite3->open,我们查阅官方手册:

其有3个参数,看到模式SQLITE3_OPEN_READWRITE,应该不难想到是否可以篡改.htaccess,我们测试一下,得到报错:

显然这里的open方法并不能直接调用,我们继续往下看。
对于SessionHandler->open,我们查阅官方手册:

其有两个参数,一个是保存session的位置,一个是session的名字,很显然在这里不太适用,我们继续往下看。
对于XMLReader->open,我们查阅官方手册:

该方法也有3个参数,但显然也与删除文件没有太大关联,对我们的漏洞利用帮助不大,所以也可以直接跳过。
最后对于ZipArchive->open,我们查阅官方手册:

发现其参数为2个,前者是文件名,后者是选择模式,这里有一个overwrite非常引人注目,这正是我们需求的模式。我们跟进查看该模式描述:

本地测试一下:

发现使用该模式,可以成功删除指定文件。
那么如果目标操作序列化中,存在open调用,同时序列化可控,就有可能进行覆盖,达到删除指定文件的目的。例如题目中的

1
2
3
function open($fakename, $realname) {
..... // 你什么也不能做
}

我们可以让fakename为.htaccess,realname为ZipArchive::OVERWRITE,并控制序列化为ZipArchive对象,即可达成删除目的。
上述过程并非凭空想象出来的环境,在一次比赛中便用到了这样的方式,在比赛Insomnihack Teaser 2018中,File Vault一题的考察点便在于此。有兴趣的可以看这篇文章:

1
https://corb3nik.github.io/blog/insomnihack-teaser-2018/file-vault

原生类魔法方法

我们知道这种情况比较少数,因为需要程序去调用函数,并且其中同名的概率也是比较低的。所以不难联系到之前的魔法方法,这类方法可以在满足条件的情况下自动触发。如果我们能挖掘原生类中魔法方法的利用点,那么攻击范围肯定是明显大于上述这种需要调用的同名方法的。

原生类探索

那么不妨列举出所有带有魔法方法的原生类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
 <?php
$classes = get_declared_classes();
foreach ($classes as $class) {
$methods = get_class_methods($class);
foreach ($methods as $method) {
if (in_array($method, array(
'__construct',
'__destruct',
'__toString',
'__wakeup',
'__call',
'__callStatic',
'__get',
'__set',
'__isset',
'__unset',
'__invoke',
'__set_state'
))) {
print $class . '::' . $method . "\n";
}
}
}

运行后不难发现,这里就有前一篇文章我们说的原生类SoapClient魔法方法__call导致SSRF的问题:

当然这么多原生类中,肯定不止这一个魔法方法可以利用。

SQLite3

例如原生类SQLite3,我们在遍历的时候,发现其存在魔法方法:SQLite3::__construct。
我们查阅一下官方手册:

此处可以发现,刚方法可以创建一个指定名称指定路径的空白文件:

1
2
3
<?php
$sky=new SQLite3('/tmp/sky/evil.php');
?>


我们可以发现成功创建evil.php,可以有一个可控文件非常重要,我们可以用其存储数据,或是进行evil code的填写,方便后续攻击。

DirectoryIterator

又如原生类DirectoryIterator,我们注意到在遍历魔法方法时,其存在如下两个魔法方法:

1
2
DirectoryIterator::__construct
DirectoryIterator::__toString

我们查阅官方手册:


发现两者可以结合使用,首先使用魔法方法construct进行路径选择,再利用toString返回对应路径下的内容:

原理也很清晰,在使用echo的时候,触发了__toString魔法方法,返回了当前路径下的文件名。

SimpleXMLElement

再如SimpleXMLElement,我们在遍历魔法方法的时候,也发现了其存在2种魔法方法:

1
2
SimpleXMLElement::__construct
SimpleXMLElement::__toString

我们同样去查阅官方手册:


这里相信大家都不陌生,看到xml肯定会想到xxe,这里也不例外,该原生类可以用来进行xxe任意文件读取。如下图:

在利用echo后会触发__toString()魔法方法,输出其中的值,但需要注意的是,这里需要options为LIBXML_NOENT,否则不会加载我们的实体:

实际案例

上述了多个原生类的利用点,实际上他们都来自于一场真实的案例,原文如下:
https://5haked.blogspot.com/2016/10/how-i-hacked-pornhub-for-fun-and-profit.html
该牛串联多个原生类序列化问题,最后成功对某知名网站的任意代码执行,有兴趣的可以好好拜读一下~

后记

本篇文章是前一篇的进阶版,后续有机会将更加结合实际问题进行剖析,欢迎有骚操作的一起讨论!

1
文章首发于嘶吼
点击赞赏二维码,您的支持将鼓励我继续创作!
CATALOG
  1. 1. 前言
  2. 2. 原生类同名函数
    1. 2.1. 问题引入
    2. 2.2. 问题探索
    3. 2.3. 方法实现探究
  3. 3. 原生类魔法方法
    1. 3.1. 原生类探索
    2. 3.2. SQLite3
    3. 3.3. DirectoryIterator
    4. 3.4. SimpleXMLElement
    5. 3.5. 实际案例
  4. 4. 后记