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:
bufbomb.c
- código a ser "atacado"
buf.c
- manipulação do buffer
hex2raw.c
- função auxiliar para criar as strings de entrada
Makefile
- configuração da compilação e ligação
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 como entrada uma string
de valores "apropriados".
Veja que a função getbuf
declara um array de char e chama Gets
para preencher esse array.
Para obtermos essa entrada "apropriada", usaremos o programa auxiliar
hex2raw
.
Esse programa lê uma sequência de valores
em formato hexadecimal e gera um arquivo binário com esses valores. Isto é, se o arquivo de entrada se
contiver:
00 00 00 00
00 00 00 01
e escrevermos:
./hex2raw < se > se.raw
o programa hex2raw
vai ler o arquivo de entrada se
e gerar o arquivo de saída 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:
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
,
cujo código está em buf.c
. Repare que getbuf
utiliza
um array local.
Você vai precisar de uma string que sobrescreva o endereço de
retorno de getbuf
!
Use
objdump -d bufbomb >bufbomb.32d
para descobrir o endereço de danger
(o endereço estará ao lado
do label danger no arquivo bufbomb.32d
). Lembre-se
que esse endereço deve aparecer na pilha
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.
Como o endereço do array é fornecido como parâmetro para Gets
,
verifique como é calculado o valor desse parâmetro (isto é, veja
qual é o offset em relação ao %ebp
correspondente ao
endereço de buf
).
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
Lembre-se que a sequência tem que ter um tamanho adequado para
que o endereço de danger sobrescreva o endereço de retorno de getbuf!
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
.
Repare que a função danger
, ao executar, remove esse arquivo!
obs: Caso sua sequência de bytes esteja bem construída, seu programa irá chamar
danger
e depois deve gerar uma segmentation fault.
Não se preocupe, iremos mudar isso no item seguinte.
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.)
danger
e protect
.
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....