Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions nxc/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@
host_info_colors = literal_eval(nxc_config.get("nxc", "host_info_colors", fallback=["green", "red", "yellow", "cyan"]))
check_guest_account = nxc_config.getboolean("nxc", "check_guest_account", fallback=False)

# MSSQL required options
discover_sql_browser = nxc_config.getboolean("MSSQL", "discover_sql_browser", fallback=True)

if len(host_info_colors) != 4:
nxc_logger.error("Config option host_info_colors must have 4 values! Using default values.")
host_info_colors = nxc_default_config.get("nxc", "host_info_colors")
Expand Down
3 changes: 3 additions & 0 deletions nxc/data/nxc.conf
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ log_mode = False
host_info_colors = ["green", "red", "yellow", "cyan"]
check_guest_account = False

[MSSQL]
discover_sql_browser = True

[BloodHound]
bh_enabled = False
bh_uri = 127.0.0.1
Expand Down
2 changes: 1 addition & 1 deletion nxc/parsers/ip.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def parse_targets(target):
if ip_interface(target).ip.version == 6 and ip_address(target).is_link_local:
yield str(target)
else:
for ip in ip_network(target, strict=False):
for ip in ip_network(target, strict=False).hosts():
yield str(ip)
except ValueError:
yield str(target)
40 changes: 37 additions & 3 deletions nxc/protocols/mssql.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import contextlib
from termcolor import colored

from nxc.config import process_secret, host_info_colors
from nxc.config import process_secret, host_info_colors, discover_sql_browser
from nxc.connection import connection
from nxc.connection import requires_admin
from nxc.helpers.misc import gen_random_string
Expand Down Expand Up @@ -43,6 +43,8 @@ def __init__(self, args, db, host):
self.os_arch = None
self.lmhash = ""
self.nthash = ""
self.is_mssql = False
self.sqlbrowser_enabled = False
self.no_ntlm = False

connection.__init__(self, args, db, host)
Expand All @@ -59,10 +61,22 @@ def proto_logger(self):

def create_conn_obj(self):
try:
# Connects to default port or --port
self.conn = tds.MSSQL(self.host, self.port, self.remoteName)
self.conn.connect(self.args.mssql_timeout)

except Exception as e:
self.logger.debug(f"Error connecting to MSSQL service on host: {self.host}, reason: {e}")

# Even if the TCP connection failed, we still check whether the SQL Browser is running
# This check is only run if the nxc.config discover_sql_browser is set to True
if discover_sql_browser:
# Ignore broadcast targets (UDP)
self.mssql_instances = self.conn.getInstances(self.args.mssql_timeout)
if len(self.mssql_instances) > 0:
self.sqlbrowser_enabled = True
self.list_instances()

with contextlib.suppress(Exception):
self.conn.disconnect()
return False
Expand Down Expand Up @@ -116,7 +130,9 @@ def enum_host_info(self):
login["Length"] = len(login.getData())

# Get number of mssql instance
self.mssql_instances = self.conn.getInstances(0)
self.mssql_instances = self.conn.getInstances(self.args.mssql_timeout)
if len(self.mssql_instances) > 0:
self.sqlbrowser_enabled = True

# Send the NTLMSSP Negotiate or SQL Auth Packet
self.conn.sendTDS(tds.TDS_LOGIN7, login.getData())
Expand Down Expand Up @@ -163,8 +179,9 @@ def enum_host_info(self):

def print_host_info(self):
encryption = colored(f"EncryptionReq:{self.encryption}", host_info_colors[0 if self.encryption else 1], attrs=["bold"])
sqlbrowser = colored(f"SqlBrowser:{self.sqlbrowser_enabled}", host_info_colors[0 if self.sqlbrowser_enabled else 1], attrs=["bold"])
ntlm = colored(f"(NTLM:{not self.no_ntlm})", host_info_colors[2], attrs=["bold"]) if self.no_ntlm else ""
self.logger.display(f"{self.server_os} (name:{self.hostname}) (domain:{self.targetDomain}) ({encryption}) {ntlm}")
self.logger.display(f"{self.server_os} (name:{self.hostname}) (domain:{self.targetDomain}) ({encryption}) ({sqlbrowser}) ({ntlm})")

@reconnect_mssql
def kerberos_login(self, domain, username, password="", ntlm_hash="", aesKey="", kdcHost="", useCache=False):
Expand Down Expand Up @@ -285,6 +302,23 @@ def hash_login(self, domain, username, ntlm_hash):
self.logger.fail(f"{self.domain}\\{self.username}:{process_secret(self.nthash)} {error_msg if error_msg else ''}")
return False

def list_instances(self):
if self.sqlbrowser_enabled is False:
self.logger.fail("MSSQL browser is not enabled, cannot enumerate...")
return

if len(self.mssql_instances) > 0:
self.logger.display("SQL Browser is enabled, listing instances :")
# Get information about instances
for index, instance in enumerate(self.mssql_instances):
instance_name = instance.get("InstanceName")
instance_port = instance.get("tcp", None)
instance_np = instance.get("np", None)
instance_version = instance.get("Version", None)
self.logger.success(f"#{index} {instance_name} (port:{instance_port}) (np:{instance_np}) (version:{instance_version})")
else:
self.logger.fail("No instance to enumerate")

def query(self):
if self.conn.lastError:
# Invalid connection
Expand Down
1 change: 1 addition & 0 deletions nxc/protocols/mssql/proto_args.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ def proto_args(parser, parents):
mssql_parser.add_argument("--mssql-timeout", help="SQL server connection timeout", type=int, default=5)
mssql_parser.add_argument("-q", "--query", metavar="QUERY", type=str, help="execute the specified query against the mssql db")
mssql_parser.add_argument("--database", nargs="?", const=True, metavar="NAME", help="list databases or list tables for NAME")
mssql_parser.add_argument("--list-instances", action="store_true", default=False, help="Lists availables MSSQL instances over the SQL browser")

dgroup = mssql_parser.add_mutually_exclusive_group()
dgroup.add_argument("-d", metavar="DOMAIN", dest="domain", type=str, help="domain name")
Expand Down