海康威视综合安防管理平台 clusters 接口任意文件上传漏洞
xhys 发表于 北京 漏洞分析 1380浏览 · 2024-08-20 05:13

通过反编译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方法,主要的功能是

  1. 移除所有的分号
  2. 移除分号后面直到下一个斜杠”/”之间的所有字符

拼接的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);
}
0 条评论
某人
表情
可输入 255
目录