mirror of https://github.com/MISP/misp-modules
				
				
				
			
		
			
				
	
	
		
			179 lines
		
	
	
		
			9.8 KiB
		
	
	
	
		
			Python
		
	
	
			
		
		
	
	
			179 lines
		
	
	
		
			9.8 KiB
		
	
	
	
		
			Python
		
	
	
import json
 | 
						|
import time
 | 
						|
import base64
 | 
						|
import xml.etree.ElementTree as ET
 | 
						|
from pymisp import MISPEvent, MISPObject
 | 
						|
 | 
						|
misperrors = {'error': 'Error'}
 | 
						|
moduleinfo = {'version': 1, 'author': 'Christian Studer',
 | 
						|
              'description': 'Import from GoAML',
 | 
						|
              'module-type': ['import']}
 | 
						|
moduleconfig = []
 | 
						|
mispattributes = {'inputSource': ['file'], 'output': ['MISP objects'],
 | 
						|
                  'format': 'misp_standard'}
 | 
						|
 | 
						|
t_from_objects = {'nodes': ['from_person', 'from_account', 'from_entity'],
 | 
						|
                  'leaves': ['from_funds_code', 'from_country']}
 | 
						|
t_to_objects = {'nodes': ['to_person', 'to_account', 'to_entity'],
 | 
						|
                'leaves': ['to_funds_code', 'to_country']}
 | 
						|
t_person_objects = {'nodes': ['addresses'],
 | 
						|
                    'leaves': ['first_name', 'middle_name', 'last_name', 'gender', 'title', 'mothers_name', 'birthdate',
 | 
						|
                               'passport_number', 'passport_country', 'id_number', 'birth_place', 'alias', 'nationality1']}
 | 
						|
t_account_objects = {'nodes': ['signatory'],
 | 
						|
                     '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']}
 | 
						|
entity_objects = {'nodes': ['addresses'],
 | 
						|
                  'leaves': ['name', 'commercial_name', 'incorporation_legal_form', 'incorporation_number', 'business', 'phone']}
 | 
						|
 | 
						|
goAMLobjects = {'report': {'nodes': ['reporting_person', 'location'],
 | 
						|
                           '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']},
 | 
						|
                'transaction': {'nodes': ['t_from', 't_from_my_client', 't_to', 't_to_my_client'],
 | 
						|
                                'leaves': ['transactionnumber', 'transaction_location', 'date_transaction',
 | 
						|
                                           'transmode_code', 'amount_local']},
 | 
						|
                't_from': t_from_objects, 't_from_my_client': t_from_objects,
 | 
						|
                't_to': t_to_objects, 't_to_my_client': t_to_objects,
 | 
						|
                'addresses': {'nodes': ['address'], 'leaves': []},
 | 
						|
                '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,
 | 
						|
                'signatory': {'nodes': ['t_person'], 'leaves': []},
 | 
						|
                'from_entity': entity_objects, 'to_entity': entity_objects,
 | 
						|
                }
 | 
						|
 | 
						|
t_account_mapping = {'misp_name': 'bank-account', 'institution_name': 'institution-name', 'institution_code': 'institution-code',
 | 
						|
                     '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'}
 | 
						|
 | 
						|
t_person_mapping = {'misp_name': 'person', 'comments': 'text', 'first_name': 'first-name', 'middle_name': 'middle-name',
 | 
						|
                    'last_name': 'last-name', 'title': 'title', 'mothers_name': 'mothers-name', 'alias': 'alias',
 | 
						|
                    'birthdate': 'date-of-birth', 'birth_place': 'place-of-birth', 'gender': 'gender', 'nationality1': 'nationality',
 | 
						|
                    'passport_number': 'passport-number', 'passport_country': 'passport-country', 'ssn': 'social-security-number',
 | 
						|
                    'id_number': 'identity-card-number'}
 | 
						|
 | 
						|
location_mapping = {'misp_name': 'geolocation', 'city': 'city', 'state': 'region', 'country_code': 'country', 'address': 'address',
 | 
						|
                    'zip': 'zipcode'}
 | 
						|
 | 
						|
t_entity_mapping = {'misp_name': 'legal-entity', 'name': 'name', 'business': 'business', 'commercial_name': 'commercial-name',
 | 
						|
                    'phone': 'phone-number', 'incorporation_legal_form': 'legal-form', 'incorporation_number': 'registration-number'}
 | 
						|
 | 
						|
goAMLmapping = {'from_account': t_account_mapping, 'to_account': t_account_mapping, 't_person': t_person_mapping,
 | 
						|
                '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,
 | 
						|
                'transaction': {'misp_name': 'transaction', 'transactionnumber': 'transaction-number', 'date_transaction': 'date',
 | 
						|
                                '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']
 | 
						|
relationship_to_keep = ['signatory', 't_from', 't_from_my_client', 't_to', 't_to_my_client', 'address']
 | 
						|
 | 
						|
 | 
						|
class GoAmlParser():
 | 
						|
    def __init__(self):
 | 
						|
        self.misp_event = MISPEvent()
 | 
						|
 | 
						|
    def read_xml(self, data):
 | 
						|
        self.tree = ET.fromstring(data)
 | 
						|
 | 
						|
    def parse_xml(self):
 | 
						|
        self.first_itteration()
 | 
						|
        for t in self.tree.findall('transaction'):
 | 
						|
            self.itterate(t, 'transaction')
 | 
						|
 | 
						|
    def first_itteration(self):
 | 
						|
        submission_date = self.tree.find('submission_date').text.split('+')[0]
 | 
						|
        self.misp_event.timestamp = int(time.mktime(time.strptime(submission_date, "%Y-%m-%dT%H:%M:%S")))
 | 
						|
        for node in goAMLobjects['report']['nodes']:
 | 
						|
            element = self.tree.find(node)
 | 
						|
            if element is not None:
 | 
						|
                self.itterate(element, element.tag)
 | 
						|
 | 
						|
    def itterate(self, tree, aml_type, referencing_uuid=None, relationship_type=None):
 | 
						|
        objects = goAMLobjects[aml_type]
 | 
						|
        referenced_uuid = referencing_uuid
 | 
						|
        rel = relationship_type
 | 
						|
        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)
 | 
						|
                if aml_type == 'transaction':
 | 
						|
                    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)
 | 
						|
                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)
 | 
						|
            except KeyError:
 | 
						|
                pass
 | 
						|
        for node in objects['nodes']:
 | 
						|
            element = tree.find(node)
 | 
						|
            if element is not None:
 | 
						|
                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)
 | 
						|
 | 
						|
    @staticmethod
 | 
						|
    def fill_transaction(element, tag, misp_object):
 | 
						|
        if 't_from' in tag:
 | 
						|
            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)
 | 
						|
        if 't_to' in tag:
 | 
						|
            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)
 | 
						|
 | 
						|
 | 
						|
def handler(q=False):
 | 
						|
    if q is False:
 | 
						|
        return False
 | 
						|
    request = json.loads(q)
 | 
						|
    if request.get('data'):
 | 
						|
        data = base64.b64decode(request['data']).decode('utf-8')
 | 
						|
    else:
 | 
						|
        misperrors['error'] = "Unsupported attributes type"
 | 
						|
        return misperrors
 | 
						|
    aml_parser = GoAmlParser()
 | 
						|
    try:
 | 
						|
        aml_parser.read_xml(data)
 | 
						|
    except Exception:
 | 
						|
        misperrors['error'] = "Impossible to read XML data"
 | 
						|
        return misperrors
 | 
						|
    aml_parser.parse_xml()
 | 
						|
    r = {'results': {'Object': [obj.to_json() for obj in aml_parser.misp_event.objects]}}
 | 
						|
    return r
 | 
						|
 | 
						|
 | 
						|
def introspection():
 | 
						|
    return mispattributes
 | 
						|
 | 
						|
 | 
						|
def version():
 | 
						|
    moduleinfo['config'] = moduleconfig
 | 
						|
    return moduleinfo
 |