As NVIDIA's 2026 index stands, 31 rows name Triton Inference Server, split across seven bulletins whose first-patched versions run from 26.01 up to 26.06. Two rows are 9.8 CRITICAL, but one of them is CVE-2026-47627, and both bulletin 5865 and the MITRE record limit its impact to denial of service while the vector reads C:H/I:H/A:H, so a scanner reading NVD alone reports a 9.8 with the wrong impact and, because the MITRE record carries no fixed-version field, no remediation target either. Parsed across all seven bulletin Impacts columns, 22 of the 31 are denial of service and nothing else, 6 list code execution somewhere in their impacts, and 3 are CWE-288 authentication bypasses. The tritonserver binary's entire built-in authentication is a per-category shared-secret header labelled BETA, covering exactly nine categories through --http-restricted-api and --grpc-restricted-protocol, and every category you do not name is served without a check. The 2025 index adds 26 more unique IDs for 57 combined with no overlap, and its highest first-patched version is r25.10, so 25.07 is not a stopping point. Every 2026 bulletin landed 36 to 130 days after the release carrying its fix had already shipped, median 53 days, and six of the seven tell you to clone a GitHub repo rather than pull a tag. Start by reading the running version off GET /v2 and comparing it against 26.06.
NVIDIA's 2026 CVE index carries 31 rows whose affected product is Triton Inference Server, and two are scored 9.8 CRITICAL. Reading only those two gets Triton Inference Server security wrong in both directions. One of them is CVE-2026-47627, which bulletin 5865 describes as a path traversal where a successful exploit "might lead to denial of service", with an Impacts cell reading denial of service and nothing else, attached to a vector of CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H. Confidentiality high and integrity high, on a bug NVIDIA says crashes the server.
The other direction matters more for triage. Parsed across all seven 2026 bulletin Impacts columns, 22 of the 31 list denial of service and nothing else, 6 list code execution somewhere in their impacts, and 3 are authentication bypasses classified CWE-288. Those seven bulletins carry six different first-patched versions, the highest of which is 26.06. None of that covers 2025, where the highest first-patched version is r25.10 and a 9.8 OS command injection is fixed only in 25.08.
Everything below is read off NVIDIA's own index files and bulletin markdown. If you are unsure whether you run Triton directly or underneath TensorRT-LLM, where Triton fits against vLLM and Ollama settles that first.
Start with NVIDIA's index, not the two 9.8 headlines
Get the running version before you read a single bulletin. Triton publishes it through server metadata:
# Server metadata carries the version. This is the `metadata` category, # so it needs the header if you restricted metadata. curl -s localhost:8000/v2 # Readiness only, no version: curl -v localhost:8000/v2/health/ready
The second source is the image tag, because the NGC tag is the version. Tag 26.07 is Triton 26.07, which is GitHub release v2.71.0, published 2026-07-29 and the newest published release on this post's date.
Now the ladder:
The CVE column sums to 31. The Fixed In strings are NVIDIA's own, reproduced exactly including the inconsistent prefix: five of the seven carry no r, so a string match written against the r26.03 form skips most of the ladder. The floor from the 2026 index is 26.06. The method behind taking the highest first-patched version is worked through in the LiteLLM proxy version and CVE audit.
It is only half the floor, because the 2026 index knows nothing about 2025:
No CVE ID appears in both indexes, so together they hold 57 unique Triton identifiers. The 2025 row is where a partial upgrade stalls: 25.07 is the first patched version for eleven rows, more than any other 2025 value, so it reads like a finish line, but CVE-2025-23316 is fixed only in 25.08 and two more rows only in r25.10.
| Bulletin | Published | CVEs | Fixed in | Release carrying the fix | Release date | Days fix shipped before bulletin |
|---|---|---|---|---|---|---|
| 5790 | 2026-03-24 | 3 | 26.01 | v2.65.0 | 2026-02-03 | 49 |
| 5816 | 2026-04-07 | 5 | r26.02 | v2.66.0 | 2026-03-02 | 36 |
| 5828 | 2026-05-19 | 8 | r26.03 | v2.67.0 | 2026-03-27 | 53 |
| 5848 | 2026-06-30 | 2 | 26.04 | v2.68.0 | 2026-04-28 | 63 |
| 5853 | 2026-07-14 | 7 | 26.05 | v2.69.0 | 2026-06-02 | 42 |
| 5860 | 2026-08-04 | 1 | 26.03 | v2.67.0 | 2026-03-27 | 130 |
| 5865 | 2026-08-18 | 5 | 26.06 | v2.70.0 | 2026-06-26 | 53 |
| NVIDIA index | Unique Triton CVE IDs | First-patched versions seen | Highest | Critical count |
|---|---|---|---|---|
| 2025 | 26 | 24.12, 25.05, 25.06, 25.07, 25.08, 25.09, r25.10 | r25.10 | 4 |
| 2026 | 31 | 26.01, r26.02, r26.03, 26.04, 26.05, 26.06 | 26.06 | 2 |
The 9.8 that NVIDIA says is only a denial of service
Bulletin 5865 gives CVE-2026-47627 CWE-22, affected versions 0.0 to 26.05, updated version 26.06, base score 9.8, severity CRITICAL, and an Impacts cell that says denial of service and stops. The vector and that cell cannot both be the full truth: confidentiality and integrity High describe a bug that reads or alters data, while a denial-of-service-only impact describes a bug that stops the process. The bulletin shows revision 1.0.0 and carries no errata.
The half that breaks your tooling is downstream. The MITRE CNA record, assigner nvidia, published 2026-08-18 and last updated 2026-08-20, carries the identical DoS-only description, the identical CWE-22, and the identical confidentiality-high, integrity-high, availability-high metrics. The contradiction sits in NVIDIA's own CNA record, not in a downstream transcription. That record also lists only affected version 0.0 to 26.05 with no fixed-version field at all, the same as CVE-2026-47606. A scanner resolving either identifier through MITRE or NVD returns a 9.8 CRITICAL with the wrong impact and no remediation target, because the target, 26.06, exists only in the bulletin.
Two consequences: do not escalate CVE-2026-47627 as a remote code execution finding, and do not let the scanner set your floor.
What the 31 actually do: 22 denial of service, 6 code execution
Parsed from the Impacts column of each of the seven bulletins:
IDs are given by suffix, all CVE-2026 except 33238 and 33254, two CVE-2025 rows NVIDIA files in the 2026 index. The rows overlap on purpose, so they do not sum to 31. The clean partition is 22 denial of service only plus 9 everything else. Of those 9, two land in the information-disclosure row and six in the code-execution row, CVE-2026-24206 appears only in the CWE-288 row, and CVE-2026-24207 and CVE-2026-47481 appear in both the code-execution and CWE-288 rows.
Partitioning by CVSS vector instead of by Impacts text gives 21 availability-only rows rather than 22. The row that moves is CVE-2026-47627 again. Trust the Impacts column, which is prose NVIDIA wrote about the outcome, over a vector that contradicts it.
The CWE spread is flatter: no CWE appears more than five times, and nine appear once each. Path traversal is the largest family at seven of 31, five CWE-22 rows plus two CWE-36 rows.
Availability is the tenancy boundary on a shared GPU box
Twenty-two availability-only bugs sounds like the good news, and on a managed endpoint it is: a crash is the provider's problem and a restart is their SLA. On a departmentally shared on-premise GPU node the arithmetic inverts. A crash in the tritonserver process drops every model that process is serving, so one team's crafted request takes out every other team's endpoints with it, and there is no autoscaler underneath to hide the gap. The 22 denial-of-service rows generate incident tickets on that box; the 6 code-execution rows generate audit findings. Only one of them pages someone, which is the honest argument for isolating tenants at the hardware or process boundary rather than inside one server. The cost of doing that is measurable: we put numbers on what confidential computing on H100 actually costs.
| Impact profile | Count | CVE IDs |
|---|---|---|
| Denial of service only | 22 | 24146, 24158, 24173, 24174, 24175, 24208, 24209, 24210, 24215, 24264, 24266, 33238, 33254, 47476, 47477, 47478, 47479, 47480, 47482, 47627, 47628, 47629 |
| Denial of service plus information disclosure | 2 | 24147, 47487 |
| Code execution among the impacts | 6 | 24207, 24213, 24214, 47481, 47606, 47630 |
| Authentication bypass (CWE-288) | 3 | 24206, 24207, 47481 |
Three authentication bypasses, and what authentication means on Triton
Bulletin 5828 contains two separate CWE-288 findings, not one with a duplicate. CVE-2026-24207 is 9.8 CRITICAL, classified CWE-288 Authentication Bypass Using an Alternate Path or Channel, affecting all versions prior to r26.03, with impacts running through code execution, escalation of privileges, data tampering, denial of service and information disclosure. CVE-2026-24206 sits in the same bulletin with the same CWE and the same affected range, but scores 7.3 HIGH on CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:L and its impacts stop short of code execution. Different scores, different impact lists. Both fixed in r26.03.
A third sits in bulletin 5853: CVE-2026-47481, 6.5 MEDIUM, CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N, affecting 0.0 to 26.04 and fixed in 26.05, described as an authentication bypass through an alternative path or channel.
Three of them across the 2026 index sits oddly against what Triton's authentication actually is. There is no API key and no token in the general sense. What it has is a per-category shared-secret header on the tritonserver binary, plus a static key on the separate OpenAI-compatible frontend added in PR 8374, merged 2025-09-10. The documentation heading introducing the header feature reads "Limit Endpoint Access (BETA)", and the class holding the configuration is documented as initially empty with all categories unrestricted. That is the control's actual shape: an allowlist of things to protect, not a gate. Anything you do not name is served with no check.
Which component is actually affected: DALI, MLflow, Python backend
The Platform or OS column of each bulletin's Security Updates table names the component, and that column decides how many rows apply to you. Three of the eight rows in bulletin 5828 read DALI Backend: CVE-2026-24213 and CVE-2026-24214 at 8.0, and CVE-2026-24215 at 5.7. The other five read Linux. The DALI three also stand out in the vectors: all three carry PR:L and UI:R, the only 2026 Triton rows that do, which rules out the unauthenticated-network-attacker reading. If you load the DALI backend they are three real rows on your ladder; if you do not, they are three rows you can argue down in a risk register with the component name as the evidence.
CVE-2026-47487 is narrower still: the single row in bulletin 5860, 4.4 MEDIUM on CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:L with CWE-22, described as a user causing files outside the model repository to be read, written to or modified by providing a path in the model name to the Triton MLflow plugin. It is one of only two AV:L rows in the 2026 set; the other is CVE-2026-47630 at 5.5.
The 2025 index shows the same lever on a different component. Bulletin 5687 carries five Python backend rows fixed in 25.07: two out-of-bounds writes at 8.1 (CVE-2025-23318 and CVE-2025-23319, both CWE-805), a 7.5 classified CWE-209 where a very large request causes the shared memory limit to be exceeded, and two out-of-bounds reads at 5.9 (CVE-2025-23333 and CVE-2025-23334, both CWE-125). The count that matters is not 31, it is the subset your loaded backends and plugins produce.
The four flags that restrict access, and the nine categories they cover
Three of the four are on the tritonserver binary. The fourth belongs to a different program:
The nine categories are fixed in src/restricted_features.h as exactly these strings: health, metadata, inference, shared-memory, model-config, model-repository, statistics, trace, logging. The quickstart's own launch line publishes all three ports (-p8000:8000 -p8001:8001 -p8002:8002), and its startup log shows the three services bound at 0.0.0.0:8000, 0.0.0.0:8001 and 0.0.0.0:8002.
The documented pattern restricts five of the nine categories and leaves health, metadata, inference and logging open:
tritonserver \ --model-repository=/models \ --model-control-mode=none \ --http-restricted-api=shared-memory,model-config,model-repository,statistics,trace:admin-key=REPLACE_ME \ --grpc-restricted-protocol=shared-memory,model-config,model-repository,statistics,trace:admin-key=REPLACE_ME
The two flags cover different listeners, not different capabilities: pass only --http-restricted-api and port 8001 serves all nine categories to anyone who can reach it. Two parse-time errors are worth knowing before you templatize this: an unrecognised name raises "unknown restricted api '<name>'" (the word is protocol for the gRPC flag), and naming the same category in two groups raises "restricted api '<name>' can not be specified in multiple config groups".
One configured secret produces two wire formats, because the gRPC path prefixes the key with a compiled-in constant:
# HTTP: the header is the bare key curl -X POST -H "admin-key: REPLACE_ME" \ localhost:8000/v2/repository/models/mymodel/load # gRPC: the same key is sent prefixed # metadata key: triton-grpc-protocol-admin-key # metadata value: REPLACE_ME
A client library that sends the bare key over gRPC gets rejected, and a filter rule written against the HTTP header name never matches gRPC traffic.
The OpenAI-compatible frontend is a separate entrypoint. Its flag is registered with nargs=3 and action="append", so it takes three separate argv tokens and can be repeated to split credentials by category:
python3 openai_frontend/main.py \ --model-repository /models \ --tokenizer <hf-model-id> \ --openai-restricted-api inference user-key USER_SECRET \ --openai-restricted-api model-repository admin-key ADMIN_SECRET
Two defects in that frontend's own documentation will cost you a startup. The project README wraps the flag's three values in one quoted shell token, which argparse rejects under nargs=3 because it wants three separate argv tokens. And the README documents five categories including embedding mapped to POST /v1/embeddings, while ENDPOINT_MAPPING in api_restriction.py holds only four keys, with /v1/embeddings grouped under inference. Copy the README's category list and the process exits:
ValueError: Unknown API 'embedding'. Available APIs: ['inference', 'model-repository', 'metrics', 'health']
Loud is the good outcome. Verify controls against the code that enforces them, not the README that describes them. The evidence a reviewer wants here is a recorded rejection against a named endpoint, not a screenshot of a flag in a values file:
HTTP/1.1 401 Unauthorized
{
"error": {
"message": "This API is restricted, expecting header 'admin-key' with valid value",
"type": "authentication_error",
"code": "invalid_auth"
}
}Assert on that body in a smoke test after every chart change. One scope caveat: that frontend compares the secret with a plain Python inequality rather than a constant-time helper, which holds for the Python middleware and was not checked in the C++ paths.
| Flag | Program | Argument shape | Covers | Client sends | Default |
|---|---|---|---|---|---|
| --http-restricted-api | tritonserver | <apis>:<key>=<value>, one token | 9 categories over port 8000 | the bare key as a header | unset, all categories open |
| --grpc-restricted-protocol | tritonserver | <protocols>:<key>=<value>, one token | the same 9 categories over port 8001 | triton-grpc-protocol- plus the key | unset, all categories open |
| --model-control-mode | tritonserver | one of none, poll, explicit | whether load and unload exist at all | n/a | none |
| --openai-restricted-api | openai_frontend/main.py | three separate tokens: APIs, key, value | 4 categories over port 9000 | the bare key as a header | None, all endpoints allowed |
Model control mode is already none, and CVE-2025-23316 is why that matters
The safe value is the shipped default. Line 198 of the Secure Deployment Considerations guide reads --model-control-mode <string>, default "none", and the C++ help string in src/command_line_parser.cc says the same: the server loads all models at startup and makes no changes to the loaded set after that. The guide's top-level warning states that enabling dynamic updates, through either the model loading APIs or directory polling, can lead to arbitrary code execution.
So the work is not changing a setting. It is proving nothing else changed it. The general form of that argument, a safe default quietly overridden by deployment tooling, is made at length in the equivalent audit for a self-hosted Ollama server; the Triton-specific part is knowing which two flags give the override away:
# The default is already none. Look for the places that override it.
grep -rn -- '--model-control-mode' deploy/ charts/ Dockerfile* entrypoint*
grep -rn -- '--repository-poll-secs\|--load-model' deploy/ charts/
# On a running container, read the actual argv:
docker inspect --format '{{json .Args}}' <container>--repository-poll-secs is valid only when the mode is poll, and --load-model only takes effect when the mode is explicit. Neither does anything under the default, so either one appearing in your deployment tree is direct evidence the mode was changed, even if the change sits three layers away in a base image.
The reason to spend an afternoon on that is CVE-2025-23316: 9.8, CWE-78 OS command injection, described in bulletin 5691 as a vulnerability in the Python backend where an attacker could cause a remote code execution by manipulating the model name parameter in the model control APIs. It is the only CVE description in either index that names the model control APIs as the attack surface, and it is fixed only in 25.08. The endpoints it runs through are the ones the explicit mode exposes: POST v2/repository/index, POST v2/repository/models/${MODEL_NAME}/load and POST v2/repository/models/${MODEL_NAME}/unload.
docker run --rm --user triton-server \
-v ${PWD}/model_repository:/models \
nvcr.io/nvidia/tritonserver:YY.MM-py3 \
tritonserver --model-repository=/modelsThe model repository is a code deployment path
The same guide is blunt about what a backend does with what it finds there: some Triton backends execute code loaded from the model repository, either in the server process or in a separate process the backend manages, that code exercises the operating-system privileges and access available to whichever process runs it including filesystem access, available credentials and network access, and Triton provides no security sandbox for arbitrary model or backend code. In a regulated estate that reclassifies the model repository. It is not a data directory a data science team owns; it is a code deployment path with the same change-control status as a container image, which makes vetting the model files before they reach the repository a release gate. It also turns the guide's least-privilege line into a control, since the pre-built containers already contain a non-root user triton-server with uid 1000: Two compliance artifacts fall out: the recorded argv showing the mode was never overridden, and the recorded uid showing that a malicious model file gets a 1000-uid process rather than root on a shared box.
The bulletin lands 36 to 130 days after the fix already shipped
Line up each bulletin's date against the GitHub release carrying its fixed version and the lag is 36 to 130 days, median 53 across the seven. Bulletin 5860 is the extreme: an estate upgrading on the monthly cadence had that fix in production for four months before NVIDIA named the CVE.
The remediation channel makes the same point from the other side. Six of the seven 2026 bulletins phrase the fix as cloning or updating the software to a given Triton Server version or later from the NVIDIA Triton Inference Server GitHub repo; only bulletin 5865 says to update to version 26.06 or later. Across all twelve Triton bulletins from 2025 and 2026, the strings nvcr, NGC and container appear zero times. NVIDIA tells you to clone a git repository to fix a server it distributes itself as an NGC container image, which is the form its own quickstart launches. The 2025 bulletins at least link the Secure Deployment Considerations guide; none of the seven 2026 ones does.
So track the monthly release tag rather than the bulletin feed. At a median lag of 53 days, a release-tag cadence is ahead of disclosure by construction.
Then record the tag, not the bulletin version string. NVIDIA's strings are inconsistent (the same fix is r26.03 in one index row and 26.03 in another) and none names the registry your estate mirrors. An air-gapped environment cannot follow "clone the GitHub repo" at all, so somebody translates the bulletin version into a mirrored tag by hand, and that mapping is the artifact linking the CVE to the image you run. The same translation problem on a different serving stack shows up in the same exercise for vLLM and CVE-2026-22778.
What to do this week
Four things, in order, and only the last needs a change window: The rest of our AI security coverage works through the same pattern on neighbouring components: the control exists, it appears in the evidence pack, and the path it is supposed to sit on has never been checked against the code.
- 1.
curl -s localhost:8000/v2on every Triton instance and write the versions down. Compare each against 26.06, and against r25.10 for anything older than 26.01. - 2. Grep your deployment tree for
--model-control-mode,--repository-poll-secsand--load-model, thendocker inspectthe argv of one running container to confirm the tree matches reality. - 3. Check whether either restricted-access flag is set, and on which listener.
--http-restricted-apiwithout--grpc-restricted-protocolmeans port 8001 is serving all nine categories unrestricted right now. - 4. Schedule the upgrade against the release tag rather than the bulletin, and record the mirrored tag in the change ticket.
FAQ
Quick answers to the questions this post tends to raise.


