Add nxcdb hosts [nla]

This commit is contained in:
Aurélien CHALOT
2025-12-12 15:42:38 +01:00
parent 69c87d2e7f
commit 29e17543bb
3 changed files with 193 additions and 31 deletions
+1
View File
@@ -107,6 +107,7 @@ class rdp(connection):
self.logger.display(f"Probably old, doesn't not support HYBRID or HYBRID_EX ({nla})")
else:
self.logger.display(f"{self.server_os} (name:{self.hostname}) (domain:{self.domain}) ({nla})")
self.db.add_host(self.host, self.port, self.hostname, self.domain, self.server_os, self.nla)
def create_conn_obj(self):
self.target = RDPTarget(ip=self.host, domain="FAKE", port=self.port, timeout=self.args.rdp_timeout)
+88 -21
View File
@@ -1,46 +1,113 @@
from sqlalchemy import Column, Integer, PrimaryKeyConstraint, String
from sqlalchemy import Column, Boolean, UniqueConstraint, Integer, PrimaryKeyConstraint, String, select
from sqlalchemy.dialects.sqlite import Insert
from sqlalchemy.orm import declarative_base
from nxc.database import BaseDB
from nxc.database import BaseDB, format_host_query
from nxc.logger import nxc_logger
Base = declarative_base()
BaseTable = declarative_base()
class database(BaseDB):
def __init__(self, db_engine):
self.CredentialsTable = None
self.HostsTable = None
super().__init__(db_engine)
class Credential(Base):
__tablename__ = "credentials"
id = Column(Integer)
username = Column(String)
password = Column(String)
pkey = Column(String)
__table_args__ = (
PrimaryKeyConstraint("id"),
)
class Host(Base):
class Host(BaseTable):
__tablename__ = "hosts"
id = Column(Integer)
ip = Column(String)
hostname = Column(String)
port = Column(Integer)
server_banner = Column(String)
hostname = Column(String)
domain = Column(String)
os = Column(String)
nla = Column(Boolean)
__table_args__ = (
PrimaryKeyConstraint("id"),
UniqueConstraint("ip"),
)
@staticmethod
def db_schema(db_conn):
Base.metadata.create_all(db_conn)
BaseTable.metadata.create_all(db_conn)
def reflect_tables(self):
self.CredentialsTable = self.reflect_table(self.Credential)
self.HostsTable = self.reflect_table(self.Host)
def add_host(self, ip, port, hostname, domain, os, nla):
"""
Check if this host has already been added to the database, if not, add it in.
TODO: return inserted or updated row ids as a list
"""
hosts = []
q = select(self.HostsTable).filter(self.HostsTable.c.ip == ip)
results = self.db_execute(q).all()
nxc_logger.debug(f"rdp add_host() - hosts returned: {results}")
# create new host
if not results:
new_host = {
"ip": ip,
"port": port,
"hostname": hostname,
"domain": domain,
"os": os,
"nla": nla
}
hosts = [new_host]
# update existing hosts data
else:
for host in results:
host_data = host._asdict()
# only update column if it is being passed in
if ip is not None:
host_data["ip"] = ip
if port is not None:
host_data["port"] = port
if hostname is not None:
host_data["hostname"] = hostname
if domain is not None:
host_data["domain"] = domain
if os is not None:
host_data["os"] = os
if nla is not None:
host_data["nla"] = nla
# only add host to be updated if it has changed
if host_data not in hosts:
hosts.append(host_data)
nxc_logger.debug(f"Update Hosts: {hosts}")
# TODO: find a way to abstract this away to a single Upsert call
q = Insert(self.HostsTable)
update_columns = {col.name: col for col in q.excluded if col.name not in "id"}
q = q.on_conflict_do_update(index_elements=self.HostsTable.primary_key, set_=update_columns)
self.db_execute(q, hosts)
def get_hosts(self, filter_term=None, domain=None):
"""Return hosts from the database."""
q = select(self.HostsTable)
# if we're returning a single host by ID
if self.is_host_valid(filter_term):
q = q.filter(self.HostsTable.c.id == filter_term)
results = self.db_execute(q).first()
# all() returns a list, so we keep the return format the same so consumers don't have to guess
return [results]
# filtering nla False because this is the misconfiguration we are looking for
elif filter_term == "nla":
q = q.filter(self.HostsTable.c.nla == False) # noqa: E712
# if we're filtering by ip/hostname
elif filter_term and filter_term != "":
q = format_host_query(q, filter_term, self.HostsTable)
results = self.db_execute(q).all()
nxc_logger.debug(f"rdp hosts() - results: {results}")
return results
def is_host_valid(self, host_id):
"""Check if this host ID is valid."""
q = select(self.HostsTable).filter(self.HostsTable.c.id == host_id)
results = self.db_execute(q).all()
return len(results) > 0
+104 -10
View File
@@ -1,15 +1,109 @@
from nxc.nxcdb import DatabaseNavigator, print_help
from nxc.nxcdb import DatabaseNavigator, print_table, print_help
from termcolor import colored
import functools
help_header = functools.partial(colored, color="cyan", attrs=["bold"])
help_kw = functools.partial(colored, color="green", attrs=["bold"])
class navigator(DatabaseNavigator):
def do_clear_database(self, line):
if input("This will destroy all data in the current database, are you SURE you want to run this? (y/n): ") == "y":
self.db.clear_database()
def display_hosts(self, hosts):
data = [
[
"HostID",
"IP",
"Port",
"Hostname",
"Domain",
"OS",
"NLA",
]
]
def help_clear_database(self):
for host in hosts:
host_id = host[0]
ip = host[1]
port = host[2]
hostname = host[3]
domain = host[4]
try:
os = host[5].decode()
except Exception:
os = host[5]
nla = host[6]
data.append(
[
host_id,
ip,
port,
hostname,
domain,
os,
nla
]
)
print_table(data, title="Hosts")
def do_hosts(self, line):
filter_term = line.strip()
if filter_term == "":
hosts = self.db.get_hosts()
self.display_hosts(hosts)
else:
hosts = self.db.get_hosts(filter_term=filter_term)
if len(hosts) > 1:
self.display_hosts(hosts)
elif len(hosts) == 1:
data = [
[
"HostID",
"IP",
"Port",
"Hostname",
"Domain",
"OS",
"NLA",
]
]
for host in hosts:
host_id = host[0]
ip = host[1]
port = host[2]
hostname = host[3]
domain = host[4]
try:
os = host[5].decode()
except Exception:
os = host[5]
nla = host[6]
data.append(
[
host_id,
ip,
port,
hostname,
domain,
os,
nla
]
)
print_table(data, title="Host")
def help_hosts(self):
help_string = """
clear_database
THIS COMPLETELY DESTROYS ALL DATA IN THE CURRENTLY CONNECTED DATABASE
YOU CANNOT UNDO THIS COMMAND
"""
print_help(help_string)
By default prints all hosts
Table format:
| 'HostID', 'IP', 'Port', 'Hostname', 'Domain', 'OS', 'NLA' |
Subcommands:
nla - list hosts with NLA disabled
"""
print_help(help_string)