|
| 1 | +import datetime |
| 2 | +from nxc.parsers.ldap_results import parse_result_attributes |
| 3 | + |
| 4 | + |
1 | 5 | class NXCModule: |
2 | 6 | """ |
3 | 7 | Basic enumeration of provided user information and privileges |
@@ -28,43 +32,103 @@ def on_login(self, context, connection): |
28 | 32 | searchFilter=searchFilter, |
29 | 33 | attributes=[ |
30 | 34 | "name", |
31 | | - "sAmAccountName", |
| 35 | + "sAMAccountName", |
32 | 36 | "description", |
33 | 37 | "distinguishedName", |
34 | 38 | "pwdLastSet", |
35 | 39 | "logonCount", |
36 | 40 | "lastLogon", |
37 | 41 | "userAccountControl", |
38 | 42 | "servicePrincipalName", |
| 43 | + "userPrincipalName", |
| 44 | + "objectSid", |
| 45 | + "mail", |
| 46 | + "badPwdCount", |
39 | 47 | "memberOf", |
40 | 48 | ], |
41 | 49 | sizeLimit=999, |
42 | 50 | ) |
43 | | - for response in r[0]["attributes"]: |
44 | | - if "userAccountControl" in str(response["type"]): |
45 | | - if str(response["vals"][0]) == "512": |
46 | | - context.log.highlight("Enabled: Yes") |
47 | | - context.log.highlight("Password Never Expires: No") |
48 | | - elif str(response["vals"][0]) == "514": |
49 | | - context.log.highlight("Enabled: No") |
50 | | - context.log.highlight("Password Never Expires: No") |
51 | | - elif str(response["vals"][0]) == "66048": |
52 | | - context.log.highlight("Enabled: Yes") |
53 | | - context.log.highlight("Password Never Expires: Yes") |
54 | | - elif str(response["vals"][0]) == "66050": |
55 | | - context.log.highlight("Enabled: No") |
56 | | - context.log.highlight("Password Never Expires: Yes") |
57 | | - elif "lastLogon" in str(response["type"]): |
58 | | - if str(response["vals"][0]) == "1601": |
| 51 | + resp_parsed = parse_result_attributes(r) |
| 52 | + |
| 53 | + for response in resp_parsed: |
| 54 | + |
| 55 | + # Process name |
| 56 | + if "name" in response: |
| 57 | + context.log.highlight(f"Name: {response['name']}") |
| 58 | + |
| 59 | + # Process Description |
| 60 | + if "description" in response: |
| 61 | + context.log.highlight(f"Description: {response['description']}") |
| 62 | + |
| 63 | + # Process sAMAccountName |
| 64 | + if "sAMAccountName" in response: |
| 65 | + context.log.highlight(f"sAMAccountName: {response['sAMAccountName']}") |
| 66 | + |
| 67 | + # Process userAccountControl |
| 68 | + if "userAccountControl" in response: |
| 69 | + uac = int(response["userAccountControl"]) |
| 70 | + ACCOUNTDISABLE = 0x0002 |
| 71 | + DONT_EXPIRE_PASSWORD = 0x10000 |
| 72 | + is_disabled = (uac & ACCOUNTDISABLE) != 0 |
| 73 | + password_never_expires = (uac & DONT_EXPIRE_PASSWORD) != 0 |
| 74 | + context.log.highlight(f"Enabled: {'No' if is_disabled else 'Yes'}") |
| 75 | + context.log.highlight(f"Password Never Expires: {'Yes' if password_never_expires else 'No'}") |
| 76 | + |
| 77 | + # Process User PrincipalName |
| 78 | + if "userPrincipalName" in response: |
| 79 | + context.log.highlight(f"User Principal Name: {response['userPrincipalName']}") |
| 80 | + |
| 81 | + # Process mail |
| 82 | + if "mail" in response: |
| 83 | + context.log.highlight(f"Email: {response['mail']}") |
| 84 | + |
| 85 | + # Process lastLogon |
| 86 | + if "lastLogon" in response: |
| 87 | + filetime_str = response["lastLogon"] |
| 88 | + filetime_int = int(filetime_str) |
| 89 | + if filetime_int == 0: |
59 | 90 | context.log.highlight("Last logon: Never") |
60 | 91 | else: |
61 | | - context.log.highlight(f"Last logon: {response['vals'][0]}") |
62 | | - elif "memberOf" in str(response["type"]): |
63 | | - for group in response["vals"]: |
64 | | - context.log.highlight(f"Member of: {group}") |
65 | | - elif "servicePrincipalName" in str(response["type"]): |
| 92 | + dt = datetime.datetime(1601, 1, 1) + datetime.timedelta(microseconds=filetime_int / 10) |
| 93 | + context.log.highlight(f"Last logon: {dt.strftime('%Y-%m-%d %H:%M:%S')} UTC") |
| 94 | + |
| 95 | + # Process pwdLastSet |
| 96 | + if "pwdLastSet" in response: |
| 97 | + filetime_str = response["pwdLastSet"] |
| 98 | + filetime_int = int(filetime_str) |
| 99 | + if filetime_int == 0: |
| 100 | + context.log.highlight("Password Last Set: Never") |
| 101 | + else: |
| 102 | + dt = datetime.datetime(1601, 1, 1) + datetime.timedelta(microseconds=filetime_int / 10) |
| 103 | + context.log.highlight(f"Password Last Set: {dt.strftime('%Y-%m-%d %H:%M:%S')} UTC") |
| 104 | + |
| 105 | + # Process Bad Password Count |
| 106 | + if "badPwdCount" in response: |
| 107 | + context.log.highlight(f"Bad Passwod Count: {response['badPwdCount']}") |
| 108 | + |
| 109 | + # Process servicePrincipalName |
| 110 | + if "servicePrincipalName" in response: |
66 | 111 | context.log.highlight("Service Account Name(s) found - Potentially Kerberoastable user!") |
67 | | - for spn in response["vals"]: |
68 | | - context.log.highlight(f"Service Account Name: {spn}") |
69 | | - else: |
70 | | - context.log.highlight(response["type"] + ": " + response["vals"][0]) |
| 112 | + spns = response["servicePrincipalName"] |
| 113 | + if isinstance(spns, list): |
| 114 | + for spn in spns: |
| 115 | + context.log.highlight(f"Service Account Name: {spn}") |
| 116 | + else: |
| 117 | + context.log.highlight(f"Service Account Name: {spns}") |
| 118 | + |
| 119 | + # Process DistinguishedName |
| 120 | + if "distinguishedName" in response: |
| 121 | + context.log.highlight(f"Distinguished Name: {response['distinguishedName']}") |
| 122 | + |
| 123 | + # Process memberOf |
| 124 | + if "memberOf" in response: |
| 125 | + groups = response["memberOf"] |
| 126 | + if isinstance(groups, list): |
| 127 | + for group in groups: |
| 128 | + context.log.highlight(f"Member of: {group}") |
| 129 | + else: |
| 130 | + context.log.highlight(f"Member of: {groups}") |
| 131 | + |
| 132 | + # Process User Sid |
| 133 | + if "objectSid" in response: |
| 134 | + context.log.highlight(f"User SID: {response['objectSid']}") |
0 commit comments