mirror of
https://github.com/Pennyw0rth/NetExec
synced 2026-06-06 16:34:30 +00:00
Updated as Neff's review
Signed-off-by: termanix <50464194+termanix@users.noreply.github.com>
This commit is contained in:
+28
-100
@@ -729,78 +729,41 @@ class ldap(connection):
|
||||
-------
|
||||
None
|
||||
"""
|
||||
def pwd_last_set_func(pwd_last_set):
|
||||
"""Helper function to format pwdLastSet"""
|
||||
if pwd_last_set:
|
||||
timestamp_seconds = int(pwd_last_set) / 10**7
|
||||
start_date = datetime(1601, 1, 1)
|
||||
parsed_pw_last_set = (start_date + timedelta(seconds=timestamp_seconds)).replace(microsecond=0).strftime("%Y-%m-%d %H:%M:%S")
|
||||
if parsed_pw_last_set == "1601-01-01 00:00:00":
|
||||
return "<never>"
|
||||
return parsed_pw_last_set
|
||||
|
||||
if len(self.args.users) > 0:
|
||||
self.logger.debug(f"Dumping users: {', '.join(self.args.users)}")
|
||||
search_filter = f"(|{''.join(f'(sAMAccountName={user})' for user in self.args.users)})"
|
||||
else:
|
||||
self.logger.debug("Trying to dump all users")
|
||||
search_filter = "(sAMAccountType=805306368)" if self.username != "" else "(&(objectclass=*))"
|
||||
search_filter = "(sAMAccountType=805306368)" if self.username != "" else "(objectclass=*)"
|
||||
|
||||
# default to these attributes to mirror the SMB --users functionality
|
||||
request_attributes = ["sAMAccountName", "description", "badPwdCount", "pwdLastSet"]
|
||||
resp = self.search(search_filter, request_attributes, sizeLimit=0)
|
||||
|
||||
if resp:
|
||||
resp_parse = parse_result_attributes(resp)
|
||||
# Handle the case for anonymous LDAP bindings
|
||||
if self.username == "":
|
||||
users = []
|
||||
self.logger.highlight(f"{'-Username-':<30}{'-Last PW Set-':<20}{'-BadPW-':<20}{'-Description-'}")
|
||||
|
||||
for item in resp:
|
||||
if not isinstance(item, ldapasn1_impacket.SearchResultEntry):
|
||||
continue
|
||||
|
||||
# Initialize default values
|
||||
sAMAccountName = "N/A"
|
||||
pwdcount = "N/A"
|
||||
parsed_pw_last_set = "N/A"
|
||||
description = "N/A"
|
||||
|
||||
# Initialize the username as a fallback
|
||||
if "objectName" in item:
|
||||
# Extract the username from the objectName
|
||||
sAMAccountName = str(item["objectName"]).split(",")[0].split("=")[1]
|
||||
|
||||
# Iterate over the attributes for each entry
|
||||
for attribute in item["attributes"]:
|
||||
attr_type = str(attribute["type"])
|
||||
attr_vals = attribute["vals"]
|
||||
|
||||
if attr_type == "sAMAccountName":
|
||||
sAMAccountName = str(attr_vals[0])
|
||||
elif attr_type == "badPwdCount":
|
||||
pwdcount = str(attr_vals[0])
|
||||
elif attr_type == "pwdLastSet":
|
||||
pwd_last_set = str(attr_vals[0])
|
||||
parsed_pw_last_set = pwd_last_set_func(pwd_last_set)
|
||||
elif attr_type == "description":
|
||||
description = str(attr_vals[0])
|
||||
|
||||
self.logger.highlight(f"{sAMAccountName:<30}{parsed_pw_last_set:<20}{pwdcount:<20}{description}")
|
||||
self.logger.highlight(f"{'-Username-':<40}{'-Last PW Set-':<20}{'-BadPW-':<20}{'-Description-'}")
|
||||
for item in resp_parse:
|
||||
sAMAccountName = item.get("sAMAccountName") if item.get("sAMAccountName") else "<unkown>"
|
||||
parsed_pw_last_set = "<unkown>" if item.get("pwdLastSet") is None else (str(datetime.fromtimestamp(self.getUnixTime(int(item.get("pwdLastSet")))).strftime("%Y-%m-%d %H:%M:%S")) if str(item.get("pwdLastSet")) != "0" else "0")
|
||||
pwdcount = item.get("pwdcount") if item.get("pwdcount") else "<unkown>"
|
||||
description = item.get("description") if item.get("description") else "<unkown>"
|
||||
|
||||
self.logger.highlight(f"{sAMAccountName:<40}{parsed_pw_last_set:<20}{pwdcount:<20}{description}")
|
||||
return
|
||||
|
||||
users = parse_result_attributes(resp)
|
||||
# we print the total records after we parse the results since often SearchResultReferences are returned
|
||||
self.logger.display(f"Enumerated {len(users):d} domain users: {self.domain}")
|
||||
self.logger.display(f"Enumerated {len(resp_parse):d} domain users: {self.domain}")
|
||||
self.logger.highlight(f"{'-Username-':<30}{'-Last PW Set-':<20}{'-BadPW-':<8}{'-Description-':<60}")
|
||||
for user in users:
|
||||
for user in resp_parse:
|
||||
# TODO: functionize this - we do this calculation in a bunch of places, different, including in the `pso` module
|
||||
parsed_pw_last_set = ""
|
||||
pwd_last_set = user.get("pwdLastSet", "")
|
||||
parsed_pw_last_set = pwd_last_set_func(pwd_last_set)
|
||||
pwd_last_set = user.get("pwdLastSet", "") if user.get("pwdLastSet") in ["", None] else ("0" if str(user.get("pwdLastSet")) == "0" else str(datetime.fromtimestamp(self.getUnixTime(int(user.get("pwdLastSet")))).strftime("%Y-%m-%d %H:%M:%S")))
|
||||
|
||||
# we default attributes to blank strings if they don't exist in the dict
|
||||
self.logger.highlight(f"{user.get('sAMAccountName', ''):<30}{parsed_pw_last_set:<20}{user.get('badPwdCount', ''):<8}{user.get('description', ''):<60}")
|
||||
self.logger.highlight(f"{user.get('sAMAccountName', ''):<30}{pwd_last_set:<20}{user.get('badPwdCount', ''):<8}{user.get('description', ''):<60}")
|
||||
|
||||
def groups(self):
|
||||
# Building the search filter
|
||||
@@ -848,17 +811,7 @@ class ldap(connection):
|
||||
self.logger.fail("Exception:", exc_info=True)
|
||||
self.logger.fail(f"Skipping item, cannot process due to error {e}")
|
||||
|
||||
def active_users(self):
|
||||
"""Helper function to format pwdLastSet"""
|
||||
def pwd_last_set_func(pwd_last_set):
|
||||
if pwd_last_set:
|
||||
timestamp_seconds = int(pwd_last_set) / 10**7
|
||||
start_date = datetime(1601, 1, 1)
|
||||
parsed_pw_last_set = (start_date + timedelta(seconds=timestamp_seconds)).replace(microsecond=0).strftime("%Y-%m-%d %H:%M:%S")
|
||||
if parsed_pw_last_set == "1601-01-01 00:00:00":
|
||||
return "<never>"
|
||||
return parsed_pw_last_set
|
||||
|
||||
def active_users(self):
|
||||
"""Helper function to format userAccountControl"""
|
||||
def user_account_control_cal(user_account_control):
|
||||
if user_account_control is not None: # Check if user_account_control is not None
|
||||
@@ -876,7 +829,7 @@ class ldap(connection):
|
||||
else:
|
||||
arg = False
|
||||
self.logger.debug("Trying to dump all users")
|
||||
search_filter = "(sAMAccountType=805306368)" if self.username != "" else "(&(objectclass=*))"
|
||||
search_filter = "(sAMAccountType=805306368)" if self.username != "" else "(objectclass=*)"
|
||||
|
||||
# default to these attributes to mirror the SMB --users functionality
|
||||
request_attributes = ["sAMAccountName", "description", "badPwdCount", "pwdLastSet", "userAccountControl"]
|
||||
@@ -884,7 +837,6 @@ class ldap(connection):
|
||||
|
||||
if resp:
|
||||
allusers = parse_result_attributes(resp)
|
||||
|
||||
activeusers = []
|
||||
argsusers = []
|
||||
|
||||
@@ -912,54 +864,30 @@ class ldap(connection):
|
||||
if self.username == "":
|
||||
self.logger.display(f"Total records returned: {len(activeusers)}")
|
||||
self.logger.highlight(f"{'-Username-':<30}{'-Last PW Set-':<20}{'-BadPW-':<8}{'-Description-':<60}")
|
||||
|
||||
for item in resp:
|
||||
if not isinstance(item, ldapasn1_impacket.SearchResultEntry):
|
||||
continue
|
||||
|
||||
# Initialize default values
|
||||
sAMAccountName = "N/A"
|
||||
pwdcount = "N/A"
|
||||
parsed_pw_last_set = "N/A"
|
||||
description = "N/A"
|
||||
|
||||
# Initialize the username as a fallback
|
||||
if "objectName" in item:
|
||||
# Extract the username from the objectName
|
||||
sAMAccountName = str(item["objectName"]).split(",")[0].split("=")[1]
|
||||
|
||||
# Iterate over the attributes for each entry
|
||||
for attribute in item["attributes"]:
|
||||
attr_type = str(attribute["type"])
|
||||
attr_vals = attribute["vals"]
|
||||
|
||||
if attr_type == "sAMAccountName":
|
||||
sAMAccountName = str(attr_vals[0])
|
||||
elif attr_type == "badPwdCount":
|
||||
pwdcount = str(attr_vals[0])
|
||||
elif attr_type == "pwdLastSet":
|
||||
pwd_last_set = str(attr_vals[0])
|
||||
parsed_pw_last_set = pwd_last_set_func(pwd_last_set)
|
||||
elif attr_type == "description":
|
||||
description = str(attr_vals[0])
|
||||
for item in resp_args:
|
||||
sAMAccountName = item.get("sAMAccountName") if item.get("sAMAccountName") else "<unkown>"
|
||||
pwd_last_set = "<unkown>" if item.get("pwdLastSet") is None else (str(datetime.fromtimestamp(self.getUnixTime(int(item.get("pwdLastSet")))).strftime("%Y-%m-%d %H:%M:%S")) if str(item.get("pwdLastSet")) != "0" else "0")
|
||||
pwdcount = item.get("pwdcount") if item.get("pwdcount") else "<unkown>"
|
||||
description = item.get("description") if item.get("description") else "<unkown>"
|
||||
|
||||
if sAMAccountName.lower() in activeusers:
|
||||
self.logger.highlight(f"{sAMAccountName:<30}{parsed_pw_last_set:<20}{pwdcount:<8}{description}")
|
||||
|
||||
self.logger.highlight(f"{sAMAccountName:<30}{pwd_last_set:<20}{pwdcount:<8}{description}")
|
||||
return
|
||||
|
||||
self.logger.display(f"Total records returned: {len(activeusers)}, total {len(allusers) - len(activeusers)} user(s) disabled") if not arg else self.logger.display(f"Total records returned: {len(argsusers)}, Total {len(allusers) - len(activeusers)} user(s) disabled")
|
||||
self.logger.highlight(f"{'-Username-':<30}{'-Last PW Set-':<20}{'-BadPW-':<8}{'-Description-':<60}")
|
||||
|
||||
for arguser in argsusers:
|
||||
pwd_last_set = arguser.get("pwdLastSet", "") # Retrieves pwdLastSet directly and defaults to an empty string.
|
||||
parsed_pw_last_set = pwd_last_set_func(pwd_last_set)
|
||||
# Retrieves pwdLastSet directly and defaults to an empty string.
|
||||
pwd_last_set = arguser.get("pwdLastSet", "") if arguser.get("pwdLastSet") in ["", None] else ("0" if str(arguser.get("pwdLastSet")) == "0" else str(datetime.fromtimestamp(self.getUnixTime(int(arguser.get("pwdLastSet")))).strftime("%Y-%m-%d %H:%M:%S")))
|
||||
|
||||
if arguser.get("sAMAccountName").lower() in activeusers and arg is False:
|
||||
self.logger.highlight(f"{arguser.get('sAMAccountName', ''):<30}{parsed_pw_last_set:<20}{arguser.get('badPwdCount', ''):<8}{arguser.get('description', ''):<60}")
|
||||
self.logger.highlight(f"{arguser.get('sAMAccountName', ''):<30}{pwd_last_set:<20}{arguser.get('badPwdCount', ''):<8}{arguser.get('description', ''):<60}")
|
||||
elif (arguser.get("sAMAccountName").lower() not in activeusers) and arg is True:
|
||||
self.logger.highlight(f"{arguser.get('sAMAccountName', '') + ' (Disabled)':<30}{parsed_pw_last_set:<20}{arguser.get('badPwdCount', ''):<8}{arguser.get('description', ''):<60}")
|
||||
self.logger.highlight(f"{arguser.get('sAMAccountName', '') + ' (Disabled)':<30}{pwd_last_set:<20}{arguser.get('badPwdCount', ''):<8}{arguser.get('description', ''):<60}")
|
||||
elif (arguser.get("sAMAccountName").lower() in activeusers):
|
||||
self.logger.highlight(f"{arguser.get('sAMAccountName', ''):<30}{parsed_pw_last_set:<20}{arguser.get('badPwdCount', ''):<8}{arguser.get('description', ''):<60}")
|
||||
self.logger.highlight(f"{arguser.get('sAMAccountName', ''):<30}{pwd_last_set:<20}{arguser.get('badPwdCount', ''):<8}{arguser.get('description', ''):<60}")
|
||||
|
||||
def asreproast(self):
|
||||
if self.password == "" and self.nthash == "" and self.kerberos is False:
|
||||
|
||||
Reference in New Issue
Block a user