|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
from collections import defaultdict
|
|
|
|
from datetime import datetime
|
|
|
|
from pymisp import MISPAttribute, MISPEvent, MISPObject
|
|
|
|
import json
|
|
|
|
import base64
|
|
|
|
|
|
|
|
misperrors = {'error': 'Error'}
|
|
|
|
userConfig = {}
|
|
|
|
inputSource = ['file']
|
|
|
|
|
|
|
|
moduleinfo = {'version': '0.1', 'author': 'Christian Studer',
|
|
|
|
'description': 'Import for Joe Sandbox JSON reports',
|
|
|
|
'module-type': ['import']}
|
|
|
|
|
|
|
|
moduleconfig = []
|
|
|
|
|
|
|
|
file_object_fields = ['filename', 'md5', 'sha1', 'sha256', 'sha512', 'ssdeep']
|
|
|
|
file_object_mapping = {'entropy': ('float', 'entropy'),
|
|
|
|
'filesize': ('size-in-bytes', 'size-in-bytes'),
|
|
|
|
'filetype': ('mime-type', 'mimetype')}
|
|
|
|
pe_object_fields = {'entrypoint': ('text', 'entrypoint-address'),
|
|
|
|
'imphash': ('imphash', 'imphash')}
|
|
|
|
pe_object_mapping = {'CompanyName': 'company-name', 'FileDescription': 'file-description',
|
|
|
|
'FileVersion': 'file-version', 'InternalName': 'internal-filename',
|
|
|
|
'LegalCopyright': 'legal-copyright', 'OriginalFilename': 'original-filename',
|
|
|
|
'ProductName': 'product-filename', 'ProductVersion': 'product-version',
|
|
|
|
'Translation': 'lang-id'}
|
|
|
|
process_object_fields = {'cmdline': 'command-line', 'name': 'name',
|
|
|
|
'parentpid': 'parent-pid', 'pid': 'pid',
|
|
|
|
'path': 'current-directory'}
|
|
|
|
process_references_mapping = {'fileCreated': 'creates', 'fileDeleted': 'deletes',
|
|
|
|
'fileMoved': 'moves', 'fileRead': 'reads', 'fileWritten': 'writes'}
|
|
|
|
section_object_mapping = {'characteristics': ('text', 'characteristic'),
|
|
|
|
'entropy': ('float', 'entropy'),
|
|
|
|
'name': ('text', 'name'), 'rawaddr': ('hex', 'offset'),
|
|
|
|
'rawsize': ('size-in-bytes', 'size-in-bytes'),
|
|
|
|
'virtaddr': ('hex', 'virtual_address'),
|
|
|
|
'virtsize': ('size-in-bytes', 'virtual_size')}
|
|
|
|
signerinfo_object_mapping = {'sigissuer': ('text', 'issuer'),
|
|
|
|
'version': ('text', 'version')}
|
|
|
|
|
|
|
|
|
|
|
|
class JoeParser():
|
|
|
|
def __init__(self, data):
|
|
|
|
self.data = data
|
|
|
|
self.misp_event = MISPEvent()
|
|
|
|
self.references = defaultdict(list)
|
|
|
|
|
|
|
|
def parse_joe(self):
|
|
|
|
self.parse_fileinfo()
|
|
|
|
self.parse_behavior()
|
|
|
|
if self.references:
|
|
|
|
self.build_references()
|
|
|
|
self.finalize_results()
|
|
|
|
|
|
|
|
def build_references(self):
|
|
|
|
for misp_object in self.misp_event.objects:
|
|
|
|
object_uuid = misp_object.uuid
|
|
|
|
if object_uuid in self.references:
|
|
|
|
for reference in self.references[object_uuid]:
|
|
|
|
misp_object.add_reference(reference['idref'], reference['relationship'])
|
|
|
|
|
|
|
|
def parse_behavior(self):
|
|
|
|
self.parse_behavior_system()
|
|
|
|
self.parse_behavior_network()
|
|
|
|
|
|
|
|
def parse_behavior_network(self):
|
|
|
|
network = self.data['behavior']['network']
|
|
|
|
|
|
|
|
def parse_behavior_system(self):
|
|
|
|
for process in self.data['behavior']['system']['processes']['process']:
|
|
|
|
general = process['general']
|
|
|
|
process_object = MISPObject('process')
|
|
|
|
for feature, relation in process_object_fields.items():
|
|
|
|
process_object.add_attribute(relation, **{'type': 'text', 'value': general[feature]})
|
|
|
|
start_time = datetime.strptime('{} {}'.format(general['date'], general['time']), '%d/%m/%Y %H:%M:%S')
|
|
|
|
process_object.add_attribute('start-time', **{'type': 'datetime', 'value': start_time})
|
|
|
|
for feature, files in process['fileactivities'].items():
|
|
|
|
if files:
|
|
|
|
for call in files['call']:
|
|
|
|
file_attribute = MISPAttribute()
|
|
|
|
file_attribute.from_dict(**{'type': 'filename', 'value': call['path']})
|
|
|
|
process_object.add_reference(file_attribute.uuid, process_references_mapping[feature])
|
|
|
|
self.misp_event.add_attribute(**file_attribute)
|
|
|
|
self.misp_event.add_object(**process_object)
|
|
|
|
self.references[self.fileinfo_uuid].append({'idref': process_object.uuid, 'relationship': 'calls'})
|
|
|
|
|
|
|
|
def parse_fileinfo(self):
|
|
|
|
fileinfo = self.data['fileinfo']
|
|
|
|
file_object = MISPObject('file')
|
|
|
|
for field in file_object_fields:
|
|
|
|
file_object.add_attribute(field, **{'type': field, 'value': fileinfo[field]})
|
|
|
|
for field, mapping in file_object_mapping.items():
|
|
|
|
attribute_type, object_relation = mapping
|
|
|
|
file_object.add_attribute(object_relation, **{'type': attribute_type, 'value': fileinfo[field]})
|
|
|
|
self.fileinfo_uuid = file_object.uuid
|
|
|
|
if not fileinfo.get('pe'):
|
|
|
|
self.misp_event.add_object(**file_object)
|
|
|
|
return
|
|
|
|
peinfo = fileinfo['pe']
|
|
|
|
pe_object = MISPObject('pe')
|
|
|
|
file_object.add_reference(pe_object.uuid, 'included-in')
|
|
|
|
self.misp_event.add_object(**file_object)
|
|
|
|
for field, mapping in pe_object_fields.items():
|
|
|
|
attribute_type, object_relation = mapping
|
|
|
|
pe_object.add_attribute(object_relation, **{'type': attribute_type, 'value': peinfo[field]})
|
|
|
|
pe_object.add_attribute('compilation-timestamp', **{'type': 'datetime', 'value': int(peinfo['timestamp'].split()[0], 16)})
|
|
|
|
program_name = fileinfo['filename']
|
|
|
|
if peinfo['versions']:
|
|
|
|
for feature in peinfo['versions']['version']:
|
|
|
|
name = feature['name']
|
|
|
|
if name == 'InternalName':
|
|
|
|
program_name = feature['value']
|
|
|
|
pe_object.add_attribute(pe_object_mapping[name], **{'type': 'text', 'value': feature['value']})
|
|
|
|
sections_number = len(peinfo['sections']['section'])
|
|
|
|
pe_object.add_attribute('number-sections', **{'type': 'counter', 'value': sections_number})
|
|
|
|
signerinfo_object = MISPObject('authenticode-signerinfo')
|
|
|
|
pe_object.add_reference(signerinfo_object.uuid, 'signed-by')
|
|
|
|
self.misp_event.add_object(**pe_object)
|
|
|
|
signerinfo_object.add_attribute('program-name', **{'type': 'text', 'value': program_name})
|
|
|
|
signatureinfo = peinfo['signature']
|
|
|
|
if signatureinfo['signed']:
|
|
|
|
for feature, mapping in signerinfo_object_mapping.items():
|
|
|
|
attribute_type, object_relation = mapping
|
|
|
|
signerinfo_object.add_attribute(object_relation, **{'type': attribute_type, 'value': signatureinfo[feature]})
|
|
|
|
self.misp_event.add_object(**signerinfo_object)
|
|
|
|
for section in peinfo['sections']['section']:
|
|
|
|
section_object = self.parse_pe_section(section)
|
|
|
|
self.references[pe_object.uuid].append({'idref': section_object.uuid, 'relationship': 'included-in'})
|
|
|
|
self.misp_event.add_object(**section_object)
|
|
|
|
|
|
|
|
def parse_pe_section(self, section):
|
|
|
|
section_object = MISPObject('pe-section')
|
|
|
|
for feature, mapping in section_object_mapping.items():
|
|
|
|
attribute_type, object_relation = mapping
|
|
|
|
section_object.add_attribute(object_relation, **{'type': attribute_type, 'value': section[feature]})
|
|
|
|
return section_object
|
|
|
|
|
|
|
|
def finalize_results(self):
|
|
|
|
event = json.loads(self.misp_event.to_json())['Event']
|
|
|
|
self.results = {key: event[key] for key in ('Attribute', 'Object') if (key in event and event[key])}
|
|
|
|
|
|
|
|
|
|
|
|
def handler(q=False):
|
|
|
|
if q is False:
|
|
|
|
return False
|
|
|
|
q = json.loads(q)
|
|
|
|
data = base64.b64decode(q.get('data')).decode('utf-8')
|
|
|
|
if not data:
|
|
|
|
return json.dumps({'success': 0})
|
|
|
|
joe_data = json.loads(data)['analysis']
|
|
|
|
joe_parser = JoeParser(joe_data)
|
|
|
|
joe_parser.parse_joe()
|
|
|
|
return {'results': joe_parser.results}
|
|
|
|
|
|
|
|
|
|
|
|
def introspection():
|
|
|
|
modulesetup = {}
|
|
|
|
try:
|
|
|
|
userConfig
|
|
|
|
modulesetup['userConfig'] = userConfig
|
|
|
|
except NameError:
|
|
|
|
pass
|
|
|
|
try:
|
|
|
|
inputSource
|
|
|
|
modulesetup['inputSource'] = inputSource
|
|
|
|
except NameError:
|
|
|
|
pass
|
|
|
|
modulesetup['format'] = 'misp_standard'
|
|
|
|
return modulesetup
|
|
|
|
|
|
|
|
|
|
|
|
def version():
|
|
|
|
moduleinfo['config'] = moduleconfig
|
|
|
|
return moduleinfo
|