Estouro de buffer baseado em pilha no R 3.4.4. Exploração completa em x86, mas apenas controle de RIP com análise de gadgets em x64 devido a restrições do programa. A mesma vulnerabilidade em duas arquiteturas, levando a diferentes caminhos de exploração.
Stack-based buffer overflow no R 3.4.4. Exploração completa no x86, mas apenas controle de RIP com análise de gadgets no x64 devido a restrições do programa. A mesma vulnerabilidade em duas arquiteturas, levando a diferentes caminhos de exploração.
Este repositório faz parte do material que utilizo ao ensinar exploração de corrupção de memória (além do meu trabalho regular, também leciono em diferentes cursos de cibersegurança onde ajudo a treinar a próxima geração de engenheiros reversos).
CVE-2019-25485 é o caso que uso quando quero que os alunos trabalhem na mesma vulnerabilidade em duas arquiteturas diferentes e vejam em primeira mão o que muda entre elas. O R 3.4.4 é distribuído nas versões x86 e x64, e o mesmo overflow existe em ambas, o mesmo campo GUI, o mesmo manipulador de entrada, o mesmo crash. Ambos estão documentados e explorados aqui como exercícios separados:
R 3.4.4 é uma aplicação de computação estatística, não um serviço de rede ou navegador. O overflow é acionado através de um campo GUI de desktop, o que significa que a superfície de ataque é completamente diferente de todos os outros casos que ensino. O que torna este caso útil para o ensino:
R é um ambiente de computação estatística e gráficos disponível para Windows, macOS e Linux. A vulnerabilidade está na caixa de diálogo Preferências do GUI, especificamente no campo Idioma para menus e mensagens, que copia a entrada do usuário para um buffer de pilha de tamanho fixo sem validar seu comprimento.
Principais detalhes técnicos:
O R 3.4.4 processa o campo Idioma para menus e mensagens copiando a string fornecida para um buffer de pilha de tamanho fixo sem verificar seu comprimento. Uma versão simplificada da lógica vulnerável se parece com isso:
char language_buffer[256];
strcpy(language_buffer, user_input);
Enviar uma string suficientemente longa faz com que a cópia escreva além do final do buffer, corrompendo a pilha até que o endereço de retorno salvo seja sobrescrito. Quando a função retorna, a CPU carrega o valor controlado pelo atacante da pilha para o RIP e tenta pular para ele.
No x64, o Windows impõe validação de endereço canônico antes que qualquer salto ocorra. Um valor não canônico como 0x4141414141414141 desencadeia uma violação de acesso imediata antes que o RIP seja carregado, o que significa que o crash parece diferente do x86, sem um RIP = 4141414141414141 limpo. O offset deve ser encontrado lendo o padrão cíclico da pilha após o crash, em vez de diretamente do RIP.
O crash pode ser reproduzido colando uma string longa no campo de idioma. Nenhuma autenticação é necessária. Exemplo usando Python para gerar o payload:
import struct
payload = b'A' * 400
with open('payload.txt', 'wb') as f:
f.write(payload)
Open R 3.4.4 x64
Edit -> GUI Preferences
Paste contents of payload.txt into Language for menus and messages
Click OK
O objetivo deste repositório não é apenas demonstrar o crash, mas percorrer o processo completo de exploração em ambas as arquiteturas, documentando o que funciona no x86, o que quebra no x64 e, mais importante, por quê.
Para manter o README principal limpo, as notas detalhadas de exploração, scripts e etapas do depurador estão colocadas dentro da pasta Vulnerability 📂 deste repositório, organizadas em subpastas separadas x86 e x64.
Lá você encontrará o fluxo de trabalho completo para ambas as arquiteturas:
x86 - Exploração completa:
x64 - Controle de RIP e análise de exploração: