chg: [farsight_passivedns] Now using the dnsdb2 python library

- Also updated the results parsing to check in
  each returned result for every field if they are
  included, to avoid key errors if any field is
  missing
chrisr3d_patch
chrisr3d 2020-11-12 16:01:14 +01:00
parent f1b6b3e637
commit fe010782f3
No known key found for this signature in database
GPG Key ID: 6BBED1B63A6D639F
1 changed files with 41 additions and 28 deletions

View File

@ -1,5 +1,5 @@
import dnsdb2
import json import json
from ._dnsdb_query.dnsdb_query import DEFAULT_DNSDB_SERVER, DnsdbClient, QueryError
from . import check_input_attribute, standard_error_message from . import check_input_attribute, standard_error_message
from pymisp import MISPEvent, MISPObject from pymisp import MISPEvent, MISPObject
@ -9,13 +9,14 @@ mispattributes = {
'format': 'misp_standard' 'format': 'misp_standard'
} }
moduleinfo = { moduleinfo = {
'version': '0.2', 'version': '0.3',
'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']
DEFAULT_DNSDB_SERVER = 'https://api.dnsdb.info'
DEFAULT_LIMIT = 10 DEFAULT_LIMIT = 10
@ -44,8 +45,10 @@ 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):
default_fields = ('count', 'rrname', 'rrname') lookup_fields = (
optional_fields = ( 'count',
'rrname',
'rrname',
'bailiwick', 'bailiwick',
'time_first', 'time_first',
'time_last', 'time_last',
@ -56,16 +59,15 @@ class FarsightDnsdbParser():
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 default_fields: for feature in lookup_fields:
passivedns_object.add_attribute(**self._parse_attribute(comment, feature, result[feature]))
for feature in optional_fields:
if result.get(feature): if result.get(feature):
passivedns_object.add_attribute(**self._parse_attribute(comment, feature, result[feature])) passivedns_object.add_attribute(**self._parse_attribute(comment, feature, result[feature]))
if isinstance(result['rdata'], list): if result.get('rdata'):
for rdata in result['rdata']: if isinstance(result['rdata'], list):
passivedns_object.add_attribute(**self._parse_attribute(comment, 'rdata', rdata)) for rdata in result['rdata']:
else: passivedns_object.add_attribute(**self._parse_attribute(comment, 'rdata', rdata))
passivedns_object.add_attribute(**self._parse_attribute(comment, 'rdata', result['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)
@ -93,12 +95,21 @@ 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']
args = {'apikey': config['apikey']} if config.get('server') is None:
for feature, default in zip(('server', 'limit'), (DEFAULT_DNSDB_SERVER, DEFAULT_LIMIT)): config['server'] = DEFAULT_DNSDB_SERVER
args[feature] = config[feature] if config.get(feature) else default client_args = {feature: config[feature] for feature in ('apikey', 'server')}
client = DnsdbClient(**args) client = dnsdb2.Client(**client_args)
if config.get('limit') is None:
config['limit'] = DEFAULT_LIMIT
lookup_args = {
'limit': config['limit'],
'offset': 0,
'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']) response = to_query(client, attribute['value'], lookup_args)
if not isinstance(response, dict):
return {'error': response}
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 queries {attribute['type']}: {attribute['value']}."}
parser = FarsightDnsdbParser(attribute) parser = FarsightDnsdbParser(attribute)
@ -106,27 +117,29 @@ def handler(q=False):
return parser.get_results() return parser.get_results()
def lookup_name(client, name): def lookup_name(client, name, lookup_args):
response = {} response = {}
try: try:
res = client.query_rrset(name) # RRSET = entries in the left-hand side of the domain name related labels # RRSET = entries in the left-hand side of the domain name related labels
res = client.lookup_rrset(name, **lookup_args)
response['rrset'] = list(res) response['rrset'] = list(res)
except QueryError: except dnsdb2.DnsdbException as e:
pass return e
try: try:
res = client.query_rdata_name(name) # RDATA = entries on the right-hand side of the domain name related labels # 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) response['rdata'] = list(res)
except QueryError: except dnsdb2.DnsdbException as e:
pass return e
return response return response
def lookup_ip(client, ip): def lookup_ip(client, ip, lookup_args):
try: try:
res = client.query_rdata_ip(ip) res = client.lookup_rdata_ip(ip, **lookup_args)
response = {'rdata': list(res)} response = {'rdata': list(res)}
except QueryError: except dnsdb2.DnsdbException as e:
response = {} return e
return response return response