def main(): # logging.basicConfig(stream=sys.stderr, level=logging.INFO) logger = logging.getLogger() logger.setLevel(logging.INFO) stream = logging.StreamHandler(sys.stderr) stream.setLevel(logging.DEBUG) formatter = logging.Formatter('%(levelname)s: %(message)s') # formatter = logging.Formatter('%(asctime)s %(levelname)s: %(message)s') stream.setFormatter(formatter) logger.addHandler(stream) parser = argparse.ArgumentParser( add_help=True, description= 'Python based ingestor for BloodHound\nFor help or reporting issues, visit https://github.com/Fox-IT/BloodHound.py', formatter_class=argparse.RawDescriptionHelpFormatter) parser.add_argument( '-c', '--collectionmethod', action='store', default='Default', help= 'Which information to collect. Supported: Group, LocalAdmin, Session, ' 'Trusts, Default (all previous), DCOnly (no computer connections), DCOM, RDP,' 'PSRemote, LoggedOn, ObjectProps, ACL, All (all except LoggedOn). ' 'You can specify more than one by separating them with a comma. (default: Default)' ) parser.add_argument( '-u', '--username', action='store', help= 'Username. Format: username[@domain]; If the domain is unspecified, the current domain is used.' ) parser.add_argument('-p', '--password', action='store', help='Password') parser.add_argument('-k', '--kerberos', action='store_true', help='Use kerberos') parser.add_argument('--hashes', action='store', help='LM:NLTM hashes') parser.add_argument('-ns', '--nameserver', action='store', help='Alternative name server to use for queries') parser.add_argument('--dns-tcp', action='store_true', help='Use TCP instead of UDP for DNS queries') parser.add_argument('--dns-timeout', action='store', type=int, default=3, help='DNS query timeout in seconds (default: 3)') parser.add_argument('-d', '--domain', action='store', help='Domain to query.') parser.add_argument('-dc', '--domain-controller', metavar='HOST', action='store', help='Override which DC to query (hostname)') parser.add_argument('-gc', '--global-catalog', metavar='HOST', action='store', help='Override which GC to query (hostname)') parser.add_argument( '-w', '--workers', action='store', type=int, default=10, help='Number of workers for computer enumeration (default: 10)') parser.add_argument('-v', action='store_true', help='Enable verbose output') parser.add_argument( '--disable-pooling', action='store_true', help= 'Don\'t use subprocesses for ACL parsing (only for debugging purposes)' ) parser.add_argument( '--disable-autogc', action='store_true', help= 'Don\'t automatically select a Global Catalog (use only if it gives errors)' ) args = parser.parse_args() if args.v is True: logger.setLevel(logging.DEBUG) if args.kerberos is True: logging.debug('Authentication: kerberos') kerberize() auth = ADAuthentication() elif 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) elif args.username is not None and args.password is None and args.hashes is None: args.password = getpass.getpass() auth = ADAuthentication(username=args.username, password=args.password, domain=args.domain) elif args.username is None and (args.password is not None or args.hashes is not None): logging.error( 'Authentication: password or hashes provided without username') sys.exit(1) elif args.hashes is not None and args.username is not None: logging.debug('Authentication: NTLM hashes') lm, nt = args.hashes.split(":") auth = ADAuthentication(lm_hash=lm, nt_hash=nt, username=args.username, domain=args.domain) else: parser.print_help() sys.exit(1) ad = AD(auth=auth, domain=args.domain, nameserver=args.nameserver, dns_tcp=args.dns_tcp, dns_timeout=args.dns_timeout) # Resolve collection methods collect = resolve_collection_methods(args.collectionmethod) if not collect: return logging.debug('Resolved collection methods: %s', ', '.join(list(collect))) logging.debug('Using DNS to retrieve domain information') ad.dns_resolve(kerberos=args.kerberos, domain=args.domain, options=args) # Override the detected DC / GC if specified if args.domain_controller: if re.match(r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}', args.domain_controller): logging.error('The specified domain controller %s looks like an IP address, but requires a hostname (FQDN).\n'\ 'Use the -ns flag to specify a DNS server IP if the hostname does not resolve on your default nameserver.', args.domain_controller) sys.exit(1) ad.override_dc(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'\ 'Use the -ns flag to specify a DNS server IP if the hostname does not resolve on your default nameserver.', args.global_catalog) sys.exit(1) ad.override_gc(args.global_catalog) bloodhound = BloodHound(ad) bloodhound.connect() bloodhound.run(collect=collect, num_workers=args.workers, disable_pooling=args.disable_pooling)
def main(): # logging.basicConfig(stream=sys.stderr, level=logging.INFO) logger = logging.getLogger() logger.setLevel(logging.INFO) stream = logging.StreamHandler(sys.stderr) stream.setLevel(logging.DEBUG) formatter = logging.Formatter('%(levelname)s: %(message)s') # formatter = logging.Formatter('%(asctime)s %(levelname)s: %(message)s') stream.setFormatter(formatter) logger.addHandler(stream) parser = argparse.ArgumentParser( add_help=True, description= 'Python based ingestor for BloodHound\nFor help or reporting issues, visit https://github.com/Fox-IT/BloodHound.py', formatter_class=argparse.RawDescriptionHelpFormatter) parser.add_argument( '-c', '--collectionmethod', action='store', default='Default', help= 'Which information to collect. Supported: Group, LocalAdmin, Session, ' 'Trusts, Default/All (all the previous). You can specify more than one by ' 'separating them with a comma. (default: Default)') parser.add_argument( '-u', '--username', action='store', help= 'Username. Format: username[@domain]; If the domain is unspecified, the current domain is used.' ) parser.add_argument('-p', '--password', action='store', help='Password') parser.add_argument('-k', '--kerberos', action='store_true', help='Use kerberos') parser.add_argument('--hashes', action='store', help='LM:NLTM hashes') parser.add_argument('-ns', '--nameserver', action='store', help='Alternative name server to use for queries') parser.add_argument('--dns-tcp', action='store_true', help='Use TCP instead of UDP for DNS queries') parser.add_argument('-d', '--domain', action='store', help='Domain to query.') parser.add_argument('-dc', '--domain-controller', metavar='HOST', action='store', help='Override which DC to query') parser.add_argument('-gc', '--global-catalog', metavar='HOST', action='store', help='Override which GC to query') parser.add_argument( '-w', '--workers', action='store', type=int, default=10, help='Number of workers for computer enumeration (default: 10)') parser.add_argument('-v', action='store_true', help='Enable verbose output') args = parser.parse_args() if args.v is True: logger.setLevel(logging.DEBUG) if args.kerberos is True: logging.debug('Authentication: kerberos') kerberize() auth = ADAuthentication() elif 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) elif args.username is not None and args.password is None and args.hashes is None: args.password = getpass.getpass() auth = ADAuthentication(username=args.username, password=args.password, domain=args.domain) elif args.username is None and (args.password is not None or args.hashes is not None): logging.error( 'Authentication: password or hashes provided without username') sys.exit(1) elif args.hashes is not None and args.username is not None: logging.debug('Authentication: NTLM hashes') lm, nt = args.hashes.split(":") auth = ADAuthentication(lm_hash=lm, nt_hash=nt, username=args.username, domain=args.domain) else: parser.print_help() sys.exit(1) ad = AD(auth=auth, domain=args.domain, nameserver=args.nameserver, dns_tcp=args.dns_tcp) # Resolve collection methods collect = resolve_collection_methods(args.collectionmethod) if not collect: return logging.debug('Resolved collection methods: %s', ', '.join(list(collect))) logging.debug('Using DNS to retrieve domain information') ad.dns_resolve(kerberos=args.kerberos, domain=args.domain) # Override the detected DC / GC if specified if args.domain_controller: ad.override_dc(args.domain_controller) if args.global_catalog: ad.override_gc(args.global_catalog) bloodhound = BloodHound(ad) bloodhound.connect() bloodhound.run(collect=collect, num_workers=args.workers)