From 92ac8be7da93566e42de6f292ed56013e5235ffa Mon Sep 17 00:00:00 2001 From: siouxchief Date: Sat, 17 May 2025 11:45:37 +0100 Subject: [PATCH 1/6] Commit --- nxc/modules/find-computer.py | 2 +- nxc/modules/whoami.py | 58 +++++++++++++++++++++++++----------- 2 files changed, 41 insertions(+), 19 deletions(-) diff --git a/nxc/modules/find-computer.py b/nxc/modules/find-computer.py index fa5dff4c..b227ec0c 100644 --- a/nxc/modules/find-computer.py +++ b/nxc/modules/find-computer.py @@ -35,7 +35,7 @@ class NXCModule: sys.exit(1) def on_login(self, context, connection): - search_filter = f"(&(objectCategory=computer)(&(|(operatingSystem=*{self.TEXT}*))(name=*{self.TEXT}*)))" + search_filter = f"(&(objectCategory=computer)(|(operatingSystem=*{self.TEXT}*)(name=*{self.TEXT}*)))" try: context.log.debug(f"Search Filter={search_filter}") diff --git a/nxc/modules/whoami.py b/nxc/modules/whoami.py index c33bf329..8ec41e37 100644 --- a/nxc/modules/whoami.py +++ b/nxc/modules/whoami.py @@ -28,7 +28,7 @@ class NXCModule: searchFilter=searchFilter, attributes=[ "name", - "sAmAccountName", + "sAMAccountName", "description", "distinguishedName", "pwdLastSet", @@ -36,29 +36,40 @@ class NXCModule: "lastLogon", "userAccountControl", "servicePrincipalName", + "userPrincipalName", + "mail", "memberOf", ], - sizeLimit=999, + sizeLimit=9999, ) for response in r[0]["attributes"]: if "userAccountControl" in str(response["type"]): - if str(response["vals"][0]) == "512": - context.log.highlight("Enabled: Yes") - context.log.highlight("Password Never Expires: No") - elif str(response["vals"][0]) == "514": - context.log.highlight("Enabled: No") - context.log.highlight("Password Never Expires: No") - elif str(response["vals"][0]) == "66048": - context.log.highlight("Enabled: Yes") - context.log.highlight("Password Never Expires: Yes") - elif str(response["vals"][0]) == "66050": - context.log.highlight("Enabled: No") - context.log.highlight("Password Never Expires: Yes") + uac_raw = response["vals"][0] + + # Ensure it's a string, then integer + uac = int(uac_raw.decode() if isinstance(uac_raw, bytes) else str(uac_raw)) + + # Flags + ACCOUNTDISABLE = 0x0002 + DONT_EXPIRE_PASSWORD = 0x10000 + + is_disabled = (uac & ACCOUNTDISABLE) != 0 + password_never_expires = (uac & DONT_EXPIRE_PASSWORD) != 0 + + context.log.highlight(f"Enabled: {'No' if is_disabled else 'Yes'}") + context.log.highlight(f"Password Never Expires: {'Yes' if password_never_expires else 'No'}") elif "lastLogon" in str(response["type"]): - if str(response["vals"][0]) == "1601": - context.log.highlight("Last logon: Never") - else: - context.log.highlight(f"Last logon: {response['vals'][0]}") + raw = response["vals"][0] + # Convert from bytes if needed + filetime_str = raw.decode() if isinstance(raw, bytes) else str(raw) + filetime_int = int(filetime_str) + + if filetime_int == 1601: + context.log.highlight("Last logon: Never") + else: + # Convert FILETIME to datetime + dt = datetime.datetime(1601, 1, 1) + datetime.timedelta(microseconds=filetime_int / 10) + context.log.highlight(f"Last logon: {dt.strftime('%Y-%m-%d %H:%M:%S')} UTC") elif "memberOf" in str(response["type"]): for group in response["vals"]: context.log.highlight(f"Member of: {group}") @@ -66,5 +77,16 @@ class NXCModule: context.log.highlight("Service Account Name(s) found - Potentially Kerberoastable user!") for spn in response["vals"]: context.log.highlight(f"Service Account Name: {spn}") + elif "pwdLastSet" in str(response["type"]): + raw = response["vals"][0] + # Convert from bytes if needed + filetime_str = raw.decode() if isinstance(raw, bytes) else str(raw) + filetime_int = int(filetime_str) + + if filetime_int == 0: + context.log.highlight("Password Last Set: Never") + else: + dt = datetime.datetime(1601, 1, 1) + datetime.timedelta(microseconds=filetime_int / 10) + context.log.highlight(f"Password Last Set: {dt.strftime('%Y-%m-%d %H:%M:%S')} UTC") else: context.log.highlight(response["type"] + ": " + response["vals"][0]) From 460c351228f0a8e623987f1b7ac52893dacf26e7 Mon Sep 17 00:00:00 2001 From: David Kennedy <33097451+Cyb3rC3lt@users.noreply.github.com> Date: Sun, 18 May 2025 10:38:31 +0100 Subject: [PATCH 2/6] Updated to use parse_results and import datetime Signed-off-by: David Kennedy <33097451+Cyb3rC3lt@users.noreply.github.com> --- nxc/modules/whoami.py | 119 ++++++++++++++++++++++++++++-------------- 1 file changed, 81 insertions(+), 38 deletions(-) diff --git a/nxc/modules/whoami.py b/nxc/modules/whoami.py index 8ec41e37..eb405877 100644 --- a/nxc/modules/whoami.py +++ b/nxc/modules/whoami.py @@ -1,3 +1,4 @@ +import datetime class NXCModule: """ Basic enumeration of provided user information and privileges @@ -37,56 +38,98 @@ class NXCModule: "userAccountControl", "servicePrincipalName", "userPrincipalName", + "objectSid", "mail", + "badPwdCount", "memberOf", ], - sizeLimit=9999, + sizeLimit=50, ) - for response in r[0]["attributes"]: - if "userAccountControl" in str(response["type"]): - uac_raw = response["vals"][0] - - # Ensure it's a string, then integer - uac = int(uac_raw.decode() if isinstance(uac_raw, bytes) else str(uac_raw)) + try: + resp_parsed = parse_result_attributes(r) + except LDAPFilterSyntaxError as e: + self.logger.fail(f"LDAP Filter Syntax Error: {e}") + return - # Flags - ACCOUNTDISABLE = 0x0002 - DONT_EXPIRE_PASSWORD = 0x10000 + for response in resp_parsed: - is_disabled = (uac & ACCOUNTDISABLE) != 0 - password_never_expires = (uac & DONT_EXPIRE_PASSWORD) != 0 + # Process name + if "name" in response: + context.log.highlight(f"Name: {response['name']}") - context.log.highlight(f"Enabled: {'No' if is_disabled else 'Yes'}") - context.log.highlight(f"Password Never Expires: {'Yes' if password_never_expires else 'No'}") - elif "lastLogon" in str(response["type"]): - raw = response["vals"][0] - # Convert from bytes if needed - filetime_str = raw.decode() if isinstance(raw, bytes) else str(raw) - filetime_int = int(filetime_str) + # Process Description + if "description" in response: + context.log.highlight(f"Description: {response['description']}") - if filetime_int == 1601: - context.log.highlight("Last logon: Never") - else: - # Convert FILETIME to datetime - dt = datetime.datetime(1601, 1, 1) + datetime.timedelta(microseconds=filetime_int / 10) - context.log.highlight(f"Last logon: {dt.strftime('%Y-%m-%d %H:%M:%S')} UTC") - elif "memberOf" in str(response["type"]): - for group in response["vals"]: - context.log.highlight(f"Member of: {group}") - elif "servicePrincipalName" in str(response["type"]): - context.log.highlight("Service Account Name(s) found - Potentially Kerberoastable user!") - for spn in response["vals"]: - context.log.highlight(f"Service Account Name: {spn}") - elif "pwdLastSet" in str(response["type"]): - raw = response["vals"][0] - # Convert from bytes if needed - filetime_str = raw.decode() if isinstance(raw, bytes) else str(raw) + # Process sAMAccountName + if "sAMAccountName" in response: + context.log.highlight(f"sAMAccountName: {response['sAMAccountName']}") + + # Process userAccountControl + if "userAccountControl" in response: + uac = int(response["userAccountControl"]) + ACCOUNTDISABLE = 0x0002 + DONT_EXPIRE_PASSWORD = 0x10000 + is_disabled = (uac & ACCOUNTDISABLE) != 0 + password_never_expires = (uac & DONT_EXPIRE_PASSWORD) != 0 + context.log.highlight(f"Enabled: {'No' if is_disabled else 'Yes'}") + context.log.highlight(f"Password Never Expires: {'Yes' if password_never_expires else 'No'}") + + # Process User PrincipalName + if "userPrincipalName" in response: + context.log.highlight(f"User Principal Name: {response['userPrincipalName']}") + + # Process mail + if "mail" in response: + context.log.highlight(f"Email: {response['mail']}") + + # Process lastLogon + if "lastLogon" in response: + filetime_str = response["lastLogon"] filetime_int = int(filetime_str) + if filetime_int == 0: + context.log.highlight("Last logon: Never") + else: + dt = datetime.datetime(1601, 1, 1) + datetime.timedelta(microseconds=filetime_int / 10) + context.log.highlight(f"Last logon: {dt.strftime('%Y-%m-%d %H:%M:%S')} UTC") + # Process pwdLastSet + if "pwdLastSet" in response: + filetime_str = response["pwdLastSet"] + filetime_int = int(filetime_str) if filetime_int == 0: context.log.highlight("Password Last Set: Never") else: dt = datetime.datetime(1601, 1, 1) + datetime.timedelta(microseconds=filetime_int / 10) context.log.highlight(f"Password Last Set: {dt.strftime('%Y-%m-%d %H:%M:%S')} UTC") - else: - context.log.highlight(response["type"] + ": " + response["vals"][0]) + + # Process Bad Password Count + if "badPwdCount" in response: + context.log.highlight(f"Bad Passwod Count: {response['badPwdCount']}") + + # Process servicePrincipalName + if "servicePrincipalName" in response: + context.log.highlight("Service Account Name(s) found - Potentially Kerberoastable user!") + spns = response["servicePrincipalName"] + if isinstance(spns, list): + for spn in spns: + context.log.highlight(f"Service Account Name: {spn}") + else: + context.log.highlight(f"Service Account Name: {spns}") + + # Process DistinguishedName + if "distinguishedName" in response: + context.log.highlight(f"Distinguished Name: {response['distinguishedName']}") + + # Process memberOf + if "memberOf" in response: + groups = response["memberOf"] + if isinstance(groups, list): + for group in groups: + context.log.highlight(f"Member of: {group}") + else: + context.log.highlight(f"Member of: {groups}") + + # Process User Sid + if "objectSid" in response: + context.log.highlight(f"User SID: {response['objectSid']}") From 07f273f8c43bd8123a7bf0216633e51f30564d86 Mon Sep 17 00:00:00 2001 From: David Kennedy <33097451+Cyb3rC3lt@users.noreply.github.com> Date: Sun, 18 May 2025 11:15:05 +0100 Subject: [PATCH 3/6] Reverted size limit to a large number i.e. 999 Signed-off-by: David Kennedy <33097451+Cyb3rC3lt@users.noreply.github.com> --- nxc/modules/whoami.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nxc/modules/whoami.py b/nxc/modules/whoami.py index eb405877..6c1482a5 100644 --- a/nxc/modules/whoami.py +++ b/nxc/modules/whoami.py @@ -43,7 +43,7 @@ class NXCModule: "badPwdCount", "memberOf", ], - sizeLimit=50, + sizeLimit=999, ) try: resp_parsed = parse_result_attributes(r) From d7333e25d963d5b3b98cbb816c252f808b354e83 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Sun, 18 May 2025 06:45:52 -0400 Subject: [PATCH 4/6] Add missing import --- nxc/modules/whoami.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/nxc/modules/whoami.py b/nxc/modules/whoami.py index 6c1482a5..c9dabb86 100644 --- a/nxc/modules/whoami.py +++ b/nxc/modules/whoami.py @@ -1,4 +1,7 @@ import datetime +from nxc.parsers.ldap_results import parse_result_attributes + + class NXCModule: """ Basic enumeration of provided user information and privileges From c11090e1e624dd3d8684da9220debde64378e473 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Sun, 18 May 2025 06:46:14 -0400 Subject: [PATCH 5/6] Remove unnecessary try&except --- nxc/modules/whoami.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/nxc/modules/whoami.py b/nxc/modules/whoami.py index c9dabb86..00b0dcf5 100644 --- a/nxc/modules/whoami.py +++ b/nxc/modules/whoami.py @@ -48,11 +48,7 @@ class NXCModule: ], sizeLimit=999, ) - try: - resp_parsed = parse_result_attributes(r) - except LDAPFilterSyntaxError as e: - self.logger.fail(f"LDAP Filter Syntax Error: {e}") - return + resp_parsed = parse_result_attributes(r) for response in resp_parsed: From e9d17004d4a4c5636a282ec15322613def4104ce Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Sun, 18 May 2025 06:46:23 -0400 Subject: [PATCH 6/6] Formatting --- nxc/modules/whoami.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/nxc/modules/whoami.py b/nxc/modules/whoami.py index 00b0dcf5..0855e89e 100644 --- a/nxc/modules/whoami.py +++ b/nxc/modules/whoami.py @@ -74,7 +74,7 @@ class NXCModule: context.log.highlight(f"Enabled: {'No' if is_disabled else 'Yes'}") context.log.highlight(f"Password Never Expires: {'Yes' if password_never_expires else 'No'}") - # Process User PrincipalName + # Process User PrincipalName if "userPrincipalName" in response: context.log.highlight(f"User Principal Name: {response['userPrincipalName']}") @@ -116,7 +116,7 @@ class NXCModule: else: context.log.highlight(f"Service Account Name: {spns}") - # Process DistinguishedName + # Process DistinguishedName if "distinguishedName" in response: context.log.highlight(f"Distinguished Name: {response['distinguishedName']}") @@ -128,7 +128,7 @@ class NXCModule: context.log.highlight(f"Member of: {group}") else: context.log.highlight(f"Member of: {groups}") - + # Process User Sid if "objectSid" in response: context.log.highlight(f"User SID: {response['objectSid']}")