def run(self, conf, args, plugins): if 'subcommand' in args: if args.subcommand == 'info': if not is_ip(unbracket(args.IP)): print("Invalid IP address") sys.exit(1) # FIXME: move code here in a library ip = unbracket(args.IP) try: ipy = IP(ip) except ValueError: print('Invalid IP format, quitting...') return ipinfo = self.ipinfo(ip) print('MaxMind: Located in %s, %s' % (ipinfo['city'], ipinfo['country'])) if ipinfo['asn'] == 0: print("MaxMind: IP not found in the ASN database") else: print('MaxMind: ASN%i, %s' % ( ipinfo['asn'], ipinfo['asn_name'] ) ) print('CAIDA Type: %s' % ipinfo['asn_type']) asndb2 = pyasn.pyasn(self.asncidr) res = asndb2.lookup(ip) if res[1] is None: print("IP not found in ASN database") else: # Search for name f = open(self.asnname, 'r') found = False line = f.readline() name = '' while not found and line != '': s = line.split('|') if s[0] == str(res[0]): name = s[1].strip() found = True line = f.readline() print('ASN %i - %s (range %s)' % ( res[0], name, res[1] ) ) if ipinfo['hostname'] != '': print('Hostname: %s' % ipinfo['hostname']) if ipinfo['specific'] != '': print("Specific: %s" % ipinfo['specific']) if ipy.iptype() == "PRIVATE": "Private IP" print("") if ipy.version() == 4: print("Censys:\t\thttps://censys.io/ipv4/%s" % ip) print("Shodan:\t\thttps://www.shodan.io/host/%s" % ip) print("IP Info:\thttp://ipinfo.io/%s" % ip) print("BGP HE:\t\thttps://bgp.he.net/ip/%s" % ip) print("IP Location:\thttps://www.iplocation.net/?query=%s" % ip) elif args.subcommand == "intel": if not is_ip(unbracket(args.IP)): print("Invalid IP address") sys.exit(1) # Start with MISP and OTX to get Intelligence Reports print('###################### %s ###################' % unbracket(args.IP)) passive_dns = [] urls = [] malware = [] files = [] # MISP misp_e = plugins['misp'].test_config(conf) if misp_e: print('[+] Downloading MISP information...') server = MispServer(url=conf['Misp']['url'], apikey=conf['Misp']['key']) misp_results = server.attributes.search(value=unbracket(args.IP)) # Binary Edge be_e = plugins['binaryedge'].test_config(conf) if be_e: print('[+] Downloading BinaryEdge information...') be = BinaryEdge(conf['BinaryEdge']['key']) # FIXME: this only get the first page res = be.domain_ip(unbracket(args.IP)) for d in res["events"]: passive_dns.append({ "domain": d['domain'], "first": parse(d['updated_at']), "last": parse(d['updated_at']), "source" : "BinaryEdge" }) # OTX otx_e = plugins['otx'].test_config(conf) if otx_e: print('[+] Downloading OTX information....') otx = OTXv2(conf["AlienVaultOtx"]["key"]) res = otx.get_indicator_details_full(IndicatorTypes.IPv4, unbracket(args.IP)) otx_pulses = res["general"]["pulse_info"]["pulses"] # Get Passive DNS if "passive_dns" in res: for r in res["passive_dns"]["passive_dns"]: passive_dns.append({ "domain": r['hostname'], "first": parse(r["first"]), "last": parse(r["last"]), "source" : "OTX" }) if "url_list" in res: for r in res["url_list"]["url_list"]: urls.append(r) # RobTex print('[+] Downloading Robtex information....') rob = Robtex() try: res = rob.get_ip_info(unbracket(args.IP)) except RobtexError: print("Error with Robtex") else: for d in ["pas", "pash", "act", "acth"]: if d in res: for a in res[d]: passive_dns.append({ 'first': a['date'], 'last': a['date'], 'domain': a['o'], 'source': 'Robtex' }) # PT pt_e = plugins['pt'].test_config(conf) if pt_e: out_pt = False print('[+] Downloading Passive Total information....') client = DnsRequest(conf['PassiveTotal']['username'], conf['PassiveTotal']['key']) try: raw_results = client.get_passive_dns(query=unbracket(args.IP)) if "results" in raw_results: for res in raw_results["results"]: passive_dns.append({ "first": parse(res["firstSeen"]), "last": parse(res["lastSeen"]), "domain": res["resolve"], "source": "PT" }) if "message" in raw_results: if "quota_exceeded" in raw_results["message"]: print("Quota exceeded for Passive Total") out_pt = True pt_osint = {} except requests.exceptions.ReadTimeout: print("Timeout on Passive Total requests") if not out_pt: try: client2 = EnrichmentRequest(conf["PassiveTotal"]["username"], conf["PassiveTotal"]['key']) # Get OSINT # TODO: add PT projects here pt_osint = client2.get_osint(query=unbracket(args.IP)) # Get malware raw_results = client2.get_malware(query=unbracket(args.IP)) if "results" in raw_results: for r in raw_results["results"]: malware.append({ 'hash': r["sample"], 'date': parse(r['collectionDate']), 'source' : 'PT (%s)' % r["source"] }) except requests.exceptions.ReadTimeout: print("Timeout on Passive Total requests") # VT vt_e = plugins['vt'].test_config(conf) if vt_e: if conf["VirusTotal"]["type"] != "public": print('[+] Downloading VT information....') vt = PrivateApi(conf["VirusTotal"]["key"]) res = vt.get_ip_report(unbracket(args.IP)) if "results" in res: if "resolutions" in res['results']: for r in res["results"]["resolutions"]: passive_dns.append({ "first": parse(r["last_resolved"]), "last": parse(r["last_resolved"]), "domain": r["hostname"], "source": "VT" }) if "undetected_downloaded_samples" in res['results']: for r in res['results']['undetected_downloaded_samples']: files.append({ 'hash': r['sha256'], 'date': parse(r['date']), 'source' : 'VT' }) if "undetected_referrer_samples" in res['results']: for r in res['results']['undetected_referrer_samples']: if 'date' in r: files.append({ 'hash': r['sha256'], 'date': parse(r['date']), 'source' : 'VT' }) else: #FIXME : should consider data without dates files.append({ 'hash': r['sha256'], 'date': datetime.datetime(1970, 1, 1), 'source' : 'VT' }) if "detected_downloaded_samples" in res['results']: for r in res['results']['detected_downloaded_samples']: malware.append({ 'hash': r['sha256'], 'date': parse(r['date']), 'source' : 'VT' }) if "detected_referrer_samples" in res['results']: for r in res['results']['detected_referrer_samples']: if "date" in r: malware.append({ 'hash': r['sha256'], 'date': parse(r['date']), 'source' : 'VT' }) else: vt_e = False print('[+] Downloading GreyNoise information....') gn = GreyNoise() try: greynoise = gn.query_ip(unbracket(args.IP)) except GreyNoiseError: greynoise = [] tg_e = plugins['threatgrid'].test_config(conf) if tg_e: print('[+] Downloading Threat Grid....') tg = ThreatGrid(conf['ThreatGrid']['key']) res = tg.search_samples(unbracket(args.IP), type='ip') already = [] if 'items' in res: for r in res['items']: if r['sample_sha256'] not in already: d = parse(r['ts']) d = d.replace(tzinfo=None) malware.append({ 'hash': r["sample_sha256"], 'date': d, 'source' : 'TG' }) already.append(r['sample_sha256']) print('----------------- Intelligence Report') if otx_e: if len(otx_pulses): print('OTX:') for p in otx_pulses: print(' -%s (%s - %s)' % ( p['name'], p['created'][:10], "https://otx.alienvault.com/pulse/" + p['id'] ) ) else: print('OTX: Not found in any pulse') if misp_e: if len(misp_results) > 0: print('MISP:') for event in misp_results: print(" -%i - %s" % (event.id, event.info)) if len(greynoise) > 0: print("GreyNoise: IP identified as") for r in greynoise: print("\t%s (%s -> %s)" % ( r["name"], r["first_seen"], r["last_updated"] ) ) else: print("GreyNoise: Not found") if pt_e: if "results" in pt_osint: if len(pt_osint["results"]): if len(pt_osint["results"]) == 1: if "name" in pt_osint["results"][0]: print("PT: %s %s" % (pt_osint["results"][0]["name"], pt_osint["results"][0]["sourceUrl"])) else: print("PT: %s" % pt_osint["results"][0]["sourceUrl"]) else: print("PT:") for r in pt_osint["results"]: if "name" in r: print("-%s %s" % (r["name"], r["sourceUrl"])) else: print("-%s" % r["sourceUrl"]) else: print("PT: Nothing found!") else: print("PT: Nothing found!") if len(malware) > 0: print('----------------- Malware') for r in sorted(malware, key=lambda x: x["date"]): print("[%s] %s %s" % ( r["source"], r["hash"], r["date"].strftime("%Y-%m-%d") ) ) if len(files) > 0: print('----------------- Files') for r in sorted(files, key=lambda x: x["date"]): print("[%s] %s %s" % ( r["source"], r["hash"], r["date"].strftime("%Y-%m-%d") ) ) if len(passive_dns) > 0: print('----------------- Passive DNS') for r in sorted(passive_dns, key=lambda x: x["first"], reverse=True): print("[+] %-40s (%s -> %s)(%s)" % ( r["domain"], r["first"].strftime("%Y-%m-%d"), r["last"].strftime("%Y-%m-%d"), r["source"] ) ) else: self.parser.print_help() else: self.parser.print_help()
def run(self, conf, args, plugins): if 'subcommand' in args: if args.subcommand == 'info': print("Not implemented yet") elif args.subcommand == "intel": # Start with MISP and OTX to get Intelligence Reports print('###################### %s ###################' % args.DOMAIN) passive_dns = [] urls = [] malware = [] files = [] # OTX otx_e = plugins['otx'].test_config(conf) if otx_e: print('[+] Downloading OTX information....') otx = OTXv2(conf["AlienVaultOtx"]["key"]) res = otx.get_indicator_details_full( IndicatorTypes.DOMAIN, unbracket(args.DOMAIN)) otx_pulses = res["general"]["pulse_info"]["pulses"] # Get Passive DNS if "passive_dns" in res: for r in res["passive_dns"]["passive_dns"]: passive_dns.append({ "ip": r['hostname'], "first": parse(r["first"]), "last": parse(r["last"]), "source": "OTX" }) if "url_list" in res: for r in res["url_list"]["url_list"]: if "result" in r: urls.append({ "date": parse(r["date"]), "url": r["url"], "ip": r["result"]["urlworker"]["ip"] if "ip" in r["result"]["urlworker"] else "", "source": "OTX" }) else: urls.append({ "date": parse(r["date"]), "url": r["url"], "ip": "", "source": "OTX" }) # RobTex print('[+] Downloading Robtex information....') rob = Robtex() res = rob.get_pdns_domain(args.DOMAIN) for d in res: if d['rrtype'] in ['A', 'AAAA']: passive_dns.append({ 'first': d['time_first_o'], 'last': d['time_last_o'], 'ip': d['rrdata'], 'source': 'Robtex' }) # PT pt_e = plugins['pt'].test_config(conf) if pt_e: print('[+] Downloading Passive Total information....') client = DnsRequest(conf['PassiveTotal']['username'], conf['PassiveTotal']['key']) raw_results = client.get_passive_dns( query=unbracket(args.DOMAIN)) if "results" in raw_results: for res in raw_results["results"]: passive_dns.append({ "first": parse(res["firstSeen"]), "last": parse(res["lastSeen"]), "ip": res["resolve"], "source": "PT" }) client2 = EnrichmentRequest( conf["PassiveTotal"]["username"], conf["PassiveTotal"]['key']) # Get OSINT # TODO: add PT projects here pt_osint = client2.get_osint(query=unbracket(args.DOMAIN)) # Get malware raw_results = client2.get_malware( query=unbracket(args.DOMAIN)) if "results" in raw_results: for r in raw_results["results"]: malware.append({ 'hash': r["sample"], 'date': parse(r['collectionDate']), 'source': 'PT (%s)' % r["source"] }) # VT vt_e = plugins['vt'].test_config(conf) if vt_e: if conf["VirusTotal"]["type"] != "public": print('[+] Downloading VT information....') vt = PrivateApi(conf["VirusTotal"]["key"]) res = vt.get_domain_report(unbracket(args.DOMAIN)) if "results" in res: if "resolutions" in res['results']: for r in res["results"]["resolutions"]: passive_dns.append({ "first": parse(r["last_resolved"]), "last": parse(r["last_resolved"]), "ip": r["ip_address"], "source": "VT" }) if "undetected_downloaded_samples" in res[ 'results']: for r in res['results'][ 'undetected_downloaded_samples']: files.append({ 'hash': r['sha256'], 'date': parse(r['date']), 'source': 'VT' }) if "undetected_referrer_samples" in res['results']: for r in res['results'][ 'undetected_referrer_samples']: files.append({ 'hash': r['sha256'], 'date': parse(r['date']), 'source': 'VT' }) if "detected_downloaded_samples" in res['results']: for r in res['results'][ 'detected_downloaded_samples']: malware.append({ 'hash': r['sha256'], 'date': parse(r['date']), 'source': 'VT' }) if "detected_referrer_samples" in res['results']: for r in res['results'][ 'detected_referrer_samples']: if "date" in r: malware.append({ 'hash': r['sha256'], 'date': parse(r['date']), 'source': 'VT' }) if "detected_urls" in res['results']: for r in res['results']['detected_urls']: urls.append({ 'date': parse(r['scan_date']), 'url': r['url'], 'ip': '', 'source': 'VT' }) else: vt_e = False tg_e = plugins['threatgrid'].test_config(conf) if tg_e: print('[+] Downloading Threat Grid....') tg = ThreatGrid(conf['ThreatGrid']['key']) res = tg.search_samples(unbracket(args.DOMAIN), type='domain') already = [] if 'items' in res: for r in res['items']: if r['sample_sha256'] not in already: d = parse(r['ts']) d = d.replace(tzinfo=None) malware.append({ 'hash': r["sample_sha256"], 'date': d, 'source': 'ThreatGrid' }) already.append(r['sample_sha256']) # TODO: Add MISP print('----------------- Intelligence Report') if otx_e: if len(otx_pulses): print('OTX:') for p in otx_pulses: print(' -%s (%s - %s)' % (p['name'], p['created'][:10], "https://otx.alienvault.com/pulse/" + p['id'])) else: print('OTX: Not found in any pulse') if pt_e: if "results" in pt_osint: if len(pt_osint["results"]): if len(pt_osint["results"]) == 1: if "name" in pt_osint["results"][0]: print( "PT: %s %s" % (pt_osint["results"][0]["name"], pt_osint["results"][0]["sourceUrl"])) else: print( "PT: %s" % (pt_osint["results"][0]["sourceUrl"])) else: print("PT:") for r in pt_osint["results"]: if "name" in r: print("-%s %s" % (r["name"], r["sourceUrl"])) else: print("-%s" % (r["sourceUrl"])) else: print("PT: Nothing found!") else: print("PT: Nothing found!") if len(malware) > 0: print('----------------- Malware') for r in sorted(malware, key=lambda x: x["date"]): print("[%s] %s %s" % (r["source"], r["hash"], r["date"].strftime("%Y-%m-%d"))) if len(files) > 0: print('----------------- Files') for r in sorted(files, key=lambda x: x["date"]): print("[%s] %s %s" % (r["source"], r["hash"], r["date"].strftime("%Y-%m-%d"))) if len(urls) > 0: print('----------------- Urls') for r in sorted(urls, key=lambda x: x["date"], reverse=True): print("[%s] %s - %s %s" % (r["source"], r["url"], r["ip"], r["date"].strftime("%Y-%m-%d"))) # TODO: add ASN + location info here if len(passive_dns) > 0: print('----------------- Passive DNS') for r in sorted(passive_dns, key=lambda x: x["first"], reverse=True): print("[+] %-40s (%s -> %s)(%s)" % (r["ip"], r["first"].strftime("%Y-%m-%d"), r["last"].strftime("%Y-%m-%d"), r["source"])) else: self.parser.print_help() else: self.parser.print_help()
def run(self, conf, args, plugins): if 'subcommand' in args: if args.subcommand == 'info': print("Not implemented yet") elif args.subcommand == "intel": # Start with MISP and OTX to get Intelligence Reports print('###################### %s ###################' % args.DOMAIN) passive_dns = [] urls = [] malware = [] files = [] # OTX otx_e = plugins['otx'].test_config(conf) if otx_e: print('[+] Downloading OTX information....') try: otx = OTXv2(conf["AlienVaultOtx"]["key"]) res = otx.get_indicator_details_full( IndicatorTypes.DOMAIN, unbracket(args.DOMAIN)) otx_pulses = res["general"]["pulse_info"]["pulses"] # Get Passive DNS if "passive_dns" in res: for r in res["passive_dns"]["passive_dns"]: passive_dns.append({ "ip": r['hostname'], "first": parse(r["first"]).astimezone(pytz.utc), "last": parse(r["last"]).astimezone(pytz.utc), "source": "OTX" }) if "url_list" in res: for r in res["url_list"]["url_list"]: if "result" in r: urls.append({ "date": parse(r["date"]).astimezone(pytz.utc), "url": r["url"], "ip": r["result"]["urlworker"]["ip"] if "ip" in r["result"]["urlworker"] else "", "source": "OTX" }) else: urls.append({ "date": parse(r["date"]).astimezone(pytz.utc), "url": r["url"], "ip": "", "source": "OTX" }) except AttributeError: print('OTX crashed ¯\_(ツ)_/¯') # UrlScan us = UrlScan() print('[+] Downloading UrlScan information....') res = us.search(args.DOMAIN) for r in res['results']: urls.append({ "date": parse(r["task"]["time"]).astimezone(pytz.utc), "url": r["page"]["url"], "ip": r["page"]["ip"] if "ip" in r["page"] else "", "source": "UrlScan" }) # CIRCL circl_e = plugins['circl'].test_config(conf) if circl_e: print('[+] Downloading CIRCL passive DNS information....') x = pypdns.PyPDNS(basic_auth=(conf['Circl']['user'], conf['Circl']['pass'])) res = x.query(unbracket(args.DOMAIN)) for answer in res: passive_dns.append({ "ip": answer['rdata'], "first": answer['time_first'].astimezone(pytz.utc), "last": answer['time_last'].astimezone(pytz.utc), "source": "CIRCL" }) # BinaryEdge be_e = plugins['binaryedge'].test_config(conf) if be_e: print('[+] Downloading BinaryEdge information....') try: be = BinaryEdge(conf['BinaryEdge']['key']) res = be.domain_dns(unbracket(args.DOMAIN)) for d in res['events']: if "A" in d: for a in d['A']: passive_dns.append({ "ip": a, "first": parse(d['updated_at']).astimezone( pytz.utc), "last": parse(d['updated_at']).astimezone( pytz.utc), "source": "BinaryEdge" }) except BinaryEdgeException: print( 'You need a paid BinaryEdge subscription for this request' ) # RobTex print('[+] Downloading Robtex information....') rob = Robtex() res = rob.get_pdns_domain(args.DOMAIN) for d in res: if d['rrtype'] in ['A', 'AAAA']: passive_dns.append({ 'first': d['time_first_o'].astimezone(pytz.utc), 'last': d['time_last_o'].astimezone(pytz.utc), 'ip': d['rrdata'], 'source': 'Robtex' }) # PT pt_e = plugins['pt'].test_config(conf) if pt_e: try: pt_osint = {} ptout = False print('[+] Downloading Passive Total information....') client = DnsRequest(conf['PassiveTotal']['username'], conf['PassiveTotal']['key']) raw_results = client.get_passive_dns( query=unbracket(args.DOMAIN)) if "results" in raw_results: for res in raw_results["results"]: passive_dns.append({ "first": parse(res["firstSeen"]).astimezone( pytz.utc), "last": parse(res["lastSeen"]).astimezone( pytz.utc), "ip": res["resolve"], "source": "PT" }) if "message" in raw_results: if "quota_exceeded" in raw_results["message"]: print("PT quota exceeded") ptout = True if not ptout: client2 = EnrichmentRequest( conf["PassiveTotal"]["username"], conf["PassiveTotal"]['key']) # Get OSINT # TODO: add PT projects here pt_osint = client2.get_osint( query=unbracket(args.DOMAIN)) # Get malware raw_results = client2.get_malware( query=unbracket(args.DOMAIN)) if "results" in raw_results: for r in raw_results["results"]: malware.append({ 'hash': r["sample"], 'date': parse(r['collectionDate']).astimezone( pytz.utc), 'source': 'PT (%s)' % r["source"] }) except requests.exceptions.ReadTimeout: print("PT: Time Out") # VT vt_e = plugins['vt'].test_config(conf) if vt_e: if conf["VirusTotal"]["type"] != "public": print('[+] Downloading VT information....') vt = PrivateApi(conf["VirusTotal"]["key"]) res = vt.get_domain_report(unbracket(args.DOMAIN)) if "results" in res: if "resolutions" in res['results']: for r in res["results"]["resolutions"]: passive_dns.append({ "first": parse(r["last_resolved"]).astimezone( pytz.utc), "last": parse(r["last_resolved"]).astimezone( pytz.utc), "ip": r["ip_address"], "source": "VT" }) if "undetected_downloaded_samples" in res[ 'results']: for r in res['results'][ 'undetected_downloaded_samples']: files.append({ 'hash': r['sha256'], 'date': parse(r['date']).astimezone(pytz.utc) if 'date' in r else '', 'source': 'VT' }) if "undetected_referrer_samples" in res['results']: for r in res['results'][ 'undetected_referrer_samples']: files.append({ 'hash': r['sha256'], 'date': parse(r['date']).astimezone(pytz.utc) if 'date' in r else '', 'source': 'VT' }) if "detected_downloaded_samples" in res['results']: for r in res['results'][ 'detected_downloaded_samples']: malware.append({ 'hash': r['sha256'], 'date': parse(r['date']).astimezone(pytz.utc), 'source': 'VT' }) if "detected_referrer_samples" in res['results']: for r in res['results'][ 'detected_referrer_samples']: if "date" in r: malware.append({ 'hash': r['sha256'], 'date': parse(r['date']).astimezone( pytz.utc), 'source': 'VT' }) if "detected_urls" in res['results']: for r in res['results']['detected_urls']: urls.append({ 'date': parse(r['scan_date']).astimezone( pytz.utc), 'url': r['url'], 'ip': '', 'source': 'VT' }) else: vt_e = False tg_e = plugins['threatgrid'].test_config(conf) if tg_e: try: print('[+] Downloading Threat Grid....') tg = ThreatGrid(conf['ThreatGrid']['key']) res = tg.search_samples(unbracket(args.DOMAIN), type='domain') already = [] if 'items' in res: for r in res['items']: if r['sample_sha256'] not in already: d = parse(r['ts']).astimezone(pytz.utc) malware.append({ 'hash': r["sample_sha256"], 'date': d, 'source': 'ThreatGrid' }) already.append(r['sample_sha256']) except ThreatGridError as e: print("Failed to connect to Threat Grid: %s" % e.message) # TODO: Add MISP print('----------------- Intelligence Report') if otx_e: if len(otx_pulses): print('OTX:') for p in otx_pulses: print(' -%s (%s - %s)' % (p['name'], p['created'][:10], "https://otx.alienvault.com/pulse/" + p['id'])) else: print('OTX: Not found in any pulse') if pt_e: if "results" in pt_osint: if len(pt_osint["results"]): if len(pt_osint["results"]) == 1: if "name" in pt_osint["results"][0]: print( "PT: %s %s" % (pt_osint["results"][0]["name"], pt_osint["results"][0]["sourceUrl"])) else: print( "PT: %s" % (pt_osint["results"][0]["sourceUrl"])) else: print("PT:") for r in pt_osint["results"]: if "name" in r: print("-%s %s" % (r["name"], r["sourceUrl"])) else: print("-%s" % (r["sourceUrl"])) else: print("PT: Nothing found!") else: print("PT: Nothing found!") if len(malware) > 0: print('----------------- Malware') for r in sorted(malware, key=lambda x: x["date"]): print("[%s] %s %s" % (r["source"], r["hash"], r["date"].strftime("%Y-%m-%d"))) if len(files) > 0: print('----------------- Files') for r in files: if r['date'] != '': print("[%s] %s (%s)" % (r["source"], r["hash"], r["date"].strftime("%Y-%m-%d"))) else: print("[%s] %s" % ( r["source"], r["hash"], )) if len(urls) > 0: print('----------------- Urls') for r in sorted(urls, key=lambda x: x["date"], reverse=True): print("[%s] %s - %s %s" % (r["source"], r["url"], r["ip"], r["date"].strftime("%Y-%m-%d"))) # TODO: add ASN + location info here if len(passive_dns) > 0: print('----------------- Passive DNS') for r in sorted(passive_dns, key=lambda x: x["first"], reverse=True): print("[+] %-40s (%s -> %s)(%s)" % (r["ip"], r["first"].strftime("%Y-%m-%d"), r["last"].strftime("%Y-%m-%d"), r["source"])) else: self.parser.print_help() else: self.parser.print_help()
def run(self, conf, args, plugins): if 'subcommand' in args: if args.subcommand == "ip": r = Robtex() res = r.get_ip_info(args.IP) if args.json: print(json.dumps(res, sort_keys=True, indent=4, default=json_serial)) else: print("AS %i: %s" % (res["as"], res["asname"])) print("Location: %s, %s" % (res["city"], res["country"])) print("BGP Route: %s, %s" % (res["bgproute"], res["routedesc"])) print("Whois: %s" % res["whoisdesc"]) print("https://www.robtex.com/ip-lookup/%s" % args.IP) if "pas" in res: if len(res["pas"]): print("Passive DNS:") for d in res["pas"]: print("\t%s %s" % (d["date"].isoformat(), d["o"])) if "pash" in res: if len(res["pash"]): print("Passive DNS History:") for d in res["pash"]: print("\t%s %s" % (d["date"].isoformat(), d["o"])) if "act" in res: if len(res["act"]): print("Active DNS:") for d in res["act"]: print("\t%s %s" % (d["date"].isoformat(), d["o"])) if "acth" in res: if len(res["acth"]): print("ACtive DNS History:") for d in res["acth"]: print("\t%s %s" % (d["date"].isoformat(), d["o"])) elif args.subcommand == "asn": r = Robtex() res = r.get_asn_info(args.ASN) if args.json: print(json.dumps(res, sort_keys=True, indent=4, default=json_serial)) else: print("ASN Routes:") for n in res["nets"]: print("[+] %s" % n["n"]) elif args.subcommand == "domain": r = Robtex() res = r.get_pdns_domain(unbracket(args.DOMAIN)) if args.json: print(json.dumps(res, sort_keys=True, indent=4, default=json_serial)) else: if len(res) == 0: print("No information on this domain") else: print("Passive DNS info:") for r in res: print("[+] %s\t%s\t(%s -> %s)" % ( r["rrtype"], r["rrdata"], r["time_first_o"].isoformat(), r["time_last_o"].isoformat() ) ) else: self.parser.print_help() else: self.parser.print_help()
def run(self, conf, args, plugins): if "subcommand" in args: if args.subcommand == "info": if not is_ip(unbracket(args.IP)): print("Invalid IP address") sys.exit(1) # FIXME: move code here in a library ip = unbracket(args.IP) try: ipy = IP(ip) except ValueError: print("Invalid IP format, quitting...") return ipinfo = self.ipinfo(ip) print("MaxMind: Located in %s, %s" % (ipinfo["city"], ipinfo["country"])) if ipinfo["asn"] == 0: print("MaxMind: IP not found in the ASN database") else: print("MaxMind: ASN%i, %s" % (ipinfo["asn"], ipinfo["asn_name"])) print("CAIDA Type: %s" % ipinfo["asn_type"]) try: asndb2 = pyasn.pyasn(self.asncidr) res = asndb2.lookup(ip) except OSError: print("Configuration files are not available") print("Please run harpoon update before using harpoon") sys.exit(1) if res[1] is None: print("IP not found in ASN database") else: # Search for name f = open(self.asnname, "r") found = False line = f.readline() name = "" while not found and line != "": s = line.split("|") if s[0] == str(res[0]): name = s[1].strip() found = True line = f.readline() print("ASN %i - %s (range %s)" % (res[0], name, res[1])) if ipinfo["hostname"] != "": print("Hostname: %s" % ipinfo["hostname"]) if ipinfo["specific"] != "": print("Specific: %s" % ipinfo["specific"]) if ipy.iptype() == "PRIVATE": "Private IP" print("") if ipy.version() == 4: print("Censys:\t\thttps://censys.io/ipv4/%s" % ip) print("Shodan:\t\thttps://www.shodan.io/host/%s" % ip) print("IP Info:\thttp://ipinfo.io/%s" % ip) print("BGP HE:\t\thttps://bgp.he.net/ip/%s" % ip) print( "IP Location:\thttps://www.iplocation.net/?query=%s" % ip) elif args.subcommand == "intel": if not is_ip(unbracket(args.IP)): print("Invalid IP address") sys.exit(1) # Start with MISP and OTX to get Intelligence Reports print("###################### %s ###################" % unbracket(args.IP)) passive_dns = [] urls = [] malware = [] files = [] # MISP misp_e = plugins["misp"].test_config(conf) if misp_e: print("[+] Downloading MISP information...") server = ExpandedPyMISP(conf["Misp"]["url"], conf["Misp"]["key"]) misp_results = server.search("attributes", value=unbracket(args.IP)) # Binary Edge be_e = plugins["binaryedge"].test_config(conf) if be_e: try: print("[+] Downloading BinaryEdge information...") be = BinaryEdge(conf["BinaryEdge"]["key"]) # FIXME: this only get the first page res = be.domain_ip(unbracket(args.IP)) for d in res["events"]: passive_dns.append({ "domain": d["domain"], "first": parse(d["updated_at"]).astimezone(pytz.utc), "last": parse(d["updated_at"]).astimezone(pytz.utc), "source": "BinaryEdge", }) except BinaryEdgeException: print( "BinaryEdge request failed, you need a paid subscription" ) # OTX otx_e = plugins["otx"].test_config(conf) if otx_e: print("[+] Downloading OTX information....") otx = OTXv2(conf["AlienVaultOtx"]["key"]) res = otx.get_indicator_details_full( IndicatorTypes.IPv4, unbracket(args.IP)) otx_pulses = res["general"]["pulse_info"]["pulses"] # Get Passive DNS if "passive_dns" in res: for r in res["passive_dns"]["passive_dns"]: passive_dns.append({ "domain": r["hostname"], "first": parse(r["first"]).astimezone(pytz.utc), "last": parse(r["last"]).astimezone(pytz.utc), "source": "OTX", }) if "url_list" in res: for r in res["url_list"]["url_list"]: if "result" in r: urls.append({ "date": parse(r["date"]).astimezone(pytz.utc), "url": r["url"], "ip": r["result"]["urlworker"]["ip"] if "ip" in r["result"]["urlworker"] else "", "source": "OTX", }) else: urls.append({ "date": parse(r["date"]).astimezone(pytz.utc), "url": r["url"], "ip": "", "source": "OTX", }) # RobTex print("[+] Downloading Robtex information....") rob = Robtex() try: res = rob.get_ip_info(unbracket(args.IP)) except RobtexError: print("Error with Robtex") else: for d in ["pas", "pash", "act", "acth"]: if d in res: for a in res[d]: passive_dns.append({ "first": a["date"].astimezone(pytz.utc), "last": a["date"].astimezone(pytz.utc), "domain": a["o"], "source": "Robtex", }) # PT pt_e = plugins["pt"].test_config(conf) if pt_e: out_pt = False print("[+] Downloading Passive Total information....") client = DnsRequest(conf["PassiveTotal"]["username"], conf["PassiveTotal"]["key"]) try: raw_results = client.get_passive_dns( query=unbracket(args.IP)) if "results" in raw_results: for res in raw_results["results"]: passive_dns.append({ "first": parse(res["firstSeen"]).astimezone( pytz.utc), "last": parse(res["lastSeen"]).astimezone( pytz.utc), "domain": res["resolve"], "source": "PT", }) if "message" in raw_results: if "quota_exceeded" in raw_results["message"]: print("Quota exceeded for Passive Total") out_pt = True pt_osint = {} except requests.exceptions.ReadTimeout: print("Timeout on Passive Total requests") if not out_pt: try: client2 = EnrichmentRequest( conf["PassiveTotal"]["username"], conf["PassiveTotal"]["key"], ) # Get OSINT # TODO: add PT projects here pt_osint = client2.get_osint( query=unbracket(args.IP)) # Get malware raw_results = client2.get_malware( query=unbracket(args.IP)) if "results" in raw_results: for r in raw_results["results"]: malware.append({ "hash": r["sample"], "date": parse(r["collectionDate"]), "source": "PT (%s)" % r["source"], }) except requests.exceptions.ReadTimeout: print("Timeout on Passive Total requests") # Urlhaus uh_e = plugins["urlhaus"].test_config(conf) if uh_e: print("[+] Checking urlhaus data...") try: urlhaus = UrlHaus(conf["UrlHaus"]["key"]) res = urlhaus.get_host(unbracket(args.IP)) except UrlHausError: print("Error with the query") else: if "urls" in res: for r in res["urls"]: urls.append({ "date": parse(r["date_added"]).astimezone( pytz.utc), "url": r["url"], "source": "UrlHaus", }) # VT vt_e = plugins["vt"].test_config(conf) if vt_e: if conf["VirusTotal"]["type"] != "public": print("[+] Downloading VT information....") vt = PrivateApi(conf["VirusTotal"]["key"]) res = vt.get_ip_report(unbracket(args.IP)) if "results" in res: if "resolutions" in res["results"]: for r in res["results"]["resolutions"]: passive_dns.append({ "first": parse(r["last_resolved"]).astimezone( pytz.utc), "last": parse(r["last_resolved"]).astimezone( pytz.utc), "domain": r["hostname"], "source": "VT", }) if "undetected_downloaded_samples" in res[ "results"]: for r in res["results"][ "undetected_downloaded_samples"]: files.append({ "hash": r["sha256"], "date": parse(r["date"]), "source": "VT", }) if "undetected_referrer_samples" in res["results"]: for r in res["results"][ "undetected_referrer_samples"]: if "date" in r: files.append({ "hash": r["sha256"], "date": parse(r["date"]), "source": "VT", }) else: # FIXME : should consider data without dates files.append({ "hash": r["sha256"], "date": datetime.datetime(1970, 1, 1), "source": "VT", }) if "detected_downloaded_samples" in res["results"]: for r in res["results"][ "detected_downloaded_samples"]: malware.append({ "hash": r["sha256"], "date": parse(r["date"]), "source": "VT", }) if "detected_referrer_samples" in res["results"]: for r in res["results"][ "detected_referrer_samples"]: if "date" in r: malware.append({ "hash": r["sha256"], "date": parse(r["date"]), "source": "VT", }) else: vt_e = False print("[+] Downloading GreyNoise information....") gn = GreyNoise(conf["GreyNoise"]["key"]) if gn == "": print("Greynoise API key is not set up.") greynoise = gn.ip(unbracket(args.IP)) tg_e = plugins["threatgrid"].test_config(conf) if tg_e: print("[+] Downloading Threat Grid....") try: tg = ThreatGrid(conf["ThreatGrid"]["key"]) res = tg.search_samples(unbracket(args.IP), type="ip") already = [] if "items" in res: for r in res["items"]: if r["sample_sha256"] not in already: d = parse(r["ts"]) d = d.replace(tzinfo=None) malware.append({ "hash": r["sample_sha256"], "date": d, "source": "TG", }) already.append(r["sample_sha256"]) except ThreatGridError as e: print("Error with threat grid: {}".format(e.message)) # ThreatMiner print("[+] Downloading ThreatMiner....") tm = ThreatMiner() response = tm.get_report(unbracket(args.IP)) if response["status_code"] == "200": tmm = response["results"] else: tmm = [] if response["status_code"] != "404": print("Request to ThreatMiner failed: {}".format( response["status_message"])) response = tm.get_related_samples(unbracket(args.IP)) if response["status_code"] == "200": for r in response["results"]: malware.append({ "hash": r, "date": None, "source": "ThreatMiner" }) print("----------------- Intelligence Report") ctor = CommandTor() tor_list = ctor.get_list() if tor_list: if unbracket(args.IP) in tor_list: print("{} is a Tor Exit node".format(unbracket( args.IP))) else: print("Impossible to reach the Tor Exit Node list") if otx_e: if len(otx_pulses): print("OTX:") for p in otx_pulses: print("- %s (%s - %s)" % ( p["name"], p["created"][:10], "https://otx.alienvault.com/pulse/" + p["id"], )) else: print("OTX: Not found in any pulse") if misp_e: if len(misp_results["Attribute"]) > 0: print("MISP:") for event in misp_results["Attribute"]: print("- {} - {}".format(event["Event"]["id"], event["Event"]["info"])) if len(greynoise) > 0: print("GreyNoise: IP identified as") for key, value in greynoise.items(): print(key, "->", value) else: print("GreyNoise: Not found") if pt_e: if "results" in pt_osint: if len(pt_osint["results"]): if len(pt_osint["results"]) == 1: if "name" in pt_osint["results"][0]: print("PT: %s %s" % ( pt_osint["results"][0]["name"], pt_osint["results"][0]["sourceUrl"], )) else: print("PT: %s" % pt_osint["results"][0]["sourceUrl"]) else: print("PT:") for r in pt_osint["results"]: if "name" in r: print("-%s %s" % (r["name"], r["sourceUrl"])) else: print("-%s" % r["sourceUrl"]) else: print("PT: Nothing found!") else: print("PT: Nothing found!") # ThreatMiner if len(tmm) > 0: print("ThreatMiner:") for r in tmm: print("- {} {} - {}".format(r["year"], r["filename"], r["URL"])) if len(malware) > 0: print("----------------- Malware") for r in malware: print("[%s] %s %s" % ( r["source"], r["hash"], r["date"].strftime("%Y-%m-%d") if r["date"] else "", )) if len(files) > 0: print("----------------- Files") for r in sorted(files, key=lambda x: x["date"]): print("[%s] %s %s" % (r["source"], r["hash"], r["date"].strftime("%Y-%m-%d"))) if len(passive_dns) > 0: print("----------------- Passive DNS") for r in sorted(passive_dns, key=lambda x: x["first"], reverse=True): print("[+] %-40s (%s -> %s)(%s)" % ( r["domain"], r["first"].strftime("%Y-%m-%d"), r["last"].strftime("%Y-%m-%d"), r["source"], )) if len(urls) > 0: print("----------------- Urls") for r in sorted(urls, key=lambda x: x["date"], reverse=True): print("[%s] %s - %s" % (r["source"], r["url"], r["date"].strftime("%Y-%m-%d"))) else: self.parser.print_help() else: self.parser.print_help()