目录
0x00 漏洞简介
0x01 漏洞影响
0x02 环境搭建
0x03 漏洞分析
0x04 漏洞复现
0x05 总结
开发者使用 官方文件上传示例进行开发时,存在一个文件上传漏洞导致 getshell
本文仅用于技术讨论与研究,文中的实现方法切勿应用在任何违法场景。如因涉嫌违法造成的一切不良影响,本文作者概不负责。
2022年12月出的一个 CVE
,漏洞作者在10月左右就写在thinkphp
的 github
下面了,链接在这:https://github.com/top-think/framework/issues/2772 ,此漏洞属于框架函数的漏洞,需要开发者编写代码时使用到该函数才有机会利用。
在官方开发手册的示例中,使用了该函数,并且没有任何过滤,因此当开发者使用 官方示例 进行开发时,就可以 getshell
。
thinkphp 5.x
系列
不影响目前的 thinkphp6.x
使用 composer
快速搭建 thinkphp5.x
系列最新版环境
composer create-project topthink/think=5.1.* tp5.1.41
之后根据官方示例 https://www.kancloud.cn/manual/thinkphp5_1/354121 编写,修改如下文件
application/index/controller/Index.php
<?php
namespace app\index\controller;class Index
{
public function Index(){
// 获取表单上传文件 例如上传了001.jpg
$file = request()->file('image');
// 移动到框架应用根目录/uploads/ 目录下
$info = $file->move( '../uploads');
if($info){
// 成功上传后 获取上传信息
// 输出 jpg
echo $info->getExtension();
// 输出 20160820/42a79759f284b767dfcb2a0197904287.jpg
echo $info->getSaveName();
// 输出 42a79759f284b767dfcb2a0197904287.jpg
echo $info->getFilename();
}else{
// 上传失败获取错误信息
echo $file->getError();
}
}
}
官方示例文件中注释写的很明白,就是先上传文件,然后将文件移动到根目录的 uploads
文件夹下,后面代码就是在输出。
我们先看第一句 request()->file('image');
request()
直接加载了 thinkphp/library/think/Request.php
中的 Request
类,然后访问 file
方法
这里就是取到了 $_FILES
与 $name
,然后传入 dealUploadFile
方法,返回一个 $array
,最后会返回一个 $array[$name]
,我们跟进 dealUploadFile
方法
我们传进来的 $files
是 $_FILES
,因此不满足第一个 if
条件,$file['name']
就是传入的 filename
,不为数组时就会进入最后的 else
,正常传入文件就不会出现 error
,因此来到 1246 行,这时候进入 thinkphp/library/think/File.php
的 setUploadInfo
方法,来到这里
这里返回了 $this
,也就是实例化后的 File
类。
然后继续返回,可以看到,我们编写的文件中,第一句最后的返回就是这个实例化后的 File
类
看到第二句 $file->move( '../uploads')
,调用该类的 move
方法,参数是 ../uploads
,跟进该方法
thinkphp/library/think/File.php
这里是 move
方法的上半部分,代码都有注释,因此很好理解,涉及到检测的有三个方法,分别是 isValid
、check
、chechPath
,我们依次看看
isValid
方法
这里只是检测是不是文件或者上传的文件,显然是满足的。
check
方法
此处的参数是没有传入的,因此 $rule
就是空数组
第一句这里,由于 $rule
为空,因此会获得 $this->validate
的值,这个 $this->validate
的值是在 validate
方法中设置的,如下
按照官方示例的代码,是没有写这个 validate
限制的,这里 validate
默认也是空数组,因此 $rule
的值也还是空的,其实如果要防止这个漏洞的话,也是可以利用这个 validate
方法进行设置的。
由于 $rule
为空数组,因此前面三个判断都不会生效,只会进行第四个,也就是 $this->checkImg()
方法,只要这个方法返回 true
,那么就会跳过这里的判断,我们来看到这个方法
这个方法检查图片后缀以及图片类型。
首先获取后缀并转为小写,得到 $extension
,下面是一个判断,图片后缀为这个数组里面的值并且后面也为 true
时就会返回错误,这里的条件很奇葩,当我们的文件后缀为 php
时就不满足第一个条件,直接返回 true
chechPath
方法
这里只是判断有没有相应文件夹,没有就创建,因此也可以过
接下来看看 move
方法的下半部分
这里并没有其他的检测了,直接将临时文件移动到了目标文件。
我们直接上传一个 php
文件,即可在使用官方示例的条件下 getshell
由于官方示例没有上传的模板,因此我们自己创建一个
上传并且抓包,将后面改为 php
,如图
这个漏洞是属于thinkphp
框架的函数的漏洞,因此需要开发者按照官方示例去使用或者类似的用法才会造成 getshell
。
但是也会觉得很奇葩,因为此处的实际检测应该就是那处 checkImg
方法,但他这里明显就写错,必须属于图片类型才会返回 false
,根本上就没有对危险的后缀进行过滤,更让人觉得离谱的是,至少从 5.0.0
开始一直到最新版本,都是这样的,没有更改过。
https://forum.butian.net/share/2058