Skip to content

Commit

Permalink
Various Kerberos auth fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
dirkjanm committed Nov 30, 2023
1 parent 44620c0 commit d5a47f3
Show file tree
Hide file tree
Showing 4 changed files with 42 additions and 8 deletions.
10 changes: 5 additions & 5 deletions bloodhound/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ def connect(self):
logging.debug('Using base DN: %s', self.ad.baseDN)

if len(self.ad.kdcs()) > 0:
kdc = self.ad.kdcs()[0]
kdc = self.ad.auth.kdc
logging.debug('Using kerberos KDC: %s', kdc)
logging.debug('Using kerberos realm: %s', self.ad.realm())

Expand Down Expand Up @@ -274,7 +274,7 @@ def main():
if args.username is not None and args.password is not None:
logging.debug('Authentication: username/password')
auth = ADAuthentication(username=args.username, password=args.password, domain=args.domain, auth_method=args.auth_method)
elif args.username is not None and args.password is None and args.hashes is None and args.no_pass is None:
elif args.username is not None and args.password is None and args.hashes is None and args.aesKey is None and args.no_pass is not None:
args.password = getpass.getpass()
auth = ADAuthentication(username=args.username, password=args.password, domain=args.domain, auth_method=args.auth_method)
elif args.username is None and (args.password is not None or args.hashes is not None):
Expand Down Expand Up @@ -313,9 +313,9 @@ def main():
args.domain_controller)
sys.exit(1)
ad.override_dc(args.domain_controller)
if not auth.kdc:
logging.debug('Using supplied domain controller as KDC')
auth.kdc = args.domain_controller
logging.debug('Using supplied domain controller as KDC')
auth.set_kdc(args.domain_controller)

if args.global_catalog:
if re.match(r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}', args.global_catalog):
logging.error('The specified global catalog server %s looks like an IP address, but requires a hostname (FQDN).\n'\
Expand Down
26 changes: 25 additions & 1 deletion bloodhound/ad/authentication.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,12 @@ def __init__(self, username='', password='', domain='',
def set_aeskey(self, aeskey):
self.aeskey = aeskey

def set_kdc(self, kdc):
# Set KDC
self.kdc = kdc
if self.userdomain == self.domain:
# Also set it for user domain if this is equal
self.userdomain_kdc = kdc

def getLDAPConnection(self, hostname='', ip='', baseDN='', protocol='ldaps', gc=False):
if gc:
Expand Down Expand Up @@ -209,6 +215,20 @@ def get_tgt(self):
# Get referral TGT
tgs, cipher, _, sessionkey = getKerberosTGS(servername, self.userdomain, self.userdomain_kdc,
tgt, cipher, session_key)
# See if this is a ticket for the correct domain
refneeded = True
while refneeded:
decoded_tgs = decoder.decode(tgs, asn1Spec = TGS_REP())[0]
next_realm = str(decoded_tgs['ticket']['sname']['name-string'][1])
if next_realm.upper() == self.domain.upper():
refneeded = False
else:
# Get next referral TGT
logging.debug('Following referral across trust to get next TGT')
servername = Principal('krbtgt/%s' % self.domain, type=constants.PrincipalNameType.NT_SRV_INST.value)
tgs, cipher, _, sessionkey = getKerberosTGS(servername, next_realm, next_realm,
tgs, cipher, sessionkey)

# Get foreign domain TGT
servername = Principal('krbtgt/%s' % self.domain, type=constants.PrincipalNameType.NT_SRV_INST.value)
tgs, cipher, _, sessionkey = getKerberosTGS(servername, self.domain, self.kdc,
Expand Down Expand Up @@ -250,7 +270,11 @@ def load_ccache(self):

# Otherwise, guess it.
if krb5cc is None:
krb5cc = '/tmp/krb5cc_%u' % os.getuid()
try:
krb5cc = '/tmp/krb5cc_%u' % os.getuid()
except AttributeError:
# This fails on Windows
krb5cc = 'nonexistingfile'

if os.path.isfile(krb5cc):
logging.debug('Using kerberos credential cache: %s', krb5cc)
Expand Down
12 changes: 10 additions & 2 deletions bloodhound/ad/computer.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
from impacket.dcerpc.v5.rpcrt import DCERPCException, RPC_C_AUTHN_LEVEL_PKT_INTEGRITY
from impacket.dcerpc.v5.ndr import NULL
from impacket.dcerpc.v5.dtypes import RPC_SID, MAXIMUM_ALLOWED
from impacket.nmb import NetBIOSTimeout, NetBIOSError
from bloodhound.ad.utils import ADUtils, AceResolver
from bloodhound.enumeration.acls import parse_binary_acl
from bloodhound.ad.structures import LDAP_SID
Expand Down Expand Up @@ -291,6 +292,8 @@ def dce_rpc_connect(self, binding, uuid, integrity=False):
self.auth_method = 'ntlm'
else:
logging.warning('Failed to get service ticket for %s, skipping host', self.hostname)
self.permanentfailure = True
return None
if hasattr(self.rpc, 'set_credentials'):
if self.auth_method == 'auto':
# Set all we have
Expand Down Expand Up @@ -821,6 +824,9 @@ def rpc_resolve_sids(self, sids, resultlist):
resp = e.get_packet()
else:
raise
except NetBIOSTimeout as e:
logging.warning('Connection timed out while resolving sids')
continue

domains = []
for entry in resp['ReferencedDomains']['Domains']:
Expand All @@ -842,5 +848,7 @@ def rpc_resolve_sids(self, sids, resultlist):
self.ad.sidcache.put(sid_string, resolved_entry)
else:
logging.warning('Resolved name is empty [%s]', entry)

dce.disconnect()
try:
dce.disconnect()
except NetBIOSError:
pass
2 changes: 2 additions & 0 deletions bloodhound/ad/domain.py
Original file line number Diff line number Diff line change
Expand Up @@ -736,6 +736,8 @@ def dns_resolve(self, domain=None, options=None):
kdc = str(r.target).rstrip('.')
logging.debug('Found KDC for user: %s' % str(r.target).rstrip('.'))
self.auth.userdomain_kdc = kdc
else:
self.auth.userdomain_kdc = self.auth.kdc

return True

Expand Down

0 comments on commit d5a47f3

Please sign in to comment.