
Technical vulnerability analysis and proof-of-concept for CVE-2026-47630, an arbitrary dlopen via TRITON_BATCH_STRATEGY_PATH in NVIDIA Triton Inference Server enabling native code execution, with detection and mitigation guidance.
dlopen via TRITON_BATCH_STRATEGY_PATHAbsolute path traversal in the custom batching-strategy loader of
triton-inference-server/core. A model configuration parameter is passed
unvalidated to dlopen(RTLD_NOW | RTLD_LOCAL), so anyone able to influence a
model's config.pbtxt obtains native code execution inside the Triton server
process at model-load time.
NVIDIA's acknowledgement in bulletin 5865 reads verbatim: CVE-2026-47630: s1ko.
Triton accepts a per-model TRITON_BATCH_STRATEGY_PATH parameter from the
model's config.pbtxt. Through 26.05 the value was treated as an arbitrary
filesystem path and handed unmodified to dlopen. There was no containment
check, no allowlist, and no signature or hash verification. Absolute paths,
traversal sequences and symlinks were all accepted.
RTLD_NOW resolves every symbol immediately and runs the object's
__attribute__((constructor)) / .init_array routines before dlopen
returns. Execution is therefore unconditional on a successful load — none of
the TRITONBACKEND_ModelBatch* entry points need to exist for the payload to
run, and it runs with the privileges of the Triton process (frequently root
in NGC containers).
src/backend_model.cc — attacker input reaches batch_libpath, validated only
for existence:
if (model_config.parameters().contains("TRITON_BATCH_STRATEGY_PATH")) {
batch_libpath = model_config.parameters()
.at("TRITON_BATCH_STRATEGY_PATH")
.string_value();
bool exists = false;
RETURN_IF_ERROR(FileExists(batch_libpath, &exists));
if (!exists) {
return Status(
triton::common::Error::Code::NOT_FOUND,
("Batching library path not found: " + batch_libpath).c_str());
}
}
Notably absent is any call to IsChildPathEscapingParentPath, which Triton
already applied to label paths and backend library paths elsewhere in the same
file.
src/backend_model.cc — SetBatchingStrategy forwards it:
Status TritonModel::SetBatchingStrategy(const std::string& batch_libpath)
{
std::unique_ptr<SharedLibrary> slib;
RETURN_IF_ERROR(SharedLibrary::Acquire(&slib));
RETURN_IF_ERROR(slib->OpenLibraryHandle(batch_libpath, &batch_dlhandle_));
src/shared_library.cc — the sink:
*handle = dlopen(path.c_str(), RTLD_NOW | RTLD_LOCAL);
Three practical routes to controlling model_config.parameters():
config.pbtxt plants a .so in their own directory and points the
parameter at it. The payload runs with the server's privileges, giving
access to every other tenant's weights, secrets and GPU.TRITONSERVER_ServerLoadModelWithParameters
combined with the documented file:<rel-path> parameter prefix lets a
caller with model-load rights submit config.pbtxt and the .so in a
single request and trigger the load immediately. No filesystem access
outside the API is required.dlopens on demand.poc/ contains the primitive, reduced to what OpenLibraryHandle
does. Sources only — build them yourself:
$ cd poc && ./build.sh
$ gcc -O0 -o test_dlopen test_dlopen.c -ldl
$ ./test_dlopen ./evil.so
dlopen OK, handle=0x55d1779652c0
$ cat /tmp/triton_dlopen_rce_proof.log
[triton-dlopen-rce] constructor fired @ Fri May 1 09:56:56 2026
pid=240157 uid=1000 euid=1000 cwd=/opt/poc
The payload is inert: it appends one line recording pid, uid, euid and cwd. Verified on Debian 13, GCC 14.2, x86_64.
For the Triton-side trigger, place the built evil.so next to
poc/config.pbtxt in the model repository and load the
model — auto-load, --load-model, or
POST /v2/repository/models/evil_model/load. On an affected version the
constructor fires during SetBatchingStrategy, before any backend symbol
lookup.
The load is logged by Triton itself at INFO:
Loading custom batching strategy library <path> for model <name>
On a fixed build, a rejected attempt surfaces as
Batching library path escapes model repository.
Any TRITON_BATCH_STRATEGY_PATH value that is absolute, contains .., or
resolves outside the model directory is worth alerting on regardless of
version. A Sigma rule covering both the log line and the config parameter is
in detection/.
Complementary signals:
config.pbtxt files carrying a TRITON_BATCH_STRATEGY_PATH parameter at
all — the feature is rare in practice, so presence alone is a useful filter.openat/mmap of a .so outside the model root by the tritonserver
process (auditd, eBPF, or Falco).Upgrade to Triton Inference Server 26.06 or later. The fix adds the containment check the loader was missing:
bool escapes{true};
RETURN_IF_ERROR(IsChildPathEscapingParentPath(
batch_libpath, localized_model_dir->Path(), &escapes));
if (escapes) {
return Status(
Status::Code::INVALID_ARG,
"Batching library path escapes model repository.");
}
Where upgrading is not immediately possible, compensating controls:
TRITON_BATCH_STRATEGY_PATH from every config.pbtxt
before it reaches the model repository.--model-control-mode other than
explicit, or authorization on the repository endpoints) unless it is
required.tritonserver as an unprivileged user with a read-only model
repository mount, so a successful load has the smallest possible blast
radius.Mapping: MITRE ATT&CK T1574.006 Hijack Execution Flow: Dynamic Linker
Hijacking and
T1129 Shared Modules;
NIST SP 800-53r5 SI-7, CM-5, SC-18; CIS Controls v8 §2, §4.
triton-inference-server/core — https://github.com/triton-inference-server/coreMIT — see LICENSE.
| CVE | CVE-2026-47630 |
| NVIDIA bulletin | 5865 (2026-08-18) |
| CWE | CWE-36 (Absolute Path Traversal); reported as CWE-114 / CWE-829 |
| CVSS v3.1 (NVIDIA) | 5.5 MEDIUM — AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N |
| Affected | Triton Inference Server 0.0 – 26.05 (Linux) |
| Fixed in | 26.06 |
| Reported by | s1ko (github.com/s1ko, [email protected]) |
| Vendor tracking | NVIDIA PSIRT ticket 6139742 |
| Date | Event |
|---|
| 2026-04-25 | Primary Triton path-handling finding reported; NVIDIA PSIRT opens ticket 6128799 |
| 2026-05-01 | This secondary finding confirmed and reported; PSIRT opens ticket 6139742 |
| 2026-05-28 | Coordinated-disclosure cadence agreed with PSIRT |
| 2026-06 | Fix ships in Triton Inference Server 26.06 |
| 2026-08-18 | NVIDIA publishes bulletin 5865, assigning CVE-2026-47630 and crediting s1ko |
| 2026-08-22 | This write-up published |