INF1018 - Software Básico

Aulas de Laboratório

Invasão da Pilha de Execução

Nesse laboratório você vai usar seus conhecimentos sobre a pilha de execução para entender como são realizados ataques do tipo buffer overflow. Este tipo de ataque se aproveita da ausência de verificação de "final de array" em C para sobrescrever a pilha de execução, alterando o endereço de retorno lá presente, e possivelmente outras informações.

Vamos utilizar o programa bufbomb.c para testar alguns exemplos. Para isso, busque aqui o arquivo bufferbomb.tar. Coloque-o em uma pasta onde possa criar uma subpasta para este laboratório e em seguida execute:

tar xvf bufferbomb.tar
Isso deve criar uma pasta bufferbomb com os arquivos: Para criar o programa executável bufbomb, escreva:
make bufbomb

O programa bufbomb é bastante simples, e basicamente chama a função getbuf, definida no arquivo buf.c. Execute ./bufbomb escrevendo um texto qualquer quando o programa ficar a espera de entrada, e veja o que acontece.

Agora vamos interferir no funcionamento de bufbomb fornecendo uma string de valores "apropriados". Veja que a função getbuf declara um array de char e chama Gets para preencher esse array. O programa auxiliar hex2raw cria um arquivo binário contendo uma sequência de valores dados na entrada em formato hexadecimal, isto é, se o arquivo se contiver:

00 00 00 00 
00 00 00 01
e escrevermos:
./hex2raw < se >se.raw
Podemos depois executar:
./bufbomb <se.raw
para fornecer a bufbomb a sequência de bytes equivalente ao conteúdo do arquivo mostrado. Como o programa não testa o tamanho dessa sequência, podemos dar como entrada uma string arbitrariamente grande.

Essa é a base do ataque de buffer overflow: sobrescrever a pilha de execução, alterando os endereços de retorno que estão empilhados para apontarem para um outro código que o intruso deseja que seja executado. (Nos primeiros ataques, esse outro código estava tipicamente presente nessa mesma string fornecida como entrada. Ao longo do tempo, os sistemas foram ficando mais protegidos, e não permitem a execução de instruções na área da pilha. Uma outra forma de ataque que se tornou bastante comum é desviar o controle para alguma função já existente no próprio código mas que não seria normalmente chamada.)

Vamos construir strings apropriadas para algumas tarefas:

  1. Examine o código de bufbomb.c. Veja que existe uma função danger, normalmente chamada pela função protect, que determina se o usuário tem as credenciais apropriadas para executar a chamada. Você irá criar uma string de bytes que desviará o controle para danger sem passar por protect. Para isto, a string dada como entrada para bufbomb deve sobrescrever a pilha de execução, colocando o endereço de danger no lugar do endereço de retorno de getbuf. Você vai ter que criar uma sequência de bytes com tamanho suficiente para ocupar desde o início do espaço ocupado por buf até o endereço de retorno de getbuf. Os valores nas posições que não correspondem ao endereço de retorno de getbuf podem ser preenchidos com qualquer valor.

    Desenhe a pilha a partir da chamada a getbuf, na main. Verifique que string você precisa escrever para sobrescrever o endereço de retorno de getbuf. Use

    objdump -d bufbomb >bufbomb.32d
    para descobrir o endereço de danger e lembre-se que esse endereço deve aparecer em little-endian. Verifique também no "desassembly" da função getbuf onde começa, na pilha de execução, o array buf. É comum o compilador alocar um pouco mais de espaço na pilha para poder alinhá-la em um endereço múltiplo de 16.

    Crie um arquivo stringinvasora contendo uma sequência de valores hexa, por exemplo (quebras de linha não fazem diferença):

    00 00 00 00 00 00 00 00
    00 00 00 00 00 00 00 00
    00 00 00 00 00 00 00 00
    00 00 00 00 50 86 04 08
    1a 2a 3a 4a 00 00 00 00
    00 00 00 00 0a 0b 0c 0d
    
    Crie agora o programa auxiliar hex2raw, executando
    make hex2raw
    
    Utilize hex2raw para gerar sua string e depois forneça o arquivo resultante a bufbomb:
    ./hex2raw < stringinvasora > stringinvasora.raw
    ./bufbomb < stringinvasora.raw
    
    Você verá se seu programa fez o que você queria pelas mensagens, mas para observar o seu efeito, crie um arquivo chamado vitima (pode ser até uma pasta) dentro da pasta bufferbomb.

    obs: Caso sua sequeência de bytes esteja bem contruída, seu programa irá chamar danger e depois deve gerar uma segmentation fault. Não se preocupe, iremos mudar isso no item seguinte.

  2. No item anterior, o programa gera um segmentation fault ao tentar retornar da função danger, pois não há um endereço de retorno no lugar esperado por ela. Corrija isto. Estenda a sua sequência de bytes de maneira a preencher a pilha com um endereço de retorno no local esperado por danger. Use o endereço da função smoke, fazendo com que o controle vá para ela depois da execução de danger. (Como smoke chama exit(), o programa agora deve terminar elegantemente.)

  3. Finalmente, vamos experimentar ativar uma função passando um argumento para ela. Esqueça agora as funções danger e fizz. Gere uma sequência de bytes que faça getbuf ``retornar'' para a função fizz. Veja o código de fizz e tente colocar na pilha o parâmetro que ela espera, para que ela imprima a linha: fizz! You called fizz....
Obs: Ainda que com muitas simplificações, esse laboratório dá uma idéia de como ocorrem araques do tipo buffer overflow. O objetivo é aumentar o entendimento da pilha de execução e da importância de escrever código que não esteja sujeito a esse ataque.