Skip to content

Commit 655c272

Browse files
committed
Functionalize rpc methods, fix kerberos auth and resolve usernames for non-admin SIDs
1 parent f24bb77 commit 655c272

1 file changed

Lines changed: 48 additions & 36 deletions

File tree

nxc/modules/presence.py

Lines changed: 48 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,10 @@ def on_admin_login(self, context, connection):
3939
context.log.fail(str(e))
4040
context.log.debug(traceback.format_exc())
4141

42-
def enumerate_admin_users(self, context, connection):
43-
admin_users = []
44-
string_binding = fr"ncacn_np:{connection.kdcHost}[\pipe\samr]"
45-
context.log.debug(f"Using string binding: {string_binding}")
46-
42+
def get_dce_rpc(self, target, string_binding, dce_binding, connection):
43+
# Create a DCE/RPC transport object with the specified string binding
4744
rpctransport = transport.DCERPCTransportFactory(string_binding)
48-
rpctransport.setRemoteHost(connection.kdcHost)
45+
rpctransport.setRemoteHost(target)
4946
rpctransport.set_credentials(
5047
connection.username,
5148
connection.password,
@@ -54,11 +51,28 @@ def enumerate_admin_users(self, context, connection):
5451
connection.nthash,
5552
aesKey=connection.aesKey,
5653
)
54+
rpctransport.set_kerberos(connection.kerberos, connection.kdcHost)
5755

56+
# Connect to the DCE/RPC endpoint
5857
dce = rpctransport.get_dce_rpc()
59-
dce.set_auth_level(RPC_C_AUTHN_LEVEL_PKT_PRIVACY)
58+
if connection.kerberos:
59+
dce.set_auth_type(RPC_C_AUTHN_GSS_NEGOTIATE)
60+
dce.set_credentials(*rpctransport.get_credentials())
6061
dce.connect()
61-
dce.bind(samr.MSRPC_UUID_SAMR)
62+
dce.set_auth_level(RPC_C_AUTHN_LEVEL_PKT_PRIVACY)
63+
dce.bind(dce_binding)
64+
return dce
65+
66+
def enumerate_admin_users(self, context, connection):
67+
admin_users = []
68+
69+
try:
70+
string_binding = fr"ncacn_np:{connection.kdcHost}[\pipe\samr]"
71+
dce = self.get_dce_rpc(connection.kdcHost, string_binding, samr.MSRPC_UUID_SAMR, connection)
72+
except Exception as e:
73+
context.log.fail(f"Failed to connect to SAMR: {e}")
74+
context.log.debug(traceback.format_exc())
75+
return admin_users
6276

6377
try:
6478
server_handle = samr.hSamrConnect2(dce)["ServerHandle"]
@@ -70,7 +84,7 @@ def enumerate_admin_users(self, context, connection):
7084
except Exception as e:
7185
context.log.fail(f"Failed to open domain {domain}: {e!s}")
7286
context.log.debug(traceback.format_exc())
73-
return []
87+
return admin_users
7488

7589
admin_rids = {
7690
"Domain Admins": 512,
@@ -140,7 +154,7 @@ def check_users_directory(self, context, connection, admin_users):
140154
def check_tasklist(self, context, connection, admin_users):
141155
"""Checks tasklist over rpc."""
142156
try:
143-
with TSTS.LegacyAPI(connection.conn, connection.host, kerberos=connection.kerberos) as legacy:
157+
with TSTS.LegacyAPI(connection.conn, connection.remoteName, kerberos=connection.kerberos) as legacy:
144158
handle = legacy.hRpcWinStationOpenServer()
145159
processes = legacy.hRpcWinStationGetAllProcesses(handle)
146160
except Exception as e:
@@ -159,31 +173,14 @@ def check_tasklist(self, context, connection, admin_users):
159173

160174
def check_scheduled_tasks(self, context, connection, admin_users):
161175
"""Checks scheduled tasks over rpc."""
162-
target = connection.hostname if connection.kerberos else connection.host
163-
stringbinding = f"ncacn_np:{target}[\\pipe\\atsvc]"
164-
rpctransport = transport.DCERPCTransportFactory(stringbinding)
165-
rpctransport.setRemoteHost(connection.hostname)
166-
rpctransport.set_credentials(
167-
connection.username,
168-
connection.password,
169-
connection.domain,
170-
connection.lmhash,
171-
connection.nthash,
172-
aesKey=connection.aesKey,
173-
)
174-
rpctransport.set_kerberos(connection.kerberos, connection.kdcHost)
175-
176176
try:
177-
dce = rpctransport.get_dce_rpc()
178-
if connection.kerberos:
179-
dce.set_auth_type(RPC_C_AUTHN_GSS_NEGOTIATE)
180-
dce.set_credentials(*rpctransport.get_credentials())
181-
dce.connect()
182-
dce.set_auth_level(RPC_C_AUTHN_LEVEL_PKT_PRIVACY)
183-
dce.bind(tsch.MSRPC_UUID_TSCHS)
177+
target = connection.remoteName if connection.kerberos else connection.host
178+
stringbinding = f"ncacn_np:{target}[\\pipe\\atsvc]"
179+
dce = self.get_dce_rpc(target, stringbinding, tsch.MSRPC_UUID_TSCHS, connection)
184180

185181
# Also extract non admins where we can get the password
186-
self.non_admin_sids = []
182+
self.non_admins = []
183+
non_admin_sids = set()
187184

188185
tasks = tsch.hSchRpcEnumTasks(dce, "\\")["pNames"]
189186
for task in tasks:
@@ -200,7 +197,22 @@ def check_scheduled_tasks(self, context, connection, admin_users):
200197
user["in_scheduled_tasks"] = True
201198
else:
202199
# If not an admin user, add to non_admin_sids for further processing
203-
self.non_admin_sids.append(sid.group(1))
200+
non_admin_sids.add(sid.group(1))
201+
202+
if non_admin_sids:
203+
string_binding = fr"ncacn_np:{connection.kdcHost}[\pipe\samr]"
204+
dce = self.get_dce_rpc(connection.kdcHost, string_binding, samr.MSRPC_UUID_SAMR, connection)
205+
206+
# Get Domain Handle
207+
server_handle = samr.hSamrConnect2(dce)["ServerHandle"]
208+
domain = samr.hSamrEnumerateDomainsInSamServer(dce, server_handle)["Buffer"]["Buffer"][0]["Name"]
209+
domain_sid = samr.hSamrLookupDomainInSamServer(dce, server_handle, domain)["DomainId"]
210+
domain_handle = samr.hSamrOpenDomain(dce, server_handle, samr.DOMAIN_LOOKUP | samr.DOMAIN_LIST_ACCOUNTS, domain_sid)["DomainHandle"]
211+
212+
for sid in non_admin_sids:
213+
user_handle = samr.hSamrOpenUser(dce, domain_handle, samr.MAXIMUM_ALLOWED, int(sid.split("-")[-1]))["UserHandle"]
214+
username = samr.hSamrQueryInformationUser2(dce, user_handle, samr.USER_INFORMATION_CLASS.UserAllInformation)["Buffer"]["All"]["UserName"]
215+
self.non_admins.append(username)
204216

205217
except Exception as e:
206218
context.log.fail(f"Failed to enumerate scheduled tasks: {e}")
@@ -231,9 +243,9 @@ def print_grouped_results(self, context, admin_users):
231243
context.log.success("Found admins in scheduled tasks:")
232244
for user in scheduled_tasks_users:
233245
context.log.highlight(f"{user['username']} ({', '.join(user['group'])})")
234-
if self.non_admin_sids:
235-
context.log.success(f"Found {len(self.non_admin_sids)} non-admin scheduled tasks with passwords stored in dpapi:")
236-
for sid in self.non_admin_sids:
246+
if self.non_admins:
247+
context.log.success(f"Found {len(self.non_admins)} non-admin scheduled tasks with passwords stored in dpapi:")
248+
for sid in self.non_admins:
237249
context.log.highlight(sid)
238250

239251
# Making this less verbose to better scan large ranges

0 commit comments

Comments
 (0)