Skip to content

Commit 4ea4b5f

Browse files
committed
Merge "main" into "NFS-Protocol"
2 parents 998b878 + aef2675 commit 4ea4b5f

33 files changed

Lines changed: 1488 additions & 568 deletions

.github/workflows/build-binaries.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ jobs:
1313
python-version: ["3.11"]
1414
#python-version: ["3.8", "3.9", "3.10", "3.11"] # for binary builds we only need one version
1515
steps:
16-
- uses: actions/checkout@v3
16+
- uses: actions/checkout@v4
1717
- name: NetExec set up python on ${{ matrix.os }}
18-
uses: actions/setup-python@v4
18+
uses: actions/setup-python@v5
1919
with:
2020
python-version: ${{ matrix.python-version }}
2121
- name: Build Native Binary

.github/workflows/build-zipapps.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@ jobs:
1212
os: [ubuntu-latest, macOS-latest, windows-latest]
1313
python-version: ["3.8", "3.9", "3.10", "3.11"]
1414
steps:
15-
- uses: actions/checkout@v3
15+
- uses: actions/checkout@v4
1616
- name: NetExec set up python on ${{ matrix.os }}
17-
uses: actions/setup-python@v4
17+
uses: actions/setup-python@v5
1818
with:
1919
python-version: ${{ matrix.python-version }}
2020
- name: Build Python ZipApp with Shiv

.github/workflows/lint.yml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ name: Lint Python code with ruff
33

44
on:
55
push:
6+
workflow_dispatch:
67

78
jobs:
89
lint:
@@ -11,12 +12,12 @@ jobs:
1112
github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository
1213

1314
steps:
14-
- uses: actions/checkout@v3
15+
- uses: actions/checkout@v4
1516
- name: Install poetry
1617
run: |
1718
pipx install poetry
1819
- name: Set up Python
19-
uses: actions/setup-python@v4
20+
uses: actions/setup-python@v5
2021
with:
2122
python-version: 3.11
2223
cache: poetry

.github/workflows/test.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,12 @@ jobs:
1616
os: [ubuntu-latest]
1717
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
1818
steps:
19-
- uses: actions/checkout@v3
19+
- uses: actions/checkout@v4
2020
- name: Install poetry
2121
run: |
2222
pipx install poetry
2323
- name: NetExec set up python ${{ matrix.python-version }} on ${{ matrix.os }}
24-
uses: actions/setup-python@v4
24+
uses: actions/setup-python@v5
2525
with:
2626
python-version: ${{ matrix.python-version }}
2727
cache: poetry

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,11 @@ sudo apt install pipx git
4343
pipx ensurepath
4444
pipx install git+https://github.com/Pennyw0rth/NetExec
4545
```
46+
47+
## Availability on Unix distributions
48+
49+
[![Packaging status](https://repology.org/badge/vertical-allrepos/netexec.svg)](https://repology.org/project/netexec/versions)
50+
4651
# Development
4752
Development guidelines and recommendations in development
4853

nxc/cli.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ def gen_cli_args():
2727

2828
generic_parser = argparse.ArgumentParser(add_help=False, formatter_class=DisplayDefaultsNotNone)
2929
generic_group = generic_parser.add_argument_group("Generic", "Generic options for nxc across protocols")
30+
generic_group.add_argument("--version", action="store_true", help="Display nxc version")
3031
generic_group.add_argument("-t", "--threads", type=int, dest="threads", default=256, help="set how many concurrent threads to use")
3132
generic_group.add_argument("--timeout", default=None, type=int, help="max timeout in seconds of each thread")
3233
generic_group.add_argument("--jitter", metavar="INTERVAL", type=str, help="sets a random delay between each authentication")
@@ -69,8 +70,6 @@ def gen_cli_args():
6970
parents=[generic_parser, output_parser, dns_parser]
7071
)
7172

72-
parser.add_argument("--version", action="store_true", help="Display nxc version")
73-
7473
# we do module arg parsing here so we can reference the module_list attribute below
7574
module_parser = argparse.ArgumentParser(add_help=False, formatter_class=DisplayDefaultsNotNone)
7675
mgroup = module_parser.add_argument_group("Modules", "Options for nxc modules")
@@ -82,7 +81,7 @@ def gen_cli_args():
8281
subparsers = parser.add_subparsers(title="Available Protocols", dest="protocol")
8382

8483
std_parser = argparse.ArgumentParser(add_help=False, parents=[generic_parser, output_parser, dns_parser], formatter_class=DisplayDefaultsNotNone)
85-
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)")
84+
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 or generic_parser.parse_known_args()[0].version) 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)")
8685
credential_group = std_parser.add_argument_group("Authentication", "Options for authenticating")
8786
credential_group.add_argument("-u", "--username", metavar="USERNAME", dest="username", nargs="+", default=[], help="username(s) or file(s) containing usernames")
8887
credential_group.add_argument("-p", "--password", metavar="PASSWORD", dest="password", nargs="+", default=[], help="password(s) or file(s) containing passwords")

nxc/connection.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ def __init__(self, args, db, target):
134134
# Authentication info
135135
self.password = ""
136136
self.username = ""
137-
self.kerberos = bool(self.args.kerberos or self.args.use_kcache or self.args.aesKey)
137+
self.kerberos = bool(self.args.kerberos or self.args.use_kcache or self.args.aesKey or (hasattr(self.args, "delegate") and self.args.delegate))
138138
self.aesKey = None if not self.args.aesKey else self.args.aesKey[0]
139139
self.use_kcache = None if not self.args.use_kcache else self.args.use_kcache
140140
self.admin_privs = False
@@ -157,7 +157,7 @@ def __init__(self, args, db, target):
157157
else:
158158
return
159159

160-
if self.args.kerberos:
160+
if self.kerberos:
161161
self.host = self.hostname
162162

163163
self.logger.info(f"Socket info: host={self.host}, hostname={self.hostname}, kerberos={self.kerberos}, ipv6={self.is_ipv6}, link-local ipv6={self.is_link_local_ipv6}")
@@ -167,6 +167,9 @@ def __init__(self, args, db, target):
167167
except Exception as e:
168168
if "ERROR_DEPENDENT_SERVICES_RUNNING" in str(e):
169169
self.logger.error(f"Exception while calling proto_flow() on target {target}: {e}")
170+
# Catching impacket SMB specific exceptions, which should not be imported due to performance reasons
171+
elif e.__class__.__name__ in ["NetBIOSTimeout", "NetBIOSError"]:
172+
self.logger.error(f"{e.__class__.__name__} on target {target}: {e}")
170173
else:
171174
self.logger.exception(f"Exception while calling proto_flow() on target {target}: {e}")
172175
finally:
@@ -470,8 +473,6 @@ def try_credentials(self, domain, username, owned, secret, cred_type, data=None)
470473
return False
471474
if self.args.continue_on_success and owned:
472475
return False
473-
if hasattr(self.args, "delegate") and self.args.delegate:
474-
self.args.kerberos = True
475476

476477
if self.args.jitter:
477478
jitter = self.args.jitter
@@ -486,7 +487,7 @@ def try_credentials(self, domain, username, owned, secret, cred_type, data=None)
486487

487488
with sem:
488489
if cred_type == "plaintext":
489-
if self.args.kerberos:
490+
if self.kerberos:
490491
self.logger.debug("Trying to authenticate using Kerberos")
491492
return self.kerberos_login(domain, username, secret, "", "", self.kdcHost, False)
492493
elif hasattr(self.args, "domain"): # Some protocols don't use domain for login
@@ -499,7 +500,7 @@ def try_credentials(self, domain, username, owned, secret, cred_type, data=None)
499500
self.logger.debug("Trying to authenticate using plaintext")
500501
return self.plaintext_login(username, secret)
501502
elif cred_type == "hash":
502-
if self.args.kerberos:
503+
if self.kerberos:
503504
return self.kerberos_login(domain, username, "", secret, "", self.kdcHost, False)
504505
return self.hash_login(domain, username, secret)
505506
elif cred_type == "aesKey":

nxc/logger.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import sys
66
import re
77
from nxc.console import nxc_console
8+
from nxc.paths import NXC_PATH
89
from termcolor import colored
910
from datetime import datetime
1011
from rich.text import Text
@@ -30,7 +31,7 @@ def setup_debug_logging():
3031
root_logger.setLevel(logging.INFO)
3132
elif debug_args.debug:
3233
nxc_logger.logger.setLevel(logging.DEBUG)
33-
root_logger.setLevel(logging.INFO)
34+
root_logger.setLevel(logging.DEBUG)
3435
else:
3536
nxc_logger.logger.setLevel(logging.ERROR)
3637
root_logger.setLevel(logging.ERROR)
@@ -163,15 +164,16 @@ def log_console_to_file(self, text, *args, **kwargs):
163164
If debug or info logging is not enabled, we still want display/success/fail logged to the file specified,
164165
so we create a custom LogRecord and pass it to all the additional handlers (which will be all the file handlers)
165166
"""
166-
if self.logger.getEffectiveLevel() >= logging.INFO and len(self.logger.handlers): # will be 0 if it's just the console output, so only do this if we actually have file loggers
167+
caller_frame = inspect.currentframe().f_back.f_back.f_back
168+
if len(self.logger.handlers): # will be 0 if it's just the console output, so only do this if we actually have file loggers
167169
try:
168170
for handler in self.logger.handlers:
169-
handler.handle(LogRecord("nxc", 20, "", kwargs, msg=text, args=args, exc_info=None))
171+
handler.handle(LogRecord("nxc", 20, pathname=caller_frame.f_code.co_filename, lineno=caller_frame.f_lineno, msg=text, args=args, exc_info=None))
170172
except Exception as e:
171173
self.logger.fail(f"Issue while trying to custom print handler: {e}")
172174

173175
def add_file_log(self, log_file=None):
174-
file_formatter = TermEscapeCodeFormatter("%(asctime)s - %(levelname)s - %(message)s")
176+
file_formatter = TermEscapeCodeFormatter("%(asctime)s | %(filename)s:%(lineno)s - %(levelname)s - %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
175177
output_file = self.init_log_file() if log_file is None else log_file
176178
file_creation = False
177179

@@ -193,11 +195,10 @@ def add_file_log(self, log_file=None):
193195

194196
@staticmethod
195197
def init_log_file():
196-
newpath = os.path.expanduser("~/.nxc") + "/logs/" + datetime.now().strftime("%Y-%m-%d")
197-
if not os.path.exists(newpath):
198-
os.makedirs(newpath)
198+
newpath = NXC_PATH + "/logs/" + datetime.now().strftime("%Y-%m-%d")
199+
os.makedirs(newpath, exist_ok=True)
199200
return os.path.join(
200-
os.path.expanduser("~/.nxc"),
201+
NXC_PATH,
201202
"logs",
202203
datetime.now().strftime("%Y-%m-%d"),
203204
f"log_{datetime.now().strftime('%Y-%m-%d-%H-%M-%S')}.log",

nxc/modules/enum_av.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,27 @@ def LsarLookupNames(self, dce, policyHandle, service):
358358
{"name": "sophoslivequery_*", "processes": [""]}
359359
]
360360
},
361+
{
362+
"name": "Trellix Endpoint Detection and Response (EDR)",
363+
"services": [
364+
{"name": "McAfee Endpoint Security Platform Service", "description": "Trellix Core Service"},
365+
{"name": "mfemactl", "description": "Trellix Management Service"},
366+
{"name": "mfemms", "description": "McAfee Management Service"},
367+
{"name": "mfefire", "description": "Trellix Firewall Core Service"},
368+
{"name": "masvc", "description": "Trellix Agent Service"},
369+
{"name": "macmnsvc", "description": "Trellix Agent Common Service"},
370+
{"name": "mfetp", "description": "Trellix Endpoint Threat Prevention Service"},
371+
{"name": "mfewc", "description": "Trellix Endpoint Security Web Control Service"},
372+
{"name": "mfeaack", "description": "Trellix Anti-Malware Core Service"}
373+
],
374+
"pipes": [
375+
{"name": "TrellixEDR_Pipe_*", "processes": ["McAfeeEDR.exe"]},
376+
{"name": "mfemactl_*", "processes": ["mfemactl.exe"]},
377+
{"name": "mfefire_*", "processes": ["mfefire.exe"]},
378+
{"name": "McAfeeAgent_Pipe_*", "processes": ["McAfeeAgent.exe"]},
379+
{"name": "mfetp_*", "processes": ["mfetp.exe"]}
380+
]
381+
},
361382
{
362383
"name": "Trend Micro Endpoint Security",
363384
"services": [

nxc/modules/handlekatz.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import sys
88

99
from nxc.helpers.bloodhound import add_user_bh
10-
import pypykatz
10+
from pypykatz.pypykatz import pypykatz
1111

1212

1313
class NXCModule:

0 commit comments

Comments
 (0)