Tulisan: Rachid.A
Yewlne
01 Teks Terjemahan
Baru-baru ini, saya dan Yasser Allam yang menggunakan nama samaran inzo_ memutuskan untuk bekerja sama dalam penelitian. Setelah mendiskusikan beberapa target potensial, kami memutuskan untuk memfokuskan perhatian pada Next.js (yang memiliki 130 ribu bintang di GitHub dan saat ini diunduh lebih dari 9,4 juta kali setiap minggu). Ini adalah kerangka kerja yang sangat saya kenal, dan saya memiliki pengalaman kreatif yang baik dengannya, seperti yang dibuktikan oleh hasil penelitian saya sebelumnya. Oleh karena itu, “kami” dalam artikel ini secara alami merujuk pada kami berdua.
Next.js adalah kerangka kerja JavaScript yang sepenuhnya fungsional berbasis React, dengan fitur yang kaya—tempat yang ideal untuk mendalami detail. Dengan keyakinan, rasa ingin tahu, dan ketahanan, kami memulai perjalanan untuk mengeksplorasi sudut-sudut yang jarang diketahui, mencari harta yang tersembunyi di dalamnya.
Tidak lama setelah itu, kami menemukan masalah besar di dalam middleware. Dampaknya luas, semua versi terpengaruh, dan eksploitasi kerentanan ini tidak memerlukan prasyarat apapun—kami akan segera menjelaskan secara rinci.
Daftar Isi
Middleware Next.js
Alat Otorisasi: Kode Lama Tingkat Harta Karun
Urutan eksekusi dengan middlewareInfo.name
Alat Otorisasi: Kemarin telah menjadi puisi, pagi ini lebih layak.
/src direktori
Kedalaman rekursi maksimum
eksploitasi kerentanan
Menghindari otorisasi/menulis ulang
Melewati CSP
Melakukan DoS melalui pemanasan cache (Apa?)
menjelaskan
Pengumuman Keamanan - CVE-2025-29927
Penyangkalan
Kesimpulan
Middleware di Next.js
Middleware memungkinkan Anda menjalankan kode sebelum permintaan selesai. Kemudian, Anda dapat mengubah konten respons dengan menulis ulang, mengalihkan, memodifikasi permintaan atau header respons, atau langsung mengembalikan respons berdasarkan permintaan yang masuk (diambil dari dokumentasi Next.js).
Sebagai kerangka kerja yang lengkap, Next.js memiliki middleware-nya sendiri — ini adalah fitur penting dan banyak digunakan. Ada banyak skenario penggunaannya, di antaranya yang paling penting adalah:
Penulisan ulang jalur (Path rewriting)
Pengalihan sisi server
Tambahkan informasi header ke respons (seperti CSP dan lainnya)
Hal yang paling penting adalah: otentikasi (Authentication) dan otorisasi (Authorization)
Salah satu penggunaan umum dari middleware adalah untuk melakukan otorisasi, yang melibatkan perlindungan jalur tertentu berdasarkan kondisi tertentu.
Verifikasi dan otorisasi: Pastikan identitas pengguna dan periksa cookie sesi sebelum memberikan akses ke halaman tertentu atau rute API (Dokumentasi Next.js).
Contoh: Ketika pengguna mencoba mengakses /dashboard/admin, permintaan pertama-tama akan melalui middleware, yang akan memeriksa apakah cookie sesi pengguna valid dan apakah pengguna memiliki izin yang diperlukan. Jika verifikasi berhasil, middleware akan meneruskan permintaan; jika tidak, middleware akan mengarahkan pengguna ke halaman login:
Alat Otorisasi: Kode Lama Berharga
Seperti yang pernah dikatakan seorang tokoh besar, “talk is cheap, show me the bug”, mari kita hindari terlalu banyak narasi dan langsung masuk ke inti; saat kami menelusuri versi lama kerangka (v12.0.7), kami menemukan kode ini:
Ketika aplikasi Next.js menggunakan middleware, fungsi runMiddleware disebut. Selain fungsi utamanya, fungsi ini mengambil nilai header x-middleware-subrequest dan menggunakannya untuk menentukan apakah middleware harus diterapkan. Nilai header dibagi menjadi daftar menggunakan titik dua (:) sebagai pemisah, dan daftar diperiksa untuk melihat apakah berisi nilai middlewareInfo.name. Ini berarti bahwa jika kita menambahkan header x-middleware-subrequest dengan nilai yang benar ke permintaan, maka middleware – terlepas dari tujuannya – akan sepenuhnya diabaikan dan permintaan akan diteruskan melalui NextResponse.next() dan jalur ke tujuan awal akan selesai tanpa pengaruh dari middleware. Kepala ini dan nilainya seperti “kunci utama” yang melewati semua aturan. Pada titik ini kami telah menyadari bahwa kami telah menemukan masalah yang luar biasa, dan beberapa potongan terakhir dari teka-teki harus diselesaikan selanjutnya.
Agar “kunci universal” kami berfungsi, nilai tersebut harus mencakup middlewareInfo.name, tetapi apa sebenarnya nilai ini?
Urutan eksekusi dengan middlewareInfo.name
Nilai middlewareInfo.name sangat mudah diperkirakan, itu hanyalah jalur tempat middleware berada. Untuk memahami hal ini, kita perlu memahami secara singkat cara konfigurasi middleware di versi lama.
Pertama, sebelum versi 12.2 — di mana kesepakatan middleware telah berubah — file harus dinamai _middleware.ts. Selain itu, router aplikasi hanya diperkenalkan di versi 13 Next.js. Router yang ada saat itu adalah router pages, sehingga file tersebut harus ditempatkan di dalam folder pages (spesifik untuk router).
Dengan informasi ini, kita dapat menyimpulkan jalur pasti dari middleware, sehingga dapat memperkirakan nilai dari header x-middleware-subrequest. Nilai ini terdiri hanya dari nama direktori (yaitu nama router unik yang ada pada saat itu) dan nama file, mengikuti konvensi penamaan yang dimulai dengan garis bawah pada saat itu:
x-middleware-subrequest: pages/_middleware
Ketika kami mencoba untuk menghindari middleware yang dikonfigurasi untuk secara sistematis mengalihkan upaya akses dari /dashboard/team/admin ke /dashboard:
Berhasil, kami telah menyusup ⚔️
Kita sekarang dapat sepenuhnya menghindari middleware, sehingga dapat melewati sistem perlindungan apa pun yang berbasis padanya, yang paling khas adalah otorisasi, seperti contoh di atas. Temuan ini cukup mengejutkan, tetapi ada poin lain yang perlu dipertimbangkan.
Versi sebelum 12.2 memungkinkan penempatan satu atau lebih file _middleware di mana saja dalam pohon direktori (dimulai dari folder pages), dan mereka memiliki urutan eksekusi, seperti yang terlihat pada tangkapan layar dokumen lama yang kami ambil dari Web Archive:
apa artinya ini bagi eksploitasi kerentanan kita?
Kemungkinan = Jumlah level dalam jalur
Oleh karena itu, untuk mengakses /dashboard/panel/admin (yang dilindungi oleh middleware), ada tiga kemungkinan nilai untuk middlewareInfo.name, dan dengan demikian ada tiga kemungkinan nilai untuk x-middleware-subrequest:
pages/_middleware
atau
pages/dashboard/_middleware
atau
pages/dashboard/panel/_middleware
Alat Otorisasi: Kemarin telah menjadi puisi, pagi ini lebih layak.
Hingga saat ini, kami percaya bahwa hanya versi sebelum 13 yang rentan terhadap serangan, karena middleware telah dipindahkan dalam kode sumber, dan kami belum menutupi semua aspeknya. Kami menduga bahwa pemelihara pasti telah menyadari kerentanan ini dan telah memperbaikinya sebelum perubahan besar di versi 13, jadi kami melaporkan kerentanan ini kepada pemelihara kerangka kerja dan melanjutkan penelitian kami.
Yang mengejutkan kami adalah, hanya dua hari setelah penemuan awal, kami menemukan bahwa semua versi Next.js—mulai dari versi 11.1.4—memiliki celah! Kode tidak lagi berada di lokasi yang sama, dan logika untuk mengeksploitasi celah juga sedikit berubah.
Seperti yang disebutkan sebelumnya, mulai versi 12.2, file tidak lagi menyertakan garis bawah dan harus dinamai sederhana sebagai middleware.ts. Selain itu, file tersebut tidak lagi berada di dalam folder pages (ini sangat nyaman bagi kami karena mulai versi 13, router app diperkenalkan, yang akan menggandakan kemungkinan).
Dengan itu dalam pikiran, payload untuk versi pertama yang dimulai dengan versi 12.2 sangat sederhana:
Mengingat hal ini, payload versi pertama yang mulai dari versi 12.2 sangat sederhana:
x-middleware-subrequest: middleware
/src direktori
Perlu juga dipertimbangkan bahwa Next.js menyediakan kemungkinan untuk membuat direktori /src:
(Dokumentasi Next.js) Sebagai alternatif untuk memiliki direktori Next.js app atau pages yang spesial di direktori akar proyek, Next.js juga mendukung penempatan kode aplikasi di bawah pola umum di direktori src. (Dokumentasi Next.js)
Dalam kasus ini, payload akan menjadi:
x-middleware-subrequest: src/middleware
Oleh karena itu, terlepas dari berapa banyak tingkat dalam jalur, hanya ada dua kemungkinan. Ini menyederhanakan tingkat kesulitan dalam mengeksploitasi kerentanan untuk versi terkait.
Dalam versi terbaru, ada sedikit perubahan lagi (kami jamin, ini yang terakhir).
Kedalaman rekursi maksimum
Dalam versi yang diperbarui, logika sedikit berubah, silakan lihat cuplikan kode ini:
v15.1.7
Sama seperti sebelumnya, sistem akan mengambil nilai dari header x-middleware-subrequest dan membentuk daftar menggunakan titik dua sebagai pemisah. Namun kali ini, syarat untuk meneruskan permintaan secara langsung—yaitu mengabaikan aturan middleware—berbeda:
Nilai konstanta depth harus lebih besar dari atau sama dengan nilai konstanta MAX_RECURSION_DEPTH (yaitu 5). Selama proses penugasan, setiap kali suatu nilai dalam daftar subrequests (yaitu nilai header yang dipisahkan oleh :) sama dengan params.name (yaitu jalur middleware), konstanta depth akan meningkat 1. Seperti yang disebutkan sebelumnya, hanya ada dua kemungkinan di sini: middleware atau src/middleware.
Oleh karena itu, untuk melewati middleware, kita hanya perlu menambahkan header/nilai berikut dalam permintaan:
x-middleware-subrequest: middleware:middleware:middleware:middleware:middleware
atau
x-middleware-subrequest: src/middleware:src/middleware:src/middleware:src/middleware:src/middleware
Apa tujuan awal dari kode ini?
Kode ini tampaknya dirancang untuk mencegah permintaan rekursif terjebak dalam loop tak terbatas.
eksploitasi kerentanan
Karena kami tahu Anda menyukai konten semacam ini, berikut adalah beberapa contoh nyata dari Program Bug Bounty.
Menghindari otorisasi/menulis ulang
Dalam contoh ini, ketika kami mencoba mengakses /admin/login, kami menerima respons 404. Dari header respons, terlihat bahwa middleware telah melakukan penulisan ulang jalur untuk mencegah akses oleh pengguna yang tidak berwenang atau tidak pantas:
Namun, gunakan alat otorisasi kami:
Kami dapat mengakses titik akhir ini tanpa hambatan, middleware sepenuhnya diabaikan. Versi Next.js yang dituju: 15.1.7
Melewati CSP
Situs web kali ini menggunakan middleware untuk mengatur—selain fitur lainnya—CSP dan cookie:
Mari kita lewati itu:
Target next.js versi: 15.0.3Target next.js version: 15.0.3
Perhatian: Harap perhatikan perbedaan payload dari kedua tujuan, salah satunya menggunakan direktori src/ sementara yang lainnya tidak.
Melakukan DoS melalui pemanasan cache (Apa?)
Ya, serangan DoS dengan racun cache juga mungkin bisa direalisasikan melalui celah ini. Ini jelas bukan yang pertama kali kita cari, tetapi jika tidak ada jalur sensitif yang dilindungi, dan tidak ada titik pemanfaatan yang lebih menarik, maka dalam beberapa keadaan bisa menyebabkan layanan ditolak karena racun cache (CPDoS):
Misalkan sebuah situs web menulis ulang jalur pengguna berdasarkan lokasi geografis pengguna, menambahkan (/en, /fr, dll), dan tidak menyediakan halaman atau sumber daya di jalur akar (/). Jika kita melewati middleware, kita akan menghindari penulisan ulang dan akhirnya mencapai halaman akar. Karena pengembang tidak bermaksud untuk membiarkan pengguna mengakses halaman akar, mereka tidak menyediakan halaman yang sesuai, kita akan mendapatkan 404 (atau tergantung pada konfigurasi/tipe penulisan ulang yang berbeda, mungkin 500).
Jika situs web menggunakan sistem cache/CDN, mungkin akan memaksa cache respons 404, menyebabkan halaman tidak tersedia, yang sangat mempengaruhi ketersediaan situs.
menjelaskan
Sejak pengumuman keamanan dirilis, kami menerima beberapa pertanyaan dari orang-orang yang khawatir tentang keamanan aplikasi mereka dan tidak terlalu memahami cakupan serangan. Perlu dijelaskan bahwa elemen yang rentan adalah middleware. Jika Anda tidak menggunakan middleware (atau setidaknya tidak menggunakannya untuk tujuan sensitif), maka tidak perlu khawatir (namun, silakan periksa aspek DoS yang disebutkan di atas), karena melewati middleware tidak akan melewati mekanisme keamanan yang sebenarnya.
Jika tidak, konsekuensinya bisa menjadi bencana, kami sarankan Anda segera melaksanakan langkah-langkah panduan dalam pengumuman keamanan.
Pengumuman Keamanan - CVE-2025-29927
patch
Untuk Next.js 15.x, masalah ini telah diperbaiki di 15.2.3
Untuk Next.js 14.x, masalah ini telah diperbaiki di 14.2.25
Untuk versi Next.js 11.1.4 hingga 13.5.6, kami sarankan untuk merujuk pada solusi berikut.
solusi
Jika tidak dapat memperbarui ke versi yang aman, kami sarankan Anda memblokir permintaan pengguna eksternal yang mengandung header permintaan x-middleware-subrequest untuk mengakses aplikasi Next.js Anda.
Serius
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N (Tingkat keparahan: 9.1/10, Kritis)
lebih banyak informasi
Saat artikel ini ditulis, aplikasi yang di-deploy di Vercel dan Netlify jelas sudah tidak lagi terpengaruh oleh kerentanan ini (pembaruan: karena banyaknya laporan salah, Cloudflare telah menyesuaikan aturan ini agar hanya berlaku ketika diaktifkan secara aktif oleh pengguna — laporan salah ini gagal membedakan secara efektif antara permintaan dari pengguna yang sah dan permintaan dari potensi penyerang).
Penyangkalan
Penelitian ini dirilis hanya untuk tujuan edukasi, bertujuan untuk membantu pengembang memahami akar penyebab masalah, atau memberikan inspirasi bagi peneliti / pencari celah dalam pekerjaan penelitian di masa depan. Artikel ini sebagai bahan tambahan untuk pengumuman keamanan, memberikan penjelasan dan klarifikasi lebih lanjut tentang sifat celah - karena pengumuman tersebut telah mengungkapkan header permintaan yang menyebabkan celah tersebut (beserta perbedaan pengajuan yang relevan).
Kami dengan tegas menyatakan tidak mendukung penggunaan tidak etis dari artikel ini.
Kesimpulan
Seperti yang ditekankan dalam artikel ini, kerentanan ini telah ada di dalam kode sumber Next.js selama beberapa tahun, berubah seiring evolusi middleware dan versinya. Setiap perangkat lunak dapat memiliki kerentanan serius, tetapi ketika itu mempengaruhi salah satu kerangka kerja paling populer, itu menjadi sangat berbahaya dan dapat menyebabkan konsekuensi serius bagi ekosistem yang lebih luas. Seperti yang disebutkan sebelumnya, pada saat penulisan artikel ini, jumlah unduhan Next.js hampir 10 juta per minggu. Ini digunakan secara luas dalam bidang-bidang penting mulai dari layanan perbankan hingga blockchain. Ketika kerentanan mempengaruhi fitur mapan yang diandalkan pengguna (seperti otorisasi dan autentikasi), risikonya menjadi lebih besar.
Tim Vercel menghabiskan beberapa hari untuk menyelesaikan kerentanan ini, tetapi yang perlu dicatat adalah, begitu mereka menyadari masalahnya, perbaikan dikirimkan dan digabungkan dalam beberapa jam untuk diterapkan ke versi baru (termasuk porting mundur).
Garis waktu :
27 Februari 2025: Melaporkan kerentanan kepada pemelihara (pada saat itu kami percaya hanya versi 12.0.0 hingga 12.0.7 yang terpengaruh, dan ini dicatat dalam laporan).
1 Maret 2025: Mengirimkan email kedua, menjelaskan bahwa sebenarnya semua versi memiliki kerentanan, termasuk versi stabil terbaru.
5 Maret 2025: Menerima balasan awal dari tim Vercel, yang menyatakan bahwa versi 12.x sudah tidak lagi dipelihara (mungkin belum membaca template pengumuman keamanan yang kami lampirkan dalam email kedua, dan tidak menyadari bahwa semua versi terkena dampak).
5 Maret 2025: Kirim ulang email, mohon tim segera meninjau email kedua dan template pengumuman keamanan.
11 Maret 2025: Kirim email lagi, konfirmasi apakah informasi baru telah diterima.
17 Maret 2025: Menerima balasan dari tim Vercel, mengonfirmasi bahwa informasi terkait telah diterima.
18 Maret 2025: Menerima email dari tim Vercel yang menyatakan bahwa laporan telah diterima dan patch perbaikan telah selesai. Beberapa jam kemudian, versi 15.2.3 yang berisi perbaikan (dan termasuk perbaikan mundur) dirilis.
21 Maret 2025: Pengumuman keamanan resmi dirilis.
Secara keseluruhan, proses mencari kerentanan zero-day hanya menjadi menggembirakan dan memicu adrenalin saat petunjuk ditemukan; sisanya adalah perjalanan penuh ketidakpastian—bagi para penasaran, ini memberikan hasil pengetahuan; bagi mereka yang kurang sabar, perjalanan ini terasa sangat panjang. Jangan ragu, bekerja sama jauh lebih mudah daripada melintasi padang pasir sendirian.