mirror of
https://github.com/Pennyw0rth/NetExec.git
synced 2026-11-09 04:10:18 -10:00
Compare commits
10
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8ab2073bfb | ||
|
|
5b5616a105 | ||
|
|
b2339f5332 | ||
|
|
8d574e2740 | ||
|
|
68b44ed7b3 | ||
|
|
fe25369f07 | ||
|
|
6ab122fa50 | ||
|
|
85bb82bc14 | ||
|
|
5df52a4e29 | ||
|
|
21fa894141 |
No files matched your search
+2
-3
@@ -545,9 +545,8 @@ class connection:
|
||||
if self.args.use_kcache:
|
||||
self.logger.debug("Trying to authenticate using Kerberos cache")
|
||||
with sem:
|
||||
username = self.args.username[0] if len(self.args.username) else ""
|
||||
password = self.args.password[0] if len(self.args.password) else ""
|
||||
self.kerberos_login(self.domain, username, password, "", "", self.kdcHost, True)
|
||||
username = self.args.username[0] # we check this in the main() function when using kcache and set it appropriately already
|
||||
self.kerberos_login(self.domain, username, "", "", "", self.kdcHost, True)
|
||||
self.logger.info("Successfully authenticated using Kerberos cache")
|
||||
return True
|
||||
|
||||
|
||||
@@ -19,6 +19,10 @@ class NXCModule:
|
||||
"""No options available"""
|
||||
|
||||
def on_login(self, context, connection):
|
||||
if not connection.username:
|
||||
context.log.debug("Blank username, cannot get a Kerberos Principal object")
|
||||
return False
|
||||
|
||||
user_name = Principal(connection.username, type=constants.PrincipalNameType.NT_PRINCIPAL.value)
|
||||
try:
|
||||
tgt_with_pac, cipher, old_session_key, session_key = getKerberosTGT(
|
||||
|
||||
+39
-3
@@ -24,6 +24,9 @@ from os.path import join as path_join
|
||||
from sys import exit
|
||||
from rich.progress import Progress
|
||||
import platform
|
||||
from impacket.krb5.ccache import CCache
|
||||
|
||||
|
||||
if sys.stdout.encoding == "cp1252":
|
||||
sys.stdout.reconfigure(encoding="utf-8")
|
||||
|
||||
@@ -92,9 +95,42 @@ def main():
|
||||
nxc_logger.fail("Password is required, even if a key file is used - if no passphrase for key, use `-p ''`")
|
||||
exit(1)
|
||||
|
||||
if args.use_kcache and not os.environ.get("KRB5CCNAME"):
|
||||
nxc_logger.error("KRB5CCNAME environment variable is not set")
|
||||
exit(1)
|
||||
if args.use_kcache:
|
||||
krb5ccname = os.getenv("KRB5CCNAME")
|
||||
if not krb5ccname:
|
||||
nxc_logger.error("KRB5CCNAME environment variable is not set!")
|
||||
exit(1)
|
||||
if not os.path.isfile(krb5ccname):
|
||||
nxc_logger.error(f"Kerberos cache file '{krb5ccname}' does not exist!")
|
||||
exit(1)
|
||||
if not os.access(krb5ccname, os.R_OK):
|
||||
nxc_logger.error(f"Kerberos cache file '{krb5ccname}' is not readable!")
|
||||
exit(1)
|
||||
|
||||
krb_domain, krb_username, krb_tgt, krb_tgs = CCache.parseFile()
|
||||
nxc_logger.debug(f"Kerberos cache file parsed: {krb_username}, {krb_domain}, {krb_tgt}, {krb_tgs}")
|
||||
|
||||
if len(args.username) > 1:
|
||||
nxc_logger.error(f"Only one username can be used when using Kerberos cache! The domain\\user for this cache file is '{krb_domain}\\{krb_username}'")
|
||||
exit(1)
|
||||
|
||||
if args.username and krb_username != args.username[0]:
|
||||
nxc_logger.error(f"Kerberos cache file username '{krb_username}' does not match provided username '{args.username[0]}', setting username to '{krb_username}'")
|
||||
else:
|
||||
nxc_logger.debug(f"No username provided, using the one from the cache file: {krb_username}")
|
||||
|
||||
if args.domain and krb_domain != args.domain:
|
||||
nxc_logger.error(f"Kerberos cache file domain '{krb_domain}' does not match provided domain '{args.domain}', setting domain to '{krb_domain}'")
|
||||
else:
|
||||
nxc_logger.debug(f"No domain provided, using the one from the cache file: {krb_domain}")
|
||||
|
||||
args.username = [krb_username]
|
||||
args.domain = krb_domain
|
||||
# at this point the user should be a single user, but in a list, because we still parse credentials the same later, and it expects a list
|
||||
nxc_logger.debug(f"Domain set to {args.domain} and username set to {args.username} for kerberos cache login")
|
||||
|
||||
if args.password:
|
||||
nxc_logger.display("Setting password is irrelevant when using Kerberos cache, ignoring")
|
||||
|
||||
targets = []
|
||||
|
||||
|
||||
@@ -27,7 +27,6 @@ from impacket.dcerpc.v5.samr import (
|
||||
)
|
||||
from impacket.krb5 import constants
|
||||
from impacket.krb5.kerberosv5 import getKerberosTGS, SessionKeyDecryptionError
|
||||
from impacket.krb5.ccache import CCache
|
||||
from impacket.krb5.types import Principal, KerberosException
|
||||
from impacket.ldap import ldap as ldap_impacket
|
||||
from impacket.ldap import ldaptypes
|
||||
@@ -313,13 +312,14 @@ class ldap(connection):
|
||||
else:
|
||||
self.no_ntlm = True
|
||||
|
||||
if self.args.domain:
|
||||
if self.args.use_kcache: # already parsed kcache domain and username in main()
|
||||
self.domain = self.args.domain
|
||||
self.username = self.args.username[0]
|
||||
elif self.args.domain:
|
||||
self.domain = self.args.domain
|
||||
elif self.args.use_kcache: # Fixing domain trust, just pull the auth domain out of the ticket
|
||||
self.domain = CCache.parseFile()[0]
|
||||
self.username = CCache.parseFile()[1]
|
||||
else:
|
||||
self.domain = self.targetDomain
|
||||
self.logger.debug(f"Domain set to {self.domain} and username set to {self.username} for LDAP connection object")
|
||||
|
||||
self.check_ldap_signing()
|
||||
if getattr(self.args, "port_explicitly_set", False) and self.port == 389:
|
||||
@@ -358,7 +358,7 @@ class ldap(connection):
|
||||
self.logger.display(f"{self.server_os} (name:{self.hostname}) (domain:{self.domain}) ({signing}) ({cbt_status}) {ntlm}")
|
||||
|
||||
def kerberos_login(self, domain, username, password="", ntlm_hash="", aesKey="", kdcHost="", useCache=False):
|
||||
self.username = username if not self.args.use_kcache else self.username # With ccache we get the username from the ticket
|
||||
self.username = username
|
||||
self.password = password
|
||||
self.domain = domain
|
||||
self.kdcHost = kdcHost
|
||||
|
||||
+17
-6
@@ -228,8 +228,6 @@ class smb(connection):
|
||||
|
||||
if self.args.domain:
|
||||
self.domain = self.args.domain
|
||||
elif self.args.use_kcache: # Fixing domain trust, just pull the auth domain out of the ticket
|
||||
self.domain = CCache.parseFile()[0]
|
||||
else:
|
||||
self.domain = self.targetDomain
|
||||
|
||||
@@ -648,8 +646,16 @@ class smb(connection):
|
||||
relay_list.write(self.host + "\n")
|
||||
|
||||
def generate_tgt(self):
|
||||
if not self.username:
|
||||
self.logger.error("No username provided, cannot generate TGT")
|
||||
return False
|
||||
|
||||
self.logger.info(f"Attempting to get TGT for {self.username}@{self.domain}")
|
||||
userName = Principal(self.username, type=constants.PrincipalNameType.NT_PRINCIPAL.value)
|
||||
try:
|
||||
userName = Principal(self.username, type=constants.PrincipalNameType.NT_PRINCIPAL.value)
|
||||
except Exception as e:
|
||||
self.logger.fail(f"Failed to create Principal object: {e}")
|
||||
return False
|
||||
|
||||
try:
|
||||
tgt, cipher, oldSessionKey, sessionKey = getKerberosTGT(
|
||||
@@ -667,13 +673,15 @@ class smb(connection):
|
||||
|
||||
ccache = CCache()
|
||||
ccache.fromTGT(tgt, oldSessionKey, sessionKey)
|
||||
tgt_file = f"{self.args.generate_tgt}.ccache"
|
||||
tgt_file = f"{self.args.generate_tgt}.ccache" if not self.args.generate_tgt.endswith(".ccache") else self.args.generate_tgt
|
||||
ccache.saveFile(tgt_file)
|
||||
|
||||
self.logger.success(f"TGT saved to: {tgt_file}")
|
||||
self.logger.success(f"Run the following command to use the TGT: export KRB5CCNAME={tgt_file}")
|
||||
return True
|
||||
except Exception as e:
|
||||
self.logger.fail(f"Failed to get TGT: {e}")
|
||||
return False
|
||||
|
||||
def check_dc_ports(self, timeout=1):
|
||||
"""Check multiple DC-specific ports in case first check fails"""
|
||||
@@ -818,7 +826,7 @@ class smb(connection):
|
||||
try:
|
||||
# https://github.com/fortra/impacket/issues/1611
|
||||
if self.kerberos:
|
||||
raise Exception("MMCExec current is buggly with kerberos")
|
||||
raise Exception("MMCExec current is buggy with kerberos")
|
||||
exec_method = MMCEXEC(
|
||||
self.remoteName,
|
||||
self.smb_share_name,
|
||||
@@ -1614,7 +1622,10 @@ class smb(connection):
|
||||
def local_groups(self):
|
||||
self.logger.display("Enumerating with SAMRPC protocol")
|
||||
try:
|
||||
groups, members = SamrFunc(self).get_local_groups(self.args.local_groups)
|
||||
samr_func = SamrFunc(self)
|
||||
if not samr_func.lsa_query.dce or not samr_func.samr_query.dce:
|
||||
return
|
||||
groups, members = samr_func.get_local_groups(self.args.local_groups)
|
||||
except DCERPCException as e:
|
||||
self.logger.fail(f"Error enumerating local groups: {e}")
|
||||
return
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
from impacket.dcerpc.v5 import transport, lsat, lsad, samr
|
||||
from impacket.dcerpc.v5.dtypes import MAXIMUM_ALLOWED
|
||||
from impacket.dcerpc.v5.rpcrt import RPC_C_AUTHN_GSS_NEGOTIATE
|
||||
from impacket.krb5.kerberosv5 import KerberosError
|
||||
from impacket.nmb import NetBIOSError
|
||||
from impacket.smbconnection import SessionError
|
||||
|
||||
@@ -18,8 +19,8 @@ class SamrFunc:
|
||||
self.password = connection.password
|
||||
self.domain = connection.domain
|
||||
self.hash = connection.hash
|
||||
self.lmhash = ""
|
||||
self.nthash = ""
|
||||
self.lmhash = connection.lmhash
|
||||
self.nthash = connection.nthash
|
||||
self.aesKey = connection.aesKey
|
||||
self.doKerberos = connection.kerberos
|
||||
self.kdcHost = connection.kdcHost
|
||||
@@ -34,8 +35,8 @@ 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, logger=self.logger)
|
||||
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.host, lmhash=self.lmhash, nthash=self.nthash, kerberos=self.doKerberos, kdcHost=self.kdcHost, aesKey=self.aesKey, logger=self.logger)
|
||||
self.lsa_query = LSAQuery(username=self.username, password=self.password, domain=self.domain, remote_name=self.addr, remote_host=self.host, lmhash=self.lmhash, nthash=self.nthash, kdcHost=self.kdcHost, kerberos=self.doKerberos, aesKey=self.aesKey, logger=self.logger)
|
||||
|
||||
def get_builtin_groups(self, group):
|
||||
domains = self.samr_query.get_domains()
|
||||
@@ -84,12 +85,12 @@ class SamrFunc:
|
||||
|
||||
|
||||
class SAMRQuery:
|
||||
def __init__(self, username="", password="", domain="", port=445, remote_name="", remote_host="", kerberos=None, kdcHost="", aesKey="", logger=None,):
|
||||
def __init__(self, username="", password="", domain="", port=445, remote_name="", remote_host="", lmhash="", nthash="", kerberos=None, kdcHost="", aesKey="", logger=None):
|
||||
self.__username = username
|
||||
self.__password = password
|
||||
self.__domain = domain
|
||||
self.__lmhash = ""
|
||||
self.__nthash = ""
|
||||
self.__lmhash = lmhash
|
||||
self.__nthash = nthash
|
||||
self.__aesKey = aesKey
|
||||
self.__port = port
|
||||
self.__remote_name = remote_name
|
||||
@@ -98,7 +99,10 @@ class SAMRQuery:
|
||||
self.__kdcHost = kdcHost
|
||||
self.logger = logger
|
||||
self.dce = self.get_dce()
|
||||
self.server_handle = self.get_server_handle()
|
||||
if self.dce:
|
||||
self.server_handle = self.get_server_handle()
|
||||
else:
|
||||
return
|
||||
|
||||
def get_transport(self):
|
||||
string_binding = rf"ncacn_np:{self.__port}[\pipe\samr]"
|
||||
@@ -126,10 +130,16 @@ class SAMRQuery:
|
||||
dce.connect()
|
||||
dce.bind(samr.MSRPC_UUID_SAMR)
|
||||
except NetBIOSError as e:
|
||||
self.logger.error(f"NetBIOSError on Connection: {e}")
|
||||
self.logger.fail(f"NetBIOSError on Connection: {e}")
|
||||
return None
|
||||
except SessionError as e:
|
||||
self.logger.error(f"SessionError on Connection: {e}")
|
||||
error_code, error_string = e.getErrorString()
|
||||
if error_code == "KDC_ERR_S_PRINCIPAL_UNKNOWN":
|
||||
self.logger.fail(f"Error {error_code} when creating SAMRPC connection, which means the server {self.__remote_host} is not an object in the Kerberos realm (try using an FQDN)")
|
||||
elif error_code == "KDC_ERR_C_PRINCIPAL_UNKNOWN":
|
||||
self.logger.fail(f"Error {error_code} when creating SAMRPC connection, which means the user {self.__username} is not an object in the Kerberos realm")
|
||||
else:
|
||||
self.logger.fail(f"SessionError on SAMRPC Connection: {e}")
|
||||
return None
|
||||
return dce
|
||||
|
||||
@@ -177,21 +187,24 @@ 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="", lmhash="", nthash="", kdcHost="", aesKey="", kerberos=None, logger=None):
|
||||
self.__username = username
|
||||
self.__password = password
|
||||
self.__domain = domain
|
||||
self.__lmhash = ""
|
||||
self.__nthash = ""
|
||||
self.__lmhash = lmhash
|
||||
self.__nthash = nthash
|
||||
self.__aesKey = aesKey
|
||||
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()
|
||||
self.logger = logger
|
||||
self.dce = self.get_dce()
|
||||
if self.dce:
|
||||
self.policy_handle = self.get_policy_handle()
|
||||
else:
|
||||
return
|
||||
|
||||
def get_transport(self):
|
||||
string_binding = f"ncacn_np:{self.__remote_name}[\\pipe\\lsarpc]"
|
||||
@@ -223,6 +236,15 @@ class LSAQuery:
|
||||
except NetBIOSError as e:
|
||||
self.logger.fail(f"NetBIOSError on Connection: {e}")
|
||||
return None
|
||||
except KerberosError as e:
|
||||
error_code, error_string = e.getErrorString()
|
||||
if error_code == "KDC_ERR_S_PRINCIPAL_UNKNOWN":
|
||||
self.logger.fail(f"Error {error_code} when creating LSA connection, which means the server {self.__remote_host} is not an object in the Kerberos realm (try using an FQDN)")
|
||||
elif error_code == "KDC_ERR_C_PRINCIPAL_UNKNOWN":
|
||||
self.logger.fail(f"Error {error_code} when creating LSA connection, which means the user {self.__username} is not an object in the Kerberos realm")
|
||||
else:
|
||||
self.logger.fail(f"KerberosError on LSA Connection: {e}")
|
||||
return None
|
||||
return dce
|
||||
|
||||
def get_policy_handle(self):
|
||||
|
||||
@@ -3,6 +3,7 @@ netexec -h
|
||||
##### SMB
|
||||
netexec smb TARGET_HOST --generate-hosts-file /tmp/hostsfile
|
||||
netexec smb TARGET_HOST --generate-krb5-file /tmp/krb5conf
|
||||
netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS --generate-tgt /tmp/tgt.ccache
|
||||
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
|
||||
|
||||
Reference in new issue
Block a user