From b792021223de35d57dad901b5318235811c75b03 Mon Sep 17 00:00:00 2001 From: E1A Date: Sat, 2 Aug 2025 17:51:23 +0200 Subject: [PATCH 01/18] Added the lockscreendoors module --- nxc/modules/lockscreendoors.py | 90 ++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 nxc/modules/lockscreendoors.py diff --git a/nxc/modules/lockscreendoors.py b/nxc/modules/lockscreendoors.py new file mode 100644 index 00000000..c5508a6b --- /dev/null +++ b/nxc/modules/lockscreendoors.py @@ -0,0 +1,90 @@ +import struct +from io import BytesIO +import pefile + +class NXCModule: + 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", + "sethc.exe": "Accessibility shortcut keys", + "osk.exe": "Accessibility On-Screen Keyboard", + "xwizard.exe": "Extensible Wizards Host Process", + "sndvol.exe": "Volume Mixer", + "ctfmon.exe": "CTF Loader", + "displayswitch.exe": "Display Switch", + "magnify.exe": "Microsoft Screen Magnifier", + "atbroker.exe": "Windows Assistive Technology Manager", + "EaseOfAccessDialog.exe": "Ease of Access Dialog Host" + } + + # 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): + pass + + 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(errors="ignore") + except Exception: + return None + return None + + def on_admin_login(self, context, connection): + target_path = "Windows/System32" + tampered = False + readable_file_found = False + + for exe, expected_desc 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() + readable_file_found = True + + # 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 + + file_desc = file_desc.strip() + + # check if the description is what we expect + if file_desc != expected_desc: + tampered = True + if file_desc in self.backdoor_descriptions: + context.log.fail(f"BACKDOOR DETECTED: {exe} has FileDescription '{file_desc}'") + else: + context.log.fail(f"SUSPICIOUS: {exe} has unexpected FileDescription '{file_desc}' (expected '{expected_desc}')") + + except Exception: + # silently skip if we can't access the file + continue + + if not readable_file_found: + return + + if not tampered: + context.log.display("All lock screen executable descriptions are consistent with the expected values") \ No newline at end of file From 60c3afd20d38242b8f6bae5a92178570e7cd31f5 Mon Sep 17 00:00:00 2001 From: E1A Date: Sat, 2 Aug 2025 17:53:52 +0200 Subject: [PATCH 02/18] added credits --- nxc/modules/lockscreendoors.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/nxc/modules/lockscreendoors.py b/nxc/modules/lockscreendoors.py index c5508a6b..e5cfc921 100644 --- a/nxc/modules/lockscreendoors.py +++ b/nxc/modules/lockscreendoors.py @@ -3,6 +3,10 @@ from io import BytesIO import pefile class NXCModule: + """ + Module by @E1A + """ + name = "lockscreendoors" description = "Detect Windows lock screen backdoors by checking FileDescriptions of accessibility binaries." supported_protocols = ["smb"] From 5f23eadc3a6a7776fd5ef137b232eb2812ed74b2 Mon Sep 17 00:00:00 2001 From: E1A Date: Sat, 2 Aug 2025 18:07:53 +0200 Subject: [PATCH 03/18] ruff changes --- nxc/modules/lockscreendoors.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/nxc/modules/lockscreendoors.py b/nxc/modules/lockscreendoors.py index e5cfc921..b591b832 100644 --- a/nxc/modules/lockscreendoors.py +++ b/nxc/modules/lockscreendoors.py @@ -1,11 +1,9 @@ -import struct from io import BytesIO import pefile + class NXCModule: - """ - Module by @E1A - """ + """Module by @E1A""" name = "lockscreendoors" description = "Detect Windows lock screen backdoors by checking FileDescriptions of accessibility binaries." @@ -41,11 +39,11 @@ class NXCModule: try: pe = pefile.PE(data=binary_data, fast_load=True) pe.parse_data_directories( - directories=[pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_RESOURCE']] + directories=[pefile.DIRECTORY_ENTRY["IMAGE_DIRECTORY_ENTRY_RESOURCE"]] ) for fileinfo in pe.FileInfo: for entry in fileinfo: - if entry.Key.decode() == 'StringFileInfo': + if entry.Key.decode() == "StringFileInfo": for st in entry.StringTable: desc = st.entries.get(b"FileDescription") if desc: @@ -91,4 +89,4 @@ class NXCModule: return if not tampered: - context.log.display("All lock screen executable descriptions are consistent with the expected values") \ No newline at end of file + context.log.display("All lock screen executable descriptions are consistent with the expected values") From c67d43310c04a9e4be0c2fe2681fbed016afa155 Mon Sep 17 00:00:00 2001 From: E1A Date: Sat, 2 Aug 2025 18:30:56 +0200 Subject: [PATCH 04/18] Added lockscreendoors --- tests/e2e_commands.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/e2e_commands.txt b/tests/e2e_commands.txt index bd6ac47d..8ce0fda0 100644 --- a/tests/e2e_commands.txt +++ b/tests/e2e_commands.txt @@ -287,3 +287,4 @@ netexec nfs TARGET_HOST -u "" -p "" --shares netexec nfs TARGET_HOST -u "" -p "" --enum-shares netexec nfs TARGET_HOST -u "" -p "" --get-file /NFStest/test/test.txt ../test.txt netexec nfs TARGET_HOST -u "" -p "" --put-file ../test.txt /NFStest/test +netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD -M lockscreendoors \ No newline at end of file From 324dc0ccdd95476dc69ba25d679df40a4f51b33e Mon Sep 17 00:00:00 2001 From: E1A Date: Mon, 4 Aug 2025 09:57:04 +0200 Subject: [PATCH 05/18] Fixed issues for PR --- nxc/modules/lockscreendoors.py | 24 ++++++++++-------------- poetry.lock | 20 ++++++++++++++++---- pyproject.toml | 1 + tests/e2e_commands.txt | 4 ++-- 4 files changed, 29 insertions(+), 20 deletions(-) diff --git a/nxc/modules/lockscreendoors.py b/nxc/modules/lockscreendoors.py index b591b832..12754fdc 100644 --- a/nxc/modules/lockscreendoors.py +++ b/nxc/modules/lockscreendoors.py @@ -10,7 +10,7 @@ class NXCModule: supported_protocols = ["smb"] def __init__(self): - # list of exe names with expected descriptions + # List of exe names with expected descriptions self.expected_descriptions = { "utilman.exe": "Utility Manager", "narrator.exe": "Screen Reader", @@ -25,7 +25,7 @@ class NXCModule: "EaseOfAccessDialog.exe": "Ease of Access Dialog Host" } - # if description matches one of these it's almost certainly backdoored + # If description matches one of these it's almost certainly backdoored self.backdoor_descriptions = [ "Windows Command Processor", "Windows PowerShell" @@ -35,19 +35,17 @@ class NXCModule: pass def get_description(self, binary_data): - # extract the file description from version info + # 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"]] - ) + 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(errors="ignore") + return desc.decode(errors="ignore").strip() except Exception: return None return None @@ -59,21 +57,19 @@ class NXCModule: for exe, expected_desc in self.expected_descriptions.items(): try: - # grab the binary from the share + # Grab the binary from the share buf = BytesIO() connection.conn.getFile("C$", f"{target_path}/{exe}", buf.write) binary = buf.getvalue() readable_file_found = True - # extract and normalize the file description + # 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 - file_desc = file_desc.strip() - - # check if the description is what we expect + # Check if the description is as expected if file_desc != expected_desc: tampered = True if file_desc in self.backdoor_descriptions: @@ -82,11 +78,11 @@ class NXCModule: context.log.fail(f"SUSPICIOUS: {exe} has unexpected FileDescription '{file_desc}' (expected '{expected_desc}')") except Exception: - # silently skip if we can't access the file + # Silently skip if we can't access the file continue if not readable_file_found: return if not tampered: - context.log.display("All lock screen executable descriptions are consistent with the expected values") + context.log.display("All lock screen executable descriptions are consistent with the expected values") \ No newline at end of file diff --git a/poetry.lock b/poetry.lock index 31c93f17..ce36fb66 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.1.3 and should not be changed by hand. [[package]] name = "aardwolf" @@ -687,7 +687,7 @@ description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" groups = ["dev"] -markers = "python_version < \"3.11\"" +markers = "python_version == \"3.10\"" files = [ {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"}, {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"}, @@ -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" @@ -2301,7 +2313,7 @@ description = "A lil' TOML parser" optional = false python-versions = ">=3.8" groups = ["dev"] -markers = "python_version < \"3.11\"" +markers = "python_version == \"3.10\"" files = [ {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}, {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"}, @@ -2463,4 +2475,4 @@ files = [ [metadata] lock-version = "2.1" python-versions = ">=3.10,<4.0" -content-hash = "e02f61c9bb3bccd22fc51f90a9e09552afe50cb309155959dbb3ff06fea4d736" +content-hash = "4bd768847594e75600f1e73f18c2dbc9064226f8c385b1b76fe4236cc97a23ad" diff --git a/pyproject.toml b/pyproject.toml index f3d43c7e..ecc5edad 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -46,6 +46,7 @@ dependencies = [ "impacket @ git+https://github.com/Pennyw0rth/impacket.git", "oscrypto @ git+https://github.com/wbond/oscrypto", "pynfsclient @ git+https://github.com/Pennyw0rth/NfsClient", + "pefile (>=2024.8.26,<2025.0.0)", ] [project.urls] diff --git a/tests/e2e_commands.txt b/tests/e2e_commands.txt index 8ce0fda0..86175aba 100644 --- a/tests/e2e_commands.txt +++ b/tests/e2e_commands.txt @@ -160,6 +160,7 @@ netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M winscp netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M zerologon #netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M change-password -o NEWPASS=Password123 #netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M change-password -o NEWNTHASH=58A478135A93AC3BF058A5EA0E8FDB71 +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 @@ -286,5 +287,4 @@ netexec ftp TARGET_HOST -u TEST_USER_FILE -p TEST_PASSWORD_FILE netexec nfs TARGET_HOST -u "" -p "" --shares netexec nfs TARGET_HOST -u "" -p "" --enum-shares netexec nfs TARGET_HOST -u "" -p "" --get-file /NFStest/test/test.txt ../test.txt -netexec nfs TARGET_HOST -u "" -p "" --put-file ../test.txt /NFStest/test -netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD -M lockscreendoors \ No newline at end of file +netexec nfs TARGET_HOST -u "" -p "" --put-file ../test.txt /NFStest/test \ No newline at end of file From d1d086400230fef89f389762ce666f974bdcfe20 Mon Sep 17 00:00:00 2001 From: E1A Date: Mon, 4 Aug 2025 16:23:02 +0200 Subject: [PATCH 06/18] Added and removed lock screen executables Changed context.log.fail to context.log.highlight when if tampered = true --- nxc/modules/lockscreendoors.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/nxc/modules/lockscreendoors.py b/nxc/modules/lockscreendoors.py index 12754fdc..f9e4d277 100644 --- a/nxc/modules/lockscreendoors.py +++ b/nxc/modules/lockscreendoors.py @@ -3,7 +3,10 @@ import pefile class NXCModule: - """Module by @E1A""" + """ + Module for detecting Windows lock screen backdoors + Module by @E1A + """ name = "lockscreendoors" description = "Detect Windows lock screen backdoors by checking FileDescriptions of accessibility binaries." @@ -16,13 +19,11 @@ class NXCModule: "narrator.exe": "Screen Reader", "sethc.exe": "Accessibility shortcut keys", "osk.exe": "Accessibility On-Screen Keyboard", - "xwizard.exe": "Extensible Wizards Host Process", - "sndvol.exe": "Volume Mixer", - "ctfmon.exe": "CTF Loader", - "displayswitch.exe": "Display Switch", "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", - "EaseOfAccessDialog.exe": "Ease of Access Dialog Host" } # If description matches one of these it's almost certainly backdoored @@ -73,12 +74,12 @@ class NXCModule: if file_desc != expected_desc: tampered = True if file_desc in self.backdoor_descriptions: - context.log.fail(f"BACKDOOR DETECTED: {exe} has FileDescription '{file_desc}'") + context.log.highlight(f"BACKDOOR DETECTED: {exe} has FileDescription '{file_desc}'") else: - context.log.fail(f"SUSPICIOUS: {exe} has unexpected FileDescription '{file_desc}' (expected '{expected_desc}')") + context.log.highlight(f"SUSPICIOUS: {exe} has unexpected FileDescription '{file_desc}' (expected '{expected_desc}')") except Exception: - # Silently skip if we can't access the file + # Silently skip if the file is not readable or doesn't exist continue if not readable_file_found: From a5fa60b62a6e0b99bca9b6baaeaba7958d6d89d4 Mon Sep 17 00:00:00 2001 From: E1A Date: Wed, 6 Aug 2025 09:40:43 +0200 Subject: [PATCH 07/18] Fixed support for Win server 2008 and multiple descriptions --- nxc/modules/lockscreendoors.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/nxc/modules/lockscreendoors.py b/nxc/modules/lockscreendoors.py index f9e4d277..891fa0ef 100644 --- a/nxc/modules/lockscreendoors.py +++ b/nxc/modules/lockscreendoors.py @@ -15,15 +15,15 @@ class NXCModule: def __init__(self): # List of exe names with expected descriptions self.expected_descriptions = { - "utilman.exe": "Utility Manager", - "narrator.exe": "Screen Reader", - "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", + "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 @@ -56,7 +56,7 @@ class NXCModule: tampered = False readable_file_found = False - for exe, expected_desc in self.expected_descriptions.items(): + for exe, expected_descs in self.expected_descriptions.items(): try: # Grab the binary from the share buf = BytesIO() @@ -71,12 +71,12 @@ class NXCModule: continue # Check if the description is as expected - if file_desc != expected_desc: + 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: - context.log.highlight(f"SUSPICIOUS: {exe} has unexpected FileDescription '{file_desc}' (expected '{expected_desc}')") + context.log.highlight(f"SUSPICIOUS: {exe} has unexpected FileDescription '{file_desc}' (expected '{expected_descs}')") except Exception: # Silently skip if the file is not readable or doesn't exist From a44fa60223b17c7fd2ba5378025d9d05e404e39b Mon Sep 17 00:00:00 2001 From: E1A Date: Wed, 6 Aug 2025 09:52:40 +0200 Subject: [PATCH 08/18] format expected descriptions as plain string in output --- nxc/modules/lockscreendoors.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/nxc/modules/lockscreendoors.py b/nxc/modules/lockscreendoors.py index 891fa0ef..e5debb85 100644 --- a/nxc/modules/lockscreendoors.py +++ b/nxc/modules/lockscreendoors.py @@ -76,7 +76,14 @@ class NXCModule: if file_desc in self.backdoor_descriptions: context.log.highlight(f"BACKDOOR DETECTED: {exe} has FileDescription '{file_desc}'") else: - context.log.highlight(f"SUSPICIOUS: {exe} has unexpected FileDescription '{file_desc}' (expected '{expected_descs}')") + 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: # Silently skip if the file is not readable or doesn't exist From 05fa63d1beca90d80f62ccbf16896d6613846f9d Mon Sep 17 00:00:00 2001 From: Z4kSec Date: Sat, 16 Aug 2025 13:02:13 +0000 Subject: [PATCH 09/18] Update Masky module to 0.2.1 --- nxc/modules/masky.py | 6 +++--- pyproject.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/nxc/modules/masky.py b/nxc/modules/masky.py index 1acb509a..711ab1ea 100644 --- a/nxc/modules/masky.py +++ b/nxc/modules/masky.py @@ -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) diff --git a/pyproject.toml b/pyproject.toml index cdbad433..2d62a659 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -26,7 +26,7 @@ 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", From 8bb47665e783c05d9301d2dbe76ebdd8260621f8 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Sat, 16 Aug 2025 09:22:20 -0400 Subject: [PATCH 10/18] Formatting --- tests/e2e_commands.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e_commands.txt b/tests/e2e_commands.txt index 86175aba..90117711 100644 --- a/tests/e2e_commands.txt +++ b/tests/e2e_commands.txt @@ -287,4 +287,4 @@ netexec ftp TARGET_HOST -u TEST_USER_FILE -p TEST_PASSWORD_FILE netexec nfs TARGET_HOST -u "" -p "" --shares netexec nfs TARGET_HOST -u "" -p "" --enum-shares netexec nfs TARGET_HOST -u "" -p "" --get-file /NFStest/test/test.txt ../test.txt -netexec nfs TARGET_HOST -u "" -p "" --put-file ../test.txt /NFStest/test \ No newline at end of file +netexec nfs TARGET_HOST -u "" -p "" --put-file ../test.txt /NFStest/test From 9ae0aecc331e99ee9a8d449844d2dd4df2e06bd7 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Sat, 16 Aug 2025 09:25:10 -0400 Subject: [PATCH 11/18] Formatting --- nxc/modules/lockscreendoors.py | 2 +- poetry.lock | 2 +- pyproject.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/nxc/modules/lockscreendoors.py b/nxc/modules/lockscreendoors.py index e5debb85..6b54a5e2 100644 --- a/nxc/modules/lockscreendoors.py +++ b/nxc/modules/lockscreendoors.py @@ -93,4 +93,4 @@ class NXCModule: return if not tampered: - context.log.display("All lock screen executable descriptions are consistent with the expected values") \ No newline at end of file + context.log.display("All lock screen executable descriptions are consistent with the expected values") diff --git a/poetry.lock b/poetry.lock index ce36fb66..c3331a3c 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2475,4 +2475,4 @@ files = [ [metadata] lock-version = "2.1" python-versions = ">=3.10,<4.0" -content-hash = "4bd768847594e75600f1e73f18c2dbc9064226f8c385b1b76fe4236cc97a23ad" +content-hash = "668295ac997900510e1f7162df978afc0de26fa32354eea7918ea7fe054e172d" diff --git a/pyproject.toml b/pyproject.toml index 559327b2..cd5e9744 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,6 +30,7 @@ dependencies = [ "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", @@ -46,7 +47,6 @@ dependencies = [ "impacket @ git+https://github.com/Pennyw0rth/impacket.git", "oscrypto @ git+https://github.com/wbond/oscrypto", "pynfsclient @ git+https://github.com/Pennyw0rth/NfsClient", - "pefile (>=2024.8.26,<2025.0.0)", ] [project.urls] From 4546db892a79ce8aa84b9e74cbf3345c4a4c4706 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Sat, 16 Aug 2025 09:32:51 -0400 Subject: [PATCH 12/18] Add debug log on parsing errors --- nxc/modules/lockscreendoors.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/nxc/modules/lockscreendoors.py b/nxc/modules/lockscreendoors.py index 6b54a5e2..a0d7ba1f 100644 --- a/nxc/modules/lockscreendoors.py +++ b/nxc/modules/lockscreendoors.py @@ -46,9 +46,9 @@ class NXCModule: for st in entry.StringTable: desc = st.entries.get(b"FileDescription") if desc: - return desc.decode(errors="ignore").strip() - except Exception: - return None + 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): From da7cb789428dd0e8d2d2677674b09dc6431be333 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Sat, 16 Aug 2025 09:33:13 -0400 Subject: [PATCH 13/18] Formatting and remove unnecessary check --- nxc/modules/lockscreendoors.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/nxc/modules/lockscreendoors.py b/nxc/modules/lockscreendoors.py index a0d7ba1f..ad34e61b 100644 --- a/nxc/modules/lockscreendoors.py +++ b/nxc/modules/lockscreendoors.py @@ -52,17 +52,15 @@ class NXCModule: return None def on_admin_login(self, context, connection): - target_path = "Windows/System32" + target_path = "\\Windows\\System32" tampered = False - readable_file_found = 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) + connection.conn.getFile("C$", f"{target_path}\\{exe}", buf.write) binary = buf.getvalue() - readable_file_found = True # Extract and normalize the file description file_desc = self.get_description(binary) @@ -89,8 +87,5 @@ class NXCModule: # Silently skip if the file is not readable or doesn't exist continue - if not readable_file_found: - return - if not tampered: context.log.display("All lock screen executable descriptions are consistent with the expected values") From db665c096a069e1a5de0cd8d492c5eb67884ecc0 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Sat, 16 Aug 2025 09:44:26 -0400 Subject: [PATCH 14/18] Don't skip silently, bug provide a debug log --- nxc/modules/lockscreendoors.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/nxc/modules/lockscreendoors.py b/nxc/modules/lockscreendoors.py index ad34e61b..4a5450ea 100644 --- a/nxc/modules/lockscreendoors.py +++ b/nxc/modules/lockscreendoors.py @@ -79,13 +79,9 @@ class NXCModule: 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: - # Silently skip if the file is not readable or doesn't exist - continue + 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") From b06316fde082b4f7c93cbe86ffe6103539eb371b Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Sat, 16 Aug 2025 09:47:03 -0400 Subject: [PATCH 15/18] Add 'No options available' to the module and the example module --- nxc/modules/example_module.py | 1 + nxc/modules/lockscreendoors.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/nxc/modules/example_module.py b/nxc/modules/example_module.py index 7cf73137..b467c26d 100644 --- a/nxc/modules/example_module.py +++ b/nxc/modules/example_module.py @@ -18,6 +18,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. diff --git a/nxc/modules/lockscreendoors.py b/nxc/modules/lockscreendoors.py index 4a5450ea..f4005b3c 100644 --- a/nxc/modules/lockscreendoors.py +++ b/nxc/modules/lockscreendoors.py @@ -33,7 +33,7 @@ class NXCModule: ] def options(self, context, module_options): - pass + """No options available""" def get_description(self, binary_data): # Extract the file description from version info From 3a580c70083a184b26ea2572f0a09d5203179945 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Sat, 16 Aug 2025 16:14:32 -0400 Subject: [PATCH 16/18] Update masky --- poetry.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/poetry.lock b/poetry.lock index 59646b80..44d38753 100644 --- a/poetry.lock +++ b/poetry.lock @@ -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] From 88684dc1754d18d13c6843566ad437b58a9f82a5 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Sat, 16 Aug 2025 16:16:51 -0400 Subject: [PATCH 17/18] Fix lock file --- poetry.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/poetry.lock b/poetry.lock index 44d38753..5980d2b7 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2463,4 +2463,4 @@ files = [ [metadata] lock-version = "2.1" python-versions = ">=3.10,<4.0" -content-hash = "e02f61c9bb3bccd22fc51f90a9e09552afe50cb309155959dbb3ff06fea4d736" +content-hash = "e99f88534ebb145b7fadef433b4eb72132b4cb6ef012bc4132c69b75df348055" From 441badbcedb1119aa04e082a962f4e5e71cadd22 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Sat, 16 Aug 2025 16:17:48 -0400 Subject: [PATCH 18/18] Fix lock file --- poetry.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/poetry.lock b/poetry.lock index 2b99cfd8..75c01e8f 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2475,4 +2475,4 @@ files = [ [metadata] lock-version = "2.1" python-versions = ">=3.10,<4.0" -content-hash = "668295ac997900510e1f7162df978afc0de26fa32354eea7918ea7fe054e172d" +content-hash = "d5a080dff08c7835da466af3c6dbe210650500a127b732f92fb1a417fee228f2"