通过反编译class文件,定位到
/clusterMgr/WEB-INF/classes/com/hikvision/clustermgr/module/transfer/controller/ClusterController.class
在下面判断上传后缀格式代码处
这段代码只允许文件以.crt
或.key
结尾。这意味着如果我们将恶意JSP文件的扩展名改为.crt
或.key
,就能绕过去,我们可以上传一个文件,名为xxxx.jsp.crt
,系统会将其视为合法文件。
同时系统只检查了文件的扩展名,而没有对文件内容进行检查:
然后POC就是利用这个漏洞jsp改成了image/png绕过去了.Content-Type: image/png,绕过去
File file = new File(tempPath + File.separator + fileName);
然后上传到这个地方,代码中没有验证fileName
的合法性。攻击者可以上传带有恶意扩展名(如.jsp
)的文件
跟进fileName函数
直接从multipartFile.getOriginalFilename()
获取的fileName
没有经过任何验证,所以这个地方可以插入../
等特殊字符,导致任意文件读取
CAS配置文件如下:/webapps/center/WEB-INF/classes/cas-client.properties,对.eot,.woff,.ttf,.svg,.gif,.css,.js,.json等文件不进行鉴权
cas.ignore.pattern=/,*.html,/login,*.eot,*.woff,*.ttf,*.svg,*.gif,*.css,*.js,*.json,/casLogin,*.ico,*.icon,*.png,*.map,/api/meta/qrcode,/api/session,/api/session/captcha,/api/locales,/api/meta,/api/menus,/api/users/*/password,/api/fileUpload/*,/center/api/fileUpload/*,/api/clientInstall/LatestVersion,/api/clientResourceInstall/LatestVersion,/center/api/clientInstall/LatestVersion,/center/api/clientResourceInstall/LatestVersion,/api/machines/*/steps,/api/verifyCodeImage,/api/encryptParam,/api/webLogin,/api/modifyPassword,/api/negotiation,/api/webMachines,/api/webDelMachines,/api/webAlerts,/api/webResources,/api/webMenus,/api/settings/firewalls/*,/api/installMachineList/*,/api/clientInstall/*,/api/task/*,/api/bic/*,/api/installation/*,/api/external/*,/api/logsOnline/*
大家都知道,Spring 在处理url时,removeSemicolonContentInternal方法,主要的功能是
- 移除所有的分号
- 移除分号后面直到下一个斜杠”/”之间的所有字符
拼接的clusters/ssl/file;.js可以绕过鉴权
同时经过removeSemicolonContentInternal方法 ==》得到clusters/ssl/file,分发到正确的路由,导致文件上传
然后POC中
Content-Disposition: form-data; name="proxyAddress"
8.8.8.8
在源码里面定位
proxyAddress
是一个参数,通常在 HTTP POST 请求中通过表单数据发送。
整体这个字段是在 uploadSSlFile
方法中作为一个请求参数接收的。
修复建议
在上传文件时,除了检查文件扩展名外,还应检查文件的MIME类型和实际内容。确保只允许上传特定类型的文件
public boolean isValidSslFile(MultipartFile file) {
String contentType = file.getContentType();
String fileName = file.getOriginalFilename();
// 允许的扩展名
List<String> allowedExtensions = Arrays.asList(".crt", ".key");
// 检查扩展名
if (allowedExtensions.stream().noneMatch(fileName::endsWith)) {
return false;
}
// 检查MIME类型
if (!"application/x-x509-ca-cert".equals(contentType) &&
!"application/x-pkcs12".equals(contentType)) {
return false;
}
// 进一步检查文件内容,可以根据需要实现
return true;
}
确保文件只能上传到特定目录,并且文件名称不得包含任何路径分隔符
if (fileName.contains("/") || fileName.contains("\\")) {
throw new IllegalArgumentException("Invalid file name.");
}
对 proxyAddress
进行严格验证,确保它是一个有效的IP地址或域名,并限制可接受的范围
public boolean isValidProxyAddress(String address) {
String ipRegex = "^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\." +
"(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\." +
"(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\." +
"(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$";
return address.matches(ipRegex);
}