A next.js note taking app that saves your notes in local storage. It has an optional login page with /api/login endpoint that responds with TE9HSU4gTk9UIElNUExFTUVOVEVE, which is LOGIN NOT IMPLEMENTED in Base64. Try to access /admin directly:
<html>
<body>
<h1>Unauthorized</h1>
<p>You're not admin. Please login</p>
<!--Request Forbidden by Next.js 15.1.1 Middleware-->
</body>
</html>Thank you so much, error page. Looking into reports on this version, I found this one to be the most helpful. In short, CVE-2025-29927 allows you to completely bypass nextjs middleware by making the app think it hit an infinite middleware loop.
Send an extra header with the request:
x-middleware-subrequest: middleware:middleware:middleware:middleware:middlewareand infiltrate the admin control panel. This page makes requests to http://backend:4000 , which is not exposed to the public. Likely an internal docker-compose path.
Inspecting some of the admin notes:
Backend stuff
WyIvc3RhdHMiLCAiL25vdGVzIiwgIi9mbGFnIiwgIi8iXQ==Base64 again:
["/stats", "/notes", "/flag", "/"]The first two paths match the backend:4000 endpoints that the page is trying to reach. Target acquired: http://backend:4000/flag.
How do we make a request to an internal route? By tricking the nextjs BFF into making it for us. Let's look at another note:
Should call Nair for some code review here https://pastebin.com/GNQ36Hn4This paste gets us a glimpse into the BFF logic. More specifically:
if (url.pathname.startsWith('/api')) {
return NextResponse.next({
headers: request.headers //required for api routes for decoding Auth Header
});
}Another CVE spotted, this time CVE-2025-57822. The app reflects user-supplied headers, and will treat Location header as a redirect, thus allowing SSRF.
Make a GET request to the placeholder /api/login endpoint and add a header:
Location: http://backend:4000/flagand it will forward the backend response back to us. Easy win.