Skip to content

Commit 3eaa163

Browse files
authored
Merge branch 'main' into main
2 parents 169a7dc + 1af2828 commit 3eaa163

80 files changed

Lines changed: 2394 additions & 1072 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/ISSUE_TEMPLATE/feature_request.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
---
2+
name: Feature request
3+
about: Request a new feature or enhancement
4+
title: ''
5+
labels: ''
6+
assignees: ''
7+
8+
---
19
**Please Describe The Problem To Be Solved**
210
(Replace This Text: Please present a concise description of the problem to be addressed by this feature request. Please be clear what parts of the problem are considered to be in-scope and out-of-scope.)
311

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
---
2+
name: Pull request
3+
about: Update code to fix a bug or add an enhancement/feature
4+
title: ''
5+
labels: ''
6+
assignees: ''
7+
8+
---
9+
## Description
10+
11+
Please include a summary of the change and which issue is fixed, or what the enhancement does.
12+
Please also include relevant motivation and context.
13+
List any dependencies that are required for this change.
14+
15+
## Type of change
16+
Please delete options that are not relevant.
17+
- [ ] Bug fix (non-breaking change which fixes an issue)
18+
- [ ] New feature (non-breaking change which adds functionality)
19+
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
20+
- [ ] This change requires a documentation update
21+
- [ ] This requires a third party update (such as Impacket, Dploot, lsassy, etc)
22+
23+
## How Has This Been Tested?
24+
Please describe the tests that you ran to verify your changes (e2e, single commands, etc)
25+
Please also list any relevant details for your test configuration, such as your locally running machine Python version & OS, as well as the target(s) you tested against, including software versions
26+
27+
If you are using poetry, you can easily run tests via:
28+
`poetry run python tests/e2e_tests.py -t $TARGET -u $USER -p $PASSWORD`
29+
There are additional options like `--errors` to display ALL errors (some may not be failures), `--poetry` (output will include the poetry run prepended), `--line-num $START-$END $SINGLE` for only running a subset
30+
31+
## Screenshots (if appropriate):
32+
Screenshots are always nice to have and can give a visual representation of the change.
33+
If appropriate include before and after screenshot(s) to show which results are to be expected.
34+
35+
## Checklist:
36+
37+
- [ ] I have ran Ruff against my changes (via poetry: `poetry run python -m ruff check . --preview`, use `--fix` to automatically fix what it can)
38+
- [ ] I have added or updated the tests/e2e_commands.txt file if necessary
39+
- [ ] New and existing e2e tests pass locally with my changes
40+
- [ ] My code follows the style guidelines of this project (should be covered by Ruff above)
41+
- [ ] If reliant on third party dependencies, such as Impacket, dploot, lsassy, etc, I have linked the relevant PRs in those projects
42+
- [ ] I have performed a self-review of my own code
43+
- [ ] I have commented my code, particularly in hard-to-understand areas
44+
- [ ] I have made corresponding changes to the documentation (PR here: https://github.com/Pennyw0rth/NetExec-Wiki)

.github/workflows/test.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ jobs:
2626
python-version: ${{ matrix.python-version }}
2727
cache: poetry
2828
cache-dependency-path: poetry.lock
29+
- name: Install with pipx
30+
run: |
31+
pipx install . --python python${{ matrix.python-version }}
2932
- name: Install poetry
3033
run: |
3134
pipx install poetry --python python${{ matrix.python-version }}
@@ -45,4 +48,4 @@ jobs:
4548
poetry run netexec mssql 127.0.0.1
4649
poetry run netexec ssh 127.0.0.1
4750
poetry run netexec ftp 127.0.0.1
48-
poetry run netexec smb 127.0.0.1 -M veeam
51+
poetry run netexec smb 127.0.0.1 -M veeam

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
data/nxc.db
22
hash_spider_default.sqlite3
3+
hash_spider_testing.sqlite3
34
*.bak
45
*.log
56
.venv

netexec.spec

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,10 @@ a = Analysis(
6767
'dploot.triage.browser',
6868
'dploot.triage.credentials',
6969
'dploot.triage.masterkeys',
70+
'dploot.triage.mobaxterm',
7071
'dploot.triage.backupkey',
7172
'dploot.triage.wifi',
73+
'dploot.triage.sccm',
7274
'dploot.lib.target',
7375
'dploot.lib.smb',
7476
'pyasn1_modules.rfc5652',

nxc/cli.py

Lines changed: 64 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from nxc.paths import NXC_PATH
1010
from nxc.loaders.protocolloader import ProtocolLoader
1111
from nxc.helpers.logger import highlight
12+
from nxc.helpers.args import DisplayDefaultsNotNone
1213
from nxc.logger import nxc_logger, setup_debug_logging
1314
import importlib.metadata
1415

@@ -21,10 +22,31 @@ def gen_cli_args():
2122
except ValueError:
2223
VERSION = importlib.metadata.version("netexec")
2324
COMMIT = ""
24-
CODENAME = "nxc4u"
25+
CODENAME = "ItsAlwaysDNS"
2526
nxc_logger.debug(f"NXC VERSION: {VERSION} - {CODENAME} - {COMMIT}")
26-
27-
parser = argparse.ArgumentParser(description=rf"""
27+
28+
generic_parser = argparse.ArgumentParser(add_help=False, formatter_class=DisplayDefaultsNotNone)
29+
generic_group = generic_parser.add_argument_group("Generic", "Generic options for nxc across protocols")
30+
generic_group.add_argument("-t", "--threads", type=int, dest="threads", default=256, help="set how many concurrent threads to use")
31+
generic_group.add_argument("--timeout", default=None, type=int, help="max timeout in seconds of each thread")
32+
generic_group.add_argument("--jitter", metavar="INTERVAL", type=str, help="sets a random delay between each authentication")
33+
34+
output_parser = argparse.ArgumentParser(add_help=False, formatter_class=DisplayDefaultsNotNone)
35+
output_group = output_parser.add_argument_group("Output", "Options to set verbosity levels and control output")
36+
output_group.add_argument("--verbose", action="store_true", help="enable verbose output")
37+
output_group.add_argument("--debug", action="store_true", help="enable debug level information")
38+
output_group.add_argument("--no-progress", action="store_true", help="do not displaying progress bar during scan")
39+
output_group.add_argument("--log", metavar="LOG", help="export result into a custom file")
40+
41+
dns_parser = argparse.ArgumentParser(add_help=False, formatter_class=DisplayDefaultsNotNone)
42+
dns_group = dns_parser.add_argument_group("DNS")
43+
dns_group.add_argument("-6", dest="force_ipv6", action="store_true", help="Enable force IPv6")
44+
dns_group.add_argument("--dns-server", action="store", help="Specify DNS server (default: Use hosts file & System DNS)")
45+
dns_group.add_argument("--dns-tcp", action="store_true", help="Use TCP instead of UDP for DNS queries")
46+
dns_group.add_argument("--dns-timeout", action="store", type=int, default=3, help="DNS query timeout in seconds")
47+
48+
parser = argparse.ArgumentParser(
49+
description=rf"""
2850
. .
2951
.| |. _ _ _ _____
3052
|| || | \ | | ___ | |_ | ____| __ __ ___ ___
@@ -42,56 +64,55 @@ def gen_cli_args():
4264
{highlight('Version', 'red')} : {highlight(VERSION)}
4365
{highlight('Codename', 'red')}: {highlight(CODENAME)}
4466
{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 connection (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")
67+
""",
68+
formatter_class=RawTextHelpFormatter,
69+
parents=[generic_parser, output_parser, dns_parser]
70+
)
71+
5372
parser.add_argument("--version", action="store_true", help="Display nxc version")
5473

5574
# we do module arg parsing here so we can reference the module_list attribute below
56-
module_parser = argparse.ArgumentParser(add_help=False)
57-
mgroup = module_parser.add_mutually_exclusive_group()
75+
module_parser = argparse.ArgumentParser(add_help=False, formatter_class=DisplayDefaultsNotNone)
76+
mgroup = module_parser.add_argument_group("Modules", "Options for nxc modules")
5877
mgroup.add_argument("-M", "--module", choices=get_module_names(), action="append", metavar="MODULE", help="module to use")
59-
module_parser.add_argument("-o", metavar="MODULE_OPTION", nargs="+", default=[], dest="module_options", help="module options")
60-
module_parser.add_argument("-L", "--list-modules", action="store_true", help="list available modules")
61-
module_parser.add_argument("--options", dest="show_module_options", action="store_true", help="display module options")
62-
module_parser.add_argument("--server", choices={"http", "https"}, default="https", help="use the selected server (default: https)")
63-
module_parser.add_argument("--server-host", type=str, default="0.0.0.0", metavar="HOST", help="IP to bind the server to (default: 0.0.0.0)")
64-
module_parser.add_argument("--server-port", metavar="PORT", type=int, help="start the server on the specified port")
65-
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)")
78+
mgroup.add_argument("-o", metavar="MODULE_OPTION", nargs="+", default=[], dest="module_options", help="module options")
79+
mgroup.add_argument("-L", "--list-modules", action="store_true", help="list available modules")
80+
mgroup.add_argument("--options", dest="show_module_options", action="store_true", help="display module options")
6681

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

69-
std_parser = argparse.ArgumentParser(add_help=False)
84+
std_parser = argparse.ArgumentParser(add_help=False, parents=[generic_parser, output_parser, dns_parser], formatter_class=DisplayDefaultsNotNone)
7085
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)")
71-
std_parser.add_argument("-id", metavar="CRED_ID", nargs="+", default=[], type=str, dest="cred_id", help="database credential ID(s) to use for authentication")
72-
std_parser.add_argument("-u", metavar="USERNAME", dest="username", nargs="+", default=[], help="username(s) or file(s) containing usernames")
73-
std_parser.add_argument("-p", metavar="PASSWORD", dest="password", nargs="+", default=[], help="password(s) or file(s) containing passwords")
74-
std_parser.add_argument("--ignore-pw-decoding", action="store_true", help="Ignore non UTF-8 characters when decoding the password file")
75-
std_parser.add_argument("-k", "--kerberos", action="store_true", help="Use Kerberos authentication")
76-
std_parser.add_argument("--no-bruteforce", action="store_true", help="No spray when using file for username and password (user1 => password1, user2 => password2")
77-
std_parser.add_argument("--continue-on-success", action="store_true", help="continues authentication attempts even after successes")
78-
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")
80-
std_parser.add_argument("--aesKey", metavar="AESKEY", nargs="+", help="AES key to use for Kerberos Authentication (128 or 256 bits)")
81-
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")
82-
83-
fail_group = std_parser.add_mutually_exclusive_group()
84-
fail_group.add_argument("--gfail-limit", metavar="LIMIT", type=int, help="max number of global failed login attempts")
85-
fail_group.add_argument("--ufail-limit", metavar="LIMIT", type=int, help="max number of failed login attempts per username")
86-
fail_group.add_argument("--fail-limit", metavar="LIMIT", type=int, help="max number of failed login attempts per host")
86+
credential_group = std_parser.add_argument_group("Authentication", "Options for authenticating")
87+
credential_group.add_argument("-u", "--username", metavar="USERNAME", dest="username", nargs="+", default=[], help="username(s) or file(s) containing usernames")
88+
credential_group.add_argument("-p", "--password", metavar="PASSWORD", dest="password", nargs="+", default=[], help="password(s) or file(s) containing passwords")
89+
credential_group.add_argument("-id", metavar="CRED_ID", nargs="+", default=[], type=str, dest="cred_id", help="database credential ID(s) to use for authentication")
90+
credential_group.add_argument("--ignore-pw-decoding", action="store_true", help="Ignore non UTF-8 characters when decoding the password file")
91+
credential_group.add_argument("--no-bruteforce", action="store_true", help="No spray when using file for username and password (user1 => password1, user2 => password2)")
92+
credential_group.add_argument("--continue-on-success", action="store_true", help="continues authentication attempts even after successes")
93+
credential_group.add_argument("--gfail-limit", metavar="LIMIT", type=int, help="max number of global failed login attempts")
94+
credential_group.add_argument("--ufail-limit", metavar="LIMIT", type=int, help="max number of failed login attempts per username")
95+
credential_group.add_argument("--fail-limit", metavar="LIMIT", type=int, help="max number of failed login attempts per host")
96+
97+
kerberos_group = std_parser.add_argument_group("Kerberos", "Options for Kerberos authentication")
98+
kerberos_group.add_argument("-k", "--kerberos", action="store_true", help="Use Kerberos authentication")
99+
kerberos_group.add_argument("--use-kcache", action="store_true", help="Use Kerberos authentication from ccache file (KRB5CCNAME)")
100+
kerberos_group.add_argument("--aesKey", metavar="AESKEY", nargs="+", help="AES key to use for Kerberos Authentication (128 or 256 bits)")
101+
kerberos_group.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")
102+
103+
server_group = std_parser.add_argument_group("Servers", "Options for nxc servers")
104+
server_group.add_argument("--server", choices={"http", "https"}, default="https", help="use the selected server")
105+
server_group.add_argument("--server-host", type=str, default="0.0.0.0", metavar="HOST", help="IP to bind the server to")
106+
server_group.add_argument("--server-port", metavar="PORT", type=int, help="start the server on the specified port")
107+
server_group.add_argument("--connectback-host", type=str, metavar="CHOST", help="IP for the remote system to connect back to")
87108

88109
p_loader = ProtocolLoader()
89110
protocols = p_loader.get_protocols()
90111

91112
try:
92113
for protocol in protocols:
93114
protocol_object = p_loader.load_protocol(protocols[protocol]["argspath"])
94-
subparsers = protocol_object.proto_args(subparsers, std_parser, module_parser)
115+
subparsers = protocol_object.proto_args(subparsers, [std_parser, module_parser])
95116
except Exception as e:
96117
nxc_logger.exception(f"Error loading proto_args from proto_args.py file in protocol folder: {protocol} - {e}")
97118

@@ -106,6 +127,10 @@ def gen_cli_args():
106127
print(f"{VERSION} - {CODENAME} - {COMMIT}")
107128
sys.exit(1)
108129

130+
# Multiply output_tries by 10 to enable more fine granural control, see exec methods
131+
if hasattr(args, "get_output_tries"):
132+
args.get_output_tries = args.get_output_tries * 10
133+
109134
return args
110135

111136

0 commit comments

Comments
 (0)