Thinkphp 5.0.x 远程命令执行漏洞
漏洞概述
本次漏洞存在于 ThinkPHP 的缓存类中。该类会将缓存数据通过序列化的方式,直接存储在 .php 文件中,攻击者通过精心构造的 payload ,即可将 webshell 写入缓存文件。缓存文件的名字和目录均可预测出来,一旦缓存目录可访问或结合任意文件包含漏洞,即可触发 远程代码执行漏洞 。
影响版本
- 5.0.0 <= ThinkPHP <= 5.0.10
漏洞环境
拉取项目
1 | composer create-project --prefer-dist topthink/think=5.0.10 thinkphp_5.0.10 |
修改 composer.json
文件的 require
字段设置成如下,然后再执行 composer update
1 | "require": { |
使用 phpstudy
搭建选择网站根目录为 ./thinkphp_5.0.10
修改 application/index/controller/Index.php
文件内容
1 |
|
漏洞复现
1 | http://thinkphp/public/?username=mochazz123%0D%0A@eval($_GET[_]);// |
漏洞分析
打断点进入 Cache
类中 set()
方法
位于 thinkphp/library/think/Cache.php
中 126 行
调用 self::init()
初始化缓存,继续跟进
self::$handler
为 null
,进入 if 判断条件中,上面调用 init()
没有参数,所以 $options
为空,进入 69 行判断
而 cache.type
默认值为 File
所以根据条件进入 72 行判断,继续紧跟 self::connetct()
$options['type']
值为 File
,找到 cache 缓存的驱动为 File
,对应类为 \think\cache\driver\file
,在第 51 行处实例化并返回,进入 \think\cache\driver\file
中 set()
方法
$value
参数可空,没有任何过滤,经过 serialize()
序列化赋值给 $data
,第 147 行处 $this->options['data_compress']
默认为 false ,数据不会经过 gzcompress()
函数处理,直接拼接 $data
数据,序列化前面的单行注释符 //
通过传入的换行符 %0D%0A
进行绕过,最后使用 file_put_contents()
写入。文件名 $filename
来自 getCacheKey()
,跟进该函数查看
第 70 行处 $this->options['cache_subdir']
默认值为 true ,进入判断中,取 $name
的 md5 加密后的值前两位拼接 /
后面剩余的 28 位字符串为完整的路径信息。
利用补充
- 该漏洞利用需要站点搭建在
thinkphp
根目录,可以访问到runtime
目录中,缓存文件可访问到。而官方推荐public
为 web 目录。 - 如果程序设置
$this->options['prefix']
的情况下,没有源码则无法判断webshell
路径。