Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add no_gc flag - Never query GC #156

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions bloodhound/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ def connect(self):
logging.debug('Using kerberos realm: %s', self.ad.realm())

# Create a domain controller object
self.pdc = ADDC(pdc, self.ad)
self.pdc = ADDC(pdc, self.ad, no_gc=self.ad.no_gc)
# Create an object resolver
self.ad.create_objectresolver(self.pdc)

Expand Down Expand Up @@ -265,6 +265,9 @@ def main():
metavar='PREFIX_NAME',
action='store',
help='String to prepend to output file names')
coopts.add_argument('--no-gc',
action='store_true',
help='Never use GC to resolve objects')



Expand Down Expand Up @@ -296,7 +299,7 @@ def main():
else:
auth = ADAuthentication(username=args.username, password=args.password, domain=args.domain, auth_method=args.auth_method)

ad = AD(auth=auth, domain=args.domain, nameserver=args.nameserver, dns_tcp=args.dns_tcp, dns_timeout=args.dns_timeout, use_ldaps=args.use_ldaps)
ad = AD(auth=auth, domain=args.domain, nameserver=args.nameserver, dns_tcp=args.dns_tcp, dns_timeout=args.dns_timeout, use_ldaps=args.use_ldaps, no_gc=args.no_gc)

# Resolve collection methods
collect = resolve_collection_methods(args.collectionmethod)
Expand Down
12 changes: 7 additions & 5 deletions bloodhound/ad/domain.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
Active Directory Domain Controller
"""
class ADDC(ADComputer):
def __init__(self, hostname=None, ad=None):
def __init__(self, hostname=None, ad=None, no_gc=False):
ADComputer.__init__(self, hostname)
self.ad = ad
# Primary LDAP connection
Expand All @@ -50,6 +50,7 @@ def __init__(self, hostname=None, ad=None):
self.resolverldap = None
# GC LDAP connection
self.gcldap = None
self.no_gc = no_gc
# Initialize GUID map
self.objecttype_guid_map = dict()

Expand Down Expand Up @@ -115,7 +116,7 @@ def gc_connect(self, protocol='ldap'):
except (resolver.NXDOMAIN, resolver.Timeout):
continue

self.gcldap = self.ad.auth.getLDAPConnection(hostname=self.hostname, ip=ip, gc=True,
self.gcldap = self.ad.auth.getLDAPConnection(hostname=self.hostname, ip=ip, gc=(True and not self.no_gc),
baseDN=self.ad.baseDN, protocol=protocol)
return self.gcldap is not None

Expand Down Expand Up @@ -362,7 +363,7 @@ def get_cache_items(self):
logging.info('Processing domain %s', domain['attributes']['name'])
query = '(|(&(objectCategory=person)(objectClass=user))(objectClass=group)(&(sAMAccountType=805306369)(!(UserAccountControl:1.2.840.113556.1.4.803:=2))))'
entries = self.search(query,
use_gc=True,
use_gc=(True and not self.no_gc),
use_resolver=True,
attributes=['sAMAccountName', 'distinguishedName', 'sAMAccountType', 'objectSid', 'name'],
search_base=nc,
Expand Down Expand Up @@ -584,7 +585,7 @@ def get_root_domain(self):
"""
class AD(object):

def __init__(self, domain=None, auth=None, nameserver=None, dns_tcp=False, dns_timeout=3.0, use_ldaps=False):
def __init__(self, domain=None, auth=None, nameserver=None, dns_tcp=False, dns_timeout=3.0, use_ldaps=False, no_gc=False):
self.domain = domain
# Object of type ADDomain, added later
self.domain_object = None
Expand Down Expand Up @@ -645,6 +646,7 @@ def __init__(self, domain=None, auth=None, nameserver=None, dns_tcp=False, dns_t
self.ldap_default_protocol = 'ldaps'
else:
self.ldap_default_protocol = 'ldap'
self.no_gc = no_gc

def realm(self):
if self.domain is not None:
Expand All @@ -668,7 +670,7 @@ def kdcs(self):
return self._kdcs

def create_objectresolver(self, addc):
self.objectresolver = ObjectResolver(addomain=self, addc=addc)
self.objectresolver = ObjectResolver(addomain=self, addc=addc, no_gc=self.no_gc)

def load_cachefile(self, cachefile):
with codecs.open(cachefile, 'r', 'utf-8') as cfile:
Expand Down
21 changes: 11 additions & 10 deletions bloodhound/enumeration/objectresolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,31 +31,32 @@ class ObjectResolver(object):
should be resolved in the GC to see which domain they belong to, or SIDs which have to be
resolved somewhere else. This resolver is thread-safe.
"""
def __init__(self, addomain, addc):
def __init__(self, addomain, addc, no_gc=False):
self.addomain = addomain
self.addc = addc
self.lock = threading.Lock()
self.no_gc = no_gc

def resolve_distinguishedname(self, distinguishedname, use_gc=True):
"""
Resolve a DistinguishedName in LDAP. This will use the GC by default
Returns a single LDAP entry
"""
with self.lock:
if use_gc and not self.addc.gcldap:
if use_gc and not self.no_gc and not self.addc.gcldap:
if not self.addc.gc_connect():
# Error connecting, bail
return None
if not use_gc and not self.addc.resolverldap:
if not self.addc.ldap_connect(resolver=True):
# Error connecting, bail
return None
if use_gc:
if use_gc and not self.no_gc:
logging.debug('Querying GC for DN %s', distinguishedname)
else:
logging.debug('Querying resolver LDAP for DN %s', distinguishedname)
distinguishedname = self.addc.ldap_get_single(distinguishedname,
use_gc=use_gc,
use_gc=(use_gc and not self.no_gc),
use_resolver=True,
attributes=['sAMAccountName', 'distinguishedName', 'sAMAccountType', 'objectSid', 'name'])
return distinguishedname
Expand All @@ -71,7 +72,7 @@ def resolve_samname(self, samname, use_gc=True, allow_filter=False):
else:
safename = samname
with self.lock:
if use_gc:
if use_gc and not self.no_dc:
if not self.addc.gcldap:
if not self.addc.gc_connect():
# Error connecting, bail
Expand All @@ -81,7 +82,7 @@ def resolve_samname(self, samname, use_gc=True, allow_filter=False):
logging.debug('Querying LDAP for SAM Name %s', samname)
entries = self.addc.search(search_base="",
search_filter='(sAMAccountName=%s)' % safename,
use_gc=use_gc,
use_gc=(use_gc and not self.no_gc),
attributes=['sAMAccountName', 'distinguishedName', 'sAMAccountType', 'objectSid', 'name'])
# This uses a generator, however we return a list
for entry in entries:
Expand All @@ -103,7 +104,7 @@ def resolve_upn(self, upn):
logging.debug('Querying GC for UPN %s', upn)
entries = self.addc.search(search_base="",
search_filter='(&(objectClass=user)(userPrincipalName=%s))' % safename,
use_gc=True,
use_gc=(not self.no_gc),
attributes=['sAMAccountName', 'distinguishedName', 'sAMAccountType', 'objectSid', 'name'])
for entry in entries:
# By definition this can be only one entry
Expand All @@ -115,23 +116,23 @@ def resolve_sid(self, sid, use_gc=True):
Returns a single LDAP entry
"""
with self.lock:
if use_gc and not self.addc.gcldap:
if use_gc and not self.no_gc and not self.addc.gcldap:
if not self.addc.gc_connect():
# Error connecting, bail
return None
if not use_gc and not self.addc.resolverldap:
if not self.addc.ldap_connect(resolver=True):
# Error connecting, bail
return None
if use_gc:
if use_gc and not self.no_gc:
base = ""
logging.debug('Querying GC for SID %s', sid)
else:
logging.debug('Querying resolver LDAP for SID %s', sid)
base = None
entries = self.addc.search(search_base=base,
search_filter='(objectSid=%s)' % sid,
use_gc=use_gc,
use_gc=(use_gc and not self.no_gc),
use_resolver=True,
attributes=['sAMAccountName', 'distinguishedName', 'sAMAccountType', 'name'])
for entry in entries:
Expand Down