logo头像

黑客的本质就是白嫖

thinkphp 5.1.20 rce漏洞学习

前言

各种漏洞复现学习

环境搭建

  • thinkphp 5.1.20
  • php 5.6.27

漏洞分析

(复现完发先这里的payload和笔者之前复现5.0.22版本时的基本上一样,后面笔者测试了一下发现,笔者5.0.22复现中用的payload2实际上利用的是这里的漏洞…怪不得那时候我跑了这么久老是感觉不到漏洞点在什么地方)

先给出POC

1
http://localhost/tp5120/public/index.php?s=index/think\request/input?data[]=phpinfo()&filter=assert

看这个url的格式就很像之前复现的5.0.22版本的rce,通过checkRoute()来调用目标函数,在这里可以看到是调用了Request类中的input()方法,并且传入了$data$filter变量

一看复现和补丁,哦豁,虽然漏洞发生点是在这里,但是补丁修改的地方貌似在一个不太明白的地方,分析一蛤

补丁的漏洞点位于thinkphp/library/think/route/dispatch/Module.php

thinkphp_rce_5120_1

这里可以看到,$controller的值是直接从$result中获取的,而$result的值是在App.php中的routeCheck()方法里获取的,这个之前文章里讲到过,这里就不重复一遍了,大致就是通过/还是\实现来着

获取到$controller后会在下面exec()方法中实例化,跟进查看

1
2
3
4
5
6
7
8
9
10
11
12
public function controller($name, $layer = 'controller', $appendSuffix = false, $empty = '')
{
list($module, $class) = $this->parseModuleAndClass($name, $layer, $appendSuffix);

if (class_exists($class)) {
return $this->__get($class);
} elseif ($empty && class_exists($emptyClass = $this->parseClass($module, $layer, $empty, $appendSuffix))) {
return $this->__get($emptyClass);
}

throw new ClassNotFoundException('class not exists:' . $class, $class);
}

该方法位于App.php文件中,跟进parseModuleAndClass()方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
protected function parseModuleAndClass($name, $layer, $appendSuffix)
{
if (false !== strpos($name, '\\')) {
$class = $name;
$module = $this->request->module();
} else {
if (strpos($name, '/')) {
list($module, $name) = explode('/', $name, 2);
} else {
$module = $this->request->module();
}

$class = $this->parseClass($module, $layer, $name, $appendSuffix);
}

return [$module, $class];
}

这里会判断传入的$name是否含有\,如果有则直接将其作为类名返回,而这里利用的就是命名空间中含有,所以可以通过命名空间来实例化任意类

再回到controller()方法,之后的代码会判断类是否存在,不存在的话则调用parseClass()方法对命名空间等进行拼接,payload这里是存在的,所以会直接进入并实例化对应类

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
public function exec()
{
// 监听module_init
$this->app['hook']->listen('module_init');

try {
// 实例化控制器
$instance = $this->app->controller($this->controller,
$this->rule->getConfig('url_controller_layer'),
$this->rule->getConfig('controller_suffix'),
$this->rule->getConfig('empty_controller'));
} catch (ClassNotFoundException $e) {
throw new HttpException(404, 'controller not exists:' . $e->getClass());
}

$this->app['middleware']->controller(function (Request $request, $next) use ($instance) {
// 获取当前操作名
$action = $this->actionName . $this->rule->getConfig('action_suffix');

if (is_callable([$instance, $action])) {
// 执行操作方法
$call = [$instance, $action];

// 严格获取当前操作方法名
$reflect = new ReflectionMethod($instance, $action);
$methodName = $reflect->getName();
$suffix = $this->rule->getConfig('action_suffix');
$actionName = $suffix ? substr($methodName, 0, -strlen($suffix)) : $methodName;
$this->request->setAction($actionName);

// 自动获取请求变量
$vars = $this->rule->getConfig('url_param_type')
? $this->request->route()
: $this->request->param();
} elseif (is_callable([$instance, '_empty'])) {
// 空操作
$call = [$instance, '_empty'];
$vars = [$this->actionName];
$reflect = new ReflectionMethod($instance, '_empty');
} else {
// 操作不存在
throw new HttpException(404, 'method not exists:' . get_class($instance) . '->' . $action . '()');
}

$this->app['hook']->listen('action_begin', $call);

$data = $this->app->invokeReflectMethod($instance, $reflect, $vars);

return $this->autoResponse($data);
});

return $this->app['middleware']->dispatch($this->request, 'controller');
}

再下面就是获取方法名、变量值并反射执行了

总结

thinkphp这两年的命令执行漏洞可能复现的差不多了,到现在的感受就是,好多都是利用了filterValue()这个方法啊,看来不同的漏洞最后都是寻找到一个相对较好利用的地方,再通过不同的pop链来利用

自己现在的漏洞复现文章还都是基于已有的复现文章来写的,还需努力,希望以后可以直接通过漏洞公告或者补丁来复现💪

References

ThinkPHP 5.0.0~5.0.23 RCE 漏洞分析

关于 ThinkPHP 5.1 框架结合 RCE 漏洞的深入分析

评论系统未开启,无法评论!