
रीप्रोड्यूसर जो Apache Polaris Iceberg REST में स्थान सत्यापन से पहले क्रेडेंशियल वितरण का दोहन करता है, जो क्रॉस-टेनेंट क्लाउड रीड्स और बकेट अस्तित्व ओरेकल को सिद्ध करता है।
register में लोकेशन सत्यापन से पहले क्रेडेंशियल वितरणCVE-2026-64640 के लिए एक स्व-निहित, एक-कमांड रीप्रोड्यूसर: Apache Polaris में Iceberg REST register एंडपॉइंट, कॉलर द्वारा दिए गए पाथ के लिए क्लाउड स्टोरेज क्रेडेंशियल जारी करते हैं और उस पाथ को कैटलॉग की allowedLocations के विरुद्ध जाँचने से पहले सर्वर-साइड पढ़ लेते हैं।
एक प्रिंसिपल जिसका एकमात्र विशेषाधिकार अपने स्वयं के कैटलॉग में टेबल बनाना है, वह Polaris को कैटलॉग के स्टोरेज क्रेडेंशियल्स का उपयोग करके उन ऑब्जेक्ट्स को पढ़ने के लिए मजबूर कर सकता है जिन्हें छूने की अनुमति कैटलॉग को कभी नहीं थी — किसी अन्य टेनेंट का प्रीफिक्स, कोई अन्य बकेट, वह सब कुछ जिस तक स्टोरेज प्रिंसिपल पहुँच सकता है।
| CVE | CVE-2026-64640 |
| घटक | polaris-runtime-service — IcebergCatalog / LocalIcebergCatalog: registerTable, registerView |
| एंडपॉइंट्स | POST /api/catalog/v1/{prefix}/namespaces/{namespace}/registerPOST /api/catalog/v1/{prefix}/namespaces/{namespace}/register-view (1.6.0+) |
| CWE | CWE-441 (confused deputy), CWE-639 (authorization bypass through user-controlled key), CWE-918 (SSRF) |
| गंभीरता | उच्च — CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N (8.1) |
| प्रभावित | ≤ 1.6.0 (1.3.0-incubating, 1.4.0, 1.4.1, 1.5.0, 1.6.0 पर सत्यापित) |
| फिक्स किया गया | 1.7.0 — 1.6.0 केवल टेबल पाथ को ठीक करता है और नए व्यू पाथ पर त्रुटि को पुनः प्रस्तुत करता है |
| आवश्यक विशेषाधिकार | किसी भी कैटलॉग पर TABLE_CREATE / CATALOG_MANAGE_CONTENT वाला एक प्रमाणित प्रिंसिपल — कोई एडमिन नहीं |
1.6.0 कोई फिक्स नहीं है। यह
registerको ठीक करता है (संयोगवश, एक फीचर PR के भीतर) और बिल्कुल नएregister-viewएंडपॉइंट को उसी क्रम-त्रुटि के साथ जारी करता है। रीप्रोड्यूसर दोनों हिस्सों को सिद्ध करता है। 1.7.0 पर अपग्रेड करें।
आवश्यकताएँ: Compose v2 प्लगइन के साथ Docker, curl, python3, bash। और कुछ नहीं — पर्यावरण प्रकाशित इमेजेस से बनाया जाता है और बाद में हटा दिया जाता है।
./exploit.sh # default: apache/polaris:1.4.1 -> reproduces via register
./exploit.sh --tag 1.6.0 # partially fixed -> reproduces via register-view
./exploit.sh --tag 1.7.0 # comprehensively fixed -> refuses cleanly on both
./scripts/version-matrix.sh # every release, side by side
एग्ज़िट कोड 0 का अर्थ है भेद्यता दोहराई गई, 1 का अर्थ है नहीं दोहराई गई, 2 का अर्थ है पर्यावरण शुरू नहीं हो सका। हर अनुरोध और प्रतिक्रिया evidence/<timestamp>-polaris-<tag>/ में लिखी जाती है।
catalog tenant_a_catalog allowedLocations = [ s3://bucket123 ]
actor low_priv_user CATALOG_MANAGE_CONTENT on that catalog, nothing else
target s3://tenant-b-private another tenant's bucket:
no allowedLocations entry, no grant, no relation
to the attacker's catalog — but reachable by the
credentials that back it
वह अंतिम पंक्ति यथार्थवादी हिस्सा है। ऑपरेटर कैटलॉग को allowedLocations से सीमित करते हैं; नीचे की IAM भूमिका लगभग हमेशा एक प्रीफिक्स से व्यापक होती है। allowedLocations ही दीवार है। यह बग उसे बायपास कर देता है।
टेस्ट 0 — दीवार वास्तविक है। s3://tenant-b-private में स्पष्ट लोकेशन के साथ टेबल बनाना, कुछ भी पढ़े जाने से पहले 403 ForbiddenException के साथ अस्वीकार कर दिया जाता है। Polaris भली-भाँति जानता है कि लोकेशन सीमा से बाहर है।
टेस्ट 1 — register फिर भी उसे पढ़ लेता है। वही प्रिंसिपल register को s3://tenant-b-private/sales/metadata/00007-tenant-b-sales.metadata.json पर इंगित करता है। प्रतिक्रिया भी 403 है — लेकिन वह s3://tenant-b-private/warehouse/CANARY-64640-4f1c9e2a-tenant-b-sales को उद्धृत करती है, एक स्ट्रिंग जो केवल उस ऑब्जेक्ट के बॉडी के भीतर मौजूद है:
{"error":{"message":"Invalid locations '[s3://tenant-b-private/warehouse/sales/data,
s3://tenant-b-private/warehouse/CANARY-64640-4f1c9e2a-tenant-b-sales]' for identifier
'tenant_a_ns.pwn_canary': s3://tenant-b-private/warehouse/sales/data is not in the list
of allowed locations: [s3://bucket123/tenant_a_ns]","type":"ForbiddenException","code":403}}
अनुरोध warehouse/ का कभी उल्लेख नहीं करता। Polaris उन स्ट्रिंग्स को केवल कैटलॉग के क्रेडेंशियल्स के साथ ऑब्जेक्ट को लाकर और पार्स करके ही उत्पन्न कर सकता था। 403 Polaris द्वारा उस रीड के एक कदम बाद उल्लंघन को पकड़ना है, जिसे उसे रोकना था।
टेस्ट 2 — क्या वापस मिलता है। पीड़ित दस्तावेज़ से पार्स किए गए दो अलग-अलग फ़ील्ड कॉलर को प्रतिध्वनित किए जाते हैं: टेबल की घोषित location और उसकी write.data.path प्रॉपर्टी।
टेस्ट 3 — एक स्टोरेज एन्यूमरेशन ओरेकल। allowedLocations के बाहर के लक्ष्य की हर अवस्था के लिए वही कॉल अलग-अलग उत्तर देती है:
चार विभेद्य उत्तरों का अर्थ है स्टोरेज के बारे में चार ऐसे तथ्य जिन्हें सीखने का कॉलर को कोई अधिकार नहीं है। वास्तविक AWS पर बकेट-अस्तित्व प्रोब कैटलॉग के क्रेडेंशियल्स का उपयोग करके वैश्विक S3 बकेट नेमस्पेस तक पहुँचती है।
फिक्स्ड बिल्ड पर उस तालिका की हर पंक्ति वही 403 होती है जो केवल अनुरोधित पाथ का नाम लेती है, और ऑब्जेक्ट के अंदर से कुछ भी वापस नहीं आता — स्क्रिप्ट उस हस्ताक्षर का पता लगाती है और NOT VULNERABLE — pre-validation observed रिपोर्ट करती है।
टेस्ट 4 — register-view पर वही त्रुटि। Polaris 1.6.0 ने POST .../namespaces/{ns}/register-view जोड़ा, जो registerView तक पहुँचता है, जो बिना किसी पूर्व सत्यापन के FileIO लोड करता है और कॉलर के दस्तावेज़ को पार्स करता है — ठीक वही जो registerTable ने अभी-अभी करना बंद किया था। 1.6.0 पर व्यू कैनरी यह लौटाती है:
{"error":{"message":"Invalid locations '[s3://tenant-b-private/warehouse/CANARY-64640-VIEW-8d3b7a15-tenant-b]'
for identifier 'tenant_a_ns.pwn_view': … is not in the list of allowed locations:
[s3://bucket123/tenant_a_ns]","type":"ForbiddenException","code":403}}
अतः 1.6.0 परिनियोजन अभी भी एक अलग एंडपॉइंट के माध्यम से उसी प्रिमिटिव के संपर्क में है। 1.4.1 और पुराने पर एंडपॉइंट मौजूद नहीं है और स्क्रिप्ट यही बताती है।
IcebergCatalog.registerTable (1.6.0 से LocalIcebergCatalog), फिक्स से पहले — registerView की संरचना समान है:
String locationDir = metadataFileLocation.substring(0, lastSlashIndex); // attacker-controlled
...
FileIO fileIO =
loadFileIOForTableLike(
identifier,
Set.of(locationDir), // credentials minted here
resolvedParent,
new HashMap<>(tableDefaultProperties),
Set.of(PolarisStorageActions.READ, PolarisStorageActions.LIST));
InputFile metadataFile = fileIO.newInputFile(metadataFileLocation); // server-side GET
TableMetadata metadata = TableMetadataParser.read(metadataFile); // server-side parse
ops.commit(null, metadata); // allowedLocations checked HERE
वितरण श्रृंखला (loadFileIOForTableLike → StorageAccessConfigProvider.getStorageAccessConfig → *StorageIntegration.getSubscopedCreds) अपनी ओर से कोई allowedLocations जाँच नहीं करती, इसलिए एकमात्र प्रवर्तन कमिट-समय वाला है — और तब तक विशेषाधिकार प्राप्त रीड हो चुकी होती है और उसका परिणाम प्रतिक्रिया में होता है।
उसी फ़ाइल में सहयोगी पाथ सही क्रम रखते हैं: व्यू निर्माण और sendNotificationForTableLike दोनों FileIO लोड करने से पहले सत्यापन करते हैं। registerTable ही असंगत था। पूरी विस्तृत समीक्षा docs/ANALYSIS.md में।
टेबल पाथ — कमिट 1dd5feeb (2026-06-02, पहली बार 1.6.0 में जारी) ने सही स्थान पर एक पंक्ति जोड़ी:
validateLocationForTableLike(identifier, metadataFileLocation, resolvedParent);
FileIO fileIO = loadFileIOForTableLike(identifier, Set.of(locationDir), ...);
यह एक फीचर PR ("add RegisterTable overwrite support") के भीतर आया, न कि किसी सुरक्षा फिक्स के रूप में, इसलिए 1.6.0 ने सुधार को किसी भी advisory में नामित किए बिना जारी कर दिया।
व्यू पाथ — कमिट 7e822f23 ("Validate locations when registering tables and views", #5114), 2026-07-20, पहली बार 1.7.0 में जारी, ने registerView में वही सुरक्षा जोड़ी और दोनों पाथों के लिए पोस्ट-पार्स जाँचों को समेकित किया।
1.7.0 में 85a0c292 (#4860, "Fix native catalog credential vending skipping allowedLocations re-validation") भी शामिल है, जो संबंधित defence-in-depth अंतराल को बंद करता है: क्रेडेंशियल पाथ अब हर कॉलर पर भरोसा करने के बजाय स्वयं पुनः-सत्यापन करता है। यह समस्या का संरचनात्मक आधा हिस्सा है, और यह एक और कारण है कि 1.6.0 के बजाय 1.7.0 वह रिलीज़ है जिस पर रहा जाना चाहिए।
फिक्स की उपस्थिति की जाँच रिलीज़ टैग्स के विरुद्ध की गई: 1dd5feeb 1.6.0 और 1.7.0 में है; 7e822f23 और 85a0c292 केवल 1.7.0 में हैं।
पुराने संस्करणों पर बने रहने वालों के लिए, दोनों परिवर्तन पैच के रूप में उपलब्ध हैं: ≤ 1.5.0 टेबल पाथ के लिए 0001, और 1.6.0 व्यू पाथ के लिए 0002।
ऑपरेटर मार्गदर्शन — अपग्रेड पाथ, शमन (mitigations), और मौजूदा लॉग्स में शोषण के निशान कैसे खोजें — docs/REMEDIATION.md में है।
./scripts/version-matrix.sh से निर्मित; हर पंक्ति एक पूर्ण स्टैक प्रारंभ और एक लाइव शोषण प्रयास है। देखें docs/AFFECTED-VERSIONS.md।
1.4.1 महत्वपूर्ण है: यह वह रिलीज़ है जिसने CVE-2026-42809 को ठीक किया था — stage-create पाथ में वही validate-after-vend गलती। वह फिक्स रिपोर्ट में दिए गए एंडपॉइंट तक सीमित था, और फिर वह पैटर्न दो बार दोहराया गया — register ने 1.6.0 तक वह व्यवहार बनाए रखा, और 1.6.0 का नया register-view उसी पैटर्न के साथ जन्मा।
यह रीप्रोड्यूसर क्या प्रदर्शित करता है, और कुछ नहीं:
register के माध्यम से और 1.6.0 पर register-view के माध्यम से।यह नहीं दर्शाता: API के माध्यम से दायरे से बाहर के ऑब्जेक्ट की पूरी सामग्री का थोक निष्कर्षण। दो बाद की जाँचें (मेटाडेटा-लोकेशन का टेबल लोकेशन के अधीन होना, और पार्स की गई लोकेशन का allowedLocations में होना) पंजीकरण को पूर्ण होने से रोकती हैं, इसलिए कॉलर को पूरा दस्तावेज़ नहीं, बल्कि एक ओरेकल और मेटाडेटा खंड मिलते हैं। कैटलॉग पर S3 एंडपॉइंट ओवरराइड कॉन्फ़िगर होने पर, वही प्रिमिटिव कॉन्फ़िगर किए गए होस्ट के विरुद्ध एक सर्वर-साइड रिक्वेस्ट फोर्जरी है।
सब कुछ स्थानीय रूप से चलता है: लूपबैक पोर्ट्स पर एक Docker Compose प्रोजेक्ट, एक अस्थायी S3 (RustFS) इंस्टेंस, और सिंथेटिक "victim" फ़ाइलें जिन्हें यह रिपॉजिटरी स्वयं रखती है। कोई बाहरी होस्ट से संपर्क नहीं होता, कोई क्रेडेंशियल मशीन से बाहर नहीं जाता, और जब तक आप --keep पास नहीं करते, बाहर निकलने पर docker compose down -v चलता है। victim-data/ ऑब्जेक्ट गढ़े गए हैं; यहाँ कहीं भी कोई वास्तविक डेटा नहीं है।
इसे केवल उन्हीं सिस्टम्स पर उपयोग करें जिनके आप स्वामी हैं या जिनके परीक्षण के लिए आप अधिकृत हैं।
CVE-2026-42809 फिक्स की समीक्षा के दौरान खोजा गया, परियोजना की SECURITY.md में दी गई प्रक्रिया के माध्यम से Apache Software Foundation सुरक्षा टीम को रिपोर्ट किया गया, और CVE-2026-64640 के रूप में ट्रैक किया गया।
Apache License 2.0 — देखें LICENSE। Compose पर्यावरण Apache Polaris क्विकस्टार्ट से व्युत्पन्न है; देखें NOTICE।
प्रोब (सभी allowedLocations के बाहर) | 1.4.1 प्रतिक्रिया |
|---|
| मान्य Iceberg मेटाडेटा, मौजूद है | 403 ForbiddenException + पार्स की गई लोकेशन्स प्रतिध्वनित |
| कुंजी अनुपस्थित | 400 NotFoundException — Location does not exist: … |
| बकेट मौजूद नहीं है | 400 NoSuchBucketException — कच्ची S3 SDK त्रुटि |
| मौजूद है परंतु Iceberg मेटाडेटा नहीं है | 503 RuntimeIOException — Failed to read file: … |
| रिलीज़ | register (टेबल) | register-view | निर्णय |
|---|
| 1.3.0-incubating | भेद्य | एंडपॉइंट अनुपस्थित | भेद्य |
| 1.4.0 | भेद्य | एंडपॉइंट अनुपस्थित | भेद्य |
| 1.4.1 | भेद्य | एंडपॉइंट अनुपस्थित | भेद्य |
| 1.5.0 | भेद्य | एंडपॉइंट अनुपस्थित | भेद्य |
| 1.6.0 | वितरण से पहले सत्यापित | भेद्य | भेद्य |
| 1.7.0 | वितरण से पहले सत्यापित | वितरण से पहले सत्यापित | फिक्स्ड |