
एक निम्न-विशेषाधिकार वाला Docmost उपयोगकर्ता एक पीड़ित के attachmentId को सामान्य अपलोड एंडपॉइंट पर प्रदान करके उसी कार्यक्षेत्र में किसी अन्य पृष्ठ के संग्रहीत अटैचमेंट को अधिलेखित कर सकता है।
एक कम-विशेषाधिकार प्राप्त Docmost उपयोगकर्ता किसी पीड़ित के attachmentId को सामान्य अपलोड एंडपॉइंट पर प्रदान कर सकता था और उसी कार्यक्षेत्र में किसी अन्य पृष्ठ के संग्रहीत अनुलग्नक को अधिलेखित कर सकता था।
मैंने Docmost, ओपन-सोर्स सहयोगी दस्तावेज़ीकरण प्लेटफ़ॉर्म में एक उच्च-गंभीरता वाले प्राधिकरण दोष की पहचान की, जिम्मेदारी से खुलासा किया और इसे पुन: उत्पन्न किया।
Docmost की आधिकारिक साइट इसे 3M+ डाउनलोड वाला एक एंटरप्राइज़-रेडी ऑन-प्रिमाइसेस विकी बताती है, और कहती है कि विल्नियस सिटी, बेक्टल, ऑस्ट्रेलियाई सरकार, रेड क्रॉस और ETS क्यूबेक सहित संगठनों की टीमों द्वारा इस पर भरोसा किया जाता है।
बग Docmost द्वारा डायग्राम सेव/अपडेट फ़्लो में उपयोग किए जाने वाले सामान्य फ़ाइल-अपलोड पथ में रहता था।
मैं उस कोड की समीक्षा एक बहुत ही विशिष्ट प्रश्न के साथ कर रहा था:
क्या होता है यदि अपलोड एंडपॉइंट एक पृष्ठ पर संपादन पहुँच साबित करता है, लेकिन अधिलेखित लक्ष्य एक अलग उपयोगकर्ता-नियंत्रित अनुलग्नक आईडी के साथ चुना जाता है?
इस मामले में, उस प्रश्न ने सीधे एक वास्तविक ऑब्जेक्ट-बाइंडिंग विफलता का नेतृत्व किया।
Docmost ने कॉलर को भेजने की अनुमति दी:
pageId जिस पृष्ठ को वे संपादित करने की अनुमति रखते थे, औरattachmentId जो उसी कार्यक्षेत्र के एक अलग पृष्ठ से संबंधित थासर्वर ने एक अधिलेखित स्थिरता जाँच की, लेकिन गार्ड ने गलत बूलियन तर्क का उपयोग किया।
---इसका मतलब था कि अनुरोध प्राधिकरण पास कर सकता था और फिर भी पीड़ित के अनुलग्नक को अधिलेखित कर सकता था।
यह मुद्दा CVE-2026-34213 बन गया।
Docmost: docmost/docmost
सलाहकार: GHSA-89fp-2hch-j9gp
CVE: CVE-2026-34213
पैच किया गया: v0.71.0 में

हमलावर-नियंत्रित pageId जिसमें संपादन पहुँच है -> हमलावर-नियंत्रित पीड़ित attachmentId -> दोषपूर्ण अधिलेखित गार्ड क्रॉस-पेज अधिलेखन को वैध मानता है -> पीड़ित attachmentId से भंडारण पथ पुनर्निर्मित -> हमलावर बाइट्स पीड़ित फ़ाइल को बदल देते हैं -> पीड़ित पृष्ठ संशोधित अनुलग्नक को परोसना जारी रखता है
Docmost पृष्ठ अनुलग्नकों को डेटाबेस रिकॉर्ड और भंडारण में बैकिंग फ़ाइलों के रूप में संग्रहीत करता है।
सामान्य अपलोड के लिए, सर्वर एक नई अनुलग्नक आईडी बनाता है और एक नई फ़ाइल लिखता है।
हालाँकि, डायग्राम सेव/अपडेट फ़्लो के लिए, क्लाइंट जानबूझकर एक मौजूदा attachmentId का पुन: उपयोग करता है ताकि हर बार एक नया अनुलग्नक रिकॉर्ड उत्पन्न करने के बजाय उसी डायग्राम फ़ाइल को स्थान पर अद्यतन किया जा सके।
यह व्यवहार अपने आप में वैध है।
समस्या यह है कि यह एक उच्च-जोखिम वाला पथ बनाता है:
जब भी कोई एंडपॉइंट इन दो जिम्मेदारियों को मिलाता है, तो कार्यान्वयन को उन्हें ठीक से एक साथ बाँधना चाहिए।
Docmost ने ऐसा नहीं किया।
मिश्रित क्रिएट/अपडेट एंडपॉइंट प्राधिकरण बग के लिए सामान्य स्थान हैं।
कारण सरल है:
यहाँ बिल्कुल यही पैटर्न है।
POST /api/files/upload ने मान्य किया कि कॉलर pageId द्वारा नामित पृष्ठ को संपादित कर सकता है।
लेकिन यदि attachmentId भी प्रदान किया गया था, तो सर्वर एक अधिलेखित पथ पर स्विच हो गया और एक मौजूदा अनुलग्नक रिकॉर्ड को अलग से चुना।
इसने महत्वपूर्ण सुरक्षा प्रश्न बनाया:
क्या अधिलेखित पथ यह साबित करता है कि चयनित अनुलग्नक वास्तव में अधिकृत पृष्ठ से संबंधित है?
कमजोर संस्करणों में उत्तर 'नहीं' था।
मूल कारण एक उपयोगकर्ता-नियंत्रित कुंजी के माध्यम से प्राधिकरण बाईपास था, जो अधिलेखित गार्ड में एक बूलियन तर्क बग के साथ संयुक्त था।
कमजोर फ़्लो इस तरह दिखता था:
AttachmentController.uploadFile() ने मल्टीपार्ट फॉर्म डेटा से pageId पढ़ा।validateCanEdit(page, user) को कॉल किया।attachmentId को अलग से स्वीकार किया।AttachmentService.uploadFile() ने हमलावर-प्रदत्त ID द्वारा मौजूदा अनुलग्नक को लोड किया।&& का उपयोग किया।कमजोर गार्ड था:
if (
existingAttachment.pageId !== pageId &&
existingAttachment.fileExt !== preparedFile.fileExtension &&
existingAttachment.workspaceId !== workspaceId
) {
throw new BadRequestException("File attachment does not match");
}
उस स्थिति ने केवल अनुरोध को अस्वीकार किया यदि:
एक साथ।
यह उसके विपरीत है जो एक अधिलेखित गार्ड को करना चाहिए।
वास्तविक हमले के मामले के लिए, हमलावर जानबूझकर उसी कार्यक्षेत्र के अंदर रहा।
इसलिए:
existingAttachment.workspaceId !== workspaceId false थाएक बार वह ऑपरेंड false हो गया, तो पूरी && शर्त false का मूल्यांकन करती है, भले ही अनुलग्नक किसी अलग पृष्ठ से संबंधित हो।
इसलिए सर्वर ने क्रॉस-पेज अधिलेखन को वैध माना।
यह बग का पहला भाग था।
दूसरा भाग वह है जिसने प्रभाव को वास्तविक बनाया।
जाँच के बाद, सेवा ने हमलावर-प्रदत्त attachmentId और फ़ाइलनाम का उपयोग करके गंतव्य भंडारण पथ को पुनर्निर्मित किया:
const filePath =
`${getAttachmentFolderPath(AttachmentType.File, workspaceId)}/` +
`${attachmentId}/${preparedFile.fileName}`;
फिर, अद्यतन पथ पर, Docmost ने केवल परिवर्तनीय मेटाडेटा को अद्यतन किया जैसे:
fileSizeupdatedAtइसने नहीं स्वामित्व को हमलावर पृष्ठ से पुन: बाँधा।
इसलिए पीड़ित पृष्ठ उसी अनुलग्नक रिकॉर्ड और उसी अनुलग्नक आईडी को इंगित करता रहा। केवल अंतर्निहित फ़ाइल बाइट्स बदल गए।
यही कारण है कि यह एक हानिरहित बेमेल नहीं था।
यह एक स्थायी अनधिकृत अधिलेखन प्रिमिटिव था।
यह एक कॉस्मेटिक बग नहीं था और फ़ाइलनाम टकराव का मुद्दा नहीं था।
हमलावर को रेस की आवश्यकता नहीं थी। हमलावर को यादृच्छिक पथ का अनुमान लगाने की आवश्यकता नहीं थी। हमलावर को पीड़ित पृष्ठ पर लिखने की पहुँच की आवश्यकता नहीं थी।
उन्हें केवल आवश्यकता थी:
वहाँ से, वे किसी अन्य पृष्ठ के अनुलग्नक के लिए संग्रहीत फ़ाइल बाइट्स को बदल सकते थे, जबकि पीड़ित पृष्ठ उस अनुलग्नक को संदर्भित और परोसता रहता था जैसे कि कुछ भी नहीं बदला हो।
यह एक सीधी अखंडता विफलता है।
व्यावहारिक रूप में, हमलावर कर सकता था:
महत्वपूर्ण बिंदु यह है:
सर्वर ने हमलावर द्वारा चुने गए अधिलेखित लक्ष्य को स्वीकार किया, बिना उसे उस पृष्ठ से बाँधे जिसकी संपादन अनुमति वास्तव में जाँची गई थी।
यह एक पहुँच-नियंत्रण विफलता है, न कि केवल खराब बूलियन स्वच्छता।
डायग्राम अनुलग्नकों के लिए शोषण विशेष रूप से व्यावहारिक था।
Docmost का क्लाइंट डायग्राम सेव के लिए जानबूझकर attachmentId का पुन: उपयोग करता है और नियतात्मक फ़ाइलनाम का उपयोग करता है:
diagram.excalidraw.svgdiagram.drawio.svgयह मायने रखता है क्योंकि यह हमलावर की आवश्यकताओं को कम करता है।
सामान्य अनुलग्नकों के लिए, हमलावर को दोनों की आवश्यकता होती है:
डायग्राम के लिए, फ़ाइलनाम पहले से ही अनुमानित है।
इसलिए यदि हमलावर पीड़ित पृष्ठ सामग्री पढ़ सकता है, तो वे अक्सर केवल एकमात्र लापता टुकड़ा प्राप्त कर सकते हैं:
attachmentIdअपने सत्यापन सेटअप में, मैंने ठीक उसी पथ का उपयोग किया:
यह पर्याप्त था।
शोषण ने एक ही कार्यक्षेत्र के अंदर पृष्ठ सीमाओं और स्थान सीमाओं को पार किया, जबकि दोषपूर्ण कार्यक्षेत्र जाँच को संतुष्ट किया।
मैंने Docmost v0.70.3 के विरुद्ध एक डिस्पोजेबल प्रयोगशाला का उपयोग करके लाइव मुद्दे को मान्य किया, जो docmost/docmost:0.70.3, Postgres और Redis से बनाई गई थी।
PoC प्रवाह था:
attachmentId नोट करें।POST /api/files/upload साथ:
pageId = हमलावर पृष्ठ आईडीattachmentId = पीड़ित अनुलग्नक आईडीfile = पीड़ित फ़ाइलनाम का उपयोग करके हमलावर-नियंत्रित प्रतिस्थापन फ़ाइलन्यूनतम अनुरोध आकार था:
POST /api/files/upload
Content-Type: multipart/form-data
pageId=<attackerPageId>
attachmentId=<victimAttachmentId>
[email protected];filename=diagram.excalidraw.svg
देखा गया लाइव परिणाम था:
019d18ae-b176-751c-8525-b5f3cede131d019d18ae-b15b-70e9-ac67-64948e87cc5e019d18ae-b12f-75ec-8c1c-5aff3ba6be9c200 OK686a0a0ede90ece1cbb975bb29304a6c3a90373a9c3ab2496345cf7ca59cc8fa
e0168298846cdaf75c4d880f4b721d7c0ef0ef310f75617bf2b833af34cdbeba
e0168298846cdaf75c4d880f4b721d7c0ef0ef310f75617bf2b833af34cdbeba
Attacker replacement from another page
यह एक पूर्ण एंड-टू-एंड अधिलेखन प्रमाण है, न कि केवल एक सैद्धांतिक स्रोत समीक्षा।
मैंने ट्राइएज के दौरान दो प्रकार के प्रमाण का उपयोग किया:
स्टैंडअलोन हार्नेस बूलियन तर्क विफलता को अलग करने के लिए उपयोगी था।
लाइव HTTP PoC मजबूत कलाकृति थी क्योंकि इसने पूर्ण सुरक्षा कहानी साबित की:
attachmentId स्वीकार किया गयापहुँच-नियंत्रण बग में यह अंतर मायने रखता है।
"शर्त गलत है" अपने आप में पर्याप्त नहीं है।
"शर्त गलत है, और एप्लिकेशन को एंड-टू-एंड एक स्थायी अनधिकृत अधिलेखन में संचालित किया जा सकता है" पूरा मामला है।
v0.71.0 में भेजा गया फिक्स और अधिलेखित गार्ड को && से || में बदल दिया गया:
if (
existingAttachment.pageId !== pageId ||
existingAttachment.fileExt !== preparedFile.fileExtension ||
existingAttachment.workspaceId !== workspaceId
) {
throw new BadRequestException("File attachment does not match");
}
वह पैच न्यूनतम, प्रत्यक्ष और रिपोर्ट किए गए बग के लिए सही है।
यह सही नियम को पुनर्स्थापित करता है:
अधिलेखन की अनुमति केवल तभी दी जाती है जब मौजूदा अनुलग्नक अधिकृत पृष्ठ/कार्यक्षेत्र/प्रकार की धारणाओं से बिल्कुल मेल खाता हो।
एक बार गार्ड किसी भी बेमेल पर अस्वीकार कर देता है:
यह सही तरह का फिक्स था:
सिर्फ अधिकृत पृष्ठ और अधिलेखित लक्ष्य के बीच एक सख्त बंधन।
अभी भी एक व्यापक इंजीनियरिंग सबक है:
सामान्य अपलोड एंडपॉइंट जो इन-प्लेस अपडेट फ़्लो भी प्रदान करते हैं, उन्हें उच्च-जोखिम वाली API सतहों के रूप में माना जाना चाहिए।
भले ही तत्काल बग ठीक हो गया हो, मजबूत दीर्घकालिक डिज़ाइन हैं:
लेकिन भेद्यता के लिए, प्रकाशित पैच ने मूल मुद्दे को साफ-सुथरा बंद कर दिया।
भले ही परियोजना ने फिक्स के आसपास अपने स्वयं के निजी परीक्षण जोड़े हों, ये वे मामले हैं जो दीर्घकालिक कवरेज के लिए मायने रखते हैं:
इन परीक्षणों का उद्देश्य केवल शुद्धता नहीं है।
यह प्राधिकरण बंधन को लॉक करना है ताकि भविष्य के "सहायक" अपलोड रिफैक्टर उसी श्रेणी के बग को फिर से न खोलें।
प्रकाशित सलाहकार ने इस मुद्दे को वर्गीकृत किया:
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:L
यह 7.1 / उच्च पर उतरता है, जो सही निष्कर्ष है।
यहाँ महत्वपूर्ण मीट्रिक अखंडता है।
यह कोई निम्न-श्रेणी का मेटाडेटा बग नहीं था। हमलावर ने किसी अन्य पृष्ठ के अनुलग्नक पथ पर लिखे गए प्रतिस्थापन बाइट्स को पूरी तरह से नियंत्रित किया, और पीड़ित पृष्ठ उसके बाद संशोधित ऑब्जेक्ट को परोसता रहा।
यह ठीक वैसा ही संग्रहीत क्रॉस-रिकॉर्ड छेड़छाड़ है जो अखंडता उच्च का हकदार है।
उपलब्धता कम रहना भी समझ में आता है क्योंकि डायग्राम या संलग्न दस्तावेज़ को दूषित करने से पीड़ित सामग्री अनुपयोगी हो सकती है, लेकिन प्राथमिक प्रभाव अभी भी पूर्ण सेवा व्यवधान के बजाय अनधिकृत संशोधन है।
मैंने GitHub Security Advisories के माध्यम से निजी रूप से मुद्दे की सूचना दी:
मुद्दे को अनुरक्षक द्वारा स्वीकार किया गया, CVE-2026-34213 सौंपा गया, और 14 अप्रैल, 2026 को प्रकाशित किया गया।
सार्वजनिक सलाहकार सूचीबद्ध करता है:
>= v0.3.0v0.71.0वह इतिहास मेरी स्थानीय स्रोत समीक्षा से भी मेल खाता था: कमजोर अधिलेखन तर्क सबसे शुरुआती टैग किए गए रिलीज़ में मौजूद था जिसे मैंने कमजोर लाइन में जाँचा था।
यहाँ दिलचस्प सबक केवल "|| को && के बजाय उपयोग करें" नहीं है।
वह लक्षण है।
गहरा सबक है:
यदि एक उपयोगकर्ता-नियंत्रित क्षेत्र प्राधिकरण साबित करता है और दूसरा उपयोगकर्ता-नियंत्रित क्षेत्र अद्यतन किए जा रहे ऑब्जेक्ट का चयन करता है, तो उन दो क्षेत्रों को स्पष्ट रूप से और ठीक से एक साथ बाँधा जाना चाहिए।
वह नियम हर जगह दिखाई देता है:
जिस क्षण कोई सिस्टम कहता है:
इसने एक सुरक्षा सीमा बनाई है जिसे सटीक-मैच अपरिवर्तनीयताओं के साथ लागू किया जाना चाहिए।
इससे नरम कुछ भी जल्दी या बाद में उपयोगकर्ता-नियंत्रित-कुंजी बग में बदल जाता है।
यह मुद्दा दूसरे बिंदु को भी पुष्ट करता है जिसे कम आंकना आसान है:
गार्ड कोड में छोटी बूलियन गलतियों के प्रथम-क्रम सुरक्षा परिणाम हो सकते हैं।
एक तीन-खंड की शर्त जो पहली नज़र में "उचित लगती है", अधिलेखित पथ के लिए सुरक्षा मॉडल को उलटने के लिए पर्याप्त थी।
यही कारण है कि ये सतहें आकस्मिक विश्वास के बजाय जानबूझकर समीक्षा की हकदार हैं।
pageId के विरुद्ध जाँचा गया था, लेकिन अधिलेखित लक्ष्य चयन एक अलग कॉलर-प्रदत्त attachmentId का उपयोग करता था।v0.71.0 में फिक्स ने गार्ड को किसी भी बेमेल पर अस्वीकार करने के लिए सही ढंग से बदल दिया।यह भेद्यता विदेशी भंडारण व्यवहार के बारे में नहीं थी।
यह एक अद्यतन पथ के बारे में था जो एक हमलावर-चयनित ऑब्जेक्ट पहचानकर्ता पर अधिक भरोसा करता था जितना उसे करना चाहिए था।
Docmost ने एक पृष्ठ पर संपादन पहुँच साबित की, दूसरे पृष्ठ से एक मौजूदा अनुलग्नक आईडी स्वीकार की, और फिर एक दोषपूर्ण अधिलेखन जाँच को उस बेमेल को एक सफल क्रॉस-पेज फ़ाइल प्रतिस्थापन में बदलने दिया।
यही कारण है कि यह CVE-2026-34213 बन गया।
v0.71.0 में पैच ने तत्काल मुद्दे को साफ-सुथरा ठीक किया, लेकिन व्यापक सबक मूल्यवान बना हुआ है:
जब प्राधिकरण और ऑब्जेक्ट चयन अलग-अलग उपयोगकर्ता-नियंत्रित क्षेत्रों में विभाजित होते हैं, तो सटीक बंधन सुरक्षा संपत्ति है।