buffer overflow

Propaganda
Segurança e auditoria
de sistemas
Carlos Oberdan Rolim
Ciência da Computação
Sistemas de Informação
Exploiting Buffer Overflow
Estatísticas
40% dos sites comprometidos usam senhas fracas.
35% devido a exploits de buffer overflow.
10% devido a descuidos administrativs
8% devido a helicópteros pretos
2.600% a IP spoofing.
Popularidade do buffer overflow
Pesquisar na web por “buffer overflow exploit”.
Verificar sites como alt.2600, rootshell.com, antionline.com –
pode ser encontradas longas listas de exploits baseadas em
buffer overflow.
Até mesmo a versão original do ssh tinha um problema com
buffer!
Exemplos
(In)famous: Morris worm (1988)
gets() in fingerd
Code Red (2001)
MS IIS .ida vulnerability
Blaster (2003)
MS DCOM RPC vulnerability
Mplayer URL heap allocation (2004)
% mplayer http://`perl –e ‘print “\””x1024;’`
O problema
void foo(char *s) {
char buf[10];
strcpy(buf,s);
printf(“buf is %s\n”,s);
}
…
foo(“thisstringistolongforfoo”);
Conceito de Buffer Overflow
O principio de explorar o buffer overflow é
sobrescrever partes da memoria que supostamente
nao poderia ser sobreescritas com codigos
arbitrarios fazendo o processo executar esse
codigo
Exploitation
A idéia geral é fornecer ao programa (servidores) strings grandes
que vão estourar o buffer.
Para servidres com códigos problemáticos é fácil derrubar o
código.
As vezes é possível fazer o servidor fazer qualquer coisa (!!!) ao
invés de derrubá-lo.
Conhecimento necessário
Funções C e pilha (stack)
Um pouco de conhecimento em linguagem assembly.
Saber como as chamadas de sistema são efetuadas (em
nível de máquina).
exec() system calls
Como “adivinhar” alguns parâmetros.
Dependência de CPU/OS
Construir um exploit requerer alguns conhecimentos
específicos do sistema operacional alvo e da arquitetura
usada.
Apesar de poucas diferenças o conceito é o mesmo
Chamada em C e a pilha
Quando uma chamada a uma função é feita o When a
function call is made, the return address is put on the stack.
Often the values of parameters are put on the stack.
Usually the function saves the stack frame pointer (on the
stack).
Local variables are on the stack.
Direção da pilha
Em sistemas Linux (x86) a pilha cresce dos endereços mais
altos para os mais baixos
Colocar algo na pilha conduz ela em direção ao endreço 0.
Buffer na pilha
Imagine um servidor web com a seguinte função:
void func(char *str) {
char buf[126];
strcpy(buf,str);
}
Aloca buffer local
(126 bytes reservados na pilha)
Copia o argumento no buffer local
Quando essa função é invocada um novo frame com variáveis
locais é colocado na pilha
Stack grows this way
buf
Local variables
sfp
ret
addr
str
Pointer to Execute
Arguments
previous
code at
frame this address
after func()
finishes
Frame of the
calling function
Top of
stack
E se o buffer for sobreescrito ??
Ponteiro de memória para str é copiado na pilha…
void func(char *str) {
char buf[126];
strcpy(buf,str);
strcpy NÃO verifica se a string
em *str contém mais que 126 caracteres
}
Se uma string maior que 126 bytes é copiada no buffer, ela vai
sobrescresver as posições adjacentes da pilha
buf
overflow
str
Isto sera
interpretado como
o endereço de retorno!
Frame of the
calling function
Top of
stack
Executando o código
Imagine que o buffer contém uma string com código malicioso
Se por exemplo, *str contém uma string recebida via rede como entrada de
algum serviço
code
Atacante coloca instruçòes assembly
nessa string de entrada, exemplo
Código binário de execve(“/bin/sh”)
ret
str
Frame of the
calling function
Top of
stack
Nessa área, um ponteiro apontando
de volta para o buffer aparece onde o sistema
esperava pelo retorno de endereço
Quando a função termina, o código nobuffer vai ser executado
fornecendo uma shell ao atacante
Root shell se o programa exploitado rodar como setuid root
“Smashing the Stack”*
A idéia é encher o buffer para ele sobrescrever o endereço
de retorno.
Quando uma função termina ela vai para um endereço na
pilha.
Nós colocamos algum código no buffer e apontamos o
endereço de retorno para ele!
*taken from the title of an article in Phrack 49-7
Antes e depois
void foo(char *s) {
char buf[100];
strcpy(buf,s);
…
address of s
address of s
return-address
pointer to pgm
saved sp
buf
Small Program
Observações
Como nós sabemos para onde o ponteiro deve apontar
(novo endereço de retorno)
É o endereço do buffer, mas como nós vamos saber onde é esse
endereço?
Como nós contruimos o “small program” e colocamos ele em
uma string ?
Adivinhando o endereço
Geralmente o código fonte é necessário para estimar o
endereço de retorno e o endereço do buffer.
Uma estimativa geralmente é o suficiente!
Construindo o “small program”
Tipicamente o exploit coloca no buffer um exec().
Outras vezes altera senhas, outras arquivos, outras abre
portas….
exec()
In Unix, the way to run a new program is with the exec()
system call.
There is actually a family of exec() system calls…
This doesn't create a new process, it changes the current process to a
new program.
To create a new process you need something else ( fork() ).
Exemplo exec()
#include <stdio.h>
char *args[] = {"/bin/ls", NULL};
void execls(void) {
execv("/bin/ls",args);
printf(“I’m not printed\n");
}
Gerando uma string
Pega-se o código desejado (exemplo do slide anterior) e
gera linguagem de máquina.
Copia-se os valores indiduais dos bytes e constroi-se uma
string.
Um exemplo simples requerer menos de 100 bytes.
Exemplo de programa/string
Exemplo de um exec() com /bin/ls: exec(‘/bin/ls’)
unsigned char cde[] =
"\xeb\x1f\x5e\x89\x76\x08\x31\xc0”
“\x88\x46\x07\x89\x46\x0c\xb0\x0b"
"\x89\xf3\x8d\x4e\x08\x8d\x56\x0c”
“\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
"\x80\xe8\xdc\xff\xff\xff/bin/ls";
Algumas considerações importantes
O “small program” deve rodar em qualquer posição da
memória não sendo dependente de uma posição
Ele não pode ser grande ou então colocaremos o programa
e o novo endereço de retorno na pilha
Sample Overflow Program
unsigned char cde[] = "\xeb\x1f\…
void tst(void) {
int *ret;
ret = (int *)&ret+2; // pointer arith!
(*ret) = (int) cde;
//change ret addr
}
int main(void) {
printf("Running tst\n");
tst();
printf("foo returned\n");
}
Atacando um programa
Relembrando: a idéia é fornecer ao servidor uma string
grande capaz de estourar o tamanho do buffer.
Esta string estoura o buffer e sobreescreve o endereco de
retorno na pilha.
Assumindo que nos colocamos o nosso “small program” em
uma string nós precisamos saber seu endereço
NOPs
Várias CPUs possuem a No-Operation instruction – ela não
faz nada somente avança o ponteiro de instrução.
Usualmente podemos colocar várias NOOP na frente do
nosso programa (na string).
Se o novo endereço de retorno apontar para um NOP está
OK.
Using NOPs
Novo endereço de retorno
Programa real
(exec /bin/ls ou outra coisa)
Instruções nop
Estimando o tamanho da pilha
Pode ser adivinhado a localização do endereço de retorno
relativo ao buffer estourado.
Colocar ele em vários novos endereços de retorno!
Estimando a localização
Novo endereço de retorno
Novo endereço de retorno
Novo endereço de retorno
Novo endereço de retorno
Novo endereço de retorno
Novo endereço de retorno
Program real
Instruções nop
vulnerable.c
void foo( char *s ) {
char name[200];
strcpy(name,s);
printf("Name is %s\n",name);
}
int main(void) {
char buf[2000];
read(0,buf,2000);
foo(buf);
}
genpgm.c
genpgm.c foi construido para estourar o buffer do
programa vulnerable.c
Ele permite ao usuário acrescer um deslocamento para um
endereço fixo imaginado (“xute”) que seja o endereço de
retorno da pilha.
Ele escreve (para stdout) uma string contendo vários
endereços de retorno e um programa que executa um :
exec /bin/ls.
Testando
./genpgm 16 | ./vulnerable
Seja ambicioso!!!! Troque a saída do programa genpgm para
exec /bin/sh!
(./genpgm; cat) | ./vulnerable
Maiores informações
Leitura obrigatória Smashing the Stack for Fun and Profit
Recomendado Exploiting Format String Vulnerabilities
Recomendado Blended Attacks by Chien and Szor para
entender melhor buffer overflows e como eles são usados em
worms internet
Opcional: The Tao of Windows Buffer Overflow by DilDog
(Cult of the Dead Cow)
Download