2023-10-22 17:05:06 +00:00
import sys
2023-09-14 21:07:15 +00:00
from nxc . helpers . logger import highlight
from nxc . helpers . misc import identify_target_file
from nxc . parsers . ip import parse_targets
from nxc . parsers . nmap import parse_nmap_xml
from nxc . parsers . nessus import parse_nessus_file
from nxc . cli import gen_cli_args
from nxc . loaders . protocolloader import ProtocolLoader
from nxc . loaders . moduleloader import ModuleLoader
from nxc . first_run import first_run_setup
2023-09-20 15:59:16 +00:00
from nxc . paths import NXC_PATH
2023-09-14 21:07:15 +00:00
from nxc . console import nxc_console
from nxc . logger import nxc_logger
from nxc . config import nxc_config , nxc_workspace , config_log , ignore_opsec
2023-05-03 20:31:54 +00:00
from concurrent . futures import ThreadPoolExecutor , as_completed
2020-11-15 23:42:28 +00:00
import asyncio
2023-10-14 22:36:20 +00:00
from nxc . helpers import powershell
2017-06-26 09:49:04 +00:00
import shutil
2016-06-04 05:42:26 +00:00
import os
2023-05-03 20:31:54 +00:00
from os . path import exists
from os . path import join as path_join
from sys import exit
2016-08-12 06:36:38 +00:00
import logging
2023-04-06 00:09:07 +00:00
import sqlalchemy
2023-03-27 16:18:29 +00:00
from rich . progress import Progress
2023-10-22 17:05:06 +00:00
import platform
2023-07-05 09:57:21 +00:00
# Increase file_limit to prevent error "Too many open files"
2023-10-22 17:05:06 +00:00
if platform != " Windows " :
2023-07-05 10:20:12 +00:00
import resource
2023-09-23 01:10:21 +00:00
2023-07-05 10:20:12 +00:00
file_limit = list ( resource . getrlimit ( resource . RLIMIT_NOFILE ) )
if file_limit [ 1 ] > 10000 :
file_limit [ 0 ] = 10000
else :
file_limit [ 0 ] = file_limit [ 1 ]
file_limit = tuple ( file_limit )
resource . setrlimit ( resource . RLIMIT_NOFILE , file_limit )
2023-03-03 19:02:47 +00:00
2023-03-06 02:12:13 +00:00
2023-10-15 14:59:22 +00:00
2023-03-06 02:12:13 +00:00
def create_db_engine ( db_path ) :
2023-10-14 18:16:28 +00:00
return sqlalchemy . create_engine ( f " sqlite:/// { db_path } " , isolation_level = " AUTOCOMMIT " , future = True )
2020-11-15 23:42:28 +00:00
2016-06-04 05:42:26 +00:00
2023-03-27 16:18:29 +00:00
async def start_run ( protocol_obj , args , db , targets ) :
2023-09-20 15:59:16 +00:00
nxc_logger . debug ( " Creating ThreadPoolExecutor " )
2023-04-04 13:56:50 +00:00
if args . no_progress or len ( targets ) == 1 :
2023-03-31 15:51:02 +00:00
with ThreadPoolExecutor ( max_workers = args . threads + 1 ) as executor :
2023-09-14 21:07:15 +00:00
nxc_logger . debug ( f " Creating thread for { protocol_obj } " )
2023-03-31 15:51:02 +00:00
_ = [ executor . submit ( protocol_obj , args , db , target ) for target in targets ]
else :
2023-10-14 19:56:22 +00:00
with Progress ( console = nxc_console ) as progress , ThreadPoolExecutor ( max_workers = args . threads + 1 ) as executor :
current = 0
total = len ( targets )
tasks = progress . add_task (
f " [green]Running nxc against { total } { ' target ' if total == 1 else ' targets ' } " ,
total = total ,
)
nxc_logger . debug ( f " Creating thread for { protocol_obj } " )
futures = [ executor . submit ( protocol_obj , args , db , target ) for target in targets ]
for _ in as_completed ( futures ) :
current + = 1
progress . update ( tasks , completed = current )
2020-11-15 23:42:28 +00:00
2023-03-02 16:01:29 +00:00
2020-11-15 23:42:28 +00:00
def main ( ) :
2023-09-14 21:07:15 +00:00
first_run_setup ( nxc_logger )
2023-03-31 17:02:22 +00:00
root_logger = logging . getLogger ( " root " )
2016-12-15 07:28:00 +00:00
args = gen_cli_args ( )
2023-03-30 03:59:22 +00:00
if args . verbose :
2023-09-14 21:07:15 +00:00
nxc_logger . logger . setLevel ( logging . INFO )
2023-03-31 17:02:22 +00:00
root_logger . setLevel ( logging . INFO )
2023-03-30 03:59:22 +00:00
elif args . debug :
2023-09-14 21:07:15 +00:00
nxc_logger . logger . setLevel ( logging . DEBUG )
2023-03-31 17:02:22 +00:00
root_logger . setLevel ( logging . DEBUG )
2023-03-30 03:59:22 +00:00
else :
2023-09-14 21:07:15 +00:00
nxc_logger . logger . setLevel ( logging . ERROR )
2023-03-31 17:02:22 +00:00
root_logger . setLevel ( logging . ERROR )
2023-03-30 03:59:22 +00:00
2023-05-03 20:31:54 +00:00
# if these are the same, it might double log to file (two FileHandlers will be added)
# but this should never happen by accident
2023-04-07 16:32:30 +00:00
if config_log :
2023-09-14 21:07:15 +00:00
nxc_logger . add_file_log ( )
2023-04-15 12:45:00 +00:00
if hasattr ( args , " log " ) and args . log :
2023-09-14 21:07:15 +00:00
nxc_logger . add_file_log ( args . log )
2023-04-07 16:32:30 +00:00
2023-10-22 17:05:06 +00:00
nxc_logger . debug ( " PYTHON VERSION: " + sys . version )
nxc_logger . debug ( " RUNNING ON: " + platform . system ( ) + " Release: " + platform . release ( ) )
2023-09-14 21:07:15 +00:00
nxc_logger . debug ( f " Passed args: { args } " )
2023-03-30 03:59:22 +00:00
2023-06-16 08:14:55 +00:00
# FROM HERE ON A PROTOCOL IS REQUIRED
if not args . protocol :
exit ( 1 )
2023-10-14 19:56:22 +00:00
if args . protocol == " ssh " and args . key_file and not args . password :
nxc_logger . fail ( " Password is required, even if a key file is used - if no passphrase for key, use `-p ' ' ` " )
exit ( 1 )
2023-05-01 14:04:08 +00:00
2023-04-12 16:10:38 +00:00
if args . use_kcache and not os . environ . get ( " KRB5CCNAME " ) :
2023-09-14 21:07:15 +00:00
nxc_logger . error ( " KRB5CCNAME environment variable is not set " )
2023-05-03 20:31:54 +00:00
exit ( 1 )
2023-04-12 16:10:38 +00:00
2017-03-27 21:09:36 +00:00
module_server = None
targets = [ ]
2023-04-06 00:09:07 +00:00
server_port_dict = { " http " : 80 , " https " : 443 , " smb " : 445 }
2017-03-27 21:09:36 +00:00
2023-04-12 16:12:51 +00:00
if hasattr ( args , " cred_id " ) and args . cred_id :
2016-09-12 06:52:50 +00:00
for cred_id in args . cred_id :
2023-04-12 16:12:51 +00:00
if " - " in str ( cred_id ) :
start_id , end_id = cred_id . split ( " - " )
2016-09-21 19:40:59 +00:00
try :
for n in range ( int ( start_id ) , int ( end_id ) + 1 ) :
args . cred_id . append ( n )
args . cred_id . remove ( cred_id )
except Exception as e :
2023-09-14 21:07:15 +00:00
nxc_logger . error ( f " Error parsing database credential id: { e } " )
2023-05-03 20:31:54 +00:00
exit ( 1 )
2016-09-12 06:52:50 +00:00
2023-04-12 16:12:51 +00:00
if hasattr ( args , " target " ) and args . target :
2016-12-15 07:28:00 +00:00
for target in args . target :
2023-08-10 03:54:58 +00:00
if exists ( target ) and os . path . isfile ( target ) :
2017-10-25 02:08:19 +00:00
target_file_type = identify_target_file ( target )
2023-04-12 16:12:51 +00:00
if target_file_type == " nmap " :
2017-10-25 02:08:19 +00:00
targets . extend ( parse_nmap_xml ( target , args . protocol ) )
2023-04-12 16:12:51 +00:00
elif target_file_type == " nessus " :
2017-10-25 02:08:19 +00:00
targets . extend ( parse_nessus_file ( target , args . protocol ) )
else :
2023-10-12 21:06:04 +00:00
with open ( target ) as target_file :
2017-10-25 02:08:19 +00:00
for target_entry in target_file :
2020-04-28 12:42:25 +00:00
targets . extend ( parse_targets ( target_entry . strip ( ) ) )
2016-12-15 07:28:00 +00:00
else :
targets . extend ( parse_targets ( target ) )
2017-06-26 09:49:04 +00:00
# The following is a quick hack for the powershell obfuscation functionality, I know this is yucky
2023-04-12 16:12:51 +00:00
if hasattr ( args , " clear_obfscripts " ) and args . clear_obfscripts :
2023-09-14 21:07:15 +00:00
shutil . rmtree ( os . path . expanduser ( " ~/.nxc/obfuscated_scripts/ " ) )
os . mkdir ( os . path . expanduser ( " ~/.nxc/obfuscated_scripts/ " ) )
nxc_logger . success ( " Cleared cached obfuscated PowerShell scripts " )
2017-06-26 09:49:04 +00:00
2023-04-12 16:12:51 +00:00
if hasattr ( args , " obfs " ) and args . obfs :
2017-06-26 09:49:04 +00:00
powershell . obfuscate_ps_scripts = True
2023-09-14 21:07:15 +00:00
nxc_logger . debug ( f " Protocol: { args . protocol } " )
2023-03-27 04:48:45 +00:00
p_loader = ProtocolLoader ( )
2023-04-12 16:12:51 +00:00
protocol_path = p_loader . get_protocols ( ) [ args . protocol ] [ " path " ]
2023-09-14 21:07:15 +00:00
nxc_logger . debug ( f " Protocol Path: { protocol_path } " )
2023-04-12 16:12:51 +00:00
protocol_db_path = p_loader . get_protocols ( ) [ args . protocol ] [ " dbpath " ]
2023-09-14 21:07:15 +00:00
nxc_logger . debug ( f " Protocol DB Path: { protocol_db_path } " )
2016-12-15 07:28:00 +00:00
protocol_object = getattr ( p_loader . load_protocol ( protocol_path ) , args . protocol )
2023-09-14 21:07:15 +00:00
nxc_logger . debug ( f " Protocol Object: { protocol_object } " )
2023-10-12 21:17:20 +00:00
protocol_db_object = p_loader . load_protocol ( protocol_db_path ) . database
2023-09-14 21:07:15 +00:00
nxc_logger . debug ( f " Protocol DB Object: { protocol_db_object } " )
2016-12-15 07:28:00 +00:00
2023-09-20 04:09:25 +00:00
db_path = path_join ( NXC_PATH , " workspaces " , nxc_workspace , f " { args . protocol } .db " )
2023-09-14 21:07:15 +00:00
nxc_logger . debug ( f " DB Path: { db_path } " )
2023-03-03 14:55:05 +00:00
2023-03-06 02:12:13 +00:00
db_engine = create_db_engine ( db_path )
2023-03-03 19:02:47 +00:00
2023-03-06 02:12:13 +00:00
db = protocol_db_object ( db_engine )
2016-06-05 20:43:51 +00:00
2023-09-14 21:07:15 +00:00
# with the new nxc/config.py this can be eventually removed, as it can be imported anywhere
2023-10-12 21:17:20 +00:00
protocol_object . config = nxc_config
2018-02-23 10:13:46 +00:00
2023-05-27 22:24:40 +00:00
if args . module or args . list_modules :
2023-09-14 21:07:15 +00:00
loader = ModuleLoader ( args , db , nxc_logger )
2023-05-27 22:24:40 +00:00
modules = loader . list_modules ( )
2023-04-15 13:01:03 +00:00
if args . list_modules :
2023-10-15 14:59:22 +00:00
nxc_logger . highlight ( " LOW PRIVILEGE MODULES " )
for name , props in sorted ( modules . items ( ) ) :
if args . protocol in props [ " supported_protocols " ] and not props [ " requires_admin " ] :
nxc_logger . display ( f " { name : <25 } { props [ ' description ' ] } " )
nxc_logger . highlight ( " \n HIGH PRIVILEGE MODULES (requires admin privs) " )
2023-04-15 13:01:03 +00:00
for name , props in sorted ( modules . items ( ) ) :
2023-10-15 14:59:22 +00:00
if args . protocol in props [ " supported_protocols " ] and props [ " requires_admin " ] :
2023-09-14 21:07:15 +00:00
nxc_logger . display ( f " { name : <25 } { props [ ' description ' ] } " )
2023-05-03 20:31:54 +00:00
exit ( 0 )
2023-04-15 13:01:03 +00:00
elif args . module and args . show_module_options :
for module in args . module :
2023-09-14 21:07:15 +00:00
nxc_logger . display ( f " { module } module options: \n { modules [ module ] [ ' options ' ] } " )
2023-05-03 20:31:54 +00:00
exit ( 0 )
2023-04-15 13:01:03 +00:00
elif args . module :
2023-09-14 21:07:15 +00:00
nxc_logger . debug ( f " Modules to be Loaded: { args . module } , { type ( args . module ) } " )
2023-04-15 13:01:03 +00:00
for m in map ( str . lower , args . module ) :
if m not in modules :
2023-09-14 21:07:15 +00:00
nxc_logger . error ( f " Module not found: { m } " )
2023-04-15 13:01:03 +00:00
exit ( 1 )
2023-09-14 21:07:15 +00:00
nxc_logger . debug ( f " Loading module { m } at path { modules [ m ] [ ' path ' ] } " )
2023-04-15 13:01:03 +00:00
module = loader . init_module ( modules [ m ] [ " path " ] )
if not module . opsec_safe :
if ignore_opsec :
2023-09-20 15:59:16 +00:00
nxc_logger . debug ( " ignore_opsec is set in the configuration, skipping prompt " )
nxc_logger . display ( " Ignore OPSEC in configuration is set and OPSEC unsafe module loaded " )
2023-04-15 13:01:03 +00:00
else :
ans = input (
2023-05-04 13:21:17 +00:00
highlight (
2023-09-14 21:07:15 +00:00
" [!] Module is not opsec safe, are you sure you want to run this? [Y/n] For global configuration, change ignore_opsec value to True on ~/nxc/nxc.conf " ,
2023-05-04 13:21:17 +00:00
" red " ,
)
)
2023-04-12 16:12:51 +00:00
if ans . lower ( ) not in [ " y " , " yes " , " " ] :
2023-05-03 20:31:54 +00:00
exit ( 1 )
2023-03-17 13:07:21 +00:00
2023-04-15 13:01:03 +00:00
if not module . multiple_hosts and len ( targets ) > 1 :
2023-05-04 13:21:17 +00:00
ans = input (
highlight (
" [!] Running this module on multiple hosts doesn ' t really make any sense, are you sure you want to continue? [Y/n] " ,
" red " ,
)
)
2023-04-15 13:01:03 +00:00
if ans . lower ( ) not in [ " y " , " yes " , " " ] :
2023-05-03 20:31:54 +00:00
exit ( 1 )
2023-04-15 13:01:03 +00:00
if hasattr ( module , " on_request " ) or hasattr ( module , " has_response " ) :
if hasattr ( module , " required_server " ) :
args . server = module . required_server
if not args . server_port :
args . server_port = server_port_dict [ args . server ]
2023-09-14 21:07:15 +00:00
nxc_logger . debug ( f " proto_object: { protocol_object } , type: { type ( protocol_object ) } " )
nxc_logger . debug ( f " proto object dir: { dir ( protocol_object ) } " )
2023-04-15 13:01:03 +00:00
# get currently set modules, otherwise default to empty list
current_modules = getattr ( protocol_object , " module " , [ ] )
current_modules . append ( module )
2023-10-12 21:17:20 +00:00
protocol_object . module = current_modules
2023-09-14 21:07:15 +00:00
nxc_logger . debug ( f " proto object module after adding: { protocol_object . module } " )
2016-09-21 19:40:59 +00:00
2023-04-12 16:12:51 +00:00
if hasattr ( args , " ntds " ) and args . ntds and not args . userntds :
2023-05-04 13:21:17 +00:00
ans = input (
highlight (
" [!] Dumping the ntds can crash the DC on Windows Server 2019. Use the option --user <user> to dump a specific user safely or the module -M ntdsutil [Y/n] " ,
" red " ,
)
)
2023-05-02 15:17:59 +00:00
if ans . lower ( ) not in [ " y " , " yes " , " " ] :
2023-05-03 20:31:54 +00:00
exit ( 1 )
2023-03-21 21:07:18 +00:00
2016-06-04 05:42:26 +00:00
try :
2023-05-02 15:17:59 +00:00
asyncio . run ( start_run ( protocol_object , args , db , targets ) )
2016-06-04 05:42:26 +00:00
except KeyboardInterrupt :
2023-09-14 21:07:15 +00:00
nxc_logger . debug ( " Got keyboard interrupt " )
2020-11-15 23:42:28 +00:00
finally :
if module_server :
module_server . shutdown ( )
2023-03-26 05:52:37 +00:00
db_engine . dispose ( )
2020-05-10 18:06:08 +00:00
2023-03-04 22:04:28 +00:00
2023-04-12 16:12:51 +00:00
if __name__ == " __main__ " :
2022-04-03 11:38:48 +00:00
main ( )