Skip to content

Commit e1f4759

Browse files
committed
Merge main into passwords_dump_update
2 parents 1f2a29f + 4eb9838 commit e1f4759

30 files changed

Lines changed: 328 additions & 317 deletions
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
**Please Describe The Problem To Be Solved**
2+
(Replace This Text: Please present a concise description of the problem to be addressed by this feature request. Please be clear what parts of the problem are considered to be in-scope and out-of-scope.)
3+
4+
**(Optional): Suggest A Solution**
5+
(Replace This Text: A concise description of your preferred solution. Things to address include:
6+
* Details of the technical implementation
7+
* Tradeoffs made in design decisions
8+
* Caveats and considerations for the future
9+
10+
If there are multiple solutions, please present each one separately. Save comparisons for the very end.)

netexec.spec

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ a = Analysis(
3838
'nxc.protocols.smb.smbspider',
3939
'nxc.protocols.smb.passpol',
4040
'nxc.protocols.mssql.mssqlexec',
41+
'nxc.parsers.ldap_results',
4142
'nxc.helpers.bash',
4243
'nxc.helpers.bloodhound',
4344
'nxc.helpers.msada_guids',
@@ -71,6 +72,7 @@ a = Analysis(
7172
'dploot.lib.smb',
7273
'pyasn1_modules.rfc5652',
7374
'unicrypto.backends.pycryptodomex',
75+
'dateutil.relativedelta',
7476
'sspilib.raw._text',
7577
],
7678
hookspath=['./nxc/.hooks'],

nxc/connection.py

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,14 @@
88

99
from nxc.config import pwned_label
1010
from nxc.helpers.logger import highlight
11+
from nxc.loaders.moduleloader import ModuleLoader
1112
from nxc.logger import nxc_logger, NXCAdapter
1213
from nxc.context import Context
1314
from nxc.protocols.ldap.laps import laps_search
1415

1516
from impacket.dcerpc.v5 import transport
1617
import sys
18+
import contextlib
1719

1820
sem = BoundedSemaphore(1)
1921
global_failed_logins = 0
@@ -125,6 +127,10 @@ def __init__(self, args, db, host):
125127
self.logger.error(f"Exception while calling proto_flow() on target {self.host}: {e}")
126128
else:
127129
self.logger.exception(f"Exception while calling proto_flow() on target {self.host}: {e}")
130+
finally:
131+
self.logger.debug(f"Closing connection to: {host}")
132+
with contextlib.suppress(Exception):
133+
self.conn.close()
128134

129135
@staticmethod
130136
def proto_args(std_parser, module_parser):
@@ -145,16 +151,7 @@ def create_conn_obj(self):
145151
def check_if_admin(self):
146152
return
147153

148-
def kerberos_login(
149-
self,
150-
domain,
151-
username,
152-
password="",
153-
ntlm_hash="",
154-
aesKey="",
155-
kdcHost="",
156-
useCache=False,
157-
):
154+
def kerberos_login(self, domain, username, password="", ntlm_hash="", aesKey="", kdcHost="", useCache=False):
158155
return
159156

160157
def plaintext_login(self, domain, username, password):
@@ -173,6 +170,7 @@ def proto_flow(self):
173170
self.enum_host_info()
174171
if self.print_host_info() and (self.login() or (self.username == "" and self.password == "")):
175172
if hasattr(self.args, "module") and self.args.module:
173+
self.load_modules()
176174
self.logger.debug("Calling modules")
177175
self.call_modules()
178176
else:
@@ -206,7 +204,7 @@ def call_modules(self):
206204
It iterates over the modules specified in the command line arguments.
207205
For each module, it loads the module and creates a context object, then calls functions based on the module's attributes.
208206
"""
209-
for module in self.module:
207+
for module in self.modules:
210208
self.logger.debug(f"Loading module {module.name} - {module}")
211209
module_logger = NXCAdapter(
212210
extra={
@@ -491,3 +489,12 @@ def login(self):
491489

492490
def mark_pwned(self):
493491
return highlight(f"({pwned_label})" if self.admin_privs else "")
492+
493+
def load_modules(self):
494+
self.logger.info(f"Loading modules for target: {self.host}")
495+
loader = ModuleLoader(self.args, self.db, self.logger)
496+
self.modules = []
497+
498+
for module_path in self.module_paths:
499+
module = loader.init_module(module_path)
500+
self.modules.append(module)

nxc/modules/adcs.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ def options(self, context, module_options):
2727
SERVER PKI Enrollment Server to enumerate templates for. Default is None, use CN name
2828
BASE_DN The base domain name for the LDAP query
2929
"""
30-
self.context = context
3130
self.regex = re.compile("(https?://.+)")
3231

3332
self.server = None
@@ -39,6 +38,7 @@ def options(self, context, module_options):
3938

4039
def on_login(self, context, connection):
4140
"""On a successful LDAP login we perform a search for all PKI Enrollment Server or Certificate Templates Names."""
41+
self.context = context
4242
if self.server is None:
4343
search_filter = "(objectClass=pKIEnrollmentService)"
4444
else:

nxc/modules/add-computer.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
from impacket.dcerpc.v5 import samr, epm, transport
44
import sys
55

6-
76
class NXCModule:
87
"""
98
Module by CyberCelt: @Cyb3rC3lt
@@ -41,6 +40,7 @@ def options(self, context, module_options):
4140

4241
if "CHANGEPW" in module_options and ("NAME" not in module_options or "PASSWORD" not in module_options):
4342
context.log.error("NAME and PASSWORD options are required!")
43+
sys.exit(1)
4444
elif "CHANGEPW" in module_options:
4545
self.__noAdd = True
4646

@@ -87,8 +87,7 @@ def on_login(self, context, connection):
8787
# If SAMR fails now try over LDAPS
8888
if not self.noLDAPRequired:
8989
self.do_ldaps_add(connection, context)
90-
else:
91-
sys.exit(1)
90+
9291

9392
def do_samr_add(self, context):
9493
"""
@@ -178,7 +177,6 @@ def do_samr_add(self, context):
178177
samr.hSamrLookupNamesInDomain(dce, domain_handle, [self.__computerName])
179178
self.noLDAPRequired = True
180179
context.log.highlight("{}".format('Computer account already exists with the name: "' + self.__computerName + '"'))
181-
sys.exit(1)
182180
except samr.DCERPCSessionError as e:
183181
if e.error_code != 0xC0000073:
184182
raise

nxc/modules/daclread.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -221,8 +221,6 @@ def options(self, context, module_options):
221221
222222
Based on the work of @_nwodtuhs and @BlWasp_.
223223
"""
224-
self.context = context
225-
226224
context.log.debug(f"module_options: {module_options}")
227225

228226
if not module_options:
@@ -273,6 +271,7 @@ def options(self, context, module_options):
273271
self.filename = None
274272

275273
def on_login(self, context, connection):
274+
self.context = context
276275
"""On a successful LDAP login we perform a search for the targets' SID, their Security Descriptors and the principal's SID if there is one specified"""
277276
context.log.highlight("Be careful, this module cannot read the DACLS recursively.")
278277
self.baseDN = connection.ldapConnection._baseDN

nxc/modules/ldap-checker.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,11 +92,12 @@ async def run_ldaps_withEPA(target, credential):
9292
def DoesLdapsCompleteHandshake(dcIp):
9393
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
9494
s.settimeout(5)
95-
ssl_sock = ssl.wrap_socket(
95+
ssl_context = ssl.create_default_context()
96+
ssl_context.check_hostname = False
97+
ssl_sock = ssl_context.wrap_socket(
9698
s,
97-
cert_reqs=ssl.CERT_OPTIONAL,
98-
suppress_ragged_eofs=False,
9999
do_handshake_on_connect=False,
100+
suppress_ragged_eofs=False,
100101
)
101102
ssl_sock.connect((dcIp, 636))
102103
try:

nxc/modules/lsassy.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ def on_admin_login(self, context, connection):
6464
context.log.fail("Unable to dump lsass")
6565
return False
6666

67-
parsed = Parser(file).parse()
67+
parsed = Parser(host, file).parse()
6868
if parsed is None:
6969
context.log.fail("Unable to parse lsass dump")
7070
return False

nxc/modules/msol.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
# Based on the article : https://blog.xpnsec.com/azuread-connect-for-redteam/
44
from sys import exit
55
from os import path
6-
import sys
6+
from nxc.paths import TMP_PATH
77
from nxc.helpers.powershell import get_ps_script
88

99

@@ -49,14 +49,14 @@ def exec_script(self, _, connection):
4949

5050
def on_admin_login(self, context, connection):
5151
if self.use_embedded:
52-
file_to_upload = "/tmp/msol.ps1"
52+
file_to_upload = f"{TMP_PATH}/msol.ps1"
5353

5454
try:
5555
with open(file_to_upload, "w") as msol:
5656
msol.write(self.msol_embedded)
5757
except FileNotFoundError:
5858
context.log.fail(f"Impersonate file specified '{file_to_upload}' does not exist!")
59-
sys.exit(1)
59+
exit(1)
6060

6161
else:
6262
if path.isfile(self.MSOL_PS1):

nxc/modules/mssql_priv.py

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,12 @@ def options(self, context, module_options):
4242
- rollback (remove sysadmin privilege)
4343
"""
4444
self.action = None
45-
self.context = context
4645

4746
if "ACTION" in module_options:
4847
self.action = module_options["ACTION"]
4948

5049
def on_login(self, context, connection):
50+
self.context = context
5151
# get mssql connection
5252
self.mssql_conn = connection.conn
5353
# fetch the current user
@@ -441,13 +441,11 @@ def is_admin_user(self, username) -> bool:
441441
:rtype: bool
442442
"""
443443
res = self.query_and_get_output(f"SELECT IS_SRVROLEMEMBER('sysadmin', '{username}')")
444-
try:
445-
if int(res):
446-
self.admin_privs = True
447-
return True
448-
else:
449-
return False
450-
except Exception:
444+
is_admin = res[0][""]
445+
if is_admin:
446+
self.admin_privs = True
447+
return True
448+
else:
451449
return False
452450

453451
def revert_context(self, exec_as):

0 commit comments

Comments
 (0)