TRITON_BATCH_STRATEGY_PATH 实现任意 dlopentriton-inference-server/core 的自定义批处理策略加载器中存在绝对路径遍历漏洞。模型配置参数未经校验即被传递给 dlopen(RTLD_NOW | RTLD_LOCAL),因此任何能够影响模型 config.pbtxt 的人,都可在模型加载时于 Triton 服务器进程内获得本机代码执行能力。
| CVE | CVE-2026-47630 |
| NVIDIA 公告 | 5865(2026-08-18) |
| CWE | CWE-36(绝对路径遍历);同时上报为 CWE-114 / CWE-829 |
| CVSS v3.1(NVIDIA) | 5.5 中危 — AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N |
| 受影响版本 | Triton Inference Server 0.0 – 26.05(Linux) |
| 修复版本 | 26.06 |
| 上报者 | s1ko(github.com/s1ko,[email protected]) |
| 厂商跟踪 | NVIDIA PSIRT 工单 6139742 |
NVIDIA 在公告 5865 中的致谢原文为:CVE-2026-47630: s1ko。
Triton 会接受模型 config.pbtxt 中按模型设置的 TRITON_BATCH_STRATEGY_PATH 参数。在 26.05 及之前版本,该值一直被当作任意文件系统路径,未经修改便传递给 dlopen。既没有包含关系检查,也没有白名单,更没有签名或哈希校验。绝对路径、遍历序列和符号链接均会被接受。
RTLD_NOW 会立即解析所有符号,并在 dlopen 返回之前运行对象的 __attribute__((constructor)) / .init_array 例程。因此,执行并不以成功加载为前提——载荷运行无需任何 TRITONBACKEND_ModelBatch* 入口点存在,并且它以 Triton 进程的权限运行(在 NGC 容器中通常为 root)。
src/backend_model.cc — 攻击者输入到达 batch_libpath,仅校验其是否存在:
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());
}
}
值得注意的是,代码中完全没有调用 IsChildPathEscapingParentPath——Triton 在同一文件的其他位置已经将该检查应用于标签路径和后端库路径。
src/backend_model.cc — SetBatchingStrategy 将其转发:
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 — 汇聚点(sink):
*handle = dlopen(path.c_str(), RTLD_NOW | RTLD_LOCAL);
控制 model_config.parameters() 有三条实际可行的途径:
config.pbtxt 的租户可以在自己的目录中植入一个 .so,并将参数指向该文件。载荷以服务器权限运行,从而能够访问其他所有租户的权重、机密和 GPU。TRITONSERVER_ServerLoadModelWithParameters 结合文档化的 file:<rel-path> 参数前缀,使拥有模型加载权限的调用方可以在单个请求中提交 config.pbtxt 和 .so,并立即触发加载。无需 API 之外的任何文件系统访问权限。dlopen。poc/ 包含该攻击原语,已精简为 OpenLibraryHandle 所做的工作。仅提供源码——请自行构建:
$ 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
该载荷是惰性的:它仅追加一行记录 pid、uid、euid 和 cwd 的日志。已在 Debian 13、GCC 14.2、x86_64 上验证。
对于 Triton 侧的触发方式,将构建好的 evil.so 放在模型仓库中 poc/config.pbtxt 旁边,然后加载该模型——可通过自动加载、--load-model 或 POST /v2/repository/models/evil_model/load。在受影响版本上,构造函数会在 SetBatchingStrategy 期间、任何后端符号查找之前触发。
Triton 本身会以 INFO 级别记录该加载:
Loading custom batching strategy library <path> for model <name>
在已修复的构建上,被拒绝的尝试会报出 Batching library path escapes model repository.
无论版本如何,任何绝对路径、包含 .. 或解析到模型目录之外的 TRITON_BATCH_STRATEGY_PATH 值都值得触发告警。涵盖该日志行和配置参数的 Sigma 规则位于 detection/ 中。
补充信号:
config.pbtxt 文件中带有 TRITON_BATCH_STRATEGY_PATH 参数——该功能在实际中极少使用,因此仅凭参数存在与否就是一个有用的过滤条件。.so)。tritonserver 进程对模型根目录之外的 .so 执行 openat/mmap(通过 auditd、eBPF 或 Falco 检测)。升级到 Triton Inference Server 26.06 或更高版本。 该修复补上了加载器所缺失的包含关系检查:
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.");
}
在无法立即升级的情况下,可采取以下补偿性控制措施:
config.pbtxt 进入模型仓库之前,拒绝或剥离其中的 TRITON_BATCH_STRATEGY_PATH。explicit 以外的 --model-control-mode,或对仓库端点实施授权)。tritonserver,并将模型仓库以只读方式挂载,从而使成功加载时的爆炸半径尽可能小。映射:MITRE ATT&CK T1574.006 劫持执行流:动态链接器劫持 与 T1129 共享模块;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 — 参见 LICENSE。
| 日期 | 事件 |
|---|
| 2026-04-25 | 上报主要的 Triton 路径处理发现;NVIDIA PSIRT 创建工单 6128799 |
| 2026-05-01 | 确认并上报本次要发现;PSIRT 创建工单 6139742 |
| 2026-05-28 | 与 PSIRT 商定协同披露节奏 |
| 2026-06 | 修复随 Triton Inference Server 26.06 发布 |
| 2026-08-18 | NVIDIA 发布公告 5865,分配 CVE-2026-47630 并致谢 s1ko |
| 2026-08-22 | 发布本文 |