Add content from: Marshal madness: A brief history of Ruby deserialization exp...

This commit is contained in:
HackTricks News Bot 2025-08-20 06:36:55 +00:00
parent 22bea233ef
commit a919fe6dc4

View File

@ -1082,7 +1082,87 @@ This payload is compiled into binary Ruby code and concatenated with a carefully
Using the arbitrary file write vulnerability, the attacker writes the crafted cache file to the computed location. Next, they trigger a server restart (by writing to tmp/restart.txt, which is monitored by Puma). During restart, when Rails requires the targeted file, the malicious cache file is loaded, resulting in remote code execution (RCE).
{{#include ../../banners/hacktricks-training.md}}
### Ruby Marshal exploitation in practice (updated)
Treat any path where untrusted bytes reach `Marshal.load`/`marshal_load` as an RCE sink. Marshal reconstructs arbitrary object graphs and triggers library/gem callbacks during materialization.
- Minimal vulnerable Rails code path:
```ruby
class UserRestoreController < ApplicationController
def show
user_data = params[:data]
if user_data.present?
deserialized_user = Marshal.load(Base64.decode64(user_data))
render plain: "OK: #{deserialized_user.inspect}"
else
render plain: "No data", status: :bad_request
end
end
end
```
- Common gadget classes seen in real chains: `Gem::SpecFetcher`, `Gem::Version`, `Gem::RequestSet::Lockfile`, `Gem::Resolver::GitSpecification`, `Gem::Source::Git`.
- Typical side-effect marker embedded in payloads (executed during unmarshal):
```
*-TmTT="$(id>/tmp/marshal-poc)"any.zip
```
Where it surfaces in real apps:
- Rails cache stores and session stores historically using Marshal
- Background job backends and file-backed object stores
- Any custom persistence or transport of binary object blobs
Industrialized gadget discovery:
- Grep for constructors, `hash`, `_load`, `init_with`, or side-effectful methods invoked during unmarshal
- Use CodeQLs Ruby unsafe deserialization queries to trace sources → sinks and surface gadgets
- Validate with public multi-format PoCs (JSON/XML/YAML/Marshal)
Detection (SAST):
- Semgrep rules:
- rails-cache-store-marshal: https://github.com/trailofbits/semgrep-rules/blob/main/ruby/rails-cache-store-marshal.yaml
- marshal-load-method: https://github.com/trailofbits/semgrep-rules/blob/main/ruby/marshal-load-method.yaml
- json-create-deserialization: https://github.com/trailofbits/semgrep-rules/blob/main/ruby/json-create-deserialization.yaml
- yaml-unsafe-load: https://github.com/trailofbits/semgrep-rules/blob/main/ruby/yaml-unsafe-load.yaml
- CodeQL:
- Query help: https://codeql.github.com/codeql-query-help/ruby/rb-unsafe-deserialization/
- Payload PoCs: https://github.com/GitHubSecurityLab/ruby-unsafe-deserialization
Mitigations (what to do):
- Never pass attacker-controlled bytes to `Marshal.load`/`marshal_load`
- Prefer safe formats and APIs:
- YAML.safe_load with strict `permitted_classes`
- JSON with manual object construction
- Typed DB columns instead of opaque blobs
- Ecosystem hardening proposal:
- Introduce `Marshal.safe_load` (primitive-only by default, with `permitted_classes`)
- Warn on `Marshal.load`, switch defaults to safe behavior, and gate legacy behavior behind `Marshal.unsafe_load`
Notes on recent timeline (selected):
- 20182022: Universal gadget chains across Ruby 2.x3.x (elttam, Bowling) and patches through Ruby 3.1/3.2
- 2019: Rails 5.2 insecure deserialization (CVE-2019-5420)
- 2024: Include Security shows gadget discovery via grep; GitHub Security Lab ships CodeQL rules + multi-format PoCs
- 2024-12: Ruby 3.4.0-rc1 near-miss in `rubygems` code path patched before GA (PR #12444)
- 2024-11/12: New Ruby 3.4 Marshal chains and SafeMarshal escape published and subsequently patched
## References
- Trail of Bits Marshal madness: A brief history of Ruby deserialization exploits: https://blog.trailofbits.com/2025/08/20/marshal-madness-a-brief-history-of-ruby-deserialization-exploits/
- elttam Ruby 2.x Universal RCE Deserialization Gadget Chain: https://www.elttam.com/blog/ruby-deserialization/
- Phrack #69 Rails 3/4 Marshal chain: https://phrack.org/issues/69/12.html
- CVE-2019-5420 (Rails 5.2 insecure deserialization): https://nvd.nist.gov/vuln/detail/CVE-2019-5420
- ZDI RCE via Ruby on Rails Active Storage insecure deserialization: https://www.zerodayinitiative.com/blog/2019/6/20/remote-code-execution-via-ruby-on-rails-active-storage-insecure-deserialization
- Include Security Discovering gadget chains in Rubyland: https://blog.includesecurity.com/2024/03/discovering-deserialization-gadget-chains-in-rubyland/
- GitHub Security Lab Ruby unsafe deserialization (query help): https://codeql.github.com/codeql-query-help/ruby/rb-unsafe-deserialization/
- GitHub Security Lab PoCs repo: https://github.com/GitHubSecurityLab/ruby-unsafe-deserialization
- Doyensec PR Ruby 3.4 gadget: https://github.com/GitHubSecurityLab/ruby-unsafe-deserialization/pull/1
- Luke Jahnke Ruby 3.4 universal chain: https://nastystereo.com/security/ruby-3.4-deserialization.html
- Luke Jahnke Gem::SafeMarshal escape: https://nastystereo.com/security/ruby-safe-marshal-escape.html
- Ruby 3.4.0-rc1 release: https://github.com/ruby/ruby/releases/tag/v3_4_0_rc1
- Ruby fix PR #12444: https://github.com/ruby/ruby/pull/12444
- Trail of Bits Auditing RubyGems.org (Marshal findings): https://blog.trailofbits.com/2024/12/11/auditing-the-ruby-ecosystems-central-package-repository/
{{#include ../../banners/hacktricks-training.md}}