Skip to content

Commit 7a1eca1

Browse files
authored
Merge branch 'main' into notepad
2 parents 171c79f + 3591950 commit 7a1eca1

94 files changed

Lines changed: 2060 additions & 750 deletions

Some content is hidden

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

.github/PULL_REQUEST_TEMPLATE.md

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,20 @@
11
## Description
22

33
Please include a summary of the change and which issue is fixed, or what the enhancement does.
4-
Please also include relevant motivation and context.
54
List any dependencies that are required for this change.
65

76
## Type of change
8-
Please delete options that are not relevant.
97
- [ ] Bug fix (non-breaking change which fixes an issue)
108
- [ ] New feature (non-breaking change which adds functionality)
119
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
1210
- [ ] This change requires a documentation update
1311
- [ ] This requires a third party update (such as Impacket, Dploot, lsassy, etc)
1412

15-
## How Has This Been Tested?
16-
Please describe the tests that you ran to verify your changes (e2e, single commands, etc)
17-
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
18-
19-
If you are using poetry, you can easily run tests via:
20-
`poetry run python tests/e2e_tests.py -t $TARGET -u $USER -p $PASSWORD`
21-
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
13+
## Setup guide for the review
14+
Please provide guidance on what setup is needed to test the introduced changes, such as your locally running machine Python version & OS, as well as the target(s) you tested against, including software versions.
15+
In particular:
16+
- Bug Fix: Please provide a short description on how to trigger the bug, to make the bug reproducable for the reviewer.
17+
- Added Feature/Enhancement: Please specify what setup is needed in order to test the changes. E.g. is additional software needed? GPO changes required? Specific registry settings that need to be changed?
2218

2319
## Screenshots (if appropriate):
2420
Screenshots are always nice to have and can give a visual representation of the change.
@@ -29,8 +25,7 @@ If appropriate include before and after screenshot(s) to show which results are
2925
- [ ] I have ran Ruff against my changes (via poetry: `poetry run python -m ruff check . --preview`, use `--fix` to automatically fix what it can)
3026
- [ ] I have added or updated the tests/e2e_commands.txt file if necessary
3127
- [ ] New and existing e2e tests pass locally with my changes
32-
- [ ] My code follows the style guidelines of this project (should be covered by Ruff above)
33-
- [ ] If reliant on third party dependencies, such as Impacket, dploot, lsassy, etc, I have linked the relevant PRs in those projects
28+
- [ ] If reliant on changes of third party dependencies, such as Impacket, dploot, lsassy, etc, I have linked the relevant PRs in those projects
3429
- [ ] I have performed a self-review of my own code
3530
- [ ] I have commented my code, particularly in hard-to-understand areas
3631
- [ ] I have made corresponding changes to the documentation (PR here: https://github.com/Pennyw0rth/NetExec-Wiki)

netexec.spec

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,13 @@ a = Analysis(
2727
'impacket.dcerpc.v5.gkdi',
2828
'impacket.dcerpc.v5.rprn',
2929
'impacket.dcerpc.v5.even',
30+
'impacket.dcerpc.v5.even6',
3031
'impacket.dpapi_ng',
3132
'impacket.tds',
3233
'impacket.version',
3334
'impacket.ldap.ldap',
3435
'jwt',
3536
'nxc.connection',
36-
'nxc.servers.smb',
3737
'nxc.protocols.smb.wmiexec',
3838
'nxc.protocols.smb.atexec',
3939
'nxc.protocols.smb.smbexec',
@@ -44,6 +44,7 @@ a = Analysis(
4444
'nxc.parsers.ldap_results',
4545
'nxc.helpers.bash',
4646
'nxc.helpers.bloodhound',
47+
'nxc.helpers.even6_parser',
4748
'nxc.helpers.msada_guids',
4849
'nxc.helpers.ntlm_parser',
4950
'paramiko',

nxc/cli.py

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
def gen_cli_args():
1818
setup_debug_logging()
19-
19+
2020
try:
2121
VERSION, COMMIT = importlib.metadata.version("netexec").split("+")
2222
DISTANCE, COMMIT = COMMIT.split(".")
@@ -25,29 +25,28 @@ def gen_cli_args():
2525
COMMIT = ""
2626
DISTANCE = ""
2727
CODENAME = "SmoothOperator"
28-
nxc_logger.debug(f"NXC VERSION: {VERSION} - {CODENAME} - {COMMIT} - {DISTANCE}")
29-
28+
3029
generic_parser = argparse.ArgumentParser(add_help=False, formatter_class=DisplayDefaultsNotNone)
3130
generic_group = generic_parser.add_argument_group("Generic", "Generic options for nxc across protocols")
3231
generic_group.add_argument("--version", action="store_true", help="Display nxc version")
3332
generic_group.add_argument("-t", "--threads", type=int, dest="threads", default=256, help="set how many concurrent threads to use")
3433
generic_group.add_argument("--timeout", default=None, type=int, help="max timeout in seconds of each thread")
3534
generic_group.add_argument("--jitter", metavar="INTERVAL", type=str, help="sets a random delay between each authentication")
36-
35+
3736
output_parser = argparse.ArgumentParser(add_help=False, formatter_class=DisplayDefaultsNotNone)
3837
output_group = output_parser.add_argument_group("Output", "Options to set verbosity levels and control output")
3938
output_group.add_argument("--verbose", action="store_true", help="enable verbose output")
4039
output_group.add_argument("--debug", action="store_true", help="enable debug level information")
4140
output_group.add_argument("--no-progress", action="store_true", help="do not displaying progress bar during scan")
4241
output_group.add_argument("--log", metavar="LOG", help="export result into a custom file")
43-
42+
4443
dns_parser = argparse.ArgumentParser(add_help=False, formatter_class=DisplayDefaultsNotNone)
4544
dns_group = dns_parser.add_argument_group("DNS")
4645
dns_group.add_argument("-6", dest="force_ipv6", action="store_true", help="Enable force IPv6")
4746
dns_group.add_argument("--dns-server", action="store", help="Specify DNS server (default: Use hosts file & System DNS)")
4847
dns_group.add_argument("--dns-tcp", action="store_true", help="Use TCP instead of UDP for DNS queries")
4948
dns_group.add_argument("--dns-timeout", action="store", type=int, default=3, help="DNS query timeout in seconds")
50-
49+
5150
parser = argparse.ArgumentParser(
5251
description=rf"""
5352
. .
@@ -107,12 +106,6 @@ def gen_cli_args():
107106
certificate_group.add_argument("--pfx-pass", metavar="PFXPASS", help="Password of the pfx certificate")
108107
certificate_group.add_argument("--pem-cert", metavar="PEMCERT", help="Use certificate authentication from PEM file")
109108
certificate_group.add_argument("--pem-key", metavar="PEMKEY", help="Private key for the PEM format")
110-
111-
server_group = std_parser.add_argument_group("Servers", "Options for nxc servers")
112-
server_group.add_argument("--server", choices={"http", "https"}, default="https", help="use the selected server")
113-
server_group.add_argument("--server-host", type=str, default="0.0.0.0", metavar="HOST", help="IP to bind the server to")
114-
server_group.add_argument("--server-port", metavar="PORT", type=int, help="start the server on the specified port")
115-
server_group.add_argument("--connectback-host", type=str, metavar="CHOST", help="IP for the remote system to connect back to")
116109

117110
p_loader = ProtocolLoader()
118111
protocols = p_loader.get_protocols()
@@ -139,7 +132,7 @@ def gen_cli_args():
139132
if hasattr(args, "get_output_tries"):
140133
args.get_output_tries = args.get_output_tries * 10
141134

142-
return args
135+
return args, [CODENAME, VERSION, COMMIT, DISTANCE]
143136

144137

145138
def get_module_names():

nxc/config.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@
1818

1919
# Check if there are any missing options in the config file
2020
for section in nxc_default_config.sections():
21+
if not nxc_config.has_section(section):
22+
nxc_logger.display(f"Adding missing section '{section}' to nxc.conf")
23+
nxc_config.add_section(section)
24+
with open(path_join(NXC_PATH, "nxc.conf"), "w") as config_file:
25+
nxc_config.write(config_file)
2126
for option in nxc_default_config.options(section):
2227
if not nxc_config.has_option(section, option):
2328
nxc_logger.display(f"Adding missing option '{option}' in config section '{section}' to nxc.conf")

nxc/connection.py

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ def call_modules(self):
275275
extra={
276276
"module_name": module.name.upper(),
277277
"host": self.host,
278-
"port": self.args.port,
278+
"port": self.port,
279279
"hostname": self.hostname,
280280
},
281281
)
@@ -284,11 +284,6 @@ def call_modules(self):
284284
context = Context(self.db, module_logger, self.args)
285285
context.localip = self.local_ip
286286

287-
if hasattr(module, "on_request") or hasattr(module, "has_response"):
288-
self.logger.debug(f"Module {module.name} has on_request or has_response methods")
289-
self.server.connection = self
290-
self.server.context.localip = self.local_ip
291-
292287
if hasattr(module, "on_login"):
293288
self.logger.debug(f"Module {module.name} has on_login method")
294289
module.on_login(context, self)
@@ -297,13 +292,8 @@ def call_modules(self):
297292
self.logger.debug(f"Module {module.name} has on_admin_login method")
298293
module.on_admin_login(context, self)
299294

300-
if (not hasattr(module, "on_request") and not hasattr(module, "has_response")) and hasattr(module, "on_shutdown"):
301-
self.logger.debug(f"Module {module.name} has on_shutdown method")
302-
module.on_shutdown(context, self)
303-
304295
def inc_failed_login(self, username):
305-
global global_failed_logins
306-
global user_failed_logins
296+
global global_failed_logins, user_failed_logins
307297

308298
if username not in user_failed_logins:
309299
user_failed_logins[username] = 0
@@ -313,16 +303,15 @@ def inc_failed_login(self, username):
313303
self.failed_logins += 1
314304

315305
def over_fail_limit(self, username):
316-
global global_failed_logins
317-
global user_failed_logins
306+
global global_failed_logins, user_failed_logins
318307

319308
if global_failed_logins == self.args.gfail_limit:
320309
return True
321310

322311
if self.failed_logins == self.args.fail_limit:
323312
return True
324313

325-
if username in user_failed_logins and self.args.ufail_limit == user_failed_logins[username]:
314+
if username in user_failed_logins and self.args.ufail_limit == user_failed_logins[username]: # noqa: SIM103
326315
return True
327316

328317
return False
@@ -427,14 +416,14 @@ def parse_credentials(self):
427416
with open(ntlm_hash) as ntlm_hash_file:
428417
for i, line in enumerate(ntlm_hash_file):
429418
line = line.strip()
430-
if len(line) != 32 and len(line) != 65:
419+
if len(line) != 32 and len(line) != 65 and len(line) != 0:
431420
self.logger.fail(f"Invalid NTLM hash length on line {(i + 1)} (len {len(line)}): {line}")
432421
continue
433422
else:
434423
secret.append(line)
435424
cred_type.append("hash")
436425
else:
437-
if len(ntlm_hash) != 32 and len(ntlm_hash) != 65:
426+
if len(ntlm_hash) != 32 and len(ntlm_hash) != 65 and len(ntlm_hash) != 0:
438427
self.logger.fail(f"Invalid NTLM hash length {len(ntlm_hash)}, authentication not sent")
439428
exit(1)
440429
else:

nxc/data/nxc.conf

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ bh_port = 7687
1515
bh_user = neo4j
1616
bh_pass = bloodhoundcommunityedition
1717

18+
[BloodHound-CE]
19+
bhce_enabled = True
20+
1821
[Empire]
1922
api_host = 127.0.0.1
2023
api_port = 1337

nxc/database.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ def initialize_db():
111111
# Even if the default workspace exists, we still need to check if every protocol has a database (in case of a new protocol)
112112
init_protocol_dbs("default")
113113

114+
114115
def format_host_query(q, filter_term, HostsTable):
115116
"""One annoying thing is that if you search for an ip such as '10.10.10.5',
116117
it will return 10.10.10.5 and 10.10.10.52, so we have to check if its an ip address first
@@ -141,6 +142,7 @@ def format_host_query(q, filter_term, HostsTable):
141142

142143
return q
143144

145+
144146
class BaseDB:
145147
def __init__(self, db_engine):
146148
self.db_engine = db_engine

nxc/helpers/args.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from argparse import ArgumentDefaultsHelpFormatter, SUPPRESS, OPTIONAL, ZERO_OR_MORE
22
from argparse import Action
33

4+
45
class DisplayDefaultsNotNone(ArgumentDefaultsHelpFormatter):
56
def _get_help_string(self, action):
67
help_string = action.help

0 commit comments

Comments
 (0)