mirror of
https://github.com/Pennyw0rth/NetExec
synced 2026-06-06 16:34:30 +00:00
Merge branch 'main' into marshall-get-gpos
Signed-off-by: Marshall Hallenbeck <Marshall.Hallenbeck@gmail.com>
This commit is contained in:
@@ -20,6 +20,7 @@ class NXCModule:
|
||||
"""Required.
|
||||
Module options get parsed here. Additionally, put the modules usage here as well
|
||||
"""
|
||||
# Put "No options available" in the docstring if there are no options for the module
|
||||
|
||||
def on_login(self, context, connection):
|
||||
"""Concurrent.
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
from io import BytesIO
|
||||
import pefile
|
||||
|
||||
|
||||
class NXCModule:
|
||||
"""
|
||||
Module for detecting Windows lock screen backdoors
|
||||
Module by @E1A
|
||||
"""
|
||||
|
||||
name = "lockscreendoors"
|
||||
description = "Detect Windows lock screen backdoors by checking FileDescriptions of accessibility binaries."
|
||||
supported_protocols = ["smb"]
|
||||
|
||||
def __init__(self):
|
||||
# List of exe names with expected descriptions
|
||||
self.expected_descriptions = {
|
||||
"utilman.exe": ["Utility Manager"],
|
||||
"narrator.exe": ["Screen Reader", "Narrator"],
|
||||
"sethc.exe": ["Accessibility shortcut keys"],
|
||||
"osk.exe": ["Accessibility On-Screen Keyboard"],
|
||||
"magnify.exe": ["Microsoft Screen Magnifier"],
|
||||
"EaseOfAccessDialog.exe": ["Ease of Access Dialog Host"],
|
||||
"voiceaccess.exe": ["Voice access"], # Only on Windows 11 / Server 2025+
|
||||
"displayswitch.exe": ["Display Switch"],
|
||||
"atbroker.exe": ["Windows Assistive Technology Manager", "Transitions Accessible technologies between desktops"],
|
||||
}
|
||||
|
||||
# If description matches one of these it's almost certainly backdoored
|
||||
self.backdoor_descriptions = [
|
||||
"Windows Command Processor",
|
||||
"Windows PowerShell"
|
||||
]
|
||||
|
||||
def options(self, context, module_options):
|
||||
"""No options available"""
|
||||
|
||||
def get_description(self, binary_data):
|
||||
# Extract the file description from version info
|
||||
try:
|
||||
pe = pefile.PE(data=binary_data, fast_load=True)
|
||||
pe.parse_data_directories(directories=[pefile.DIRECTORY_ENTRY["IMAGE_DIRECTORY_ENTRY_RESOURCE"]])
|
||||
for fileinfo in pe.FileInfo:
|
||||
for entry in fileinfo:
|
||||
if entry.Key.decode() == "StringFileInfo":
|
||||
for st in entry.StringTable:
|
||||
desc = st.entries.get(b"FileDescription")
|
||||
if desc:
|
||||
return desc.decode().strip()
|
||||
except Exception as e:
|
||||
self.context.log.debug(f"Failed to extract PE info: {e}")
|
||||
return None
|
||||
|
||||
def on_admin_login(self, context, connection):
|
||||
target_path = "\\Windows\\System32"
|
||||
tampered = False
|
||||
|
||||
for exe, expected_descs in self.expected_descriptions.items():
|
||||
try:
|
||||
# Grab the binary from the share
|
||||
buf = BytesIO()
|
||||
connection.conn.getFile("C$", f"{target_path}\\{exe}", buf.write)
|
||||
binary = buf.getvalue()
|
||||
|
||||
# Extract and normalize the file description
|
||||
file_desc = self.get_description(binary)
|
||||
if not file_desc:
|
||||
context.log.fail(f"{exe}: could not extract FileDescription")
|
||||
continue
|
||||
|
||||
# Check if the description is as expected
|
||||
if file_desc not in expected_descs:
|
||||
tampered = True
|
||||
if file_desc in self.backdoor_descriptions:
|
||||
context.log.highlight(f"BACKDOOR DETECTED: {exe} has FileDescription '{file_desc}'")
|
||||
else:
|
||||
if len(expected_descs) == 1:
|
||||
expected_str = f"'{expected_descs[0]}'"
|
||||
else:
|
||||
expected_str = ", ".join(f"'{d}'" for d in expected_descs)
|
||||
expected_str = f"one of: {expected_str}"
|
||||
context.log.highlight(f"SUSPICIOUS: {exe} has unexpected FileDescription '{file_desc}' (expected {expected_str})")
|
||||
except Exception as e:
|
||||
context.log.debug(f"Failed to process {exe}: {e}")
|
||||
|
||||
if not tampered:
|
||||
context.log.display("All lock screen executable descriptions are consistent with the expected values")
|
||||
@@ -79,8 +79,8 @@ class NXCModule:
|
||||
|
||||
pwned_users = 0
|
||||
for user in rslts.users:
|
||||
if user.nthash:
|
||||
context.log.highlight(f"{user.domain}\\{user.name} {user.nthash}")
|
||||
if user.nt_hash:
|
||||
context.log.highlight(f"{user.domain}\\{user.name} {user.nt_hash}")
|
||||
self.process_credentials(connection, context, user)
|
||||
pwned_users += 1
|
||||
|
||||
@@ -96,7 +96,7 @@ class NXCModule:
|
||||
"hash",
|
||||
user.domain,
|
||||
user.name,
|
||||
user.nthash,
|
||||
user.nt_hash,
|
||||
pillaged_from=host,
|
||||
)
|
||||
add_user_bh(user.name, user.domain, context.log, connection.config)
|
||||
|
||||
Generated
+16
-4
@@ -1252,14 +1252,14 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "masky"
|
||||
version = "0.2.0"
|
||||
version = "0.2.1"
|
||||
description = "Python library with CLI allowing to remotely dump domain user credentials via an ADCS"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "masky-0.2.0-py3-none-any.whl", hash = "sha256:04f29988e659bd265bf393c833ee473cfd16bf8a32ffdeaacfbefe8f466f53ab"},
|
||||
{file = "masky-0.2.0.tar.gz", hash = "sha256:fc0a99086da54e1cf91bb5e9c809aa311ea1519f10a3b6faf6d8e0a47c471ec9"},
|
||||
{file = "masky-0.2.1-py3-none-any.whl", hash = "sha256:bcf545f5e2b762fc49df2f29aa934498bb5dd37ba0a28e0c1683acee2e5ef14b"},
|
||||
{file = "masky-0.2.1.tar.gz", hash = "sha256:973615bebbd9455a1c2a81a6cec626a2032ec2209f242e3e69a6226e4b60b6fc"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@@ -1436,6 +1436,18 @@ all = ["gssapi (>=1.4.1) ; platform_system != \"Windows\"", "invoke (>=2.0)", "p
|
||||
gssapi = ["gssapi (>=1.4.1) ; platform_system != \"Windows\"", "pyasn1 (>=0.1.7)", "pywin32 (>=2.1.8) ; platform_system == \"Windows\""]
|
||||
invoke = ["invoke (>=2.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "pefile"
|
||||
version = "2024.8.26"
|
||||
description = "Python PE parsing module"
|
||||
optional = false
|
||||
python-versions = ">=3.6.0"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "pefile-2024.8.26-py3-none-any.whl", hash = "sha256:76f8b485dcd3b1bb8166f1128d395fa3d87af26360c2358fb75b80019b957c6f"},
|
||||
{file = "pefile-2024.8.26.tar.gz", hash = "sha256:3ff6c5d8b43e8c37bb6e6dd5085658d658a7a0bdcd20b6a07b1fcfc1c4e9d632"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pillow"
|
||||
version = "11.1.0"
|
||||
@@ -2463,4 +2475,4 @@ files = [
|
||||
[metadata]
|
||||
lock-version = "2.1"
|
||||
python-versions = ">=3.10,<4.0"
|
||||
content-hash = "e02f61c9bb3bccd22fc51f90a9e09552afe50cb309155959dbb3ff06fea4d736"
|
||||
content-hash = "d5a080dff08c7835da466af3c6dbe210650500a127b732f92fb1a417fee228f2"
|
||||
|
||||
+2
-1
@@ -26,10 +26,11 @@ dependencies = [
|
||||
"dsinternals>=1.2.4",
|
||||
"jwt>=1.3.1",
|
||||
"lsassy>=3.1.11",
|
||||
"masky>=0.2.0",
|
||||
"masky>=0.2.1",
|
||||
"minikerberos>=0.4.1",
|
||||
"neo4j>=5.0.0",
|
||||
"paramiko>=3.3.1",
|
||||
"pefile (>=2024.8.26,<2025.0.0)",
|
||||
"pyasn1-modules>=0.3.0",
|
||||
"pylnk3>=0.4.3",
|
||||
"pypsrp>=0.8.1",
|
||||
|
||||
@@ -168,6 +168,7 @@ netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M get_gpos
|
||||
netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M get_gpos -o NAME="Default" -o FUZZY=True -o DOWNLOAD=False
|
||||
netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M get_gpos -o NAME="Default" -o FUZZY=True -o ALL_PROPS=True -o DOWNLOAD=False
|
||||
netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M get_gpos -o ALL_PROPS=True -o DOWNLOAD=False
|
||||
netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M lockscreendoors
|
||||
# test for multiple modules at once
|
||||
netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M spooler -M petitpotam -M zerologon -M nopac -M enum_av -M enum_dns -M gpp_autologin -M gpp_password -M lsassy -M impersonate -M install_elevated -M ioxidresolver -M ms17-010 -M ntlmv1 -M runasppl -M uac -M webdav -M wifi -M coerce_plus
|
||||
##### SMB Anonymous Auth
|
||||
|
||||
Reference in New Issue
Block a user