
Kit de herramientas forenses y habilidades de agente para investigar Rails Active Storage/libvips CVE-2026-66066: detecta indicadores de blobs manipulados, ventanas de exposición y exfiltración de datos.
Herramientas y método para responder dos preguntas sobre una aplicación Rails y la CVE-2026-66066, la lectura arbitraria de archivos de Active Storage y libvips conocida como KindaRails2Shell:
El aviso GHSA-xr9x-r78c-5hrm explicaba la vulnerabilidad y nombraba las versiones parcheadas, pero no revelaba suficiente detalle para evaluar estas preguntas. Este proyecto pretende ayudar con eso.
Este es un repositorio de documentación y skills más que una biblioteca. Nada de esto se instala en tu aplicación. Un agente lo lee y trabaja contra tu aplicación clonada en otro lugar, y el único script que se ejecuta en producción es un único archivo autocontenido que copias en un contenedor.
Rails decide que un blob puede procesarse como imagen leyendo una columna content_type
que una subida directa permite que el cliente establezca sin que los bytes se examinen nunca.
libvips entonces decide qué es realmente el archivo oliendo los bytes mágicos. Un archivo
cuyos primeros diez bytes declaran MATLAB 5.0 se enruta al cargador MATLAB de libvips,
que lo entrega a libmatio, que despacha en un rango de bytes distinto y
encuentra MAT 7.3, que es HDF5. La External File List de HDF5 permite que los bytes de un dataset
vivan en otro archivo nombrado por ruta y desplazamiento, por lo que renderizar la "imagen" lee un
archivo elegido por el atacante desde el servidor y devuelve su contenido como píxeles. La misma
confusión, dos veces, en dos capas que no pueden ver los campos de la otra.
reference/the-attack.md rastrea toda la cadena de llamadas, desde la solicitud entrante
hasta el punto en que libmatio abre el archivo objetivo, con los cuatro puntos de
apoyo marcados. reference/the-investigation.md explica después por qué el ataque
es investigable en absoluto: deja tres artefactos de fuerza creciente, y
el más fuerte de ellos es la variante renderizada, que contiene los bytes robados como
valores de píxel en tu propio almacén de objetos.
Los dos skills se ejecutan en orden. El primero produce la ventana de exposición; el segundo no puede comenzar sin ella.
| Skill | Pregunta | Produce |
|---|---|---|
kr2s-was-i-vulnerable | ¿Estuvo alguna vez expuesta esta aplicación? | Una ventana de exposición y los hechos que necesita el barrido |
kr2s-was-i-exploited | ¿Alguien la usó, y qué obtuvo? | Un análisis forense escrito |
Ambos están escritos para un agente, y ambos son legibles para una persona que quiera hacer
el trabajo a mano. Cada uno tiene un punto de entrada SKILL.md y un references/guide.md
que contiene el proceso real, sus comprobaciones de evaluación y sus modos de fallo.
Si ya tienes el parche y quieres saber si debería importarte, ejecuta el primero. Su entregable es una ventana en lugar de un sí o un no, porque para la mayoría de quienes leen esto la respuesta en presente es "no, ya aplicamos el parche", y la respuesta útil es qué años tendría que cubrir una investigación.
Clona este repositorio y conserva el clon. Los dos skills comparten los documentos
de referencia en reference/, y sus guías los leen mediante una ruta relativa al
repositorio, por lo que un directorio de skill copiado aislado llegará a un callejón sin salida.
Lo más fácil es iniciar tu agente dentro del clon y decirle dónde está tu
aplicación. AGENTS.md lo orienta desde allí, incluida la parte en la que la gente
se equivoca: que este repositorio es la herramienta y no el sujeto.
De lo contrario, apunta al archivo y dilo, lo que funciona en cualquier agente que pueda leer archivos locales:
Read /path/to/rails-forensics-CVE-2026-66066/skills/kr2s-was-i-vulnerable/SKILL.md
and follow it. My application is at /path/to/my-app.
En Claude Code puedes, en cambio, instalarlos para que se activen por sí solos, mediante enlaces simbólicos en lugar de copias, lo que los mantiene apuntando al clon:
ln -s "$PWD/skills/kr2s-was-i-vulnerable" ~/.claude/skills/
ln -s "$PWD/skills/kr2s-was-i-exploited" ~/.claude/skills/
Usa .claude/skills/ dentro de un proyecto en lugar de ~/.claude/skills/ si
prefieres limitarlos a una sola aplicación.
En cualquier caso, dile al agente dónde está tu aplicación. Los skills son explícitos en que este repositorio y la aplicación bajo investigación son dos raíces distintas, y que pueden leer la segunda pero nunca escribir en ella.
AGENTS.md orientation for an agent started in this directory
reference/the-attack.md how the vulnerability works, traced call by call
reference/the-investigation.md why an investigation is possible, and where it runs out
skills/ the two skills
RUNBOOK.md operator's guide to the scanner: flags, verdicts, resuming
lib/crafted_mat_file.rb the detector
bin/ the scanner and three analysis utilities
test/ the test suite
Los dos documentos de referencia son la mitad en inglés sencillo de este repositorio y se sostienen por sí solos. Léelos si quieres entender el problema; ejecuta los skills si quieres una respuesta sobre una aplicación concreta.
bin/kr2s_scan_active_storage_blobs.rb se genera a partir de lib/ con rake build
y se confirma en el repositorio, por lo que ejecutarlo en producción no necesita paso de
compilación ni un segundo archivo.
lib/crafted_mat_file.rb identifica el archivo manipulado (crafted) que requiere este ataque.
Lee dos campos de cabecera de los primeros 128 bytes de un objeto y no necesita
nada más, por lo que el escaneo puede clasificar un candidato con una lectura por rangos
en lugar de descargarlo. reference/the-attack.md explica qué campos y por qué
ningún escritor legítimo produce esa combinación.
Este repositorio no incluye deliberadamente código que construya archivos manipulados,
ni archivos manipulados. Las pruebas del detector sintetizan en Ruby puro los 128 bytes de
cabecera que necesitan, que es todo lo que lee el detector. reference/the-attack.md
describe la estructura por completo, por lo que no se le oculta nada a un lector
que intente entender el ataque. Lo que se oculta es un generador funcional que
podría reorientarse hacia una ruta arbitraria cambiando un argumento.
Cubierto: Active Storage, en aplicaciones que usan el procesador de variantes :vips,
donde se hacía seguimiento de los registros de variantes.
No cubierto: cualquier otro lugar donde una aplicación entregue archivos proporcionados por
el usuario a libvips. Otras bibliotecas de subida, clientes directos de object store y sistemas
de avatar o logo a medida necesitan cada uno su propio barrido, y el escáner de aquí no lo hará.
kr2s-was-i-vulnerable te hará enumerarlos para que queden nombrados en lugar de omitirse
silenciosamente de una conclusión.
Un resultado limpio de estas herramientas es una evidencia sólida, no una prueba. Ambos skills lo dicen, repetida y específicamente, y la plantilla de análisis tiene una sección de limitaciones que no es opcional.
Esto surgió de una investigación real en 37signals, en la que se barrió un gran almacén de Active Storage en producción durante todo el período en que existió la vulnerabilidad. Las herramientas identificaron correctamente los archivos de prueba subidos por los investigadores de seguridad que informaron de la falla, que es la única evidencia externa disponible de que el detector encuentra lo que se supone que debe encontrar.
El análisis desarrollado en kr2s-was-i-exploited/references/analysis-template.md es
ficticio. Está modelado sobre el real, pero cada identificador, dirección y
fecha que contiene es inventado.
Toma los números de versión de Rails parcheados del aviso, no de nada escrito aquí.
mise exec -- rake # build and test
MIT. Ver LICENSE.