Skip to content

Commit bc712c8

Browse files
Merge branch 'main' into main
2 parents 7b1f60d + 024ff0a commit bc712c8

24 files changed

Lines changed: 456 additions & 549 deletions

.github/workflows/build-binaries.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ jobs:
2020
python-version: ${{ matrix.python-version }}
2121
- name: Build Native Binary
2222
run: |
23-
pip install pyinstaller
23+
pip install pyinstaller pillow
2424
pip install .
2525
pyinstaller netexec.spec
2626
- name: Upload Windows Binary

.github/workflows/lint.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,14 @@ name: Lint Python code with ruff
44
on:
55
push:
66
workflow_dispatch:
7+
pull_request_review:
8+
types: [submitted]
79

810
jobs:
911
lint:
1012
runs-on: ubuntu-latest
1113
if:
12-
github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository
14+
github.event_name == 'push' || github.event.review.state == 'APPROVED' || github.event_name == 'workflow_dispatch'
1315

1416
steps:
1517
- uses: actions/checkout@v4

netexec.spec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ a = Analysis(
2020
'aardwolf.commons.target',
2121
'aardwolf.protocol.x224.constants',
2222
'impacket.examples.secretsdump',
23+
'impacket.examples.regsecrets',
2324
'impacket.dcerpc.v5.lsat',
2425
'impacket.dcerpc.v5.transport',
2526
'impacket.dcerpc.v5.lsad',
@@ -47,7 +48,6 @@ a = Analysis(
4748
'nxc.helpers.ntlm_parser',
4849
'paramiko',
4950
'pypsrp.client',
50-
'pywerview.cli.helpers',
5151
'pylnk3',
5252
'pypykatz',
5353
'pyNfsClient',

nxc/cli.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ def gen_cli_args():
2424
VERSION = importlib.metadata.version("netexec")
2525
COMMIT = ""
2626
DISTANCE = ""
27-
CODENAME = "NeedForSpeed"
27+
CODENAME = "SmoothOperator"
2828
nxc_logger.debug(f"NXC VERSION: {VERSION} - {CODENAME} - {COMMIT} - {DISTANCE}")
2929

3030
generic_parser = argparse.ArgumentParser(add_help=False, formatter_class=DisplayDefaultsNotNone)
@@ -124,7 +124,7 @@ def gen_cli_args():
124124
except Exception as e:
125125
nxc_logger.exception(f"Error loading proto_args from proto_args.py file in protocol folder: {protocol} - {e}")
126126

127-
argcomplete.autocomplete(parser)
127+
argcomplete.autocomplete(parser, always_complete_options=False)
128128
args = parser.parse_args()
129129

130130
if len(sys.argv) == 1:

nxc/first_run.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,17 @@ def first_run_setup(logger=nxc_logger):
2929
logger.display(f"Creating missing folder {folder}")
3030
mkdir(path_join(NXC_PATH, folder))
3131

32+
log_subfolders = (
33+
"sam",
34+
"lsa",
35+
"ntds",
36+
"dpapi",
37+
)
38+
for subfolder in log_subfolders:
39+
if not exists(path_join(NXC_PATH, f"logs/{subfolder}")):
40+
logger.display(f"Creating missing folder logs/{subfolder}")
41+
mkdir(path_join(NXC_PATH, f"logs/{subfolder}"))
42+
3243
initialize_db()
3344

3445
if not exists(CONFIG_PATH):

nxc/modules/firefox.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,23 @@ class NXCModule:
1010
"""
1111

1212
name = "firefox"
13-
description = "Dump credentials from Firefox"
13+
description = "[REMOVED] Dump credentials from Firefox"
1414
supported_protocols = ["smb"]
1515
opsec_safe = True # Does the module touch disk?
1616
multiple_hosts = True # Does it make sense to run this module on multiple hosts at a time?
1717

1818
def options(self, context, module_options):
19-
"""COOKIES Get also Firefox cookies"""
19+
"""
20+
[REMOVED] use the --dpapi flag instead of the module firefox.
21+
22+
COOKIES Get also Firefox cookies
23+
"""
2024
self.gather_cookies = "COOKIES" in module_options
2125

2226
def on_admin_login(self, context, connection):
27+
context.log.fail("[REMOVED] Use the --dpapi flag instead of the module firefox.")
28+
return
29+
2330
host = connection.host if not connection.kerberos else connection.hostname + "." + connection.domain
2431
domain = connection.domain
2532
username = connection.username

nxc/modules/get-desc-users.py

Lines changed: 5 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
from impacket.ldap import ldapasn1 as ldapasn1_impacket
21
from impacket.ldap import ldap as ldap_impacket
32
import re
43
from nxc.logger import nxc_logger
4+
from nxc.parsers.ldap_results import parse_result_attributes
55

66

77
class NXCModule:
@@ -55,24 +55,10 @@ def on_login(self, context, connection):
5555
nxc_logger.debug(e)
5656
return False
5757

58-
answers = []
5958
context.log.debug(f"Total of records returned {len(resp)}")
60-
for item in resp:
61-
if isinstance(item, ldapasn1_impacket.SearchResultEntry) is not True:
62-
continue
63-
sAMAccountName = ""
64-
description = ""
65-
try:
66-
for attribute in item["attributes"]:
67-
if str(attribute["type"]) == "sAMAccountName":
68-
sAMAccountName = str(attribute["vals"][0])
69-
elif str(attribute["type"]) == "description":
70-
description = attribute["vals"][0]
71-
if sAMAccountName != "" and description != "":
72-
answers.append([sAMAccountName, description])
73-
except Exception as e:
74-
context.log.debug("Exception:", exc_info=True)
75-
context.log.debug(f"Skipping item, cannot process due to error {e!s}")
59+
resp_parsed = parse_result_attributes(resp)
60+
answers = [[x["sAMAccountName"], x.get("description")] for x in resp_parsed if x.get("description")]
61+
7662
answers = self.filter_answer(context, answers)
7763
if len(answers) > 0:
7864
context.log.success("Found following users: ")
@@ -108,5 +94,5 @@ def filter_answer(self, context, answers):
10894
answersFiltered.append([answer[0], description])
10995
elif (self.FILTER != "" and conditionFilter) and (conditionPasswordPolicy == self.PASSWORDPOLICY):
11096
context.log.highlight(f"'{self.FILTER}' found in user: '{answer[0]}' description: '{description}'")
111-
97+
11298
return answersFiltered

nxc/modules/group-mem.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ class NXCModule:
1111
"""
1212

1313
name = "group-mem"
14-
description = "Retrieves all the members within a Group"
14+
description = "[REMOVED] Retrieves all the members within a Group"
1515
supported_protocols = ["ldap"]
1616
opsec_safe = True
1717
multiple_hosts = False

nxc/modules/putty.py

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -69,19 +69,23 @@ def sid_to_name(self, all_users):
6969
def load_missing_users(self, unloaded_user_objects):
7070
"""Load missing users into registry to access their registry keys."""
7171
for user_object in unloaded_user_objects:
72-
# Extract profile Path of NTUSER.DAT
73-
reg_handle = rrp.hOpenLocalMachine(self.rrp._RemoteOperations__rrp)["phKey"]
74-
key_handle = rrp.hBaseRegOpenKey(self.rrp._RemoteOperations__rrp, reg_handle, f"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList\\{user_object}")["phkResult"]
75-
user_profile_path = rrp.hBaseRegQueryValue(self.rrp._RemoteOperations__rrp, key_handle, "ProfileImagePath")[1].split("\x00")[:-1][0]
76-
rrp.hBaseRegCloseKey(self.rrp._RemoteOperations__rrp, key_handle)
72+
try:
73+
# Extract profile Path of NTUSER.DAT
74+
reg_handle = rrp.hOpenLocalMachine(self.rrp._RemoteOperations__rrp)["phKey"]
75+
key_handle = rrp.hBaseRegOpenKey(self.rrp._RemoteOperations__rrp, reg_handle, f"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList\\{user_object}")["phkResult"]
76+
user_profile_path = rrp.hBaseRegQueryValue(self.rrp._RemoteOperations__rrp, key_handle, "ProfileImagePath")[1].split("\x00")[:-1][0]
77+
rrp.hBaseRegCloseKey(self.rrp._RemoteOperations__rrp, key_handle)
7778

78-
# Load Profile
79-
reg_handle = rrp.hOpenUsers(self.rrp._RemoteOperations__rrp)["phKey"]
80-
key_handle = rrp.hBaseRegOpenKey(self.rrp._RemoteOperations__rrp, reg_handle, "")["phkResult"]
79+
# Load Profile
80+
reg_handle = rrp.hOpenUsers(self.rrp._RemoteOperations__rrp)["phKey"]
81+
key_handle = rrp.hBaseRegOpenKey(self.rrp._RemoteOperations__rrp, reg_handle, "")["phkResult"]
8182

82-
self.context.log.debug(f"LOAD USER INTO REGISTRY: {user_object}")
83-
rrp.hBaseRegLoadKey(self.rrp._RemoteOperations__rrp, key_handle, user_object, f"{user_profile_path}\\NTUSER.DAT")
84-
rrp.hBaseRegCloseKey(self.rrp._RemoteOperations__rrp, key_handle)
83+
self.context.log.debug(f"LOAD USER INTO REGISTRY: {user_object}")
84+
rrp.hBaseRegLoadKey(self.rrp._RemoteOperations__rrp, key_handle, user_object, f"{user_profile_path}\\NTUSER.DAT")
85+
rrp.hBaseRegCloseKey(self.rrp._RemoteOperations__rrp, key_handle)
86+
except rrp.DCERPCSessionError as e:
87+
self.context.log.fail(f"Error loading user {user_object} into registry: {e}")
88+
self.context.log.debug(traceback.format_exc())
8589

8690
def unload_missing_users(self, unloaded_user_objects):
8791
"""If some user were not logged in at the beginning we unload them from registry."""
@@ -92,7 +96,7 @@ def unload_missing_users(self, unloaded_user_objects):
9296
self.context.log.debug(f"UNLOAD USER FROM REGISTRY: {user_object}")
9397
try:
9498
rrp.hBaseRegUnLoadKey(self.rrp._RemoteOperations__rrp, key_handle, user_object)
95-
except Exception as e:
99+
except rrp.DCERPCSessionError as e:
96100
self.context.log.fail(f"Error unloading user {user_object} in registry: {e}")
97101
self.context.log.debug(traceback.format_exc())
98102
rrp.hBaseRegCloseKey(self.rrp._RemoteOperations__rrp, key_handle)

nxc/modules/recent_files.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import pylnk3
2+
from io import BytesIO
3+
4+
5+
class NXCModule:
6+
# Get a list of recently modified files via LNK's stored in AppData\Roaming\Microsoft\Windows\Recent
7+
# Module by @Defte_
8+
9+
name = "recent_files"
10+
description = "Extracts recently modified files"
11+
supported_protocols = ["smb"]
12+
opsec_safe = True
13+
multiple_hosts = True
14+
false_positive = [".", "..", "desktop.ini", "Public", "Default", "Default User", "All Users", ".NET v4.5", ".NET v4.5 Classic"]
15+
16+
def options(self, context, module_options):
17+
"""No options"""
18+
19+
def on_admin_login(self, context, connection):
20+
lnks = []
21+
for directory in connection.conn.listPath("C$", "Users\\*"):
22+
if directory.get_longname() not in self.false_positive and directory.is_directory():
23+
context.log.highlight(f"C:\\{directory.get_longname()}")
24+
recent_files_dir = f"Users\\{directory.get_longname()}\\AppData\\Roaming\\Microsoft\\Windows\\Recent\\"
25+
for file in connection.conn.listPath("C$", f"{recent_files_dir}\\*"):
26+
file_path = f"{recent_files_dir}{file.get_longname()}"
27+
if file.get_longname() not in self.false_positive and not file.is_directory():
28+
file_path = f"{recent_files_dir}{file.get_longname()}"
29+
try:
30+
buf = BytesIO()
31+
connection.conn.getFile("C$", file_path, buf.write)
32+
buf.seek(0)
33+
lnk = pylnk3.parse(buf).path.strip()
34+
if lnk and lnk not in lnks:
35+
context.log.highlight(f"\t{lnk}")
36+
lnks.append(lnk)
37+
except Exception as e:
38+
# Sometimes PyLnk3 can't parse the lnk file...
39+
context.log.debug(f"Couldn't open {file_path} because of {e}")

0 commit comments

Comments
 (0)