IST/MEIC – MERC
Tagus / Alameda
Segurança Informática em Redes e Sistemas
2008/2009
Trabalho 4
Buffer Overflows
Objectivos
•
Explorar algumas vulnerabilidades associadas a buffer overflows.
1 Introdução
Os exercícios propostos demonstram como explorar buffer overflows. Simula-se a exploração de
tais vulnerabilidades em aplicações que se executam temporariamente com privilégios root para
poderem efectuar determinados serviços (e.g. ping ou passwd) utilizando um conjunto de
programas exemplo. Se os ataques forem bem sucedidos, um utilizador normal (e.g. utilizador
fireman) consegue executar um interpretador com privilégios root e, portanto, ter total controlo
sobre a máquina.
Comece por autenticar-se na máquina virtual como utilizador fireman (password “inseguro”).
Todos os exercícios devem realizar-se numa consola fireman. Sempre que necessitar de executar
comandos privilegiados (como por exemplo mount), deve executar o comando su, introduzir a
password de root e executar os comandos. Quando terminar, faça exit.
Todos os programas de exemplo usados nesta aula se encontram na imagem buffer_overflow.iso.
Prepare o ambiente de trabalho:
• Na máquina virtual, capture a imagem buffer_overflow.iso.
• Execute o comando privilegiado seguinte para aceder aos respectivos programas.
# mount /media/cdrom
•
•
Crie o directório trab4 na home do utilizador fireman e copie para lá os programas
exemplo.
Execute todos os exercícios no directório trab4.
Sempre que for pedido para compilar um programa exemplo x.c fazer:
gcc –g –o x x.c
Sempre que for pedido para instalar um programa exemplo x.c, deve executar a seguinte
sequência de comandos (em modo privilegiado):
• Compilar x.c.
• Alterar as permissões para o programa correr com privilégios root ( ‘4’ activa flag
SUID).
# chmod 4755 x
• Colocar o programa x na directoria /tmp, acessível ao utilizador fireman.
1
2 Buffer Overflows
2.1
Alteração do endereço de retorno
Através dum buffer overflow é possível alterar o endereço de retorno de uma função.
• Compilar e executar o programa overflow.c.
• Executar agora dentro do gdb.
• Fazer
> bt
e verificar qual o endereço de retorno das funções. Porquê o valor 0x41414141?
O comando seguinte mostra o conteúdo do stack pointer :
> x $esp
2.2
Buffer overflow na pilha
O programa vuln.c é vulnerável a um buffer overflow.
• Verificar o que faz o programa vuln.c.
• Instalar o programa vuln.c (ver Introdução).
• Alterar o exploit.c para executar o programa /tmp/vuln.
• Compilar e executar o programa.
• Fazer:
# whoami
•
Verificar o que faz o programa exploit.c.
O perl é uma boa ferramenta para injectar strings noutros programas.
• Executar a linha que se encontra no ficheiro exploit.txt.
• Fazer:
# whoami
•
2.3
Alterar o endereço de retorno da função se necessário.
Verificar o que faz esse comando.
Buffer overflow usando as variáveis do ambiente
Nem sempre o buffer tem o tamanho suficiente para colocar lá o código shell. O programa vuln2.c
é um exemplo disso. No entanto pode ser explorado o buffer overflow fazendo correr o código
shell na posição de memória onde se encontram as variáveis do ambiente.
•
•
Instalar o programa vuln2.c (ver Introdução).
Alterar env_exploit.c para executar o programa /tmp/vuln2 (implica alteração do código
em dois locais).
Compilar e executar env_exploit.c.
Fazer:
•
Verificar o que faz o programa env_exploit.c.
•
•
# whoami
Utilize agora o pearl:
• Criar uma variável do ambiente com o comando que se encontra em env_exploit.txt
$ source env_exploit.txt
2
•
No gdb verificar onde se encontra a variável em /tmp/vuln2. Para tal colocar um
breakpoint no main usando o comando seguinte e execute o programa até ao main:
•
Ver o que encontra em memória na pilha:
•
Procurar pelas variáveis do ambiente pressionando “Enter” sucessivamente até encontrar
o endereço do código da shell. Ter em conta o nome da variável de ambiente para cálculo
do endereço onde começa o código da shell. Esse endereço será então usado como
endereço para onde o main vai retornar. Se número 10 não for suficiente para obter uma
shell de root deve alterá-lo:
> b main
> x/20s $esp
$ /tmp/vuln2 `perl –e ‘print “<endereço>”x10’`
Nota: supondo que o endereço obtido é 0xbffff7c4, este deve ser passado na linha de
comandos como \xc4\xf7\xff\xbf pois os valores estão representados em memória em
little endian.
3 Format strings (opcional)
É possível explorar um programa que faça uso do printf da forma: printf(str). No ficheiro
fmt_vuln.c encontra-se um exemplo em que a string é impressa da forma correcta e da forma
incorrecta. Instalar o programa fmt_vuln.c (ver Introdução).
Como utilizador fireman executar os seguintes passos:
• Determinar onde se encontra a própria string.
o A string encontra-se mais à frente na pilha. Logo para a encontrar fazer o seguinte:
# /tmp/fmt_vuln `printf
“AAAA”`%x.
Acrescentar %x. ao comando até encontrar a string.
Ao encontrar início da string significa que o último parâmetro da string acede ao
‘AAAA’
Escolher um endereço para alterar o seu conteúdo.
o Vamos escolher o endereço do test_val para assim verificarmos que estamos a
alterar a posição certa:
o
o
•
$ /tmp/fmt_vuln test
para saber qual o endereço do test_val
o
o
o
“\x01\x02\x03\x04”`%x<.%x suficientes>
em que 0x04030201 é o endereço do test_val (“.%x suficientes” quando permite
observar o valor inserido).
$ /tmp/fmt_vuln `printf “\x01\x02\x03\x04”`%x<.%x suficientes-1>%n
Verificar que test_val foi alterado. Que valor é esse? O que faz a opção %n?
Considere a alteração de %x e %n para %N\$x ou %M\$n em que N e M são os
números do parâmetro da string. Admitindo que o número de %x suficientes é 8,
verifique que o comando seguinte colocaria o mesmo valor em test_val. Porquê?
$ /tmp/fmt_vuln `printf
$ /tmp/fmt_vuln `printf
•
“\x01\x02\x03\x04”`%7\$34x%8\$n
Com o procedimento anterior podemos colocar qualquer valor em qualquer posição de
memória. Podemos por exemplo alterar o valor de retorno que está na pilha para
apontar para o código shell que se encontra numa variável do ambiente. Comecemos por
colocar o valor do endereço do código shell na variável test_val para verificar que está
correcto:
o Fazer export da variável SHELLCODE
o Ver com gdb /tmp/fmt_vuln onde se encontra a variável SHELLCODE
3
o
o
o
o
•
Para colocar o endereço na variável test_val, é necessário fazê-lo byte a byte.
Usando %N\$x e %M\$n, passamos a ter um par %x%n por cada byte que queremos
escrever:
# /tmp/fmt_test `printf “\x01\x02\x03\x04”`%N\$x%M\$n
# /tmp/fmt_test `printf
“\x01\x02\x03\x04\x02\x02\x03\x04\x03\x02\x03\x04\x04\x02\x03\x04”` %N\$x%M\$n
Em que 0x04030201, 0x04030202, 0x04030203, 0x04030204 são os endereços do inteiro
test_val.
Acrescentar agora o valor L ao parâmetro %x:
%N\$Lx em que L é o número de caracteres que ocupa o parâmetro x. Isto
permite aumentar a string por forma a escrevermos o valor certo no test_val. O
valor que queremos escrever será o byte menos significativo do endereço do
código shell. Verificar que se escreve o byte menos significativo correctamente no
test_val.
Acrescentar agora um novo %N\$Lx%(M+1)\$n. Isto permite escrever no 2º byte de
test_val. Fazer o mesmo para os restantes 2 bytes. E temos o endereço pronto. A
figura seguinte mostra um exemplo de como colocar o endereço 0xc4f7ffbf na
variável test_val.
Só falta agora colocá-lo na posição de memória certa. Visto que não é fácil saber onde
está o endereço de retorno de uma função vamos escolher outra posição. Em C é possível
definir funções destrutoras. Essas funções encontram-se na secção .dtors no array que
começa com 0xffffffff e acaba com 0x00000000. Sendo essas funções sempre chamadas
basta alterar o ponteiro para essa função para o valor do código shell:
$ objdump –s –j .dtors /tmp/fmt_vuln
•
Isto permite-nos ter a posição de memória onde está o endereço para onde o programa
vai saltar quando sair. Esta posição de memória é o endereço imediatamente após ao
endereço onde se encontra o valor 0xffffffff. Colocar esse valor em vez do endereço do
test_val
Fazer:
# whoami
4
Download

Trabalho 4 - Buffer Overflows e Format Strings