mirror of
https://github.com/Pennyw0rth/NetExec
synced 2026-06-06 16:34:30 +00:00
Merge pull request #597 from Pennyw0rth/neff-bug-fixes
This commit is contained in:
@@ -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")
|
||||
|
||||
@@ -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
@@ -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)]]),
|
||||
|
||||
@@ -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
@@ -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
@@ -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,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")
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user