BloodHound.py icon indicating copy to clipboard operation
BloodHound.py copied to clipboard

Fix to solve DNS resolution fail when different DNs sufix

Open helviojunior opened this issue 1 year ago • 1 comments
trafficstars

I faced some issue when using a BloodHound in a environment with a different DNS sufix.

Error sample 1

bloodhound-python -c All -ns 192.168.30.230 -d alunos.sec4us.local -u 'aluno.0' -p '...'
INFO: Found AD domain: alunos.sec4us.local
Traceback (most recent call last):
  File "/usr/local/bin/bloodhound-python", line 8, in <module>
    sys.exit(main())
             ^^^^^^
  File "/usr/local/lib/python3.11/dist-packages/bloodhound/__init__.py", line 308, in main
    ad.dns_resolve(domain=args.domain, options=args)
  File "/usr/local/lib/python3.11/dist-packages/bloodhound/ad/domain.py", line 720, in dns_resolve
    q = self.dnsresolver.query(query.replace('pdc','gc'), 'SRV', tcp=self.dns_tcp)
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/dns/resolver.py", line 1262, in query
    return self.resolve(
           ^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/dns/resolver.py", line 1204, in resolve
    timeout = self._compute_timeout(start, lifetime, resolution.errors)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/dns/resolver.py", line 988, in _compute_timeout
    raise LifetimeTimeout(timeout=duration, errors=errors)
dns.resolver.LifetimeTimeout: The resolution lifetime expired after 3.103 seconds: Server 192.168.30.230 UDP port 53 answered The DNS operation timed out.

Error sample 2

bloodhound-python -c All -ns 192.168.30.230 -d alunos.sec4us.local -u 'aluno.0' -p '...' --disable-autogc --dns-timeout 50
INFO: Found AD domain: alunos.sec4us.local
Traceback (most recent call last):
  File "/usr/local/bin/bloodhound-python", line 8, in <module>
    sys.exit(main())
             ^^^^^^
  File "/usr/local/lib/python3.11/dist-packages/bloodhound/__init__.py", line 308, in main
    ad.dns_resolve(domain=args.domain, options=args)
  File "/usr/local/lib/python3.11/dist-packages/bloodhound/ad/domain.py", line 720, in dns_resolve
    q = self.dnsresolver.query(query.replace('pdc','gc'), 'SRV', tcp=self.dns_tcp)
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/dns/resolver.py", line 1262, in query
    return self.resolve(
           ^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/dns/resolver.py", line 1201, in resolve
    (nameserver, port, tcp, backoff) = resolution.next_nameserver()
                                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/dns/resolver.py", line 704, in next_nameserver
    raise NoNameservers(request=self.request, errors=self.errors)
dns.resolver.NoNameservers: All nameservers failed to answer the query _ldap._tcp.gc._msdcs.alunos.sec4us.local.labpentestbrasil.com.br. IN SRV: Server 192.168.30.230 UDP port 53 answered SERVFAIL

This second error show me what is the issue

Looking at my DNS config

ccat /etc/resolv.conf
───────┬────────────────────────────────
       │ File: /etc/resolv.conf
───────┼────────────────────────────────
    1  │ # Generated by NetworkManager
    2  │ search labpentestbrasil.com.br
    3  │ nameserver 192.168.30.1
───────┴────────────────────────────────

Solution PoC

To test i just put the "." at the end of the domain

bloodhound-python -c All -ns 192.168.30.230 -d alunos.sec4us.local. -u 'aluno.0' -p '...'
INFO: Found AD domain: alunos.sec4us.local
WARNING: Could not find a global catalog server, assuming the primary DC has this role
If this gives errors, either specify a hostname with -gc or disable gc resolution with --disable-autogc
INFO: Getting TGT for user
WARNING: Failed to get Kerberos TGT. Falling back to NTLM authentication. Error: [Errno Connection error (dc02.alunos.sec4us.local:88)] [Errno 113] No route to host
INFO: Connecting to LDAP server: dc02.alunos.sec4us.local
INFO: Found 1 domains
INFO: Found 2 domains in the forest
INFO: Found 5 computers
INFO: Connecting to LDAP server: dc02.alunos.sec4us.local
INFO: Connecting to GC LDAP server: dc02.alunos.sec4us.local
INFO: Found 132 users
INFO: Found 75 groups
INFO: Found 2 gpos
INFO: Found 16 ous
INFO: Found 19 containers
INFO: Found 1 trusts
...

Solution

Add the following code to force "." at the end

args.domain = f'{args.domain.rstrip(" .")}.'

Final test

ccat /usr/local/lib/python3.11/dist-packages/bloodhound/__init__.py -l 285:310
─────┬──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
     │ File: /usr/local/lib/python3.11/dist-packages/bloodhound/__init__.py
─────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
 ... │
 285 │     elif args.hashes is not None and args.username is not None:
 286 │         logging.debug('Authentication: NT hash')
 287 │         lm, nt = args.hashes.split(":")
 288 │         auth = ADAuthentication(lm_hash=lm, nt_hash=nt, username=args.username, domain=args.domain, auth_method=args.auth_method)
 289 │     elif args.aesKey is not None and args.username is not None:
 290 │         logging.debug('Authentication: Kerberos AES')
 291 │         auth = ADAuthentication(username=args.username, domain=args.domain, aeskey=args.aesKey, auth_method=args.auth_method)
 292 │     else:
 293 │         if not args.kerberos:
 294 │             parser.print_help()
 295 │             sys.exit(1)
 296 │         else:
 297 │             auth = ADAuthentication(username=args.username, password=args.password, domain=args.domain, auth_method=args.auth_method)
 298 │
 299 │     # Put "." at the end to prevent DNS resolution error
 300 │     args.domain = f'{args.domain.rstrip(" .")}.'
 301 │
 302 │     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)
 303 │
 304 │     # Resolve collection methods
 305 │     collect = resolve_collection_methods(args.collectionmethod)
 306 │     if not collect:
 307 │         return
 308 │     logging.debug('Resolved collection methods: %s', ', '.join(list(collect)))
 309 │
 310 │     logging.debug('Using DNS to retrieve domain information')
 ... │
─────┴──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
bloodhound-python -c All -ns 192.168.30.230 -d alunos.sec4us.local -u 'aluno.0' -p '...'
INFO: Found AD domain: alunos.sec4us.local
WARNING: Could not find a global catalog server, assuming the primary DC has this role
If this gives errors, either specify a hostname with -gc or disable gc resolution with --disable-autogc
INFO: Getting TGT for user
WARNING: Failed to get Kerberos TGT. Falling back to NTLM authentication. Error: [Errno Connection error (dc02.alunos.sec4us.local:88)] [Errno 113] No route to host
INFO: Connecting to LDAP server: dc02.alunos.sec4us.local
INFO: Found 1 domains
INFO: Found 2 domains in the forest
INFO: Found 5 computers
INFO: Connecting to LDAP server: dc02.alunos.sec4us.local
INFO: Connecting to GC LDAP server: dc02.alunos.sec4us.local
INFO: Found 132 users
INFO: Found 75 groups
INFO: Found 2 gpos
INFO: Found 16 ous
INFO: Found 19 containers
INFO: Found 1 trusts

helviojunior avatar Aug 21 '24 19:08 helviojunior

+1

som3canadian avatar May 18 '25 03:05 som3canadian

Thanks for debugging this, I've resolved this in a slightly different way but it should be resolved now

dirkjanm avatar Aug 22 '25 14:08 dirkjanm