2016-03-25 11:02:32 +01:00
import pypdns
2020-07-28 11:47:53 +02:00
from . import check_input_attribute , standard_error_message
2019-12-17 11:18:21 +01:00
from pymisp import MISPAttribute , MISPEvent , MISPObject
2016-03-25 11:02:32 +01:00
2020-06-03 11:12:47 +02:00
mispattributes = { ' input ' : [ ' hostname ' , ' domain ' , ' ip-src ' , ' ip-dst ' , ' ip-src|port ' , ' ip-dst|port ' ] , ' format ' : ' misp_standard ' }
2024-08-12 11:23:10 +02:00
moduleinfo = {
' version ' : ' 0.2 ' ,
' author ' : ' Alexandre Dulaunoy ' ,
' description ' : ' Module to access CIRCL Passive DNS. ' ,
' module-type ' : [ ' expansion ' , ' hover ' ] ,
' name ' : ' CIRCL Passive DNS ' ,
' logo ' : ' passivedns.png ' ,
' requirements ' : [ ' pypdns: Passive DNS python library ' , ' A CIRCL passive DNS account with username & password ' ] ,
' features ' : ' This module takes a hostname, domain or ip-address (ip-src or ip-dst) attribute as input, and queries the CIRCL Passive DNS REST API to get the asssociated passive dns entries and return them as MISP objects. \n \n To make it work a username and a password are thus required to authenticate to the CIRCL Passive DNS API. ' ,
' references ' : [ ' https://www.circl.lu/services/passive-dns/ ' , ' https://datatracker.ietf.org/doc/draft-dulaunoy-dnsop-passive-dns-cof/ ' ] ,
' input ' : ' Hostname, domain, or ip-address attribute. ' ,
' output ' : ' ' ,
' ouput ' : ' Passive DNS objects related to the input attribute. ' ,
}
2016-03-25 11:02:32 +01:00
moduleconfig = [ ' username ' , ' password ' ]
2024-01-06 14:58:47 +01:00
class PassiveDNSParser :
2019-12-17 11:18:21 +01:00
def __init__ ( self , attribute , authentication ) :
self . misp_event = MISPEvent ( )
self . attribute = MISPAttribute ( )
self . attribute . from_dict ( * * attribute )
self . misp_event . add_attribute ( * * self . attribute )
self . pdns = pypdns . PyPDNS ( basic_auth = authentication )
def get_results ( self ) :
if hasattr ( self , ' result ' ) :
return self . result
2024-01-06 14:58:47 +01:00
event = self . misp_event . to_dict ( )
2019-12-17 14:29:29 +01:00
results = { key : event [ key ] for key in ( ' Attribute ' , ' Object ' ) }
2019-12-17 11:18:21 +01:00
return { ' results ' : results }
2020-06-03 11:12:47 +02:00
def parse ( self ) :
value = self . attribute . value . split ( ' | ' ) [ 0 ] if ' | ' in self . attribute . type else self . attribute . value
2019-12-17 11:18:21 +01:00
try :
2020-06-03 11:12:47 +02:00
results = self . pdns . query ( value )
2019-12-17 11:18:21 +01:00
except Exception :
self . result = { ' error ' : ' There is an authentication error, please make sure you supply correct credentials. ' }
return
2020-06-03 10:48:43 +02:00
if not results :
self . result = { ' error ' : ' Not found ' }
return
2019-12-17 11:18:21 +01:00
mapping = { ' count ' : ' counter ' , ' origin ' : ' text ' ,
' time_first ' : ' datetime ' , ' rrtype ' : ' text ' ,
' rrname ' : ' text ' , ' rdata ' : ' text ' ,
' time_last ' : ' datetime ' }
for result in results :
pdns_object = MISPObject ( ' passive-dns ' )
for relation , attribute_type in mapping . items ( ) :
pdns_object . add_attribute ( relation , type = attribute_type , value = result [ relation ] )
pdns_object . add_reference ( self . attribute . uuid , ' associated-to ' )
self . misp_event . add_object ( * * pdns_object )
2024-01-06 14:58:47 +01:00
def dict_handler ( request : dict ) :
2019-12-17 11:18:21 +01:00
if not request . get ( ' config ' ) :
return { ' error ' : ' CIRCL Passive DNS authentication is missing. ' }
if not request [ ' config ' ] . get ( ' username ' ) or not request [ ' config ' ] . get ( ' password ' ) :
return { ' error ' : ' CIRCL Passive DNS authentication is incomplete, please provide your username and password. ' }
authentication = ( request [ ' config ' ] [ ' username ' ] , request [ ' config ' ] [ ' password ' ] )
2020-07-28 11:47:53 +02:00
if not request . get ( ' attribute ' ) or not check_input_attribute ( request [ ' attribute ' ] ) :
return { ' error ' : f ' { standard_error_message } , which should contain at least a type, a value and an uuid. ' }
2019-12-17 11:18:21 +01:00
attribute = request [ ' attribute ' ]
if not any ( input_type == attribute [ ' type ' ] for input_type in mispattributes [ ' input ' ] ) :
2020-07-28 11:47:53 +02:00
return { ' error ' : ' Unsupported attribute type. ' }
2019-12-17 11:18:21 +01:00
pdns_parser = PassiveDNSParser ( attribute , authentication )
2020-06-03 11:12:47 +02:00
pdns_parser . parse ( )
2019-12-17 11:18:21 +01:00
return pdns_parser . get_results ( )
2016-03-25 11:02:32 +01:00
def introspection ( ) :
return mispattributes
def version ( ) :
moduleinfo [ ' config ' ] = moduleconfig
return moduleinfo