SvelteKit es un popular framework JavaScript full-stack, y Vercel es su plataforma de despliegue más común. ¿Y si te dijéramos que todas las aplicaciones construidas con esta combinación eran vulnerables a que los atacantes leyeran las respuestas de cualquier ruta de otros usuarios con sesión iniciada?
Pues bien, es cierto. Este vector de ataque, denominado engaño de caché, es exactamente lo que uno de los agentes de IA encontró y nos reportó mientras probábamos Aikido Attack. Y aunque inicialmente escépticos, rastreamos sus pasos y descubrimos que podíamos reproducir la vulnerabilidad a la perfección. Notificamos rápidamente a Vercel, y la vulnerabilidad ha sido corregida automáticamente para todos los usuarios.
Nota: El problema se puede consultar en la base de datos Intel de Aikido y tiene el número CVE reservado de: CVE-2026-27118. También encontramos un problema separado que permite la Denegación de Servicio en una característica experimental de SvelteKit. Esto también ha sido divulgado y corregido.
Resumen rápido
El __pathname El parámetro de consulta en el adaptador de Vercel de SvelteKit puede sobrescribir la ruta desde cualquier lugar. En Vercel, cada archivo bajo /_app/immutable/ tiene diferentes Cache-Control: cabeceras para que pueda ser almacenado en caché. Al prefijar esto con una ruta falsa y reescribirlo para que contenga contenido sensible, la respuesta se almacenará en caché de forma forzada, permitiendo a un atacante recuperarla visitando la misma URL sin cookies.
URL de Prueba de Concepto:
https://example.vercel.app/_app/immutable/x?__pathname=/api/session
Descubrimiento
Contaremos esta historia desde la perspectiva del agente de pentesting de IA que encontró la vulnerabilidad, desde el gadget inicial hasta la explotación completa, y hablaremos de algunas ideas interesantes por el camino. ¡Abraza tu robot interior y pongámonos a investigar!
Si lees el código fuente de SvelteKit, encontrarás algunos «adaptadores», que son el middleware entre una plataforma de alojamiento como Vercel y el framework SvelteKit. Esto permite aplicar algunas reglas especiales a la solicitud y respuesta requeridas para los componentes internos de la plataforma. Vercel ha implementado lo siguiente en serverless.js:
const DATA_SUFFIX = '/__data.json';
fetch(request) {
// If this is an ISR request, the requested pathname is encoded
// as a search parameter, so we need to extract it
const url = new URL(request.url);
let pathname = url.searchParams.get('__pathname');
if (pathname) {
// Optional routes' pathname replacements look like `/foo/$1/bar` which means we could end up with an url like /foo//bar
pathname = pathname.replace(/\/+/g, '/');
url.pathname = pathname + (url.pathname.endsWith(DATA_SUFFIX) ? DATA_SUFFIX : '');
url.searchParams.delete('__pathname');
request = new Request(url, request);
}
return server.respond(request, {
getClientAddress() {
return /** @type {string} */ (request.headers.get('x-forwarded-for'));
}
});
}
Podemos leer «Si se trata de una solicitud ISR, el pathname solicitado se codifica como un parámetro de búsqueda.» El código toma esta variable pathname (del ?__pathname= parámetro de consulta) y reescribe el url.pathname con él. La solicitud ignora la URL original y simplemente utiliza este __pathname en su lugar.
Pero no parece haber ninguna comprobación con respecto a estas solicitudes de Regeneración Estática Incremental (ISR), ¿así que todas las solicitudes admiten este parámetro?
La respuesta es sí, ¡y ahí es exactamente donde reside la vulnerabilidad! Cualquier ruta puede ser sobrescrita por cualquier otra ruta con solo un parámetro de consulta. Una prueba simple como /?__pathname=/404 mostraría de hecho un error 404 en lugar de la página de inicio.

Esto puede sonar simplemente como una característica extraña. ¿Por qué sería peligroso? Bueno, no lo es, hasta que interviene el almacenamiento en caché.
¿Envenenamiento de caché?
Si podemos reescribir cualquier ruta para que apunte a cualquier otro recurso, ¿qué ocurre si ese recurso "envenenado" se almacena en caché? Esta fue también nuestra primera idea. Si revisamos el código fuente de cualquier aplicación SvelteKit, veremos algo como:
import("./_app/immutable/entry/start.CLO1Dlt2.js"),
import("./_app/immutable/entry/app.kQF6jJr8.js")
La ruta /_app/immutable/entry/start.CLO1Dlt2.js devuelve algo de JavaScript para ejecutar, y, al observar la respuesta, Cache-Control: las cabeceras nos indican que, en efecto, está en caché. Esto es lo esperado para recursos estáticos:
Age: 618
Cache-Control: public, immutable, max-age=31536000
X-Vercel-Cache: HIT
Si pudiéramos usar nuestro ?__pathname= parámetro para reescribir la solicitud y que apunte a una ruta controlada por un atacante, como una subida de archivos, y aún se almacenara en caché bajo la misma ruta base /_app/immutable/entry/start.CLO1Dlt2.js que carga cada usuario, tendríamos XSS en cada usuario. Veamos qué ocurre cuando añadimos este parámetro de consulta a nuestra solicitud:
GET /_app/immutable/start.CLO1Dlt2.js?__pathname=/ HTTP/2
Host: example.vercel.app
En la respuesta, recibimos buenas y malas noticias de las cabeceras:
Age: 935
Cache-Control: public, immutable, max-age=31536000
X-Vercel-Cache: HIT
El Age: ha aumentado el tiempo que ha tardado en escribirse el párrafo anterior, y el HIT significa que con nuestro parámetro de consulta añadido, se accede a la misma entrada de caché que la que carga todo el mundo. Pero, al mismo tiempo, eso significa que también se devuelve el contenido JavaScript antiguo, no el contenido de nuestra ruta reescrita. Si ese es el caso, ¿podríamos vaciar la caché y ser los primeros en solicitarlo para que nuestra carga útil pueda almacenarse en caché?
Desafortunadamente, no. La plataforma Vercel es más compleja debido a su arquitectura serverless, y en realidad ni siquiera llega al código del adaptador para un recurso estático de este tipo. Esto se debe a que los activos estáticos se almacenan en caché y se devuelven en una capa superior, por lo que nunca podremos "envenenarlos".
En este caso, Vercel es la única plataforma vulnerable, por lo que no podemos encontrar otros escenarios explotables. Hemos llegado a un callejón sin salida.
¡Engaño de caché!
Pasemos al siguiente problema con el almacenamiento en caché web: engaño de caché. En esta técnica menos conocida, un atacante redirige a una víctima a una página sensible que esta puede recuperar con sus cookies. Si la caché almacena esta página sin considerar qué usuario la solicitó, el atacante puede visitar posteriormente la misma URL y ver los datos privados de la víctima desde la caché. El problema es que la caché guarda las respuestas basándose únicamente en la URL, ignorando que diferentes usuarios con diferentes cookies de inicio de sesión deberían ver contenido distinto.
Aplicando esta idea a nuestro ?__pathname= gadget, podemos hacer que nuestra URL parezca que apunta a una ruta estática, pero en realidad apunta a contenido sensible. La capa de caché puede activarse en esa ruta de apariencia estática y añadir Cache-Control: cabeceras y anular la respuesta privada.
Aunque algunos criterios de caché están documentados, nuestra investigación reveló más reglas. Una con la que ya estamos familiarizados: rutas que empiezan por /_app/immutable/. Resulta que no solo los archivos estáticos esperados son cacheables bajo este prefijo, sino que cualquier respuesta 200 OK. Para evitar los activos ya generados, podemos apuntar inicialmente a un activo falso. Luego reescribirlo a cualquier ruta sensible, digamos /api/session:
https://example.vercel.app/_app/immutable/x?__pathname=/api/session
Y ahí tenemos nuestra carga útil final. Visitar este enlace como una víctima con sesión iniciada enviará una solicitud como la siguiente:
GET /_app/immutable/x?__pathname=/api/session HTTP/2
Host: example.vercel.app
Cookie: auth=...
El adaptador de Vercel de SvelteKit reescribe el pathname a /api/session, y es manejado por la aplicación. La auth= cookie es verificada, y su token de sesión es devuelto:
HTTP/2 200 OK
Age: 0
Cache-Control: public, immutable, max-age=31536000
...
Server: Vercel
X-Vercel-Cache: MISS
Content-Length: 16
{"token":"1337"}Aunque la Cache-Control: cabecera normalmente diría max-age=0 para este endpoint, la capa de caché de Vercel habilita forzosamente el almacenamiento en caché debido al /_app/immutable/ prefijo.
Una vez que el atacante sabe que una víctima ha sido redirigida, puede solicitar la misma URL por sí mismo, sin ninguna cookie:
GET /_app/immutable/x?__pathname=/api/session HTTP/2
Host: example.vercel.appReciben la misma respuesta que un HIT ahora, y puede leer el token de la víctima.
HTTP/2 200 OK
Age: 22
Cache-Control: public, immutable, max-age=31536000
...
Server: Vercel
X-Vercel-Cache: HIT
Content-Length: 16
{"token":"1337"}
Utilizando «cache busters» (rutas ficticias únicas) y verificando después de la redirección, esto podría haberse explotado a gran escala. Dado que esta vulnerabilidad es parte de la base de SvelteKit, cualquier sitio web de SvelteKit en Vercel que utilice cookies para la autenticación permitiría la recuperación de respuestas arbitrarias.
El desenlace
Informamos rápidamente del problema a Vercel, quienes propusieron una solución para devolver forzosamente un 404 en cualquier /_app/immutable/ ruta, y para eliminar el __pathname parámetro. Dado que controlan toda su plataforma, esto resuelve el problema automáticamente para todos los usuarios, sin necesidad de un parche manual.
Una conclusión importante de esta vulnerabilidad es que el almacenamiento en caché siempre es delicado. Reglas simples, como una coincidencia de prefijo, pueden ser aprovechadas por características inesperadas de la plataforma que ni siquiera implementaste.
Por eso el pentesting puede ser tan útil. Problemas como este serían difíciles de descubrir solo con el código, pero a través de un despliegue real, se pueden encontrar y explotar reglas ocultas. El pentest de IA de Aikido puede detectar ataques de envenenamiento de caché y engaño de caché automáticamente, como encontró este problema.
Puntos clave
- El 20 de enero de 2026, el sistema de pentest de IA de Aikido detectó algo interesante en las aplicaciones SvelteKit desplegadas en Vercel.
- Confirmamos que era una vulnerabilidad de engaño de caché que afectaba a las configuraciones predeterminadas.
- No se requería ninguna mala configuración. Solo SvelteKit + Vercel haciendo lo que se supone que deben hacer.
- Impacto: Las respuestas autenticadas pueden ser almacenadas en caché y expuestas a otros usuarios.
- Cualquier aplicación SvelteKit ejecutándose en Vercel con endpoints protegidos podría verse afectada.
Cómo comprobar si estás afectado
Usando Aikido:
Si eres usuario de Aikido, consulta tu feed central. Puedes ver el problema en Aikido Intel aquí. Consejo: Aikido vuelve a escanear tus repositorios cada noche, aunque recomendamos activar un reescaneo completo también.
Si aún no eres usuario de Aikido, crea una cuenta y conecta tus repositorios. Nuestra cobertura de malware propietaria está incluida en el plan gratuito (sin tarjeta).
El pentest de IA y el escaneo de seguridad web de Aikido detectan automáticamente los flujos de engaño de caché y el comportamiento de reescritura inseguro.
Estado de la solución
Revelamos esto a Vercel el 21 de enero de 2026.
Cronología
- 20 de enero de 2026: Aikido Security identificó la vulnerabilidad, construyó un PoC funcional
- 21 de enero de 2026: Divulgación responsable a Vercel
- 23 de enero de 2026: Informe clasificado
- 9 de febrero de 2026: Vercel confirma el informe y comienza a trabajar en una solución
- 19 de febrero de 2026: Vercel ha corregido la vulnerabilidad para todos los usuarios, y se ha publicado un aviso (GHSA-9pq4-5hcf-288c). El problema se puede consultar en la base de datos Intel de Aikido y tiene el número CVE reservado de: CVE-2026-27118.
También encontramos un problema separado que permite la Denegación de Servicio en una característica experimental de SvelteKit. Esto también ha sido divulgado y corregido.

