大概思路就是我们可以修改requests类的filter属性、method属性以及get属性的值,从而在调用param方法时,call_user_func_array的值我们就可以控制,造成了远程代码执行漏洞。
0. 大致流程
经过入口文件进入run函数
首先在116行根据url获取调度信息时,触发变量覆盖漏洞从而修改requests对象的属性值,然后获取s=captcha的调度信息并返回给$dispatch
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Td04jYMC-1648648634252)(C:Users91136AppDataRoamingTypora ypora-user-imagesimage-20220330210627549.png)]](https://i-blog.csdnimg.cn/blog_migrate/10ac4ad95776fc497067388ba23acfe9.png)
再到139行进入exec函数并将$dispatch作为参数带入
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BulklkCS-1648648634253)(C:Users91136AppDataRoamingTypora ypora-user-imagesimage-20220330212548714.png)]](https://i-blog.csdnimg.cn/blog_migrate/aa5cfadb0eef8bf70bc8bb5956409020.png)
跟进后根据$dispatch的type进入到case ‘method’,从而调用requests的param函数,进而造成了rce漏洞
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Gd5cDwZP-1648648634253)(C:Users91136AppDataRoamingTypora ypora-user-imagesimage-20220330212730853.png)]](https://i-blog.csdnimg.cn/blog_migrate/5747e255fb1ca3c3533c30c47f7b5504.png)
1.环境搭建
这里用的环境是thinkphp5.0.20+php5.6.27+apache+phpstorm
POC:
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fq1IdDPU-1648648634253)(C:Users91136AppDataRoamingTypora ypora-user-imagesimage-20220330191546501.png)]](https://i-blog.csdnimg.cn/blog_migrate/fdda67995692d5fc9eee9c121b99c4ec.png)
复现成功
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ThpHpF6m-1648648634254)(C:Users91136AppDataRoamingTypora ypora-user-imagesimage-20220330213551329.png)]](https://i-blog.csdnimg.cn/blog_migrate/e2c643ff73a731b8d15682abebfaf1f9.png)
1.1 POC参数解析
method=get 因为captcha的路由规则是get方式下的,所以我们得让method为get,才能获取到captcha的路由
s=captcha 因为在进入exec函数后我们要switch到method中执行param函数,而这个captcha的路由刚好对应类型为method,所以我们选择captcha
filter[]=system 覆盖变量
get[]=whoami 覆盖变量
_method=__construct 为了能够进入construct,从而覆盖变量
2.漏洞分析
这是一个变量覆盖漏洞导致的rce,我们首先来说下变量覆盖漏洞
2.1 变量覆盖漏洞
漏洞触发点在thinkphp/library/think/Request.php的509行:
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HO0XepxW-1648648634254)(C:Users91136AppDataRoamingTypora ypora-user-imagesimage-20220330191944892.png)]](https://i-blog.csdnimg.cn/blog_migrate/aa7cca4f6d74e8b529529f037a0a46cb.png)
这里509行的this−>method我们可控,∗∗该值就来自与上一行的this->method我们可控,**该值就来自与上一行的this−>method我们可控,∗∗该值就来自与上一行的_POST[Config::get(‘var_method’)],其中Config::get(‘var_method’)的值是_method**
我们post传入_method为__construct,就会调用request对象的构造函数,参数为post内容
跟进__construct,可以看到他将传入的参数依次赋值给相应的属性,这就造成了变量覆盖漏洞,我们可以随便给requests对象的属性赋值,这为后面的rce打下基础。从poc也能看出来,为了能够rce,这里我们需要修改的属性值有filter,get,method,_method
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PZWgoq1B-1648648634254)(C:Users91136AppDataRoamingTypora ypora-user-imagesimage-20220330193019724.png)]](https://i-blog.csdnimg.cn/blog_migrate/49c10418c8f80eddb95c7e8f0b028af6.png)
2.2 远程代码执行
RCE的触发点在thinkphp/library/think/Request.php的param函数:
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ezS6Q8Pe-1648648634254)(C:Users91136AppDataRoamingTypora ypora-user-imagesimage-20220330193750477.png)]](https://i-blog.csdnimg.cn/blog_migrate/87cc38176584364579acfa923adfd99f.png)
第一个if是获取post的提交内容并赋值给$vars
然后再整合一下赋值给requests对象的param属性
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0Bg9P8xt-1648648634255)(C:Users91136AppDataRoamingTypora ypora-user-imagesimage-20220330194725243.png)]](https://i-blog.csdnimg.cn/blog_migrate/f93cd9fb72925d342901ca7b903bb326.png)
最后调用该类下的input方法,跟进input方法,$data的值为上图红框
public function input($data = [], $name = '', $default = null, $filter = '')
{
if (false === $name) {
// 获取原始数据
return $data;
}
$name = (string) $name;
if ('' != $name) {
// 解析name
if (strpos($name, '/')) {
list($name, $type) = explode('/', $name);
} else {
$type = 's';
}
// 按.拆分成多维数组进行判断
foreach (explode('.', $name) as $val) {
if (isset($data[$val])) {
$data = $data[$val];
} else {
// 无输入数据,返回默认值
return $default;
}
}
if (is_object($data)) {
return $data;
}
}
// 解析过滤器
$filter = $this->getFilter($filter, $default);
if (is_array($data)) {
array_walk_recursive($data, [$this, 'filterValue'], $filter);
reset($data);
} else {
$this->filterValue($data, $name, $filter);
}
if (isset($type) && $data !== $default) {
// 强制类型转换
$this->typeCast($data, $type);
}
return $data;
}
执行到这一行:$filter = this−>getFilter(this->getFilter(this−>getFilter(filter, $default);
跟进,看代码意思就是将this−>filter的值赋给this->filter的值赋给this−>filter的值赋给filter变量并返回,这个$this->filter是我们可控的即[“system”]
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mHPK8EcE-1648648634255)(C:Users91136AppDataRoamingTypora ypora-user-imagesimage-20220330195220631.png)]](https://i-blog.csdnimg.cn/blog_migrate/88d839d0ccca3c87dac785f786f2f435.png)
回到input函数,这时候$filter为[“system”]
往下走,$data确实是数组,所以进入if
走到array_walk_recursive(KaTeX parse error: Undefined control sequence: \[ at position 7: data, \̲[̲this, ‘filterValue’], $filter);
array_walk_recursive这个函数大概意思是每次从data中取一个值(第一次的取值由上面图红框可知是whoami),应用于第二个参数所指示的函数
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZLKKnCU3-1648648634255)(C:Users91136AppDataRoamingTypora ypora-user-imagesimage-20220330195721842.png)]](https://i-blog.csdnimg.cn/blog_migrate/2a00bf23d6c1ef528cfbfc77f56ad339.png)
跟进到filterValue方法,看到此时filter为"system",filter为"system",filter为"system",value为whoami,走到1073行即可执行我们设置的回调函数并将结果赋值给value
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Al48JhBr-1648648634255)(C:Users91136AppDataRoamingTypora ypora-user-imagesimage-20220330195856545.png)]](https://i-blog.csdnimg.cn/blog_migrate/94614d4af333d13e0621445c7c29dc0a.png)
最终返回给我们value值,这就造成任意代码执行漏洞
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oZ4SP6Ye-1648648634256)(C:Users91136AppDataRoamingTypora ypora-user-imagesimage-20220330200653307.png)]](https://i-blog.csdnimg.cn/blog_migrate/251c9ec3992888466fe499d1fa9eab46.png)
这就是因为我们覆盖了requests对象的属性值导致的rce漏洞
3.具体流程
当我们执行poc后,从入口函数开始分析
首先加载框架的引导文件
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9qYdWKAN-1648648634256)(C:Users91136AppDataRoamingTypora ypora-user-imagesimage-20220330201445736.png)]](https://i-blog.csdnimg.cn/blog_migrate/97333a3937c7dcd7051c981da488b697.png)
往下走,首先加载基础文件。
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jXCJ6MRK-1648648634256)(C:Users91136AppDataRoamingTypora ypora-user-imagesimage-20220330201518480.png)]](https://i-blog.csdnimg.cn/blog_migrate/b9ebbc598b58bf77923c0a40b6d14be9.png)
然后执行app类的run函数
public static function run(Request $request = null)
{
$request = is_null($request) ? Request::instance() : $request;
try {
$config = self::initCommon();
// 模块/控制器绑定
if (defined('BIND_MODULE')) {
BIND_MODULE && Route::bind(BIND_MODULE);
} elseif ($config['auto_bind_module']) {
// 入口自动绑定
$name = pathinfo($request->baseFile(), PATHINFO_FILENAME);
if ($name && 'index' != $name && is_dir(APP_PATH . $name)) {
Route::bind($name);
}
}
$request->filter($config['default_filter']);
// 默认语言
Lang::range($config['default_lang']);
// 开启多语言机制 检测当前语言
$config['lang_switch_on'] && Lang::detect();
$request->langset(Lang::range());
// 加载系统语言包
Lang::load([
THINK_PATH . 'lang' . DS . $request->langset() . EXT,
APP_PATH . 'lang' . DS . $request->langset() . EXT,
]);
// 监听 app_dispatch
Hook::listen('app_dispatch', self::$dispatch);
// 获取应用调度信息
$dispatch = self::$dispatch;
// 未设置调度信息则进行 URL 路由检测
if (empty($dispatch)) {
$dispatch = self::routeCheck($request, $config);
}
// 记录当前调度信息
$request->dispatch($dispatch);
// 记录路由和请求信息
if (self::$debug) {
Log::record('[ ROUTE ] ' . var_export($dispatch, true), 'info');
Log::record('[ HEADER ] ' . var_export($request->header(), true), 'info');
Log::record('[ PARAM ] ' . var_export($request->param(), true), 'info');
}
// 监听 app_begin
Hook::listen('app_begin', $dispatch);
// 请求缓存检查
$request->cache(
$config['request_cache'],
$config['request_cache_expire'],
$config['request_cache_except']
);
$data = self::exec($dispatch, $config);
} catch (HttpResponseException $exception) {
$data = $exception->getResponse();
}
// 清空类的实例化
Loader::clearInstance();
// 输出数据到客户端
if ($data instanceof Response) {
$response = $data;
} elseif (!is_null($data)) {
// 默认自动识别响应输出类型
$type = $request->isAjax() ?
Config::get('default_ajax_return') :
Config::get('default_return_type');
$response = Response::create($data, $type);
} else {
$response = Response::create();
}
// 监听 app_end
Hook::listen('app_end', $response);
return $response;
}
首先实例化一个requests对象,这个包含了请求的相关信息
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-275uC95M-1648648634256)(C:Users91136AppDataRoamingTypora ypora-user-imagesimage-20220330202035941.png)]](https://i-blog.csdnimg.cn/blog_migrate/1e4d9ae2f2a547fb2599354a79310b88.png)
往下走到115行,因为我们并没有调度信息,则进入routeCheck函数进行url路由检测产生该url的调度信息,这个调度信息就是匹配s=captcha对应的路由和类型值供后面的exec函数使用
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-luaT0x8s-1648648634257)(C:Users91136AppDataRoamingTypora ypora-user-imagesimage-20220330202216999.png)]](https://i-blog.csdnimg.cn/blog_migrate/ed37e827286961d145465998657093df.png)
进入函数,首先将url传给$path
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-e99yC0R1-1648648634257)(C:Users91136AppDataRoamingTypora ypora-user-imagesimage-20220330202632304.png)]](https://i-blog.csdnimg.cn/blog_migrate/367ee138916d385456221a495c9ec4b6.png)
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mO5RjfCI-1648648634257)(C:Users91136AppDataRoamingTypora ypora-user-imagesimage-20220330202728917.png)]](https://i-blog.csdnimg.cn/blog_migrate/f6b9d15b53e27a8c3cc0b6de761ed930.png)
然后设置分割符,到643行进行路由检测,根据定义好的路由返回对应的url调度信息
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ozY6xYqU-1648648634257)(C:Users91136AppDataRoamingTypora ypora-user-imagesimage-20220330202934897.png)]](https://i-blog.csdnimg.cn/blog_migrate/bb01012c48638240bf6bdea5ae97d4f8.png)
进入check函数,走到848行,注意这里就是我们触发变量覆盖漏洞的点
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6qs1Zglw-1648648634257)(C:Users91136AppDataRoamingTypora ypora-user-imagesimage-20220330203110132.png)]](https://i-blog.csdnimg.cn/blog_migrate/abbd9797427ae1c817c95585d6453906.png)
进入method函数,接下来就是最上面讲的变量覆盖了,这里就不在多赘述
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Wq5rHhsq-1648648634258)(C:Users91136AppDataRoamingTypora ypora-user-imagesimage-20220330203138692.png)]](https://i-blog.csdnimg.cn/blog_migrate/0cebe376c2cd5f69c91c651ff335af3b.png)
这个函数执行完会返回我们设置的$this->method即GET,注意这里设置为get是为了获取到s=captcha的路由规则
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lhjCfdXi-1648648634258)(C:Users91136AppDataRoamingTypora ypora-user-imagesimage-20220330203238779.png)]](https://i-blog.csdnimg.cn/blog_migrate/a5a11a29405237a7a199130eef5c049b.png)
返回到check函数,跟进到863行,这里给$item赋值
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iYyemMHO-1648648634258)(C:Users91136AppDataRoamingTypora ypora-user-imagesimage-20220330203637954.png)]](https://i-blog.csdnimg.cn/blog_migrate/5178c76a5490523b2ed0354e101132cf.png)
然后检查是否存在$item的路由规则,我们先看一下路由规则里面都有啥,就只有一个当访问captcha/[:id]时路由为 hinkcaptchaCaptchaController@index
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uRz8HMVj-1648648634258)(C:Users91136AppDataRoamingTypora ypora-user-imagesimage-20220330203818995.png)]](https://i-blog.csdnimg.cn/blog_migrate/8b8457807f0fd0a5e340c9993bf1a887.png)
这个路由规则是在vendor opthink hink-captchasrchelper.php定义的,这也就是为啥我们将method设置为get,因为只有这样才能获得captcha的路由规则
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-E4WfGpQh-1648648634258)(C:Users91136AppDataRoamingTypora ypora-user-imagesimage-20220330204013616.png)]](https://i-blog.csdnimg.cn/blog_migrate/36c8867fbcd9b98910893fa8cdf62645.png)
好了,回到正题,继续跟进到877行,开始路由规则以及类型匹配!!!!!!!!
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DKn6iRVG-1648648634259)(C:Users91136AppDataRoamingTypora ypora-user-imagesimage-20220330204233623.png)]](https://i-blog.csdnimg.cn/blog_migrate/dba4461f5aa1217424dfc400f5373cd9.png)
进入checkRoute函数后走到955行,调用checkRule
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oBOXoRSD-1648648634259)(C:Users91136AppDataRoamingTypora ypora-user-imagesimage-20220330205658402.png)]](https://i-blog.csdnimg.cn/blog_migrate/4f4457d06b127982ae06c1ebd379a3b7.png)
走到1194行调用parseRule函数,注意看此时route参数为hinkcaptchaCaptchaController@index已经匹配到路由∗∗∗∗进入这个函数看看要干嘛,这个函数太长了,直接看精华部分,根据route的取值我们进入红框分支,在这个分支中,‘route参数为 hinkcaptchaCaptchaController@index已经匹配到路由**
**进入这个函数看看要干嘛,这个函数太长了,直接看精华部分,根据route的取值我们进入红框分支,在这个分支中,`route参数为hinkcaptchaCaptchaController@index已经匹配到路由∗∗∗∗进入这个函数看看要干嘛,这个函数太长了,直接看精华部分,根据route的取值我们进入红框分支,在这个分支中,‘result的’type’键对应的值为‘method’。然后将result‘层层返回到run函数中,并赋值给了‘result`层层返回到run函数中,并赋值给了`result‘层层返回到run函数中,并赋值给了‘dispatch`。
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-85qCfQO8-1648648634259)(C:Users91136AppDataRoamingTypora ypora-user-imagesimage-20220330210127953.png)]](https://i-blog.csdnimg.cn/blog_migrate/1eb9c4dced261284cb6aa8f9732f6588.png)
返回到最开始的run函数,可以看到$result已经包含了captcha的调度信息
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gOWlqdoo-1648648634259)(C:Users91136AppDataRoamingTypora ypora-user-imagesimage-20220330210627549.png)]](https://i-blog.csdnimg.cn/blog_migrate/8011b47af220201dd1e5ce76c03ae86f.png)
继续往下走,因为我们没开debug模式所以123行直接跳过,如果我们开启了debug模式,则直接在126行就可以进入到param函数执行rce
走到139行,带着调度信息进入exec函数
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kHQCPjjc-1648648634259)(C:Users91136AppDataRoamingTypora ypora-user-imagesimage-20220330210754588.png)]](https://i-blog.csdnimg.cn/blog_migrate/884de9efd295e9f8aa6e3943cc7ddfba.png)
exec函数,因为我们的$dispatch[‘type’]为method,所以进入case ‘method’
protected static function exec($dispatch, $config)
{
switch ($dispatch['type']) {
case 'redirect': // 重定向跳转
$data = Response::create($dispatch['url'], 'redirect')
->code($dispatch['status']);
break;
case 'module': // 模块/控制器/操作
$data = self::module(
$dispatch['module'],
$config,
isset($dispatch['convert']) ? $dispatch['convert'] : null
);
break;
case 'controller': // 执行控制器操作
$vars = array_merge(Request::instance()->param(), $dispatch['var']);
$data = Loader::action(
$dispatch['controller'],
$vars,
$config['url_controller_layer'],
$config['controller_suffix']
);
break;
case 'method': // 回调方法
$vars = array_merge(Request::instance()->param(), $dispatch['var']);
$data = self::invokeMethod($dispatch['method'], $vars);
break;
case 'function': // 闭包
$data = self::invokeFunction($dispatch['function']);
break;
case 'response': // Response 实例
$data = $dispatch['response'];
break;
default:
throw new InvalidArgumentException('dispatch type not support');
}
return $data;
}
进入requests的param函数
然后就是之前2.2节分析的rce流程了,不再赘述。
4.参考链接
https://paper.seebug.org/787/
https://www.freebuf.com/vuls/307413.html
https://xz.aliyun.com/t/8143#toc-6
https://www.kancloud.cn/zmwtp/tp5/119426
先自我介绍一下,小编13年上师交大毕业,曾经在小公司待过,去过华为OPPO等大厂,18年进入阿里,直到现在。深知大多数初中级java工程师,想要升技能,往往是需要自己摸索成长或是报班学习,但对于培训机构动则近万元的学费,着实压力不小。自己不成体系的自学效率很低又漫长,而且容易碰到天花板技术停止不前。因此我收集了一份《java开发全套学习资料》送给大家,初衷也很简单,就是希望帮助到想自学又不知道该从何学起的朋友,同时减轻大家的负担。添加下方名片,即可获取全套学习资料哦
Thinkphp 5.0.24 变量覆盖漏洞原理及RCE分析

本文详细分析了Thinkphp 5.0.24版本中的变量覆盖漏洞,通过环境搭建、漏洞复现及详细步骤解释了如何利用该漏洞实现远程代码执行(RCE)。主要涉及变量覆盖导致的方法调用控制,通过特定参数设置,可以修改requests对象的属性,最终触发RCE。
1万+

被折叠的 条评论
为什么被折叠?



