Refactoring in order to load objects

pull/111/head
Raphaël Vinot 2017-08-28 19:01:53 +02:00
parent 44f32bc443
commit 2bc0745fbf
15 changed files with 317 additions and 239 deletions

View File

@ -30,11 +30,11 @@ if __name__ == '__main__':
if peo:
template_id = pymisp.get_object_template_id(peo.template_uuid)
r = pymisp.add_object(args.event, template_id, peo)
for ref in peo.references:
for ref in peo.ObjectReference:
r = pymisp.add_object_reference(ref)
if fo:
template_id = pymisp.get_object_template_id(fo.template_uuid)
response = pymisp.add_object(args.event, template_id, fo)
for ref in fo.references:
for ref in fo.ObjectReference:
r = pymisp.add_object_reference(ref)

View File

@ -10,6 +10,7 @@ try:
except ImportError:
pass
def check():
missing_dependencies = {'pydeep': False, 'lief': False, 'magic': False, 'pymisp': False}
try:
@ -38,18 +39,18 @@ def make_objects(path):
if seos:
for s in seos:
to_return['objects'].append(s)
if s.references:
to_return['references'] += s.references
if s.ObjectReference:
to_return['references'] += s.ObjectReference
if peo:
to_return['objects'].append(peo)
if peo.references:
to_return['references'] += peo.references
if peo.ObjectReference:
to_return['references'] += peo.ObjectReference
if fo:
to_return['objects'].append(fo)
if fo.references:
to_return['references'] += fo.references
if fo.ObjectReference:
to_return['references'] += fo.ObjectReference
return json.dumps(to_return, cls=MISPEncode)
if __name__ == '__main__':

View File

@ -1,12 +1,12 @@
__version__ = '2.4.77'
try:
from .exceptions import PyMISPError, NewEventError, NewAttributeError, MissingDependency, NoURL, NoKey
from .api import PyMISP
from .abstract import AbstractMISP, MISPEncode
from .mispevent import MISPEvent, MISPAttribute, EncodeUpdate, EncodeFull
from .tools import Neo4j
from .tools import stix
from .tools import MISPObjectGenerator
from .exceptions import PyMISPError, NewEventError, NewAttributeError, MissingDependency, NoURL, NoKey, InvalidMISPObject, UnknownMISPObjectTemplate # noqa
from .api import PyMISP # noqa
from .abstract import AbstractMISP, MISPEncode # noqa
from .defaultobjects import MISPObject, AbstractMISPObjectGenerator # noqa
from .mispevent import MISPEvent, MISPAttribute, EncodeUpdate, EncodeFull # noqa
from .tools import Neo4j # noqa
from .tools import stix # noqa
except ImportError:
pass

View File

@ -5,9 +5,13 @@ import abc
import json
from json import JSONEncoder
import collections
import six # Remove that import when discarding python2 support.
if six.PY2:
import warnings
warnings.warn("You're using python 2, it is strongly recommended to use python >=3.5")
class MISPEncode(JSONEncoder):
def default(self, obj):
@ -17,7 +21,6 @@ class MISPEncode(JSONEncoder):
@six.add_metaclass(abc.ABCMeta) # Remove that line when discarding python2 support.
# Python3 way: class MISPObjectGenerator(metaclass=abc.ABCMeta):
class AbstractMISP(collections.MutableMapping):
attributes = None

195
pymisp/defaultobjects.py Normal file
View File

@ -0,0 +1,195 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import abc
import os
import json
import sys
import uuid
from collections import Counter
from .abstract import AbstractMISP
from .exceptions import UnknownMISPObjectTemplate, InvalidMISPObject
import six # Remove that import when discarding python2 support.
if six.PY2:
import warnings
warnings.warn("You're using python 2, it is strongly recommended to use python >=3.5")
class MISPObjectReference(AbstractMISP):
attributes = ['source_uuid', 'destination_uuid', 'relationship_type', 'comment', 'uuid', 'deleted']
def __init__(self):
super(MISPObjectReference, self).__init__()
def from_dict(self, source_uuid, destination_uuid, relationship_type, comment=None, **kwargs):
self.source_uuid = source_uuid
self.destination_uuid = destination_uuid
self.relationship_type = relationship_type
self.comment = comment
for k, v in kwargs:
setattr(self, k, v)
class MISPObjectAttribute(AbstractMISP):
# This list is very limited and hardcoded to fit the current needs (file/pe/pesection creation): MISPAttriute will follow the
# same spec and just add one attribute: object_relation
attributes = ['object_relation', 'value', 'type', 'category', 'disable_correlation', 'to_ids',
'data', 'encrypt', 'distribution', 'comment', 'uuid', 'event_id']
def __init__(self, definition):
super(MISPObjectAttribute, self).__init__()
self.definition = definition
def from_dict(self, object_relation, value, **kwargs):
from .mispevent import MISPAttribute
self.object_relation = object_relation
self.value = value
# Initialize the new MISPAttribute
# Get the misp attribute type from the definition
self.type = kwargs.pop('type', None)
if self.type is None:
self.type = self.definition.get('misp-attribute')
self.disable_correlation = kwargs.pop('disable_correlation', None)
if self.disable_correlation is None:
# The correlation can be disabled by default in the object definition.
# Use this value if it isn't overloaded by the object
self.disable_correlation = self.definition.get('disable_correlation')
self.to_ids = kwargs.pop('to_ids', None)
if self.to_ids is None:
# Same for the to_ids flag
self.to_ids = self.definition.get('to_ids')
# FIXME: dirty hack until all the classes are ported to the new format but we get the default values
# Initialise rest of the values
for k, v in kwargs.items():
setattr(self, k, v)
temp_attribute = MISPAttribute()
temp_attribute.set_all_values(**self)
# Update default values
for k, v in temp_attribute.to_dict().items():
setattr(self, k, v)
class MISPObject(AbstractMISP):
attributes = ['name', 'meta-category', 'uuid', 'description', 'template_version', 'template_uuid', 'Attribute']
def __init__(self, name, strict=True):
super(MISPObject, self).__init__()
self.strict = strict
self.name = name
self.misp_objects_path = os.path.join(
os.path.abspath(os.path.dirname(sys.modules['pymisp'].__file__)),
'data', 'misp-objects', 'objects')
if os.path.exists(os.path.join(self.misp_objects_path, self.name, 'definition.json')):
self.known_template = True
else:
if self.strict:
raise UnknownMISPObjectTemplate('{} is unknown in the MISP object directory.')
else:
self.known_template = False
if self.known_template:
with open(os.path.join(self.misp_objects_path, self.name, 'definition.json'), 'r') as f:
self.definition = json.load(f)
setattr(self, 'meta-category', self.definition['meta-category'])
self.template_uuid = self.definition['uuid']
self.description = self.definition['description']
self.template_version = self.definition['version']
else:
# FIXME We need to set something for meta-category, template_uuid, description and template_version
pass
self.uuid = str(uuid.uuid4())
self.Attribute = []
self.ObjectReference = []
def from_dict(self, **kwargs):
if self.known_template:
if kwargs.get('template_uuid') and kwargs['template_uuid'] != self.template_uuid:
if self.strict:
raise UnknownMISPObjectTemplate('UUID of the object is different from the one of the template.')
else:
self.known_template = False
if kwargs.get('template_version') and int(kwargs['template_version']) != self.template_version:
if self.strict:
raise UnknownMISPObjectTemplate('Version of the object ({}) is different from the one of the template ({}).'.format(kwargs['template_version'], self.template_version))
else:
self.known_template = False
for key, value in kwargs.items():
if key == 'Attribute':
for v in value:
self.add_attribute(**v)
elif key == 'ObjectReference':
for v in value:
self.add_reference(**v)
else:
setattr(self, key, value)
def to_dict(self, strict=True):
if strict or self.strict and self.known_template:
self._validate()
return super(MISPObject, self).to_dict()
def to_json(self, strict=True):
if strict or self.strict and self.known_template:
self._validate()
return super(MISPObject, self).to_json()
def _validate(self):
"""Make sure the object we're creating has the required fields"""
all_object_relations = []
for a in self.Attribute:
all_object_relations.append(a.object_relation)
count_relations = dict(Counter(all_object_relations))
for key, counter in count_relations.items():
if counter == 1:
continue
if not self.definition['attributes'][key].get('multiple'):
raise InvalidMISPObject('Multiple occurrences of {} is not allowed'.format(key))
all_attribute_names = set(count_relations.keys())
if self.definition.get('requiredOneOf'):
if not set(self.definition['requiredOneOf']) & all_attribute_names:
raise InvalidMISPObject('At least one of the following attributes is required: {}'.format(', '.join(self.definition['requiredOneOf'])))
if self.definition.get('required'):
for r in self.definition.get('required'):
if r not in all_attribute_names:
raise InvalidMISPObject('{} is required'.format(r))
return True
def add_reference(self, destination_uuid, relationship_type, comment=None, **kwargs):
"""Add a link (uuid) to an other object"""
if kwargs.get('source_uuid'):
# Load existing object
source_uuid = kwargs.get('source_uuid')
else:
# New reference
source_uuid = self.uuid
reference = MISPObjectReference()
reference.from_dict(source_uuid=source_uuid, destination_uuid=destination_uuid,
relationship_type=relationship_type, comment=comment, **kwargs)
self.ObjectReference.append(reference)
def add_attribute(self, object_relation, **value):
if value.get('value') is None:
return None
if self.known_template:
attribute = MISPObjectAttribute(self.definition['attributes'][object_relation])
else:
attribute = MISPObjectAttribute({})
attribute.from_dict(object_relation, **value)
self.Attribute.append(attribute)
return attribute
@six.add_metaclass(abc.ABCMeta) # Remove that line when discarding python2 support.
# Python3 way: class MISPObjectGenerator(metaclass=abc.ABCMeta):
class AbstractMISPObjectGenerator(MISPObject):
@abc.abstractmethod
def generate_attributes(self):
"""Contains the logic where all the values of the object are gathered"""
pass

View File

@ -1,6 +1,7 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
class PyMISPError(Exception):
def __init__(self, message):
super(PyMISPError, self).__init__(message)
@ -29,3 +30,16 @@ class NoURL(PyMISPError):
class NoKey(PyMISPError):
pass
class MISPObjectException(PyMISPError):
pass
class InvalidMISPObject(MISPObjectException):
"""Exception raised when an object doesn't respect the contrains in the definition"""
pass
class UnknownMISPObjectTemplate(MISPObjectException):
"""Exception raised when the template is unknown"""
pass

View File

@ -11,7 +11,6 @@ import base64
from io import BytesIO
from zipfile import ZipFile
import hashlib
from .abstract import AbstractMISP
try:
from dateutil.parser import parse
@ -38,6 +37,7 @@ except ImportError:
has_pyme = False
from .exceptions import PyMISPError, NewEventError, NewAttributeError
from .defaultobjects import MISPObject
# Least dirty way to support python 2 and 3
try:
@ -285,7 +285,7 @@ def _int_to_str(d):
return d
class MISPEvent(AbstractMISP):
class MISPEvent(object):
def __init__(self, describe_types=None, strict_validation=False):
self.ressources_path = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'data')
@ -338,6 +338,7 @@ class MISPEvent(AbstractMISP):
self.RelatedEvent = []
self.Tag = []
self.Galaxy = None
self.Object = None
def _serialize(self):
return '{date}{threat_level_id}{info}{uuid}{analysis}{timestamp}'.format(
@ -510,6 +511,12 @@ class MISPEvent(AbstractMISP):
self.sig = kwargs['sig']
if kwargs.get('global_sig'):
self.global_sig = kwargs['global_sig']
if kwargs.get('Object'):
self.Object = []
for obj in kwargs['Object']:
tmp_object = MISPObject(obj['name'])
tmp_object.from_dict(**obj)
self.Object.append(tmp_object)
def _json(self):
# DEPTECATED

View File

@ -1,5 +1,4 @@
from .neo4j import Neo4j # noqa
from .objectgenerator import MISPObjectGenerator, MISPObjectException, InvalidMISPObject # noqa
from .fileobject import FileObject # noqa
from .peobject import PEObject, PESectionObject # noqa
from .elfobject import ELFObject, ELFSectionObject # noqa

View File

@ -1,7 +1,8 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pymisp.tools import FileObject, PEObject, ELFObject, MachOObject, MISPObjectException
from pymisp.tools import FileObject, PEObject, ELFObject, MachOObject
from pymisp.exceptions import MISPObjectException
import warnings
try:

View File

@ -1,7 +1,7 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from pymisp.tools import MISPObjectGenerator
from pymisp.defaultobjects import AbstractMISPObjectGenerator
from io import BytesIO
from hashlib import md5, sha1, sha256, sha512
import warnings
@ -20,7 +20,7 @@ except ImportError:
HAS_PYDEEP = False
class ELFObject(MISPObjectGenerator):
class ELFObject(AbstractMISPObjectGenerator):
def __init__(self, parsed=None, filepath=None, pseudofile=None):
if not HAS_PYDEEP:
@ -49,10 +49,10 @@ class ELFObject(MISPObjectGenerator):
def generate_attributes(self):
# General information
self._create_attribute('type', value=str(self.elf.header.file_type).split('.')[1])
self._create_attribute('entrypoint-address', value=self.elf.entrypoint)
self._create_attribute('arch', value=str(self.elf.header.machine_type).split('.')[1])
self._create_attribute('os_abi', value=str(self.elf.header.identity_os_abi).split('.')[1])
self.add_attribute('type', value=str(self.elf.header.file_type).split('.')[1])
self.add_attribute('entrypoint-address', value=self.elf.entrypoint)
self.add_attribute('arch', value=str(self.elf.header.machine_type).split('.')[1])
self.add_attribute('os_abi', value=str(self.elf.header.identity_os_abi).split('.')[1])
# Sections
self.sections = []
if self.elf.sections:
@ -62,10 +62,10 @@ class ELFObject(MISPObjectGenerator):
self.add_reference(s.uuid, 'included-in', 'Section {} of ELF'.format(pos))
pos += 1
self.sections.append(s)
self._create_attribute('number-sections', value=len(self.sections))
self.add_attribute('number-sections', value=len(self.sections))
class ELFSectionObject(MISPObjectGenerator):
class ELFSectionObject(AbstractMISPObjectGenerator):
def __init__(self, section):
# Python3 way
@ -76,16 +76,16 @@ class ELFSectionObject(MISPObjectGenerator):
self.generate_attributes()
def generate_attributes(self):
self._create_attribute('name', value=self.section.name)
self._create_attribute('type', value=str(self.section.type).split('.')[1])
self.add_attribute('name', value=self.section.name)
self.add_attribute('type', value=str(self.section.type).split('.')[1])
for flag in self.section.flags_list:
self._create_attribute('flag', value=str(flag).split('.')[1])
size = self._create_attribute('size-in-bytes', value=self.section.size)
self.add_attribute('flag', value=str(flag).split('.')[1])
size = self.add_attribute('size-in-bytes', value=self.section.size)
if int(size.value) > 0:
self._create_attribute('entropy', value=self.section.entropy)
self._create_attribute('md5', value=md5(self.data).hexdigest())
self._create_attribute('sha1', value=sha1(self.data).hexdigest())
self._create_attribute('sha256', value=sha256(self.data).hexdigest())
self._create_attribute('sha512', value=sha512(self.data).hexdigest())
self.add_attribute('entropy', value=self.section.entropy)
self.add_attribute('md5', value=md5(self.data).hexdigest())
self.add_attribute('sha1', value=sha1(self.data).hexdigest())
self.add_attribute('sha256', value=sha256(self.data).hexdigest())
self.add_attribute('sha512', value=sha512(self.data).hexdigest())
if HAS_PYDEEP:
self._create_attribute('ssdeep', value=pydeep.hash_buf(self.data).decode())
self.add_attribute('ssdeep', value=pydeep.hash_buf(self.data).decode())

View File

@ -1,7 +1,7 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from pymisp.tools import MISPObjectGenerator
from pymisp.defaultobjects import AbstractMISPObjectGenerator
import os
from io import BytesIO
from hashlib import md5, sha1, sha256, sha512
@ -22,7 +22,7 @@ except ImportError:
HAS_MAGIC = False
class FileObject(MISPObjectGenerator):
class FileObject(AbstractMISPObjectGenerator):
def __init__(self, filepath=None, pseudofile=None, filename=None):
if not HAS_PYDEEP:
@ -48,19 +48,19 @@ class FileObject(MISPObjectGenerator):
self.generate_attributes()
def generate_attributes(self):
self._create_attribute('filename', value=self.filename)
size = self._create_attribute('size-in-bytes', value=len(self.data))
self.add_attribute('filename', value=self.filename)
size = self.add_attribute('size-in-bytes', value=len(self.data))
if int(size.value) > 0:
self._create_attribute('entropy', value=self.__entropy_H(self.data))
self._create_attribute('md5', value=md5(self.data).hexdigest())
self._create_attribute('sha1', value=sha1(self.data).hexdigest())
self._create_attribute('sha256', value=sha256(self.data).hexdigest())
self._create_attribute('sha512', value=sha512(self.data).hexdigest())
self._create_attribute('malware-sample', value=self.filename, data=self.pseudofile)
self.add_attribute('entropy', value=self.__entropy_H(self.data))
self.add_attribute('md5', value=md5(self.data).hexdigest())
self.add_attribute('sha1', value=sha1(self.data).hexdigest())
self.add_attribute('sha256', value=sha256(self.data).hexdigest())
self.add_attribute('sha512', value=sha512(self.data).hexdigest())
self.add_attribute('malware-sample', value=self.filename, data=self.pseudofile)
if HAS_MAGIC:
self._create_attribute('mimetype', value=magic.from_buffer(self.data))
self.add_attribute('mimetype', value=magic.from_buffer(self.data))
if HAS_PYDEEP:
self._create_attribute('ssdeep', value=pydeep.hash_buf(self.data).decode())
self.add_attribute('ssdeep', value=pydeep.hash_buf(self.data).decode())
def __entropy_H(self, data):
"""Calculate the entropy of a chunk of data."""

View File

@ -1,7 +1,7 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from pymisp.tools import MISPObjectGenerator
from pymisp.defaultobjects import AbstractMISPObjectGenerator
from io import BytesIO
from hashlib import md5, sha1, sha256, sha512
import warnings
@ -20,7 +20,7 @@ except ImportError:
HAS_PYDEEP = False
class MachOObject(MISPObjectGenerator):
class MachOObject(AbstractMISPObjectGenerator):
def __init__(self, parsed=None, filepath=None, pseudofile=None):
if not HAS_PYDEEP:
@ -48,11 +48,11 @@ class MachOObject(MISPObjectGenerator):
self.generate_attributes()
def generate_attributes(self):
self._create_attribute('type', value=str(self.macho.header.file_type).split('.')[1])
self._create_attribute('name', value=self.macho.name)
self.add_attribute('type', value=str(self.macho.header.file_type).split('.')[1])
self.add_attribute('name', value=self.macho.name)
# General information
if self.macho.has_entrypoint:
self._create_attribute('entrypoint-address', value=self.macho.entrypoint)
self.add_attribute('entrypoint-address', value=self.macho.entrypoint)
# Sections
self.sections = []
if self.macho.sections:
@ -62,10 +62,10 @@ class MachOObject(MISPObjectGenerator):
self.add_reference(s.uuid, 'included-in', 'Section {} of MachO'.format(pos))
pos += 1
self.sections.append(s)
self._create_attribute('number-sections', value=len(self.sections))
self.add_attribute('number-sections', value=len(self.sections))
class MachOSectionObject(MISPObjectGenerator):
class MachOSectionObject(AbstractMISPObjectGenerator):
def __init__(self, section):
# Python3 way
@ -76,13 +76,13 @@ class MachOSectionObject(MISPObjectGenerator):
self.generate_attributes()
def generate_attributes(self):
self._create_attribute('name', value=self.section.name)
size = self._create_attribute('size-in-bytes', value=self.section.size)
self.add_attribute('name', value=self.section.name)
size = self.add_attribute('size-in-bytes', value=self.section.size)
if int(size.value) > 0:
self._create_attribute('entropy', value=self.section.entropy)
self._create_attribute('md5', value=md5(self.data).hexdigest())
self._create_attribute('sha1', value=sha1(self.data).hexdigest())
self._create_attribute('sha256', value=sha256(self.data).hexdigest())
self._create_attribute('sha512', value=sha512(self.data).hexdigest())
self.add_attribute('entropy', value=self.section.entropy)
self.add_attribute('md5', value=md5(self.data).hexdigest())
self.add_attribute('sha1', value=sha1(self.data).hexdigest())
self.add_attribute('sha256', value=sha256(self.data).hexdigest())
self.add_attribute('sha512', value=sha512(self.data).hexdigest())
if HAS_PYDEEP:
self._create_attribute('ssdeep', value=pydeep.hash_buf(self.data).decode())
self.add_attribute('ssdeep', value=pydeep.hash_buf(self.data).decode())

View File

@ -1,142 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from collections import Counter
from pymisp import MISPEvent, MISPAttribute, AbstractMISP
import os
import json
import uuid
import abc
import sys
import six # Remove that import when discarding python2 support.
class MISPObjectException(Exception):
pass
class InvalidMISPObject(MISPObjectException):
"""Exception raised when an object doesn't contains the required field(s)"""
pass
if six.PY2:
import warnings
warnings.warn("You're using python 2, it is strongly recommended to use python >=3.5")
class MISPObjectReference(AbstractMISP):
attributes = ['source_uuid', 'destination_uuid', 'relationship_type', 'comment']
def __init__(self, source_uuid, destination_uuid, relationship_type, comment=None):
self.source_uuid = source_uuid
self.destination_uuid = destination_uuid
self.relationship_type = relationship_type
self.comment = comment
class MISPObjectAttribute(AbstractMISP):
# This list is very limited and hardcoded to fit the current needs (file/pe/pesection creation): MISPAttriute will follow the
# same spec and just add one attribute: object_relation
attributes = ['object_relation', 'value', 'type', 'category', 'disable_correlation', 'to_ids',
'data', 'encrypt', 'distribution', 'comment']
def __init__(self, definition, object_relation, value, **kwargs):
self.object_relation = object_relation
self.value = value
# Initialize the new MISPAttribute
# Get the misp attribute type from the definition
self.type = kwargs.pop('type', None)
if self.type is None:
self.type = definition['misp-attribute']
self.disable_correlation = kwargs.pop('disable_correlation', None)
if self.disable_correlation is None:
# The correlation can be disabled by default in the object definition.
# Use this value if it isn't overloaded by the object
self.disable_correlation = definition.get('disable_correlation')
self.to_ids = kwargs.pop('to_ids', None)
if self.to_ids is None:
# Same for the to_ids flag
self.to_ids = definition.get('to_ids')
# Initialise rest of the values
for k, v in kwargs.items():
self[k] = v
# FIXME: dirty hack until all the classes are ported to the new format but we get the default values
temp_attribute = MISPAttribute()
temp_attribute.set_all_values(**self)
# Update default values
self.from_dict(**temp_attribute.to_dict())
class MISPObjectGenerator(AbstractMISP):
attributes = ['name', 'meta-category', 'uuid', 'description', 'version', 'Attribute']
def __init__(self, template_dir):
"""This class is used to fill a new MISP object with the default values defined in the object template
* template is the path to the template within the misp-object repository
* misp_objects_path is the path to the misp-object repository
"""
self.misp_objects_path = os.path.join(
os.path.abspath(os.path.dirname(sys.modules['pymisp'].__file__)),
'data', 'misp-objects', 'objects')
with open(os.path.join(self.misp_objects_path, template_dir, 'definition.json'), 'r') as f:
self.definition = json.load(f)
self.misp_event = MISPEvent()
self.name = self.definition['name']
setattr(self, 'meta-category', self.definition['meta-category'])
self.template_uuid = self.definition['uuid']
self.description = self.definition['description']
self.version = self.definition['version']
self.uuid = str(uuid.uuid4())
self.Attribute = []
self.references = []
def _create_attribute(self, object_type, **value):
if value.get('value') is None:
return None
attribute = MISPObjectAttribute(self.definition['attributes'][object_type], object_type, **value)
self.Attribute.append(attribute)
return attribute
def to_dict(self, strict=True):
if strict:
self._validate()
return super(MISPObjectGenerator, self).to_dict()
def to_json(self, strict=True):
if strict:
self._validate()
return super(MISPObjectGenerator, self).to_json()
def _validate(self):
"""Make sure the object we're creating has the required fields"""
all_object_relations = []
for a in self.Attribute:
all_object_relations.append(a.object_relation)
count_relations = dict(Counter(all_object_relations))
for key, counter in count_relations.items():
if counter == 1:
continue
if not self.definition['attributes'][key].get('multiple'):
raise InvalidMISPObject('Multiple occurrences of {} is not allowed'.format(key))
all_attribute_names = set(count_relations.keys())
if self.definition.get('requiredOneOf'):
if not set(self.definition['requiredOneOf']) & all_attribute_names:
raise InvalidMISPObject('At least one of the following attributes is required: {}'.format(', '.join(self.definition['requiredOneOf'])))
if self.definition.get('required'):
for r in self.definition.get('required'):
if r not in all_attribute_names:
raise InvalidMISPObject('{} is required'.format(r))
return True
def add_reference(self, destination_uuid, relationship_type, comment=None):
"""Add a link (uuid) to an other object"""
self.references.append(MISPObjectReference(self.uuid, destination_uuid, relationship_type, comment))
@abc.abstractmethod
def generate_attributes(self):
"""Contains the logic where all the values of the object are gathered"""
pass

View File

@ -1,7 +1,7 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from pymisp.tools import MISPObjectGenerator
from pymisp.defaultobjects import AbstractMISPObjectGenerator
from io import BytesIO
from hashlib import md5, sha1, sha256, sha512
from datetime import datetime
@ -21,7 +21,7 @@ except ImportError:
HAS_PYDEEP = False
class PEObject(MISPObjectGenerator):
class PEObject(AbstractMISPObjectGenerator):
def __init__(self, parsed=None, filepath=None, pseudofile=None):
if not HAS_PYDEEP:
@ -74,10 +74,10 @@ class PEObject(MISPObjectGenerator):
return 'unknown'
def generate_attributes(self):
self._create_attribute('type', value=self._get_pe_type())
self.add_attribute('type', value=self._get_pe_type())
# General information
self._create_attribute('entrypoint-address', value=self.pe.entrypoint)
self._create_attribute('compilation-timestamp', value=datetime.utcfromtimestamp(self.pe.header.time_date_stamps).isoformat())
self.add_attribute('entrypoint-address', value=self.pe.entrypoint)
self.add_attribute('compilation-timestamp', value=datetime.utcfromtimestamp(self.pe.header.time_date_stamps).isoformat())
# self.imphash = self.pe.get_imphash()
try:
if (self.pe.has_resources and
@ -85,15 +85,15 @@ class PEObject(MISPObjectGenerator):
self.pe.resources_manager.version.has_string_file_info and
self.pe.resources_manager.version.string_file_info.langcode_items):
fileinfo = dict(self.pe.resources_manager.version.string_file_info.langcode_items[0].items.items())
self._create_attribute('original-filename', value=fileinfo.get('OriginalFilename'))
self._create_attribute('internal-filename', value=fileinfo.get('InternalName'))
self._create_attribute('file-description', value=fileinfo.get('FileDescription'))
self._create_attribute('file-version', value=fileinfo.get('FileVersion'))
self._create_attribute('lang-id', value=self.pe.resources_manager.version.string_file_info.langcode_items[0].key)
self._create_attribute('product-name', value=fileinfo.get('ProductName'))
self._create_attribute('product-version', value=fileinfo.get('ProductVersion'))
self._create_attribute('company-name', value=fileinfo.get('CompanyName'))
self._create_attribute('legal-copyright', value=fileinfo.get('LegalCopyright'))
self.add_attribute('original-filename', value=fileinfo.get('OriginalFilename'))
self.add_attribute('internal-filename', value=fileinfo.get('InternalName'))
self.add_attribute('file-description', value=fileinfo.get('FileDescription'))
self.add_attribute('file-version', value=fileinfo.get('FileVersion'))
self.add_attribute('lang-id', value=self.pe.resources_manager.version.string_file_info.langcode_items[0].key)
self.add_attribute('product-name', value=fileinfo.get('ProductName'))
self.add_attribute('product-version', value=fileinfo.get('ProductVersion'))
self.add_attribute('company-name', value=fileinfo.get('CompanyName'))
self.add_attribute('legal-copyright', value=fileinfo.get('LegalCopyright'))
except lief.read_out_of_bound:
# The file is corrupted
pass
@ -106,14 +106,14 @@ class PEObject(MISPObjectGenerator):
self.add_reference(s.uuid, 'included-in', 'Section {} of PE'.format(pos))
if ((self.pe.entrypoint >= section.virtual_address) and
(self.pe.entrypoint < (section.virtual_address + section.virtual_size))):
self._create_attribute('entrypoint-section|position', value='{}|{}'.format(section.name, pos))
self.add_attribute('entrypoint-section|position', value='{}|{}'.format(section.name, pos))
pos += 1
self.sections.append(s)
self._create_attribute('number-sections', value=len(self.sections))
self.add_attribute('number-sections', value=len(self.sections))
# TODO: TLSSection / DIRECTORY_ENTRY_TLS
class PESectionObject(MISPObjectGenerator):
class PESectionObject(AbstractMISPObjectGenerator):
def __init__(self, section):
# Python3 way
@ -124,13 +124,13 @@ class PESectionObject(MISPObjectGenerator):
self.generate_attributes()
def generate_attributes(self):
self._create_attribute('name', value=self.section.name)
size = self._create_attribute('size-in-bytes', value=self.section.size)
self.add_attribute('name', value=self.section.name)
size = self.add_attribute('size-in-bytes', value=self.section.size)
if int(size.value) > 0:
self._create_attribute('entropy', value=self.section.entropy)
self._create_attribute('md5', value=md5(self.data).hexdigest())
self._create_attribute('sha1', value=sha1(self.data).hexdigest())
self._create_attribute('sha256', value=sha256(self.data).hexdigest())
self._create_attribute('sha512', value=sha512(self.data).hexdigest())
self.add_attribute('entropy', value=self.section.entropy)
self.add_attribute('md5', value=md5(self.data).hexdigest())
self.add_attribute('sha1', value=sha1(self.data).hexdigest())
self.add_attribute('sha256', value=sha256(self.data).hexdigest())
self.add_attribute('sha512', value=sha512(self.data).hexdigest())
if HAS_PYDEEP:
self._create_attribute('ssdeep', value=pydeep.hash_buf(self.data).decode())
self.add_attribute('ssdeep', value=pydeep.hash_buf(self.data).decode())

View File

@ -230,18 +230,18 @@ class TestOffline(unittest.TestCase):
if seos:
for s in seos:
to_return['objects'].append(s)
if s.references:
to_return['references'] += s.references
if s.ObjectReference:
to_return['references'] += s.ObjectReference
if peo:
to_return['objects'].append(peo)
if peo.references:
to_return['references'] += peo.references
if peo.ObjectReference:
to_return['references'] += peo.ObjectReference
if fo:
to_return['objects'].append(fo)
if fo.references:
to_return['references'] += fo.references
if fo.ObjectReference:
to_return['references'] += fo.ObjectReference
return json.dumps(to_return, cls=MISPEncode)
def test_objects(self, m):