def run(self, conf, args, plugins): # FIXME: have this in conf if args.no_tls: server = MispServer(url=conf['Misp']['url'], apikey=conf['Misp']['key'], ssl_chain=False) else: server = MispServer(url=conf['Misp']['url'], apikey=conf['Misp']['key']) if args.list: # List events events = server.events.list(0) for event in sorted(events, key=lambda x: x.id): print("%i : %s" % (event.id, event.info)) elif args.attr is not None: res = server.attributes.search(value=args.attr) if len(res) == 0: print("Search %s: no results" % args.attr) else: print("Search %s, result founds" % args.attr) for event in res: print("[+] %i - %s" % (event.id, event.info)) for attr in event.attributes: if args.type is not None: if attr.type == args.type: if args.attr.lower() in str(attr.value).lower() or \ args.attr.lower() in str(attr.comment).lower(): print("\t%s (%s / %s) %s" % (attr.value, attr.category, attr.type, attr.comment)) else: if args.attr.lower() in str(attr.value).lower() or \ args.attr.lower() in str(attr.comment).lower(): print("\t%s (%s / %s) %s" % (attr.value, attr.category, attr.type, attr.comment)) elif args.event is not None: event = server.events.get(args.event) if args.attr is None and args.type is None: print("Event %i : %s" % (event.id, event.info)) print("Tags : %s" % ", ".join(map(lambda x: str(x.name), event.tags))) print("%i Attributes including:" % len(event.attributes)) attrs = Counter(map(lambda x: x.type, event.attributes)) attrs_ids = Counter( map(lambda x: x.type, filter(lambda x: x.to_ids, event.attributes))) for type in attrs: print("\t- %i %s (%i for detection)" % (attrs[type], type, attrs_ids[type])) else: if args.type is not None: # Display all attributes from this type for attr in event.attributes: if attr.type == args.type: if args.raw: print("%s" % attr.value) else: print("%s\t%s\t%s\t%s\t%s" % (attr.category, attr.type, attr.value, attr.comment, attr.to_ids)) elif args.attr is not None: # search by attribute value for attr in event.attributes: if args.attr in str(attr.value): print("%s\t%s\t%s\t%s\t%s" % (attr.category, attr.type, attr.value, attr.comment, attr.to_ids)) elif args.search: with open(args.search, 'r') as infile: data = infile.read().split() for d in data: print("Searching for %s" % d.strip()) res = server.attributes.search(value=d.strip()) if len(res) == 0: print("\tNo results") else: for event in res: print("\t[+] %i - %s" % (event.id, event.info)) for attr in event.attributes: if d.strip().lower() in str(attr.value).lower() or \ d.strip().lower() in str(attr.comment).lower(): print("\t\t%s (%s / %s) %s" % (attr.value, attr.category, attr.type, attr.comment)) 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']) 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: 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"]: 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'].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") # 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() 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()
parser.add_argument('--server', '-s', help='Server used for the request') parser.add_argument('--event', '-e', help='Event infos', type=int) parser.add_argument('--attr', '-a', help='Search for this attribute') parser.add_argument('--type', '-t', help='Search for attributes of this type') parser.add_argument('--no-ids', help='Disable IDS for these attributes', action='store_true') parser.add_argument('--to-ids', help='Enable IDS for these attributes', action='store_true') parser.add_argument('--detection', help='Summarize IOCs for detection', action='store_true') parser.add_argument('--raw', '-r', help='Print raw information', action='store_true') parser.add_argument('-v', '--verbose', action='count', default=0) args = parser.parse_args() config = parse_config() if args.server is not None: if args.server.lower() in config.keys(): server = MispServer(url=config[args.server.lower()]['url'], apikey=config[args.server.lower()]['key'], ssl_chain=False) else: print("Server not found, quitting...") sys.exit(1) else: if 'default' not in config.keys(): print("No default severs in MISP conf, quitting...") sys.exit(1) else: server = MispServer(url=config['default']['url'], apikey=config['default']['key'], ssl_chain=False) if args.list: