Skip to content

Commit 70a99a7

Browse files
feat(options): change how args are parsed and passed, allowing --debug, etc to be used anywhere
1 parent e3baadb commit 70a99a7

10 files changed

Lines changed: 58 additions & 48 deletions

File tree

nxc/cli.py

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,22 @@ def gen_cli_args():
2323
COMMIT = ""
2424
CODENAME = "nxc4u"
2525
nxc_logger.debug(f"NXC VERSION: {VERSION} - {CODENAME} - {COMMIT}")
26-
27-
parser = argparse.ArgumentParser(description=rf"""
26+
27+
generic_parser = argparse.ArgumentParser(add_help=False)
28+
generic_group = generic_parser.add_argument_group("Generic Options", "Generic options for nxc across protocols")
29+
generic_group.add_argument("-t", "--threads", type=int, dest="threads", default=256, help="set how many concurrent threads to use (default: 256)")
30+
generic_group.add_argument("--timeout", default=None, type=int, help="max timeout in seconds of each thread (default: None)")
31+
generic_group.add_argument("--jitter", metavar="INTERVAL", type=str, help="sets a random delay between each authentication (default: None)")
32+
33+
output_parser = argparse.ArgumentParser(add_help=False)
34+
output_group = output_parser.add_argument_group("Output Options", "Options to set verbosity levels and control output")
35+
output_group.add_argument("--verbose", action="store_true", help="enable verbose output")
36+
output_group.add_argument("--debug", action="store_true", help="enable debug level information")
37+
output_group.add_argument("--no-progress", action="store_true", help="do not displaying progress bar during scan")
38+
output_group.add_argument("--log", metavar="LOG", help="export result into a custom file")
39+
40+
parser = argparse.ArgumentParser(
41+
description=rf"""
2842
. .
2943
.| |. _ _ _ _____
3044
|| || | \ | | ___ | |_ | ____| __ __ ___ ___
@@ -42,14 +56,11 @@ def gen_cli_args():
4256
{highlight('Version', 'red')} : {highlight(VERSION)}
4357
{highlight('Codename', 'red')}: {highlight(CODENAME)}
4458
{highlight('Commit', 'red')} : {highlight(COMMIT)}
45-
""", formatter_class=RawTextHelpFormatter)
46-
47-
parser.add_argument("-t", type=int, dest="threads", default=256, help="set how many concurrent threads to use (default: 256)")
48-
parser.add_argument("--timeout", default=None, type=int, help="max timeout in seconds of each thread (default: None)")
49-
parser.add_argument("--jitter", metavar="INTERVAL", type=str, help="sets a random delay between each authentication (default: None)")
50-
parser.add_argument("--no-progress", action="store_true", help="Not displaying progress bar during scan")
51-
parser.add_argument("--verbose", action="store_true", help="enable verbose output")
52-
parser.add_argument("--debug", action="store_true", help="enable debug level information")
59+
""",
60+
formatter_class=RawTextHelpFormatter,
61+
parents=[generic_parser, output_parser]
62+
)
63+
5364
parser.add_argument("--version", action="store_true", help="Display nxc version")
5465

5566
# we do module arg parsing here so we can reference the module_list attribute below
@@ -64,9 +75,9 @@ def gen_cli_args():
6475
module_parser.add_argument("--server-port", metavar="PORT", type=int, help="start the server on the specified port")
6576
module_parser.add_argument("--connectback-host", type=str, metavar="CHOST", help="IP for the remote system to connect back to (default: same as server-host)")
6677

67-
subparsers = parser.add_subparsers(title="protocols", dest="protocol", description="available protocols")
78+
subparsers = parser.add_subparsers(title="Available Protocols", dest="protocol")
6879

69-
std_parser = argparse.ArgumentParser(add_help=False)
80+
std_parser = argparse.ArgumentParser(add_help=False, parents=[generic_parser, output_parser])
7081
std_parser.add_argument("target", nargs="+" if not (module_parser.parse_known_args()[0].list_modules or module_parser.parse_known_args()[0].show_module_options) else "*", type=str, help="the target IP(s), range(s), CIDR(s), hostname(s), FQDN(s), file(s) containing a list of targets, NMap XML or .Nessus file(s)")
7182
std_parser.add_argument("-id", metavar="CRED_ID", nargs="+", default=[], type=str, dest="cred_id", help="database credential ID(s) to use for authentication")
7283
std_parser.add_argument("-u", metavar="USERNAME", dest="username", nargs="+", default=[], help="username(s) or file(s) containing usernames")
@@ -76,7 +87,6 @@ def gen_cli_args():
7687
std_parser.add_argument("--no-bruteforce", action="store_true", help="No spray when using file for username and password (user1 => password1, user2 => password2")
7788
std_parser.add_argument("--continue-on-success", action="store_true", help="continues authentication attempts even after successes")
7889
std_parser.add_argument("--use-kcache", action="store_true", help="Use Kerberos authentication from ccache file (KRB5CCNAME)")
79-
std_parser.add_argument("--log", metavar="LOG", help="Export result into a custom file")
8090
std_parser.add_argument("--aesKey", metavar="AESKEY", nargs="+", help="AES key to use for Kerberos Authentication (128 or 256 bits)")
8191
std_parser.add_argument("--kdcHost", metavar="KDCHOST", help="FQDN of the domain controller. If omitted it will use the domain part (FQDN) specified in the target parameter")
8292

@@ -91,7 +101,7 @@ def gen_cli_args():
91101
try:
92102
for protocol in protocols:
93103
protocol_object = p_loader.load_protocol(protocols[protocol]["argspath"])
94-
subparsers = protocol_object.proto_args(subparsers, std_parser, module_parser)
104+
subparsers = protocol_object.proto_args(subparsers, [std_parser, module_parser])
95105
except Exception as e:
96106
nxc_logger.exception(f"Error loading proto_args from proto_args.py file in protocol folder: {protocol} - {e}")
97107

nxc/protocols/ftp/proto_args.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
def proto_args(parser, std_parser, module_parser):
2-
ftp_parser = parser.add_parser("ftp", help="own stuff using FTP", parents=[std_parser, module_parser])
1+
def proto_args(parser, parents):
2+
ftp_parser = parser.add_parser("ftp", help="own stuff using FTP", parents=parents)
33
ftp_parser.add_argument("--port", type=int, default=21, help="FTP port (default: 21)")
44

55
cgroup = ftp_parser.add_argument_group("FTP Access", "Options for enumerating your access")

nxc/protocols/ldap/proto_args.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
def proto_args(parser, std_parser, module_parser):
2-
ldap_parser = parser.add_parser("ldap", help="own stuff using LDAP", parents=[std_parser, module_parser])
1+
def proto_args(parser, parents):
2+
ldap_parser = parser.add_parser("ldap", help="own stuff using LDAP", parents=parents)
33
ldap_parser.add_argument("-H", "--hash", metavar="HASH", dest="hash", nargs="+", default=[], help="NTLM hash(es) or file(s) containing NTLM hashes")
44
ldap_parser.add_argument("--port", type=int, choices={389, 636}, default=389, help="LDAP port (default: 389)")
55
ldap_parser.add_argument("--no-smb", action="store_true", help="No smb connection")

nxc/protocols/mssql/proto_args.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
def proto_args(parser, std_parser, module_parser):
2-
mssql_parser = parser.add_parser("mssql", help="own stuff using MSSQL", parents=[std_parser, module_parser])
1+
def proto_args(parser, parents):
2+
mssql_parser = parser.add_parser("mssql", help="own stuff using MSSQL", parents=parents)
33
mssql_parser.add_argument("-H", "--hash", metavar="HASH", dest="hash", nargs="+", default=[], help="NTLM hash(es) or file(s) containing NTLM hashes")
44
mssql_parser.add_argument("--port", default=1433, type=int, metavar="PORT", help="MSSQL port (default: 1433)")
55
mssql_parser.add_argument("--mssql-timeout", help="SQL server connection timeout, default is %(default)s seconds", type=int, default=5)

nxc/protocols/rdp/proto_args.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
def proto_args(parser, std_parser, module_parser):
2-
rdp_parser = parser.add_parser("rdp", help="own stuff using RDP", parents=[std_parser, module_parser])
1+
def proto_args(parser, parents):
2+
rdp_parser = parser.add_parser("rdp", help="own stuff using RDP", parents=parents)
33
rdp_parser.add_argument("-H", "--hash", metavar="HASH", dest="hash", nargs="+", default=[], help="NTLM hash(es) or file(s) containing NTLM hashes")
44
rdp_parser.add_argument("--port", type=int, default=3389, help="Custom RDP port")
55
rdp_parser.add_argument("--rdp-timeout", type=int, default=5, help="RDP timeout on socket connection, defalut is %(default)ss")

nxc/protocols/smb/proto_args.py

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
from argparse import _StoreTrueAction
22

33

4-
def proto_args(parser, std_parser, module_parser):
5-
smb_parser = parser.add_parser("smb", help="own stuff using SMB", parents=[std_parser, module_parser])
4+
def proto_args(parser, parents):
5+
smb_parser = parser.add_parser("smb", help="own stuff using SMB", parents=parents)
66
smb_parser.add_argument("-H", "--hash", metavar="HASH", dest="hash", nargs="+", default=[], help="NTLM hash(es) or file(s) containing NTLM hashes")
77
delegate_arg = smb_parser.add_argument("--delegate", action="store", help="Impersonate user with S4U2Self + S4U2Proxy")
88
self_delegate_arg = smb_parser.add_argument("--self", dest="no_s4u2proxy", action=get_conditional_action(_StoreTrueAction), make_required=[], help="Only do S4U2Self, no S4U2Proxy (use with delegate)")
@@ -22,12 +22,10 @@ def proto_args(parser, std_parser, module_parser):
2222
cgroup.add_argument("--lsa", action="store_true", help="dump LSA secrets from target systems")
2323
cgroup.add_argument("--ntds", choices={"vss", "drsuapi"}, nargs="?", const="drsuapi", help="dump the NTDS.dit from target DCs using the specifed method\n(default: drsuapi)")
2424
cgroup.add_argument("--dpapi", choices={"cookies", "nosystem"}, nargs="*", help='dump DPAPI secrets from target systems, can dump cookies if you add "cookies", will not dump SYSTEM dpapi if you add nosystem\n')
25-
26-
ngroup = smb_parser.add_argument_group("Credential Gathering", "Options for gathering credentials")
27-
ngroup.add_argument("--mkfile", action="store", help="DPAPI option. File with masterkeys in form of {GUID}:SHA1")
28-
ngroup.add_argument("--pvk", action="store", help="DPAPI option. File with domain backupkey")
29-
ngroup.add_argument("--enabled", action="store_true", help="Only dump enabled targets from DC")
30-
ngroup.add_argument("--user", dest="userntds", type=str, help="Dump selected user from DC")
25+
cgroup.add_argument("--mkfile", action="store", help="DPAPI option. File with masterkeys in form of {GUID}:SHA1")
26+
cgroup.add_argument("--pvk", action="store", help="DPAPI option. File with domain backupkey")
27+
cgroup.add_argument("--enabled", action="store_true", help="Only dump enabled targets from DC")
28+
cgroup.add_argument("--user", dest="userntds", type=str, help="Dump selected user from DC")
3129

3230
egroup = smb_parser.add_argument_group("Mapping/Enumeration", "Options for Mapping/Enumerating")
3331
egroup.add_argument("--shares", action="store_true", help="enumerate shares and access")
@@ -63,16 +61,18 @@ def proto_args(parser, std_parser, module_parser):
6361
tgroup.add_argument("--get-file", action="append", nargs=2, metavar="FILE", help="Get a remote file, ex: \\\\Windows\\\\Temp\\\\whoami.txt whoami.txt")
6462
tgroup.add_argument("--append-host", action="store_true", help="append the host to the get-file filename")
6563

66-
cgroup = smb_parser.add_argument_group("Command Execution", "Options for executing commands")
67-
cgroup.add_argument("--exec-method", choices={"wmiexec", "mmcexec", "smbexec", "atexec"}, default=None, help="method to execute the command. Ignored if in MSSQL mode (default: wmiexec)")
68-
cgroup.add_argument("--dcom-timeout", help="DCOM connection timeout, default is 5 secondes", type=int, default=5)
69-
cgroup.add_argument("--get-output-tries", help="Number of times atexec/smbexec/mmcexec tries to get results, default is 5", type=int, default=5)
70-
cgroup.add_argument("--codec", default="utf-8", help="Set encoding used (codec) from the target's output (default: utf-8). If errors are detected, run chcp.com at the target & map the result with https://docs.python.org/3/library/codecs.html#standard-encodings and then execute again with --codec and the corresponding codec")
71-
cgroup.add_argument("--force-ps32", action="store_true", help="force the PowerShell command to run in a 32-bit process")
72-
cgroup.add_argument("--no-output", action="store_true", help="do not retrieve command output")
73-
cegroup = cgroup.add_mutually_exclusive_group()
74-
cegroup.add_argument("-x", metavar="COMMAND", dest="execute", help="execute the specified CMD command")
75-
cegroup.add_argument("-X", metavar="PS_COMMAND", dest="ps_execute", help="execute the specified PowerShell command")
64+
cegroup = smb_parser.add_argument_group("Command Execution", "Options for executing commands")
65+
cegroup.add_argument("--exec-method", choices={"wmiexec", "mmcexec", "smbexec", "atexec"}, default=None, help="method to execute the command. Ignored if in MSSQL mode (default: wmiexec)")
66+
cegroup.add_argument("--dcom-timeout", help="DCOM connection timeout, default is 5 secondes", type=int, default=5)
67+
cegroup.add_argument("--get-output-tries", help="Number of times atexec/smbexec/mmcexec tries to get results, default is 5", type=int, default=5)
68+
cegroup.add_argument("--codec", default="utf-8", help="Set encoding used (codec) from the target's output (default: utf-8). If errors are detected, run chcp.com at the target & map the result with https://docs.python.org/3/library/codecs.html#standard-encodings and then execute again with --codec and the corresponding codec")
69+
cegroup.add_argument("--force-ps32", action="store_true", help="force the PowerShell command to run in a 32-bit process")
70+
cegroup.add_argument("--no-output", action="store_true", help="do not retrieve command output")
71+
# command execution method
72+
cemgroup = cgroup.add_mutually_exclusive_group()
73+
cemgroup.add_argument("-x", metavar="COMMAND", dest="execute", help="execute the specified CMD command")
74+
cemgroup.add_argument("-X", metavar="PS_COMMAND", dest="ps_execute", help="execute the specified PowerShell command")
75+
7676
psgroup = smb_parser.add_argument_group("Powershell Obfuscation", "Options for PowerShell script obfuscation")
7777
psgroup.add_argument("--obfs", action="store_true", help="Obfuscate PowerShell scripts")
7878
psgroup.add_argument("--amsi-bypass", nargs=1, metavar="FILE", help="File with a custom AMSI bypass")

nxc/protocols/ssh/proto_args.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from argparse import _StoreAction
22

3-
def proto_args(parser, std_parser, module_parser):
4-
ssh_parser = parser.add_parser("ssh", help="own stuff using SSH", parents=[std_parser, module_parser])
3+
def proto_args(parser, parents):
4+
ssh_parser = parser.add_parser("ssh", help="own stuff using SSH", parents=parents)
55
ssh_parser.add_argument("--key-file", type=str, help="Authenticate using the specified private key. Treats the password parameter as the key's passphrase.")
66
ssh_parser.add_argument("--port", type=int, default=22, help="SSH port (default: 22)")
77
ssh_parser.add_argument("--ssh-timeout", help="SSH connection timeout, default is %(default)s secondes", type=int, default=15)

nxc/protocols/vnc/proto_args.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
def proto_args(parser, std_parser, module_parser):
2-
vnc_parser = parser.add_parser("vnc", help="own stuff using VNC", parents=[std_parser, module_parser])
1+
def proto_args(parser, parents):
2+
vnc_parser = parser.add_parser("vnc", help="own stuff using VNC", parents=parents)
33
vnc_parser.add_argument("--port", type=int, default=5900, help="Custom VNC port")
44
vnc_parser.add_argument("--vnc-sleep", type=int, default=5, help="VNC Sleep on socket connection to avoid rate limit")
55

nxc/protocols/winrm/proto_args.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
def proto_args(parser, std_parser, module_parser):
2-
winrm_parser = parser.add_parser("winrm", help="own stuff using WINRM", parents=[std_parser, module_parser])
1+
def proto_args(parser, parents):
2+
winrm_parser = parser.add_parser("winrm", help="own stuff using WINRM", parents=parents)
33
winrm_parser.add_argument("-H", "--hash", metavar="HASH", dest="hash", nargs="+", default=[], help="NTLM hash(es) or file(s) containing NTLM hashes")
44
winrm_parser.add_argument("--port", nargs="+", default=["5985", "5986"], help="Custom WinRM port, default is %(default)s, format: 'http-port https-port'(with space separated) or 'single-port'"
55
"(http & https will use same port when given single port)")

nxc/protocols/wmi/proto_args.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
def proto_args(parser, std_parser, module_parser):
2-
wmi_parser = parser.add_parser("wmi", help="own stuff using WMI", parents=[std_parser, module_parser], conflict_handler="resolve")
1+
def proto_args(parser, parents):
2+
wmi_parser = parser.add_parser("wmi", help="own stuff using WMI", conflict_handler="resolve", parents=parents)
33
wmi_parser.add_argument("-H", "--hash", metavar="HASH", dest="hash", nargs="+", default=[], help="NTLM hash(es) or file(s) containing NTLM hashes")
44
wmi_parser.add_argument("--port", type=int, choices={135}, default=135, help="WMI port (default: 135)")
55
wmi_parser.add_argument("--rpc-timeout", help="RPC/DCOM(WMI) connection timeout, default is %(default)s seconds", type=int, default=2)

0 commit comments

Comments
 (0)