2018-12-11 15:29:09 +01:00
import json
import time
import base64
2018-02-20 17:00:13 +01:00
import xml . etree . ElementTree as ET
2018-02-28 15:07:55 +01:00
from pymisp import MISPEvent , MISPObject
2018-02-20 17:00:13 +01:00
misperrors = { ' error ' : ' Error ' }
2024-08-12 11:23:10 +02:00
moduleinfo = {
' version ' : 1 ,
' author ' : ' Christian Studer ' ,
' description ' : ' Module to import MISP objects about financial transactions from GoAML files. ' ,
' module-type ' : [ ' import ' ] ,
' name ' : ' GoAML Import ' ,
' logo ' : ' goAML.jpg ' ,
' requirements ' : [ ' PyMISP ' ] ,
' features ' : ' Unlike the GoAML export module, there is here no special feature to import data from GoAML external files, since the module will import MISP Objects with their References on its own, as it is required for the export module to rebuild a valid GoAML document. ' ,
' references ' : ' http://goaml.unodc.org/ ' ,
' input ' : ' GoAML format file, describing financial transactions, with their origin and target (bank accounts, persons or entities). ' ,
' output ' : ' MISP objects (transaction, bank-account, person, legal-entity, geolocation), with references, describing financial transactions and their origin and target. ' ,
}
2018-02-20 17:00:13 +01:00
moduleconfig = [ ]
2019-05-01 22:28:19 +02:00
mispattributes = { ' inputSource ' : [ ' file ' ] , ' output ' : [ ' MISP objects ' ] ,
' format ' : ' misp_standard ' }
2018-02-20 17:00:13 +01:00
2018-02-26 18:13:43 +01:00
t_from_objects = { ' nodes ' : [ ' from_person ' , ' from_account ' , ' from_entity ' ] ,
2018-12-11 15:29:09 +01:00
' leaves ' : [ ' from_funds_code ' , ' from_country ' ] }
2018-02-26 18:13:43 +01:00
t_to_objects = { ' nodes ' : [ ' to_person ' , ' to_account ' , ' to_entity ' ] ,
2018-12-11 15:29:09 +01:00
' leaves ' : [ ' to_funds_code ' , ' to_country ' ] }
2018-02-26 18:13:43 +01:00
t_person_objects = { ' nodes ' : [ ' addresses ' ] ,
2018-12-11 15:29:09 +01:00
' leaves ' : [ ' first_name ' , ' middle_name ' , ' last_name ' , ' gender ' , ' title ' , ' mothers_name ' , ' birthdate ' ,
' passport_number ' , ' passport_country ' , ' id_number ' , ' birth_place ' , ' alias ' , ' nationality1 ' ] }
2018-02-26 18:13:43 +01:00
t_account_objects = { ' nodes ' : [ ' signatory ' ] ,
2018-02-28 15:07:55 +01:00
' leaves ' : [ ' institution_name ' , ' institution_code ' , ' swift ' , ' branch ' , ' non_banking_insitution ' ,
' account ' , ' currency_code ' , ' account_name ' , ' iban ' , ' client_number ' , ' opened ' , ' closed ' ,
' personal_account_type ' , ' balance ' , ' date_balance ' , ' status_code ' , ' beneficiary ' ,
' beneficiary_comment ' , ' comments ' ] }
2018-02-26 18:13:43 +01:00
entity_objects = { ' nodes ' : [ ' addresses ' ] ,
2018-02-28 15:07:55 +01:00
' leaves ' : [ ' name ' , ' commercial_name ' , ' incorporation_legal_form ' , ' incorporation_number ' , ' business ' , ' phone ' ] }
2018-02-20 17:00:13 +01:00
2018-02-26 11:47:35 +01:00
goAMLobjects = { ' report ' : { ' nodes ' : [ ' reporting_person ' , ' location ' ] ,
2018-02-26 18:13:43 +01:00
' leaves ' : [ ' rentity_id ' , ' submission_code ' , ' report_code ' , ' submission_date ' , ' currency_code_local ' ] } ,
' reporting_person ' : { ' nodes ' : [ ' addresses ' ] , ' leaves ' : [ ' first_name ' , ' middle_name ' , ' last_name ' , ' title ' ] } ,
' location ' : { ' nodes ' : [ ] , ' leaves ' : [ ' address_type ' , ' address ' , ' city ' , ' zip ' , ' country_code ' , ' state ' ] } ,
2018-02-20 17:00:13 +01:00
' transaction ' : { ' nodes ' : [ ' t_from ' , ' t_from_my_client ' , ' t_to ' , ' t_to_my_client ' ] ,
' leaves ' : [ ' transactionnumber ' , ' transaction_location ' , ' date_transaction ' ,
' transmode_code ' , ' amount_local ' ] } ,
2018-02-26 18:13:43 +01:00
' t_from ' : t_from_objects , ' t_from_my_client ' : t_from_objects ,
' t_to ' : t_to_objects , ' t_to_my_client ' : t_to_objects ,
2018-02-20 17:00:13 +01:00
' addresses ' : { ' nodes ' : [ ' address ' ] , ' leaves ' : [ ] } ,
2018-02-26 18:13:43 +01:00
' address ' : { ' nodes ' : [ ] , ' leaves ' : [ ' address_type ' , ' address ' , ' city ' , ' zip ' , ' country_code ' , ' state ' ] } ,
' from_person ' : t_person_objects , ' to_person ' : t_person_objects , ' t_person ' : t_person_objects ,
' from_account ' : t_account_objects , ' to_account ' : t_account_objects ,
2018-02-20 17:00:13 +01:00
' signatory ' : { ' nodes ' : [ ' t_person ' ] , ' leaves ' : [ ] } ,
2018-02-26 18:13:43 +01:00
' from_entity ' : entity_objects , ' to_entity ' : entity_objects ,
2018-02-20 17:00:13 +01:00
}
2018-02-28 15:07:55 +01:00
t_account_mapping = { ' misp_name ' : ' bank-account ' , ' institution_name ' : ' institution-name ' , ' institution_code ' : ' institution-code ' ,
2018-02-26 18:13:43 +01:00
' iban ' : ' iban ' , ' swift ' : ' swift ' , ' branch ' : ' branch ' , ' non_banking_institution ' : ' non-bank-institution ' ,
' account ' : ' account ' , ' currency_code ' : ' currency-code ' , ' account_name ' : ' account-name ' ,
' client_number ' : ' client-number ' , ' personal_account_type ' : ' personal-account-type ' , ' opened ' : ' opened ' ,
' closed ' : ' closed ' , ' balance ' : ' balance ' , ' status_code ' : ' status-code ' , ' beneficiary ' : ' beneficiary ' ,
' beneficiary_comment ' : ' beneficiary-comment ' , ' comments ' : ' comments ' }
2018-02-28 15:07:55 +01:00
t_person_mapping = { ' misp_name ' : ' person ' , ' comments ' : ' text ' , ' first_name ' : ' first-name ' , ' middle_name ' : ' middle-name ' ,
2018-02-26 18:13:43 +01:00
' last_name ' : ' last-name ' , ' title ' : ' title ' , ' mothers_name ' : ' mothers-name ' , ' alias ' : ' alias ' ,
2018-12-11 15:29:09 +01:00
' birthdate ' : ' date-of-birth ' , ' birth_place ' : ' place-of-birth ' , ' gender ' : ' gender ' , ' nationality1 ' : ' nationality ' ,
2018-02-26 18:13:43 +01:00
' passport_number ' : ' passport-number ' , ' passport_country ' : ' passport-country ' , ' ssn ' : ' social-security-number ' ,
' id_number ' : ' identity-card-number ' }
2018-02-28 15:07:55 +01:00
location_mapping = { ' misp_name ' : ' geolocation ' , ' city ' : ' city ' , ' state ' : ' region ' , ' country_code ' : ' country ' , ' address ' : ' address ' ,
' zip ' : ' zipcode ' }
2018-02-26 18:13:43 +01:00
2018-02-28 15:07:55 +01:00
t_entity_mapping = { ' misp_name ' : ' legal-entity ' , ' name ' : ' name ' , ' business ' : ' business ' , ' commercial_name ' : ' commercial-name ' ,
2018-02-26 18:13:43 +01:00
' phone ' : ' phone-number ' , ' incorporation_legal_form ' : ' legal-form ' , ' incorporation_number ' : ' registration-number ' }
2018-02-28 15:07:55 +01:00
goAMLmapping = { ' from_account ' : t_account_mapping , ' to_account ' : t_account_mapping , ' t_person ' : t_person_mapping ,
2018-02-26 18:13:43 +01:00
' from_person ' : t_person_mapping , ' to_person ' : t_person_mapping , ' reporting_person ' : t_person_mapping ,
' from_entity ' : t_entity_mapping , ' to_entity ' : t_entity_mapping ,
' location ' : location_mapping , ' address ' : location_mapping ,
2018-02-28 15:07:55 +01:00
' transaction ' : { ' misp_name ' : ' transaction ' , ' transactionnumber ' : ' transaction-number ' , ' date_transaction ' : ' date ' ,
2018-02-26 18:13:43 +01:00
' transaction_location ' : ' location ' , ' transmode_code ' : ' transmode-code ' , ' amount_local ' : ' amount ' ,
' transmode_comment ' : ' transmode-comment ' , ' date_posting ' : ' date-posting ' , ' teller ' : ' teller ' ,
' authorized ' : ' authorized ' , ' transaction_description ' : ' text ' } }
nodes_to_ignore = [ ' addresses ' , ' signatory ' ]
2018-03-05 14:58:31 +01:00
relationship_to_keep = [ ' signatory ' , ' t_from ' , ' t_from_my_client ' , ' t_to ' , ' t_to_my_client ' , ' address ' ]
2018-02-26 18:13:43 +01:00
2018-12-11 15:29:09 +01:00
2018-02-20 17:00:13 +01:00
class GoAmlParser ( ) :
def __init__ ( self ) :
2018-02-26 18:13:43 +01:00
self . misp_event = MISPEvent ( )
2018-02-20 17:00:13 +01:00
2018-03-02 11:03:21 +01:00
def read_xml ( self , data ) :
self . tree = ET . fromstring ( data )
2018-02-20 17:00:13 +01:00
def parse_xml ( self ) :
2018-02-28 15:07:55 +01:00
self . first_itteration ( )
2018-02-26 11:47:35 +01:00
for t in self . tree . findall ( ' transaction ' ) :
2018-02-28 15:07:55 +01:00
self . itterate ( t , ' transaction ' )
def first_itteration ( self ) :
2018-02-28 17:41:45 +01:00
submission_date = self . tree . find ( ' submission_date ' ) . text . split ( ' + ' ) [ 0 ]
self . misp_event . timestamp = int ( time . mktime ( time . strptime ( submission_date , " % Y- % m- %d T % H: % M: % S " ) ) )
2018-02-28 15:07:55 +01:00
for node in goAMLobjects [ ' report ' ] [ ' nodes ' ] :
element = self . tree . find ( node )
if element is not None :
self . itterate ( element , element . tag )
2018-02-20 17:00:13 +01:00
2018-03-05 14:58:31 +01:00
def itterate ( self , tree , aml_type , referencing_uuid = None , relationship_type = None ) :
2018-02-28 15:07:55 +01:00
objects = goAMLobjects [ aml_type ]
2018-03-05 14:58:31 +01:00
referenced_uuid = referencing_uuid
rel = relationship_type
2018-02-28 15:07:55 +01:00
if aml_type not in nodes_to_ignore :
try :
mapping = goAMLmapping [ aml_type ]
misp_object = MISPObject ( name = mapping [ ' misp_name ' ] )
for leaf in objects [ ' leaves ' ] :
element = tree . find ( leaf )
if element is not None :
object_relation = mapping [ element . tag ]
attribute = { ' object_relation ' : object_relation , ' value ' : element . text }
misp_object . add_attribute ( * * attribute )
2018-02-27 11:08:37 +01:00
if aml_type == ' transaction ' :
2018-02-28 15:07:55 +01:00
for node in objects [ ' nodes ' ] :
element = tree . find ( node )
if element is not None :
self . fill_transaction ( element , element . tag , misp_object )
self . misp_event . add_object ( misp_object )
2018-03-05 14:58:31 +01:00
last_object = self . misp_event . objects [ - 1 ]
referenced_uuid = last_object . uuid
if referencing_uuid and relationship_type :
referencing_object = self . misp_event . get_object_by_uuid ( referencing_uuid )
referencing_object . add_reference ( referenced_uuid , rel , None , * * last_object )
2018-02-28 15:07:55 +01:00
except KeyError :
pass
for node in objects [ ' nodes ' ] :
element = tree . find ( node )
if element is not None :
2018-03-05 14:58:31 +01:00
tag = element . tag
if tag in relationship_to_keep :
rel = tag [ 2 : ] if tag . startswith ( ' t_ ' ) else tag
self . itterate ( element , element . tag , referencing_uuid = referenced_uuid , relationship_type = rel )
2018-02-27 11:08:37 +01:00
@staticmethod
2018-02-28 15:07:55 +01:00
def fill_transaction ( element , tag , misp_object ) :
2018-02-27 11:08:37 +01:00
if ' t_from ' in tag :
2018-02-28 15:07:55 +01:00
from_funds = element . find ( ' from_funds_code ' ) . text
from_funds_attribute = { ' object_relation ' : ' from-funds-code ' , ' value ' : from_funds }
misp_object . add_attribute ( * * from_funds_attribute )
from_country = element . find ( ' from_country ' ) . text
from_country_attribute = { ' object_relation ' : ' from-country ' , ' value ' : from_country }
misp_object . add_attribute ( * * from_country_attribute )
2018-02-27 11:08:37 +01:00
if ' t_to ' in tag :
2018-02-28 15:07:55 +01:00
to_funds = element . find ( ' to_funds_code ' ) . text
to_funds_attribute = { ' object_relation ' : ' to-funds-code ' , ' value ' : to_funds }
misp_object . add_attribute ( * * to_funds_attribute )
to_country = element . find ( ' to_country ' ) . text
to_country_attribute = { ' object_relation ' : ' to-country ' , ' value ' : to_country }
misp_object . add_attribute ( * * to_country_attribute )
2018-02-20 17:00:13 +01:00
2018-12-11 15:29:09 +01:00
2018-02-20 17:00:13 +01:00
def handler ( q = False ) :
if q is False :
return False
request = json . loads ( q )
2018-03-02 11:03:21 +01:00
if request . get ( ' data ' ) :
data = base64 . b64decode ( request [ ' data ' ] ) . decode ( ' utf-8 ' )
2018-02-20 17:00:13 +01:00
else :
misperrors [ ' error ' ] = " Unsupported attributes type "
return misperrors
aml_parser = GoAmlParser ( )
try :
2018-03-02 11:03:21 +01:00
aml_parser . read_xml ( data )
2018-12-11 15:29:09 +01:00
except Exception :
2018-03-02 11:03:21 +01:00
misperrors [ ' error ' ] = " Impossible to read XML data "
2018-02-20 17:00:13 +01:00
return misperrors
aml_parser . parse_xml ( )
2019-05-14 10:50:11 +02:00
r = { ' results ' : { ' Object ' : [ obj . to_json ( ) for obj in aml_parser . misp_event . objects ] } }
2018-02-28 17:41:45 +01:00
return r
2018-02-20 17:00:13 +01:00
2018-12-11 15:29:09 +01:00
2018-02-20 17:00:13 +01:00
def introspection ( ) :
return mispattributes
2018-12-11 15:29:09 +01:00
2018-02-20 17:00:13 +01:00
def version ( ) :
moduleinfo [ ' config ' ] = moduleconfig
return moduleinfo