Merge pull request #597 from Pennyw0rth/neff-bug-fixes

This commit is contained in:
Alex
2025-03-16 11:16:27 +01:00
committed by GitHub
8 changed files with 48 additions and 75 deletions
+6 -23
View File
@@ -1,6 +1,6 @@
from impacket.ldap import ldapasn1 as ldapasn1_impacket
from impacket.ldap import ldap as ldap_impacket
from nxc.logger import nxc_logger
from nxc.parsers.ldap_results import parse_result_attributes
class NXCModule:
@@ -20,7 +20,7 @@ class NXCModule:
"""
def on_login(self, context, connection):
searchFilter = "(objectclass=user)"
searchFilter = "(unixUserPassword=*)"
try:
context.log.debug(f"Search Filter={searchFilter}")
@@ -37,27 +37,10 @@ class NXCModule:
nxc_logger.debug(e)
return False
answers = []
context.log.debug(f"Total of records returned {len(resp)}")
for item in resp:
if isinstance(item, ldapasn1_impacket.SearchResultEntry) is not True:
continue
sAMAccountName = ""
unixUserPassword = []
try:
for attribute in item["attributes"]:
if str(attribute["type"]) == "sAMAccountName":
sAMAccountName = str(attribute["vals"][0])
elif str(attribute["type"]) == "unixUserPassword":
unixUserPassword = [str(i) for i in attribute["vals"]]
if sAMAccountName != "" and len(unixUserPassword) > 0:
answers.append([sAMAccountName, unixUserPassword])
except Exception as e:
context.log.debug("Exception:", exc_info=True)
context.log.debug(f"Skipping item, cannot process due to error {e!s}")
if len(answers) > 0:
if resp:
resp_parsed = parse_result_attributes(resp)
context.log.success("Found following users: ")
for answer in answers:
context.log.highlight(f"User: {answer[0]} unixUserPassword: {answer[1]}")
for user in resp_parsed:
context.log.highlight(f"User: {user['sAMAccountName']} unixUserPassword: {user['unixUserPassword']}")
else:
context.log.fail("No unixUserPassword Found")
+7 -24
View File
@@ -1,6 +1,6 @@
from impacket.ldap import ldapasn1 as ldapasn1_impacket
from impacket.ldap import ldap as ldap_impacket
from nxc.logger import nxc_logger
from nxc.parsers.ldap_results import parse_result_attributes
class NXCModule:
@@ -20,7 +20,7 @@ class NXCModule:
"""
def on_login(self, context, connection):
searchFilter = "(objectclass=user)"
searchFilter = "(userPassword=*)"
try:
context.log.debug(f"Search Filter={searchFilter}")
@@ -37,27 +37,10 @@ class NXCModule:
nxc_logger.debug(e)
return False
answers = []
context.log.debug(f"Total of records returned {len(resp)}")
for item in resp:
if isinstance(item, ldapasn1_impacket.SearchResultEntry) is not True:
continue
sAMAccountName = ""
userPassword = []
try:
for attribute in item["attributes"]:
if str(attribute["type"]) == "sAMAccountName":
sAMAccountName = str(attribute["vals"][0])
elif str(attribute["type"]) == "userPassword":
userPassword = [str(i) for i in attribute["vals"]]
if sAMAccountName != "" and len(userPassword) > 0:
answers.append([sAMAccountName, userPassword])
except Exception as e:
context.log.debug("Exception:", exc_info=True)
context.log.debug(f"Skipping item, cannot process due to error {e!s}")
if len(answers) > 0:
if resp:
resp_parsed = parse_result_attributes(resp)
context.log.success("Found following users: ")
for answer in answers:
context.log.highlight(f"User: {answer[0]} userPassword: {answer[1]}")
for user in resp_parsed:
context.log.highlight(f"User: {user['sAMAccountName']} unixUserPassword: {user['userPassword']}")
else:
context.log.fail("No userPassword Found")
context.log.fail("No unixUserPassword Found")
+1 -1
View File
@@ -174,7 +174,7 @@ class HostChecker:
ConfigCheck("IPv4 preferred over IPv6", "Checks if IPv4 is preferred over IPv6", checker_args=[[self, ("HKLM\\SYSTEM\\CurrentControlSet\\Services\\Tcpip6\\Parameters", "DisabledComponents", (32, 255), in_)]]),
ConfigCheck("Spooler service disabled", "Checks if the spooler service is disabled", checkers=[self.check_spooler_service]),
ConfigCheck("WDigest authentication disabled", "Checks if WDigest authentication is disabled", checker_args=[[self, ("HKLM\\SYSTEM\\CurrentControlSet\\Control\\SecurityProviders\\WDigest", "UseLogonCredential", 0)]]),
ConfigCheck("WSUS configuration", "Checks if WSUS configuration uses HTTPS", checkers=[self.check_wsus_running, None], checker_args=[[], [self, ("HKLM\\Software\\Policies\\Microsoft\\Windows\\WindowsUpdate", "WUServer", "https://", startswith), ("HKLM\\Software\\Policies\\Microsoft\\Windows\\WindowsUpdate", "UseWUServer", 0, operator.eq)]], checker_kwargs=[{}, {"options": {"lastWins": True}}]),
ConfigCheck("WSUS configuration", "Checks if WSUS configuration uses HTTPS", checkers=[self.check_wsus_running, None], checker_args=[[], [self, ("HKLM\\Software\\Policies\\Microsoft\\Windows\\WindowsUpdate", "WUServer", "https://", startswith), ("HKLM\\Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU", "UseWUServer", 0, operator.eq)]], checker_kwargs=[{}, {"options": {"lastWins": True}}]),
ConfigCheck("Small LSA cache", "Checks how many logons are kept in the LSA cache", checker_args=[[self, ("HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon", "CachedLogonsCount", 2, le)]]),
ConfigCheck("AppLocker rules defined", "Checks if there are AppLocker rules defined", checkers=[self.check_applocker]),
ConfigCheck("RDP expiration time", "Checks RDP session timeout", checker_args=[[self, ("HKLM\\SOFTWARE\\Policies\\Microsoft\\Windows NT\\Terminal Services", "MaxDisconnectionTime", 0, operator.gt), ("HKCU\\SOFTWARE\\Policies\\Microsoft\\Windows NT\\Terminal Services", "MaxDisconnectionTime", 0, operator.gt)]]),
+4 -1
View File
@@ -18,6 +18,9 @@ def parse_result_attributes(ldap_response):
# If we can't decode the value, we'll just return the bytes
val_decoded = val.__bytes__()
val_list.append(val_decoded)
attribute_map[str(attribute["type"])] = val_list if len(val_list) > 1 else val_list[0]
if len(val_list) == 1:
attribute_map[str(attribute["type"])] = val_list[0]
else:
attribute_map[str(attribute["type"])] = val_list
parsed_response.append(attribute_map)
return parsed_response
+11 -10
View File
@@ -992,19 +992,20 @@ class ldap(connection):
self.logger.debug(f"Querying LDAP server with filter: {search_filter} and attributes: {attributes}")
try:
resp = self.search(search_filter, attributes, 0)
resp_parsed = parse_result_attributes(resp)
except LDAPFilterSyntaxError as e:
self.logger.fail(f"LDAP Filter Syntax Error: {e}")
return
for item in resp:
if isinstance(item, ldapasn1_impacket.SearchResultEntry) is not True:
continue
self.logger.success(f"Response for object: {item['objectName']}")
for attribute in item["attributes"]:
attr = f"{attribute['type']}:"
vals = str(attribute["vals"]).replace("\n", "")
if "SetOf: " in vals:
vals = vals.replace("SetOf: ", "")
self.logger.highlight(f"{attr:<20} {vals}")
for idx, entry in enumerate(resp_parsed):
self.logger.success(f"Response for object: {resp[idx]['objectName']}")
for attribute in entry:
if isinstance(entry[attribute], list) and entry[attribute]:
# Display first item in the same line as attribute
self.logger.highlight(f"{attribute:<20} {entry[attribute].pop(0)}")
for item in entry[attribute]:
self.logger.highlight(f"{'':<20} {item}")
else:
self.logger.highlight(f"{attribute:<20} {entry[attribute]}")
def find_delegation(self):
def printTable(items, header):
+14 -13
View File
@@ -246,22 +246,23 @@ class nfs(connection):
mnt_info = self.mount.mnt(share, self.auth)
self.logger.debug(f"Mounted {share} - {mnt_info}")
if mnt_info["status"] != 0:
self.logger.fail(f"Error mounting share {share}: {NFSSTAT3[mnt_info['status']]}")
continue
file_handle = mnt_info["mountinfo"]["fhandle"]
self.logger.debug(f"Error mounting share {share}: {NFSSTAT3[mnt_info['status']]}")
self.logger.highlight(f"{'-':<11}{'---':<9}{'---'}/{'---':<12} {share:<30} {', '.join(network) if network else 'No network':<15}")
else:
file_handle = mnt_info["mountinfo"]["fhandle"]
info = self.nfs3.fsstat(file_handle, self.auth)
free_space = info["resok"]["fbytes"]
total_space = info["resok"]["tbytes"]
used_space = total_space - free_space
info = self.nfs3.fsstat(file_handle, self.auth)
free_space = info["resok"]["fbytes"]
total_space = info["resok"]["tbytes"]
used_space = total_space - free_space
# Autodetectting the uid needed for the share
attrs = self.nfs3.getattr(file_handle, auth=self.auth)
self.auth["uid"] = attrs["attributes"]["uid"]
# Autodetectting the uid needed for the share
attrs = self.nfs3.getattr(file_handle, auth=self.auth)
self.auth["uid"] = attrs["attributes"]["uid"]
read_perm, write_perm, exec_perm = self.get_permissions(file_handle)
self.mount.umnt(self.auth)
self.logger.highlight(f"{self.auth['uid']:<11}{'r' if read_perm else '-'}{'w' if write_perm else '-'}{('x' if exec_perm else '-'):<7}{convert_size(used_space)}/{convert_size(total_space):<9} {share:<30} {', '.join(network) if network else 'No network':<15}")
read_perm, write_perm, exec_perm = self.get_permissions(file_handle)
self.mount.umnt(self.auth)
self.logger.highlight(f"{self.auth['uid']:<11}{'r' if read_perm else '-'}{'w' if write_perm else '-'}{('x' if exec_perm else '-'):<7}{convert_size(used_space) + "/" + convert_size(total_space):<16} {share:<30} {', '.join(network) if network else 'No network':<15}")
except Exception as e:
self.logger.fail(f"Failed to list share: {share} - {e}")
+1 -1
View File
@@ -1,7 +1,7 @@
def proto_args(parser, parents):
nfs_parser = parser.add_parser("nfs", help="own stuff using NFS", parents=parents)
nfs_parser.add_argument("--port", type=int, default=111, help="NFS portmapper port (default: %(default)s)")
nfs_parser.add_argument("--nfs-timeout", type=int, default=30, help="NFS connection timeout (default: %(default)ss)")
nfs_parser.add_argument("--nfs-timeout", type=int, default=5, help="NFS connection timeout (default: %(default)ss)")
dgroup = nfs_parser.add_argument_group("NFS Mapping/Enumeration", "Options for Mapping/Enumerating NFS")
dgroup.add_argument("--share", help="Specify a share, e.g. for --ls, --get-file, --put-file")
+4 -2
View File
@@ -1084,6 +1084,7 @@ class smb(connection):
try:
self.conn.createDirectory(share_name, temp_dir)
write_dir = True
self.logger.debug(f"WRITE access with DIR creation on share: {share_name}")
try:
self.conn.deleteDirectory(share_name, temp_dir)
except SessionError as e:
@@ -1094,13 +1095,14 @@ class smb(connection):
self.logger.debug(f"Error DELETING created temp dir {temp_dir} on share {share_name}: {error}")
except SessionError as e:
error = get_error_string(e)
self.logger.debug(f"Error checking WRITE access on share {share_name}: {error}")
self.logger.debug(f"Error checking WRITE access with DIR creation on share {share_name}: {error}")
try:
tid = self.conn.connectTree(share_name)
fid = self.conn.createFile(tid, temp_file, desiredAccess=FILE_SHARE_WRITE, shareMode=FILE_SHARE_DELETE)
self.conn.closeFile(tid, fid)
write_file = True
self.logger.debug(f"WRITE access with FILE creation on share: {share_name}")
try:
self.conn.deleteFile(share_name, temp_file)
except SessionError as e:
@@ -1111,7 +1113,7 @@ class smb(connection):
self.logger.debug(f"Error DELETING created temp file {temp_file} on share {share_name}")
except SessionError as e:
error = get_error_string(e)
self.logger.debug(f"Error checking WRITE access with file on share {share_name}: {error}")
self.logger.debug(f"Error checking WRITE access with FILE creation on share {share_name}: {error}")
# If we either can create a file or a directory we add the write privs to the output. Agreed on in https://github.com/Pennyw0rth/NetExec/pull/404
if write_dir or write_file: