Skip to content

Commit d4168b4

Browse files
authored
Merge branch 'main' into wcc_llmnr
2 parents d76127c + aec6448 commit d4168b4

94 files changed

Lines changed: 1530 additions & 558 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

netexec.spec

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ a = Analysis(
2727
'impacket.dcerpc.v5.gkdi',
2828
'impacket.dcerpc.v5.rprn',
2929
'impacket.dcerpc.v5.even',
30+
'impacket.dcerpc.v5.even6',
3031
'impacket.dpapi_ng',
3132
'impacket.tds',
3233
'impacket.version',
@@ -43,6 +44,7 @@ a = Analysis(
4344
'nxc.parsers.ldap_results',
4445
'nxc.helpers.bash',
4546
'nxc.helpers.bloodhound',
47+
'nxc.helpers.even6_parser',
4648
'nxc.helpers.msada_guids',
4749
'nxc.helpers.ntlm_parser',
4850
'paramiko',

nxc/cli.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ def gen_cli_args():
2525
COMMIT = ""
2626
DISTANCE = ""
2727
CODENAME = "SmoothOperator"
28-
nxc_logger.debug(f"NXC VERSION: {VERSION} - {CODENAME} - {COMMIT} - {DISTANCE}")
2928

3029
generic_parser = argparse.ArgumentParser(add_help=False, formatter_class=DisplayDefaultsNotNone)
3130
generic_group = generic_parser.add_argument_group("Generic", "Generic options for nxc across protocols")
@@ -133,7 +132,7 @@ def gen_cli_args():
133132
if hasattr(args, "get_output_tries"):
134133
args.get_output_tries = args.get_output_tries * 10
135134

136-
return args
135+
return args, [CODENAME, VERSION, COMMIT, DISTANCE]
137136

138137

139138
def get_module_names():

nxc/config.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
import os
21
from os.path import join as path_join
32
import configparser
4-
from nxc.paths import NXC_PATH, DATA_PATH
3+
from nxc.paths import DATA_PATH, CONFIG_PATH
54
from nxc.first_run import first_run_setup
65
from nxc.logger import nxc_logger
76
from ast import literal_eval
@@ -10,20 +9,25 @@
109
nxc_default_config.read(path_join(DATA_PATH, "nxc.conf"))
1110

1211
nxc_config = configparser.ConfigParser()
13-
nxc_config.read(os.path.join(NXC_PATH, "nxc.conf"))
12+
nxc_config.read(CONFIG_PATH)
1413

1514
if "nxc" not in nxc_config.sections():
1615
first_run_setup()
17-
nxc_config.read(os.path.join(NXC_PATH, "nxc.conf"))
16+
nxc_config.read(CONFIG_PATH)
1817

1918
# Check if there are any missing options in the config file
2019
for section in nxc_default_config.sections():
20+
if not nxc_config.has_section(section):
21+
nxc_logger.display(f"Adding missing section '{section}' to nxc.conf")
22+
nxc_config.add_section(section)
23+
with open(CONFIG_PATH, "w") as config_file:
24+
nxc_config.write(config_file)
2125
for option in nxc_default_config.options(section):
2226
if not nxc_config.has_option(section, option):
2327
nxc_logger.display(f"Adding missing option '{option}' in config section '{section}' to nxc.conf")
2428
nxc_config.set(section, option, nxc_default_config.get(section, option))
2529

26-
with open(path_join(NXC_PATH, "nxc.conf"), "w") as config_file:
30+
with open(CONFIG_PATH, "w") as config_file:
2731
nxc_config.write(config_file)
2832

2933
# THESE OPTIONS HAVE TO EXIST IN THE DEFAULT CONFIG FILE

nxc/connection.py

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ def call_modules(self):
275275
extra={
276276
"module_name": module.name.upper(),
277277
"host": self.host,
278-
"port": self.args.port,
278+
"port": self.port,
279279
"hostname": self.hostname,
280280
},
281281
)
@@ -293,8 +293,7 @@ def call_modules(self):
293293
module.on_admin_login(context, self)
294294

295295
def inc_failed_login(self, username):
296-
global global_failed_logins
297-
global user_failed_logins
296+
global global_failed_logins, user_failed_logins
298297

299298
if username not in user_failed_logins:
300299
user_failed_logins[username] = 0
@@ -304,16 +303,15 @@ def inc_failed_login(self, username):
304303
self.failed_logins += 1
305304

306305
def over_fail_limit(self, username):
307-
global global_failed_logins
308-
global user_failed_logins
306+
global global_failed_logins, user_failed_logins
309307

310308
if global_failed_logins == self.args.gfail_limit:
311309
return True
312310

313311
if self.failed_logins == self.args.fail_limit:
314312
return True
315313

316-
if username in user_failed_logins and self.args.ufail_limit == user_failed_logins[username]:
314+
if username in user_failed_logins and self.args.ufail_limit == user_failed_logins[username]: # noqa: SIM103
317315
return True
318316

319317
return False
@@ -418,14 +416,14 @@ def parse_credentials(self):
418416
with open(ntlm_hash) as ntlm_hash_file:
419417
for i, line in enumerate(ntlm_hash_file):
420418
line = line.strip()
421-
if len(line) != 32 and len(line) != 65:
419+
if len(line) != 32 and len(line) != 65 and len(line) != 0:
422420
self.logger.fail(f"Invalid NTLM hash length on line {(i + 1)} (len {len(line)}): {line}")
423421
continue
424422
else:
425423
secret.append(line)
426424
cred_type.append("hash")
427425
else:
428-
if len(ntlm_hash) != 32 and len(ntlm_hash) != 65:
426+
if len(ntlm_hash) != 32 and len(ntlm_hash) != 65 and len(ntlm_hash) != 0:
429427
self.logger.fail(f"Invalid NTLM hash length {len(ntlm_hash)}, authentication not sent")
430428
exit(1)
431429
else:

nxc/context.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
11
import configparser
22
import os
33

4+
from nxc.paths import NXC_PATH, CONFIG_PATH
5+
46

57
class Context:
68
def __init__(self, db, logger, args):
79
for key, value in vars(args).items():
810
setattr(self, key, value)
911

1012
self.db = db
11-
self.log_folder_path = os.path.join(os.path.expanduser("~/.nxc"), "logs")
13+
self.log_folder_path = os.path.join(NXC_PATH, "logs")
1214
self.localip = None
1315

1416
self.conf = configparser.ConfigParser()
15-
self.conf.read(os.path.expanduser("~/.nxc/nxc.conf"))
17+
self.conf.read(CONFIG_PATH)
1618

1719
self.log = logger

nxc/data/nxc.conf

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ bh_port = 7687
1515
bh_user = neo4j
1616
bh_pass = bloodhoundcommunityedition
1717

18+
[BloodHound-CE]
19+
bhce_enabled = True
20+
1821
[Empire]
1922
api_host = 127.0.0.1
2023
api_port = 1337

nxc/database.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ def initialize_db():
111111
# Even if the default workspace exists, we still need to check if every protocol has a database (in case of a new protocol)
112112
init_protocol_dbs("default")
113113

114+
114115
def format_host_query(q, filter_term, HostsTable):
115116
"""One annoying thing is that if you search for an ip such as '10.10.10.5',
116117
it will return 10.10.10.5 and 10.10.10.52, so we have to check if its an ip address first
@@ -141,6 +142,7 @@ def format_host_query(q, filter_term, HostsTable):
141142

142143
return q
143144

145+
144146
class BaseDB:
145147
def __init__(self, db_engine):
146148
self.db_engine = db_engine

nxc/first_run.py

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,16 @@
88

99

1010
def first_run_setup(logger=nxc_logger):
11-
if not exists(TMP_PATH):
12-
mkdir(TMP_PATH)
13-
1411
if not exists(NXC_PATH):
1512
logger.display("First time use detected")
1613
logger.display("Creating home directory structure")
1714
mkdir(NXC_PATH)
15+
if not exists(TMP_PATH):
16+
mkdir(TMP_PATH)
1817

1918
folders = (
2019
"logs",
2120
"modules",
22-
"protocols",
2321
"workspaces",
2422
"obfuscated_scripts",
2523
"screenshots",
@@ -46,7 +44,3 @@ def first_run_setup(logger=nxc_logger):
4644
logger.display("Copying default configuration file")
4745
default_path = path_join(DATA_PATH, "nxc.conf")
4846
shutil.copy(default_path, NXC_PATH)
49-
50-
# if not exists(CERT_PATH):
51-
# if os.name != 'nt':
52-
# if e.errno == errno.ENOENT:

nxc/helpers/args.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from argparse import ArgumentDefaultsHelpFormatter, SUPPRESS, OPTIONAL, ZERO_OR_MORE
22
from argparse import Action
33

4+
45
class DisplayDefaultsNotNone(ArgumentDefaultsHelpFormatter):
56
def _get_help_string(self, action):
67
help_string = action.help

nxc/helpers/even6_parser.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
from datetime import datetime
77

8+
89
class Substitution:
910
def __init__(self, buf, offset):
1011
(sub_token, sub_id, sub_type) = struct.unpack_from("<BHB", buf, offset)
@@ -46,6 +47,7 @@ def xml(self, template=None):
4647
else:
4748
print("Unknown value type", hex(value.type))
4849

50+
4951
class Value:
5052
def __init__(self, buf, offset):
5153
token, string_type, length = struct.unpack_from("<BBH", buf, offset)
@@ -56,6 +58,7 @@ def __init__(self, buf, offset):
5658
def xml(self, template=None):
5759
return self._val
5860

61+
5962
class Attribute:
6063
def __init__(self, buf, offset):
6164
struct.unpack_from("<B", buf, offset)
@@ -75,13 +78,15 @@ def xml(self, template=None):
7578
val = self._value.xml(template)
7679
return None if val is None else f'{self._name.val}="{val}"'
7780

81+
7882
class Name:
7983
def __init__(self, buf, offset):
8084
hashs, length = struct.unpack_from("<HH", buf, offset)
8185

8286
self.val = buf[offset + 4:offset + 4 + length * 2].decode("utf16")
8387
self.length = 4 + (length + 1) * 2
8488

89+
8590
class Element:
8691
def __init__(self, buf, offset):
8792
token, dependency_id, length = struct.unpack_from("<BHI", buf, offset)
@@ -151,6 +156,7 @@ def xml(self, template=None):
151156
children = (x.xml(template) for x in self._children)
152157
return "<{}{}>{}</{}>".format(self._name.val, attrs, "".join(children), self._name.val)
153158

159+
154160
class ValueSpec:
155161
def __init__(self, buf, offset, value_offset):
156162
self.length, self.type, value_eof = struct.unpack_from("<HBB", buf, offset)
@@ -159,6 +165,7 @@ def __init__(self, buf, offset, value_offset):
159165
if self.type == 0x21:
160166
self.template = BinXML(buf, value_offset)
161167

168+
162169
class TemplateInstance:
163170
def __init__(self, buf, offset):
164171
token, unknown0, guid, length, next_token = struct.unpack_from("<BB16sIB", buf, offset)
@@ -179,6 +186,7 @@ def __init__(self, buf, offset):
179186
def xml(self, template=None):
180187
return self._xml.xml(self)
181188

189+
182190
class BinXML:
183191
def __init__(self, buf, offset):
184192
header_token, major_version, minor_version, flags, next_token = struct.unpack_from("<BBBBB", buf, offset)
@@ -195,6 +203,7 @@ def __init__(self, buf, offset):
195203
def xml(self, template=None):
196204
return self._element.xml(template)
197205

206+
198207
class ResultSet:
199208
def __init__(self, buf):
200209
total_size, header_size, event_offset, bookmark_offset, binxml_size = struct.unpack_from("<IIIII", buf)

0 commit comments

Comments
 (0)