Alibaba Sentinel SSRF漏洞代码审计
字数 1066 2025-08-29 22:41:11
Alibaba Sentinel SSRF漏洞代码审计与分析
一、Sentinel组件概述
Sentinel是阿里巴巴开源的面向分布式、多语言异构化服务架构的流量治理组件,主要功能包括:
- 流量路由
- 流量控制
- 流量整形
- 熔断降级
- 系统自适应过载保护
- 热点流量防护
二、漏洞背景
本文分析的SSRF漏洞不同于已知的CVE-2021-44139漏洞,而是与获取集群状态功能相关的新SSRF漏洞。
三、受影响版本
分析版本:v1.8.8(2025年5月时的最新Releases版)
四、漏洞详细分析
1. 漏洞入口点
漏洞位于com/alibaba/csp/sentinel/dashboard/controller/cluster/ClusterConfigController.java文件的119-146行,主要功能是获取集群或目标机器状态。
2. 参数处理流程
// 接收三个参数
String app = request.getParam("app");
String ip = request.getParam("ip");
String portStr = request.getParam("port");
// 参数非空检查
if (StringUtil.isEmpty(app)) {
throw new IllegalArgumentException("app cannot be null or empty");
}
if (StringUtil.isEmpty(ip)) {
throw new IllegalArgumentException("ip cannot be null or empty");
}
if (StringUtil.isEmpty(portStr)) {
throw new IllegalArgumentException("port cannot be null or empty");
}
// 版本支持检查
if (!checkIfSupported(app, ip, port)) {
return unsupportedVersion(app, ip, port);
}
3. 集群状态获取逻辑
// 异步获取集群状态
CompletableFuture<ClusterUniversalStateVO> future = clusterConfigService
.getClusterUniversalState(app, ip, port)
.thenApply(e -> new Result<>(e).setSuccess(true));
// 阻塞等待结果
try {
return future.get();
} catch (ExecutionException e) {
logger.error("Error when getting cluster state", e);
return new Result<>().setSuccess(false)
.setMsg(e.getCause().getMessage());
} catch (Exception e) {
logger.error("Error when getting cluster state", e);
return new Result<>().setSuccess(false)
.setCode(-1)
.setMsg(e.getMessage());
}
4. 服务层调用链
进入com/alibaba/csp/sentinel/dashboard/service/ClusterConfigService.java的148-166行:
public CompletableFuture<ClusterUniversalStateVO> getClusterUniversalState(
String app, String ip, Integer port) {
// 获取集群模式信息
return sentinelApiClient.fetchClusterMode(ip, port)
.thenApply(e -> new ClusterUniversalStateVO().setStateInfo(e))
.thenCompose(state -> {
// 获取客户端信息
return sentinelApiClient.fetchClusterClientInfo(ip, port)
.thenApply(e -> state.setClientInfo(e));
})
.thenCompose(state -> {
// 获取服务器信息
return sentinelApiClient.fetchClusterServerInfo(ip, port)
.thenApply(e -> state.setServerInfo(e));
});
}
5. API客户端实现
进入com/alibaba/csp/sentinel/dashboard/client/SentinelApiClient.java的626-637行:
public CompletableFuture<ClusterStateSimpleEntity> fetchClusterMode(
String ip, Integer port) {
// 构建请求路径
String url = String.format(FETCH_CLUSTER_MODE_PATH, ip, port);
// 执行命令获取响应
return executeCommand(ip, port, url, null,
response -> JsonUtils.parseObject(
response.getContent(), ClusterStateSimpleEntity.class));
}
6. HTTP请求执行核心
最终在280-315行执行HTTP请求:
private <R> CompletableFuture<R> executeCommand(
String ip, Integer port, String url, String body,
Function<HttpResponse, R> responseParser) {
// 参数校验
if (StringUtil.isEmpty(ip)) {
throw new IllegalArgumentException("ip cannot be null or empty");
}
if (port == null || port <= 0) {
throw new IllegalArgumentException("Invalid port");
}
// 构建基础URL
String fullUrl = "http://" + ip + ":" + port + url;
// 构造HTTP请求
HttpRequestBase request;
if (StringUtil.isEmpty(body)) {
request = new HttpGet(fullUrl);
} else {
HttpPost postRequest = new HttpPost(fullUrl);
postRequest.setEntity(new StringEntity(body, ContentType.APPLICATION_JSON));
request = postRequest;
}
// 执行请求(漏洞点:未对IP地址进行充分校验)
return asyncRequest(request, responseParser);
}
五、漏洞复现步骤
- 漏洞接口:
/cluster/state_single(需要登录后访问) - 完整路径:
http://localhost:8080/cluster/state_single - 构造请求示例:
http://localhost:8080/cluster/state_single?app=SSRF-TEST&ip=127.0.0.1&port=80 - 可修改
ip参数为任意目标地址进行SSRF攻击
六、漏洞原理总结
漏洞产生原因:
- 在构建HTTP请求时,直接拼接用户输入的IP地址和端口
- 缺乏对IP地址的完整合法性校验:
- 未检查是否为内网IP
- 未限制可访问的协议类型(只能使用HTTP)
- 未对目标地址进行白名单限制
七、修复建议
-
增加IP地址校验逻辑:
- 检查是否为内网地址
- 实现IP白名单机制
-
限制协议类型:
- 强制使用HTTPS
- 禁用非常用协议
-
增加输入验证:
if (!isValidIp(ip) || isInternalIp(ip)) { throw new IllegalArgumentException("Invalid IP address"); } -
使用安全的URL构建方式:
URI uri = new URIBuilder() .setScheme("https") .setHost(validatedIp) .setPort(validatedPort) .setPath(url) .build();
八、学习要点
-
SSRF漏洞常见触发场景:
- 直接拼接用户输入构建URL
- 缺乏对目标地址的校验
- 能够访问内部服务
-
代码审计关键点:
- 追踪用户输入流向
- 检查HTTP请求构建过程
- 验证所有外部输入
-
防御措施:
- 输入验证和过滤
- 输出编码
- 最小权限原则
- 网络层隔离