
PoC de un solo archivo para Rails CVE-2026-66066: lectura arbitraria de archivos, recuperación de secretos, escaneo multihilo y RCE condicional mediante variaciones de imágenes firmadas.
Un proof of concept de un solo archivo y sin dependencias, más un laboratorio Docker aislado en loopback
para la vulnerabilidad de lectura arbitraria de archivos en Rails Active Storage/libvips. También
demuestra la escalación condicional del aviso desde una secret_key_base recuperada
hasta una variación de imagen falsificada y ejecución de comandos.
Referencias principales: Aviso de Rails, Análisis forense de Rails, el informe técnico de Ethiack y el lanzamiento 8.1.3.1.
https://github.com/user-attachments/assets/fdcb5651-c1d1-4bc4-b322-8f1205b09635
kr2s.py contiene el cliente completo en un solo archivo:
h5py ni numpy;La herramienta no instala paquetes, no inicia un listener de callback, no requiere curl
en el objetivo ni depende de make. La fase RCE usa la ruta de representación integrada de Active
Storage y no requiere un gadget de Marshal.
Inicia el laboratorio vulnerable:
cd lab
RAILS_VERSION=8.1.3 docker compose up --build
El puerto de Compose está vinculado a 127.0.0.1 y el contenedor se ejecuta como un
usuario sin privilegios con todas las capacidades de Linux eliminadas.
En otra terminal:
# Intrusivo: sube un blob y luego confirma que /etc/hostname es legible.
python3 kr2s.py check http://127.0.0.1:3000
# Recuperar un prefijo de tamaño fijo. La salida binaria va a stdout salvo que se use -o.
python3 kr2s.py read http://127.0.0.1:3000 /etc/passwd --size 4096
python3 kr2s.py read http://127.0.0.1:3000 /etc/passwd -o passwd.bin
# Recuperar secretos seleccionados del entorno. Los valores se redactan por defecto.
python3 kr2s.py secrets http://127.0.0.1:3000
# Mostrar los valores completos de entorno coincidentes para análisis autorizado.
python3 kr2s.py secrets http://127.0.0.1:3000 --show-full
# Preflight pasivo de un objetivo o de un archivo con un objetivo por línea.
python3 kr2s.py scan http://127.0.0.1:3000
python3 kr2s.py scan targets.txt --threads 10 --json scan.json
# Confirmar intrusivamente la lectura de archivos en cada objetivo de la lista.
python3 kr2s.py scan targets.txt --active
# Probar cargadores seleccionados de libvips.
python3 kr2s.py probe http://127.0.0.1:3000
# Escalación condicional. Esto ejecuta el comando suministrado.
python3 kr2s.py rce http://127.0.0.1:3000 "id; uname -a"
Ejecuta python3 kr2s.py COMMAND --help para ver todas las opciones. Controles comunes
en aplicaciones reales incluyen --harvest-url, --show-path, --proxy, --insecure y
--skb.
secrets --show-full imprime los valores completos de las variables de entorno coincidentes.
Sin esa opción, la herramienta imprime solo los primeros 12 caracteres y una elipsis.
Los archivos solicitados con --also-read, incluidos los archivos de credenciales predeterminados
de Rails, se imprimen hasta 600 caracteres cuando son legibles.
El scan pasivo solo informa si la página expone una representación de Active Storage
recolectable; no puede identificar el nivel de parche de Rails instalado.
scan --active sube un blob malicioso e intenta la lectura canario. Un resultado activo
fallido se etiqueta como BLOCKED_OR_INCONCLUSIVE, no como SAFE, porque los WAF,
la autenticación, las variaciones no compatibles y el parche de Rails pueden parecer idénticos
desde fuera de la aplicación.
Detén el laboratorio con:
cd lab
docker compose down
image/png.matload de libvips; los bytes del archivo objetivo
se convierten en píxeles de imagen y se devuelven en un PNG generado.El archivo híbrido satisface a dos analizadores a la vez: su texto comienza con
MATLAB 5.0, mientras que la palabra de versión en el desplazamiento 124 selecciona la ruta
MATLAB 7.3 respaldada por HDF5. El parche de Rails bloquea las operaciones de libvips marcadas
como no confiables antes de que un archivo controlado por el atacante pueda llegar a matload.
rce primero lee SECRET_KEY_BASE de /proc/self/environ, salvo que se proporcione --skb.
Deriva la clave del verificador de Active Storage y firma una variación JSON que contiene una
operación instance_eval. En la ruta vips afectada, la operación llega a Vips::Image.public_send.
El PoC la envía a través de la ruta de representación integrada, ejecuta el comando suministrado
mediante /bin/sh y captura ambos flujos de salida.
Esta segunda fase es condicional a recuperar el secreto de firma y alcanzar el comportamiento vulnerable de transformación de vips descrito en el análisis de Ethiack sobre CVE-2025-24293. El parche de CVE-2026-66066 cierra el punto de entrada de lectura de archivos; no es una solución general para secretos de firma filtrados previamente. Rota los secretos ante una posible exposición, como recomienda el aviso de Rails.
La salida del comando se escribe en un archivo aleatorio /tmp/.kr2s_*, se recupera mediante
la misma primitiva de lectura de archivos y luego se elimina con una segunda carga útil firmada.
Las lecturas devuelven la región de tamaño fijo solicitada. Si el origen es más corto, su prefijo
va seguido de bytes cero. Usa --offset para regiones posteriores.
La variación predeterminada del laboratorio solo convierte a PNG, por lo que la recuperación es
exacta byte a byte. Las aplicaciones reales pueden redimensionar las variaciones. El modo de clave
recolectada usa una columna de un píxel para evitar el remuestreo horizontal, pero una variación con
una altura menor aún puede cambiar la geometría. En ese caso, el cliente falla explícitamente;
reduce --size o recolecta una representación que no se redimensione.
Cada comando es intrusivo: crea blobs persistentes de Active Storage y puede crear variantes
procesadas. rce además ejecuta el comando suministrado.
El script de matriz compila cada versión, ejecuta comprobaciones reales de lectura y RCE en versiones vulnerables, verifica que las lecturas estén bloqueadas en versiones corregidas y devuelve un código distinto de cero ante cualquier discrepancia:
./lab/matrix.sh
./lab/matrix.sh 8.1.3
./lab/matrix.sh 8.1.3.1:PATCHED
Validado el 2026-07-31 con Ruby 3.4.10, image_processing 1.14.0, ruby-vips 2.3.0 y libvips 8.16.1:
Las filas corregidas deliberadamente no lanzan un payload RCE con un secreto de firma previamente suministrado; la matriz prueba el límite de lectura de archivos del CVE.
research/craft.py regenera payloads candidatos para la investigación de cargadores y requiere
h5py además de numpy. No es importado por el PoC de lanzamiento.
lab/probe/probe.sh inspecciona esos candidatos con ruby-vips dentro de una imagen de laboratorio:
uv run --script research/craft.py all -o /tmp/kr2s-payloads
docker run --rm \
-v "$PWD/lab/probe:/probe:ro" \
-v /tmp/kr2s-payloads:/payloads:ro \
--entrypoint /probe/probe.sh kr2s-rails:8.1.3
Ejecuta las pruebas unitarias sin dependencias con:
python3 -m unittest discover -s tests -v
La carga útil MAT/HDF5 y el cliente sin dependencias se desarrollaron mediante pruebas locales. Después de que aparecieran PoCs públicos, este lanzamiento incorporó la recolección de claves de variación y las rutas integradas de Active Storage documentadas por el PoC de Zer0SumGam3. La implementación en Python aquí es original y usa solo la biblioteca estándar.
Una revisión anterior demostró que el RCE también era posible con un payload Marshal firmado
usando Puma::MiniSSL::Context y un proxy de ActiveSupport, basado en
la investigación pública de gadgets de Behrad Taher.
Esa ruta evitaba binarios auxiliares en el objetivo, pero requería un sumidero de ID firmado
compatible con Marshal, además de clases de gadget Puma y ActiveSupport cargadas. Fue reemplazada
después de que el equipo de investigación de Ethiack publicara su
informe técnico de KindaRails2Shell,
que documenta la técnica más simple de variación JSON firmada. Usa una ruta estándar
y elimina esos requisitos previos.
El crédito del descubrimiento pertenece a André Baptista (0xacb), Bruno Mendes (s3np41k1r1t0), Rafael Castilho y el equipo de investigación de Ethiack, así como a RyotaK (GMO Flatt Security). Abdelmounaim Moulahcene (bl0rph) publicó de forma independiente la primitiva subyacente poco antes de la divulgación coordinada. Consulta el repositorio forense de Rails para ver la cronología completa y los créditos de los mantenedores.
Úsalo solo en sistemas que te pertenezcan o para los que tengas autorización explícita de pruebas. Los autores no aceptan ninguna responsabilidad por el mal uso. Consulta SECURITY.md para informar problemas en este artefacto de investigación.
Publicado bajo la Licencia MIT.
| Rails | File read | RCE check | Result |
|---|
| 7.2.3.1 | READ-OK | RCE-OK | pass |
| 7.2.3.2 | blocked | not run | pass |
| 8.0.5 | READ-OK | RCE-OK | pass |
| 8.0.5.1 | blocked | not run | pass |
| 8.1.3 | READ-OK | RCE-OK | pass |
| 8.1.3.1 | blocked | not run | pass |