mirror of https://github.com/MISP/misp-modules
add: [farsight-passivedns] Optional feature to submit flex queries
- The rrset and rdata queries remain the same but with the parameter `flex_queries`, users can also get the results of the flex rrnames & flex rdata regex queries about their domain, hostname or ip address - Results can thus include passive-dns objects containing the `raw_rdata` object_relation added with 0a3e948chrisr3d_patch
parent
993a614a20
commit
dfec0e5cf4
|
@ -9,12 +9,12 @@ mispattributes = {
|
||||||
'format': 'misp_standard'
|
'format': 'misp_standard'
|
||||||
}
|
}
|
||||||
moduleinfo = {
|
moduleinfo = {
|
||||||
'version': '0.3',
|
'version': '0.4',
|
||||||
'author': 'Christophe Vandeplas',
|
'author': 'Christophe Vandeplas',
|
||||||
'description': 'Module to access Farsight DNSDB Passive DNS',
|
'description': 'Module to access Farsight DNSDB Passive DNS',
|
||||||
'module-type': ['expansion', 'hover']
|
'module-type': ['expansion', 'hover']
|
||||||
}
|
}
|
||||||
moduleconfig = ['apikey', 'server', 'limit']
|
moduleconfig = ['apikey', 'server', 'limit', 'flex_queries']
|
||||||
|
|
||||||
DEFAULT_DNSDB_SERVER = 'https://api.dnsdb.info'
|
DEFAULT_DNSDB_SERVER = 'https://api.dnsdb.info'
|
||||||
DEFAULT_LIMIT = 10
|
DEFAULT_LIMIT = 10
|
||||||
|
@ -28,6 +28,7 @@ class FarsightDnsdbParser():
|
||||||
self.passivedns_mapping = {
|
self.passivedns_mapping = {
|
||||||
'bailiwick': {'type': 'text', 'object_relation': 'bailiwick'},
|
'bailiwick': {'type': 'text', 'object_relation': 'bailiwick'},
|
||||||
'count': {'type': 'counter', 'object_relation': 'count'},
|
'count': {'type': 'counter', 'object_relation': 'count'},
|
||||||
|
'raw_rdata': {'type': 'text', 'object_relation': 'raw_rdata'},
|
||||||
'rdata': {'type': 'text', 'object_relation': 'rdata'},
|
'rdata': {'type': 'text', 'object_relation': 'rdata'},
|
||||||
'rrname': {'type': 'text', 'object_relation': 'rrname'},
|
'rrname': {'type': 'text', 'object_relation': 'rrname'},
|
||||||
'rrtype': {'type': 'text', 'object_relation': 'rrtype'},
|
'rrtype': {'type': 'text', 'object_relation': 'rrtype'},
|
||||||
|
@ -45,29 +46,15 @@ class FarsightDnsdbParser():
|
||||||
self.comment = 'Result from an %s lookup on DNSDB about the %s: %s'
|
self.comment = 'Result from an %s lookup on DNSDB about the %s: %s'
|
||||||
|
|
||||||
def parse_passivedns_results(self, query_response):
|
def parse_passivedns_results(self, query_response):
|
||||||
lookup_fields = (
|
|
||||||
'count',
|
|
||||||
'rrname',
|
|
||||||
'rrtype',
|
|
||||||
'bailiwick',
|
|
||||||
'time_first',
|
|
||||||
'time_last',
|
|
||||||
'zone_time_first',
|
|
||||||
'zone_time_last'
|
|
||||||
)
|
|
||||||
for query_type, results in query_response.items():
|
for query_type, results in query_response.items():
|
||||||
comment = self.comment % (query_type, self.type_to_feature[self.attribute['type']], self.attribute['value'])
|
comment = self.comment % (query_type, self.type_to_feature[self.attribute['type']], self.attribute['value'])
|
||||||
for result in results:
|
for result in results:
|
||||||
passivedns_object = MISPObject('passive-dns')
|
passivedns_object = MISPObject('passive-dns')
|
||||||
for feature in lookup_fields:
|
if result.get('rdata') and isinstance(result['rdata'], list):
|
||||||
if result.get(feature):
|
for rdata in result.pop('rdata'):
|
||||||
passivedns_object.add_attribute(**self._parse_attribute(comment, feature, result[feature]))
|
passivedns_object.add_attribute(**self._parse_attribute(comment, 'rdata', rdata))
|
||||||
if result.get('rdata'):
|
for feature, value in result.items():
|
||||||
if isinstance(result['rdata'], list):
|
passivedns_object.add_attribute(**self._parse_attribute(comment, feature, value))
|
||||||
for rdata in result['rdata']:
|
|
||||||
passivedns_object.add_attribute(**self._parse_attribute(comment, 'rdata', rdata))
|
|
||||||
else:
|
|
||||||
passivedns_object.add_attribute(**self._parse_attribute(comment, 'rdata', result['rdata']))
|
|
||||||
passivedns_object.add_reference(self.attribute['uuid'], 'related-to')
|
passivedns_object.add_reference(self.attribute['uuid'], 'related-to')
|
||||||
self.misp_event.add_object(passivedns_object)
|
self.misp_event.add_object(passivedns_object)
|
||||||
|
|
||||||
|
@ -95,11 +82,12 @@ def handler(q=False):
|
||||||
if attribute['type'] not in mispattributes['input']:
|
if attribute['type'] not in mispattributes['input']:
|
||||||
return {'error': 'Unsupported attributes type'}
|
return {'error': 'Unsupported attributes type'}
|
||||||
config = request['config']
|
config = request['config']
|
||||||
if config.get('server') is None:
|
if not config.get('server'):
|
||||||
config['server'] = DEFAULT_DNSDB_SERVER
|
config['server'] = DEFAULT_DNSDB_SERVER
|
||||||
client_args = {feature: config[feature] for feature in ('apikey', 'server')}
|
client_args = {feature: config[feature] for feature in ('apikey', 'server')}
|
||||||
client = dnsdb2.Client(**client_args)
|
client = dnsdb2.Client(**client_args)
|
||||||
if config.get('limit') is None:
|
flex = add_flex_queries(config.get('flex_queries'))
|
||||||
|
if not config.get('limit'):
|
||||||
config['limit'] = DEFAULT_LIMIT
|
config['limit'] = DEFAULT_LIMIT
|
||||||
lookup_args = {
|
lookup_args = {
|
||||||
'limit': config['limit'],
|
'limit': config['limit'],
|
||||||
|
@ -107,39 +95,58 @@ def handler(q=False):
|
||||||
'ignore_limited': True
|
'ignore_limited': True
|
||||||
}
|
}
|
||||||
to_query = lookup_ip if attribute['type'] in ('ip-src', 'ip-dst') else lookup_name
|
to_query = lookup_ip if attribute['type'] in ('ip-src', 'ip-dst') else lookup_name
|
||||||
response = to_query(client, attribute['value'], lookup_args)
|
try:
|
||||||
if not isinstance(response, dict):
|
response = to_query(client, attribute['value'], lookup_args, flex)
|
||||||
return {'error': response}
|
except dnsdb2.DnsdbException as e:
|
||||||
|
return {'error': e.__str__()}
|
||||||
if not response:
|
if not response:
|
||||||
return {'error': f"Empty results on Farsight DNSDB for the queries {attribute['type']}: {attribute['value']}."}
|
return {'error': f"Empty results on Farsight DNSDB for the {self.type_to_feature[attribute['type']]}: {attribute['value']}."}
|
||||||
parser = FarsightDnsdbParser(attribute)
|
parser = FarsightDnsdbParser(attribute)
|
||||||
parser.parse_passivedns_results(response)
|
parser.parse_passivedns_results(response)
|
||||||
return parser.get_results()
|
return parser.get_results()
|
||||||
|
|
||||||
|
|
||||||
def lookup_name(client, name, lookup_args):
|
def add_flex_queries(flex):
|
||||||
|
if not flex:
|
||||||
|
return False
|
||||||
|
if flex in ('True', 'true', True, '1', 1):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def flex_queries(client, name, lookup_args):
|
||||||
response = {}
|
response = {}
|
||||||
try:
|
rdata = list(client.flex_rdata_regex(name.replace('.', '\.'), **lookup_args))
|
||||||
# RRSET = entries in the left-hand side of the domain name related labels
|
if rdata:
|
||||||
res = client.lookup_rrset(name, **lookup_args)
|
response['flex_rdata'] = rdata
|
||||||
response['rrset'] = list(res)
|
rrnames = list(client.flex_rrnames_regex(name.replace('.', '\.'), **lookup_args))
|
||||||
except dnsdb2.DnsdbException as e:
|
if rrnames:
|
||||||
return e
|
response['flex_rrnames'] = rrnames
|
||||||
try:
|
|
||||||
# RDATA = entries on the right-hand side of the domain name related labels
|
|
||||||
res = client.lookup_rdata_name(name, **lookup_args)
|
|
||||||
response['rdata'] = list(res)
|
|
||||||
except dnsdb2.DnsdbException as e:
|
|
||||||
return e
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
def lookup_ip(client, ip, lookup_args):
|
def lookup_name(client, name, lookup_args, flex):
|
||||||
try:
|
response = {}
|
||||||
res = client.lookup_rdata_ip(ip, **lookup_args)
|
# RRSET = entries in the left-hand side of the domain name related labels
|
||||||
response = {'rdata': list(res)}
|
rrset_response = list(client.lookup_rrset(name, **lookup_args))
|
||||||
except dnsdb2.DnsdbException as e:
|
if rrset_response:
|
||||||
return e
|
response['rrset'] = rrset_response
|
||||||
|
# RDATA = entries on the right-hand side of the domain name related labels
|
||||||
|
rdata_response = client.lookup_rdata_name(name, **lookup_args)
|
||||||
|
if rdata_response:
|
||||||
|
response['rdata'] = rdata_response
|
||||||
|
if flex:
|
||||||
|
response.update(flex_queries(client, name, lookup_args))
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
def lookup_ip(client, ip, lookup_args, flex):
|
||||||
|
response = {}
|
||||||
|
res = list(client.lookup_rdata_ip(ip, **lookup_args))
|
||||||
|
if res:
|
||||||
|
response['rdata'] = res
|
||||||
|
if flex:
|
||||||
|
response.update(flex_queries(client, ip, lookup_args))
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue