Merge branch '2.4' of github.com:MISP/MISP into 2.4

pull/6043/head
iglocska 2020-06-22 11:11:31 +02:00
commit 6b95047c1e
No known key found for this signature in database
GPG Key ID: BEA224F1FEF113AC
21 changed files with 7644 additions and 6197 deletions

2
PyMISP

@ -1 +1 @@
Subproject commit cc16f2ed247f4aa5f7c28491fc4ae7fb3e8b05e0
Subproject commit d05412161186657a7676dbbb4118eeeafa6fba5a

View File

@ -2132,15 +2132,16 @@ class EventsController extends AppController
if (!$this->userRole['perm_modify']) {
throw new UnauthorizedException(__('You do not have permission to do that.'));
}
$scriptDir = APP . 'files' . DS . 'scripts';
if ($this->request->is('post')) {
if ($this->_isRest()) {
$randomFileName = $this->Event->generateRandomFileName();
$tmpDir = APP . "files" . DS . "scripts" . DS . "tmp";
$tempFile = new File($tmpDir . DS . $randomFileName, true, 0644);
$tempFile = new File($scriptDir . DS . 'tmp' . DS . $randomFileName, true, 0644);
$tempFile->write($this->request->input());
$tempFile->close();
$result = $this->Event->upload_stix(
$this->Auth->user(),
$scriptDir,
$randomFileName,
$stix_version,
'uploaded_stix_file.' . ($stix_version == '1' ? 'xml' : 'json'),
@ -2162,10 +2163,10 @@ class EventsController extends AppController
$original_file = !empty($this->data['Event']['original_file']) ? $this->data['Event']['stix']['name'] : '';
if (isset($this->data['Event']['stix']) && $this->data['Event']['stix']['size'] > 0 && is_uploaded_file($this->data['Event']['stix']['tmp_name'])) {
$randomFileName = $this->Event->generateRandomFileName();
$tmpDir = APP . "files" . DS . "scripts" . DS . "tmp";
move_uploaded_file($this->data['Event']['stix']['tmp_name'], $tmpDir . DS . $randomFileName);
move_uploaded_file($this->data['Event']['stix']['tmp_name'], $scriptDir . DS . 'tmp' . DS . $randomFileName);
$result = $this->Event->upload_stix(
$this->Auth->user(),
$scriptDir,
$randomFileName,
$stix_version,
$original_file,

View File

@ -0,0 +1,102 @@
<?php
class AuthenticationFailureWidget
{
public $title = 'Authentication Failure Data';
public $render = 'BarChart';
public $width = 3;
public $height = 10;
public $params = array(
'event_info' => 'Substring included in the info field of relevant Authentication Failure events.',
'type' => 'Type of data used for the widget (sshd, etc.).'
);
public $description = 'Widget visualising authentication failures collected in d4.';
public $placeholder =
'{
"event_info": "%Authentication Failure Daily Event%",
"type": "sshd",
"absciss": "username"
}';
public function handler($user, $options = array())
{
$this->Event = ClassRegistry::init('Event');
$event_info_condition = empty($options['event_info']) ? '%Authentication Failure Daily Event%' : $options['event_info'];
$params = array(
'eventinfo' => $event_info_condition,
'order' => 'date desc',
'limit' => 1,
'page' => 1
);
$eventIds = $this->Event->filterEventIds($user, $params);
$params['eventid'] = $eventIds;
$data = array();
if (empty($options['type'])) {
$options['type'] = 'sshd';
}
if (empty($options['absciss'])) {
$options['absciss'] = 'username';
}
if (!empty($eventIds)) {
$events = $this->Event->fetchEvent($user, $params);
$data = $this->__handleEvents($events, $options);
arsort($data);
}
$data = array('data' => $data);
return $data;
}
private function __handleEvents($events, $options)
{
$data = array();
if (!empty($events)) {
foreach ($events as $event) {
if (!empty($event['Object'])) {
$data = $this->__handleObjects($data, $event['Object'], $options);
}
}
}
return $data;
}
private function __handleObjects($data, $objects, $options)
{
foreach ($objects as $object) {
if ($object['name'] === 'authentication-failure-report') {
$temp = $this->__interpretObject($object);
$data = $this->__rearrangeResults($data, $temp, $options);
}
}
return $data;
}
private function __rearrangeResults($data, $temp, $options)
{
$target = $temp[$options['absciss']];
$type = $options['type'];
if ($temp['type'] === $type || $type === 'all' ) {
$data[$target] = $temp['total'];
}
return $data;
}
private function __interpretObject($object)
{
$temp = array();
$validFields = array('type', 'username', 'total', 'ip-dst', 'ip-src');
foreach ($object['Attribute'] as $attribute) {
if (in_array($attribute['object_relation'], $validFields)) {
if ($attribute['object_relation'] == 'total') {
$attribute['value'] = (int)($attribute['value']);
}
$temp[$attribute['object_relation']] = $attribute['value'];
}
}
return $temp;
}
}

View File

@ -32,7 +32,7 @@ class Attribute extends AppModel
// explanations of certain fields to be used in various views
public $fieldDescriptions = array(
'signature' => array('desc' => 'Is this attribute eligible to automatically create an IDS signature (network IDS or host IDS) out of it ?'),
'distribution' => array('desc' => 'Describes who will have access to the event.')
'distribution' => array('desc' => 'Describes who will have access to the attribute.')
);
public $defaultFields = array(
@ -1967,13 +1967,47 @@ class Attribute extends AppModel
$ipValues = array();
$ip = $a['value1'];
if (strpos($ip, '/') !== false) { // IP is CIDR
$ip_array = explode('/', $ip);
$ip_version = filter_var($ip_array[0], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) ? 4 : 6;
list($networkIp, $mask) = explode('/', $ip);
$ip_version = filter_var($networkIp, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) ? 4 : 6;
$conditions = array(
'type' => array('ip-src', 'ip-dst', 'ip-src|port', 'ip-dst|port'),
'value1 NOT LIKE' => '%/%', // do not return CIDR, just plain IPs
'disable_correlation' => 0,
'deleted' => 0,
);
if (in_array($this->getDataSource()->config['datasource'], array('Database/Mysql', 'Database/MysqlObserver'))) {
// Massive speed up for CIDR correlation. Instead of testing all in PHP, database can do that work much
// faster. But these methods are just supported by MySQL.
if ($ip_version === 4) {
$startIp = ip2long($networkIp) & ((-1 << (32 - $mask)));
$endIp = $startIp + pow(2, (32 - $mask)) - 1;
// Just fetch IP address that fit in CIDR range.
$conditions['INET_ATON(value1) BETWEEN ? AND ?'] = array($startIp, $endIp);
// Just fetch IPv4 address that starts with given prefix. This is fast, because value1 is indexed.
// This optimisation is possible just to mask bigger than 8 bites.
if ($mask >= 8) {
$ipv4Parts = explode('.', $networkIp);
$ipv4Parts = array_slice($ipv4Parts, 0, intval($mask / 8));
$prefix = implode('.', $ipv4Parts);
$conditions['value1 LIKE'] = $prefix . '%';
}
} else {
$conditions[] = 'IS_IPV6(value1)';
// Just fetch IPv6 address that starts with given prefix. This is fast, because value1 is indexed.
if ($mask >= 16) {
$ipv6Parts = explode(':', rtrim($networkIp, ':'));
$ipv6Parts = array_slice($ipv6Parts, 0, intval($mask / 16));
$prefix = implode(':', $ipv6Parts);
$conditions['value1 LIKE'] = $prefix . '%';
}
}
}
$ipList = $this->find('list', array(
'conditions' => array(
'type' => array('ip-src', 'ip-dst'),
'value1 NOT LIKE' => '%/%', // do not return CIDR, just plain IPs
),
'conditions' => $conditions,
'group' => 'value1', // return just unique values
'fields' => array('value1'),
'order' => false
@ -2046,7 +2080,7 @@ class Attribute extends AppModel
return true;
}
if (Configure::read('MISP.enable_advanced_correlations') && in_array($a['type'], array('ip-src', 'ip-dst'))) {
if (Configure::read('MISP.enable_advanced_correlations') && in_array($a['type'], array('ip-src', 'ip-dst', 'ip-src|port', 'ip-dst|port'))) {
$extraConditions = $this->__cidrCorrelation($a);
} else if ($a['type'] === 'ssdeep' && function_exists('ssdeep_fuzzy_compare')) {
$this->FuzzyCorrelateSsdeep = ClassRegistry::init('FuzzyCorrelateSsdeep');

View File

@ -5761,26 +5761,30 @@ class Event extends AppModel
return $this->save($event);
}
public function upload_stix($user, $filename, $stix_version, $original_file, $publish)
public function upload_stix($user, $scriptDir, $filename, $stix_version, $original_file, $publish)
{
App::uses('Folder', 'Utility');
App::uses('File', 'Utility');
$tempFilePath = $scriptDir . DS . 'tmp' . DS . $filename;
if ($stix_version == '2') {
$scriptFile = APP . 'files/scripts/stix2/stix2misp.py';
$tempFilePath = APP . 'files/scripts/tmp/' . $filename;
$scriptFile = $scriptDir . DS . 'stix2' . DS . 'stix2misp.py';
$shell_command = $this->getPythonVersion() . ' ' . $scriptFile . ' ' . $tempFilePath;
$output_path = $tempFilePath . '.stix2';
$stix_version = "STIX 2.0";
} elseif ($stix_version == '1' || $stix_version == '1.1' || $stix_version == '1.2') {
$scriptFile = APP . 'files/scripts/stix2misp.py';
$tempFilePath = APP . 'files/scripts/tmp/' . $filename;
$scriptFile = $scriptDir . DS . 'stix2misp.py';
$shell_command = $this->getPythonVersion() . ' ' . $scriptFile . ' ' . $filename;
$output_path = $tempFilePath . '.json';
$stix_version = "STIX 1.1";
} else {
throw new MethodNotAllowedException('Invalid STIX version');
}
$shell_command .= ' ' . escapeshellarg(Configure::read('MISP.default_event_distribution')) . ' ' . escapeshellarg(Configure::read('MISP.default_attribute_distribution')) . ' 2>' . APP . 'tmp/logs/exec-errors.log';
$shell_command .= ' ' . escapeshellarg(Configure::read('MISP.default_event_distribution')) . ' ' . escapeshellarg(Configure::read('MISP.default_attribute_distribution'));
$synonymsToTagNames = $this->__getTagNamesFromSynonyms($scriptDir);
if ($synonymsToTagNames) {
$shell_command .= ' ' . $synonymsToTagNames;
}
$shell_command .= ' 2>' . APP . 'tmp/logs/exec-errors.log';
$result = shell_exec($shell_command);
$result = preg_split("/\r\n|\n|\r/", trim($result));
$result = end($result);
@ -5824,6 +5828,48 @@ class Event extends AppModel
}
}
private function __getTagNamesFromSynonyms($scriptDir)
{
$synonymsToTagNames = $scriptDir . DS . 'synonymsToTagNames.json';
if (!file_exists($synonymsToTagNames) || (time() - filemtime($synonymsToTagNames)) > 600) {
if (empty($this->GalaxyCluster)) {
$this->GalaxyCluster = ClassRegistry::init('GalaxyCluster');
}
$clusters = $this->GalaxyCluster->find('all', array(
'recursive' => -1,
'fields' => array(
'GalaxyCluster.value',
'MAX(GalaxyCluster.version)',
'GalaxyCluster.tag_name',
'GalaxyCluster.id'
),
'group' => array('GalaxyCluster.tag_name')
));
$synonyms = $this->GalaxyCluster->GalaxyElement->find('all', array(
'recursive' => -1,
'fields' => array('galaxy_cluster_id', 'value'),
'conditions' => array('key' => 'synonyms')
));
$idToSynonyms = array();
foreach($synonyms as $synonym) {
$idToSynonyms[$synonym['GalaxyElement']['galaxy_cluster_id']][] = $synonym['GalaxyElement']['value'];
}
$mapping = array();
foreach($clusters as $cluster) {
$mapping[$cluster['GalaxyCluster']['value']][] = $cluster['GalaxyCluster']['tag_name'];
if (!empty($idToSynonyms[$cluster['GalaxyCluster']['id']])) {
foreach($idToSynonyms[$cluster['GalaxyCluster']['id']] as $synonym) {
$mapping[$synonym][] = $cluster['GalaxyCluster']['tag_name'];
}
}
}
$file = new File($synonymsToTagNames, true, 0644);
$file->write(json_encode($mapping));
$file->close();
}
return $synonymsToTagNames;
}
public function enrichmentRouter($options)
{
if (Configure::read('MISP.background_jobs')) {

View File

@ -55,7 +55,7 @@ from stix.extensions.identity.ciq_identity_3_0 import Address as ciq_Address
from stix.extensions.marking.simple_marking import SimpleMarkingStructure
from stix.extensions.marking.tlp import TLPMarkingStructure
from stix.extensions.test_mechanism.snort_test_mechanism import SnortTestMechanism
from stix.incident import Incident, Time, ExternalID, AffectedAsset, AttributedThreatActors
from stix.incident import Incident, Time, ExternalID, AffectedAsset, AttributedThreatActors, COATaken
from stix.incident.history import History, HistoryItem
from stix.indicator import Indicator
from stix.indicator.valid_time import ValidTime
@ -109,9 +109,6 @@ class StixBuilder(object):
'course-of-action': 'parse_course_of_action',
'vulnerability': 'parse_vulnerability',
'weakness': 'parse_weakness'}
self.types_mapping = {CourseOfAction: 'add_course_of_action',
ThreatActor: 'add_threat_actor',
TTP: 'add_ttp'}
self.objects_mapping = {"asn": 'parse_asn_object',
"credential": 'parse_credential_object',
"domain-ip": 'parse_domain_ip_object',
@ -153,8 +150,9 @@ class StixBuilder(object):
stix_packages = [' {}\n'.format(s) for s in stix_packages]
else:
stix_packages = ['{"package": %s}' % s for s in stix_packages]
self.stix_package = separator.join(stix_packages) if len(stix_packages) > 1 else stix_packages[0]
self.saveFile()
outputfile = "{}.out".format(self.filename)
with open(outputfile, 'wt', encoding='utf-8') as f:
f.write(separator.join(stix_packages) if len(stix_packages) > 1 else stix_packages[0])
print(json.dumps({'success': 1}))
except Exception as e:
print(json.dumps({'error': e.__str__()}))
@ -163,42 +161,22 @@ class StixBuilder(object):
self.objects_to_parse = defaultdict(dict)
self.misp_event = event
self.header_comment = []
package_name = "{}:STIXPackage-{}".format(self.orgname, self.misp_event['uuid'])
stix_package = STIXPackage(id_=package_name, timestamp=self.get_datetime_from_timestamp(self.misp_event['timestamp']))
stix_package.version = "1.1.1"
self.create_stix_package()
self.create_incident()
self.generate_stix_objects()
for uuid, ttp in self.ttps.items():
self.parse_ttp_references(uuid, ttp)
self.stix_package.add_ttp(ttp)
if hasattr(self, 'attributed_threat_actors'):
self.incident.attributed_threat_actors = self.attributed_threat_actors
self.stix_package.add_incident(self.incident)
stix_header = STIXHeader()
stix_header.title = "Export from {} MISP".format(self.namespace_prefix)
stix_header.package_intents = "Threat Report"
self.create_incident(self.orgname)
self.generate_stix_objects()
if self.galaxies.get('course_of_action'):
for course_of_action in self.galaxies['course_of_action']:
stix_package.add_course_of_action(course_of_action)
rcoa = CourseOfAction(idref=course_of_action.id_, timestamp=course_of_action.timestamp)
self.incident.add_coa_taken(rcoa)
if self.galaxies.get('threat_actor'):
ata = AttributedThreatActors()
for threat_actor in self.galaxies['threat_actor']:
stix_package.add_threat_actor(threat_actor)
rta = ThreatActor(idref=threat_actor.id_, timestamp=threat_actor.timestamp)
related_ta = RelatedThreatActor(rta, relationship='ThreatActor')
ata.append(related_ta)
self.incident.attributed_threat_actors = ata
stix_package.add_incident(self.incident)
for ttp in self.ttps:
stix_package.add_ttp(ttp)
for uuid, ttp in self.ttps_from_objects.items():
self.parse_ttp_references(uuid, ttp)
getattr(stix_package, self.types_mapping[type(ttp)])(ttp)
if self.header_comment and len(self.header_comment) == 1:
stix_header.description = self.header_comment[0]
stix_package.stix_header = stix_header
return stix_package
def saveFile(self):
outputfile = "{}.out".format(self.filename)
with open(outputfile, 'wt', encoding='utf-8') as f:
f.write(self.stix_package)
self.stix_package.stix_header = stix_header
return self.stix_package
def generate_stix_objects(self):
self.history = History()
@ -218,9 +196,7 @@ class StixBuilder(object):
self.incident.information_source = self.set_src()
self.orgc_name = self.misp_event['Orgc'].get('name')
self.incident.reporter = self.set_rep()
self.galaxies = defaultdict(list)
self.ttps = []
self.ttps_from_objects = {}
self.ttps = {}
self.ttp_references = {}
self.resolve_galaxies()
self.resolve_attributes()
@ -228,8 +204,14 @@ class StixBuilder(object):
if self.history.history_items:
self.incident.history = self.history
def create_incident(self, org):
incident_id = "{}:Incident-{}".format(org, self.misp_event['uuid'])
def create_stix_package(self):
package_name = "{}:STIXPackage-{}".format(self.orgname, self.misp_event['uuid'])
stix_package = STIXPackage(id_=package_name, timestamp=self.get_datetime_from_timestamp(self.misp_event['timestamp']))
stix_package.version = "1.1.1"
self.stix_package = stix_package
def create_incident(self):
incident_id = "{}:Incident-{}".format(self.orgname, self.misp_event['uuid'])
incident = Incident(id_=incident_id, title=self.misp_event['info'])
timestamp = self.get_datetime_from_timestamp(self.misp_event['publish_timestamp'])
incident.timestamp = timestamp
@ -343,8 +325,8 @@ class StixBuilder(object):
def parse_ttp_references(self, uuid, ttp):
if uuid in self.ttp_references:
for referenced_uuid, relationship in self.ttp_references[uuid]:
if referenced_uuid in self.ttps_from_objects and isinstance(self.ttps_from_objects[referenced_uuid], TTP):
referenced_ttp = self.ttps_from_objects[referenced_uuid]
if referenced_uuid in self.ttps and isinstance(self.ttps[referenced_uuid], TTP):
referenced_ttp = self.ttps[referenced_uuid]
ttp.add_related_ttp(self.append_ttp_from_object(relationship, referenced_ttp))
def create_indicator(self, misp_object, observable):
@ -355,7 +337,7 @@ class StixBuilder(object):
handling = self.set_handling(tuple(tags))
if handling is not None:
indicator.handling = handling
title = "{} (MISP Object #{})".format(misp_object['name'], misp_object['id'])
title = "{}: {} (MISP Object)".format(misp_object['meta-category'], misp_object['name'])
indicator.title = title
indicator.description = misp_object['comment'] if misp_object.get('comment') else title
indicator.add_indicator_type("Malware Artifacts")
@ -363,12 +345,6 @@ class StixBuilder(object):
indicator.add_observable(observable)
return indicator
def add_related_indicators(self):
for rindicator in self.incident.related_indicators:
for ttp in self.ttps:
ittp = TTP(idref=ttp.id_, timestamp=ttp.timestamp)
rindicator.item.add_indicated_ttp(ittp)
def handle_attribute(self, attribute):
observable = self.generate_observable(attribute)
if observable:
@ -377,7 +353,7 @@ class StixBuilder(object):
indicator.add_indicator_type("Malware Artifacts")
try:
indicator.add_indicator_type(misp_indicator_type[attribute['type']])
except Exception:
except KeyError:
pass
indicator.add_valid_time_position(ValidTime())
indicator.add_observable(observable)
@ -412,9 +388,10 @@ class StixBuilder(object):
self.header_comment.append(attribute['value'])
elif attribute_category == "Payload type":
ttp = self.generate_ttp(attribute)
self.incident.leveraged_ttps.append(self.append_ttp(attribute_category, ttp))
self.append_ttp(ttp, attribute['category'], attribute['uuid'])
elif attribute_category == "Attribution":
self.ttps_from_objects[attribute['uuid']] = self.generate_threat_actor(attribute)
threat_actor = self.generate_threat_actor(attribute)
self.append_threat_actor(threat_actor, attribute['category'])
else:
entry_line = "attribute[{}][{}]: {}".format(attribute_category, attribute['type'], attribute['value'])
self.add_journal_entry(entry_line)
@ -496,7 +473,7 @@ class StixBuilder(object):
handling = self.set_handling(tags)
if handling is not None:
indicator.handling = handling
indicator.title = "{}: {} (MISP Attribute #{})".format(attribute['category'], attribute['value'], attribute['id'])
indicator.title = "{}: {} (MISP Attribute)".format(attribute['category'], attribute['value'])
indicator.description = indicator.title
confidence_description = "Derived from MISP's IDS flag. If an attribute is marked for IDS exports, the confidence will be high, otherwise none"
confidence_value = confidence_mapping.get(attribute['to_ids'], None)
@ -608,7 +585,7 @@ class StixBuilder(object):
attribute_value = attribute['value']
ta = ThreatActor(timestamp=self.get_datetime_from_timestamp(attribute['timestamp']))
ta.id_ = "{}:ThreatActor-{}".format(self.orgname, attribute['uuid'])
ta.title = "{}: {} (MISP Attribute #{})".format(attribute['category'], attribute_value, attribute['id'])
ta.title = "{}: {} (MISP Attribute)".format(attribute['category'], attribute_value)
description = attribute_value
if attribute.get('comment'):
description += " ({})".format(attribute['comment'])
@ -650,7 +627,7 @@ class StixBuilder(object):
ET.title = "Vulnerability {}".format(attribute['value'])
ET.add_vulnerability(vulnerability)
ttp.add_exploit_target(ET)
self.ttps_from_objects[uuid] = ttp
self.ttps[uuid] = ttp
def parse_asn_object(self, misp_object):
to_ids, attributes_dict = self.create_attributes_dict(misp_object['Attribute'])
@ -678,31 +655,32 @@ class StixBuilder(object):
for relation, feature in attack_pattern_object_mapping.items():
if relation in attributes_dict:
setattr(attack_pattern, feature, attributes_dict[relation])
if attack_pattern.capec_id and not attack_pattern.capec_id.startswith('CAPEC'):
attack_pattern.capec_id = 'CAPEC-{}'.format(attack_pattern.capec_id)
if misp_object.get('ObjectReference'):
references = ((reference['referenced_uuid'], reference['relationship_type']) for reference in misp_object['ObjectReference'])
self.ttp_references[uuid] = references
behavior = Behavior()
behavior.add_attack_pattern(attack_pattern)
ttp.behavior = behavior
self.ttps_from_objects[uuid] = ttp
self.append_ttp(ttp, misp_object['meta-category'], uuid)
def parse_attack_pattern_galaxy(self, galaxy):
galaxy_name = galaxy['name']
ttp = self.create_ttp_from_galaxy(galaxy['GalaxyCluster'][0]['collection_uuid'], galaxy_name)
behavior = Behavior()
for cluster in galaxy['GalaxyCluster']:
uuid = cluster['collection_uuid']
ttp = self.create_ttp_from_galaxy(uuid, galaxy_name, cluster['id'], cluster['type'])
attack_pattern = AttackPattern()
attack_pattern.id_ = "{}:AttackPattern-{}".format(self.namespace_prefix, uuid)
attack_pattern.title = "{}: {}".format(galaxy_name, cluster['value'])
attack_pattern.id_ = "{}:AttackPattern-{}".format(self.namespace_prefix, cluster['uuid'])
attack_pattern.title = cluster['value']
attack_pattern.description = cluster['description']
if cluster['meta'].get('external_id'):
external_id = cluster['meta']['external_id'][0]
if external_id.startswith('CAPEC'):
attack_pattern.capec_id = external_id
behavior = Behavior()
behavior.add_attack_pattern(attack_pattern)
ttp.behavior = behavior
self.incident.add_leveraged_ttps(self.append_ttp(galaxy_name, ttp))
ttp.behavior = behavior
self.stix_package.add_ttp(ttp)
def parse_credential_object(self, misp_object):
to_ids, attributes_dict = self.create_attributes_dict_multiple(misp_object['Attribute'])
@ -774,17 +752,15 @@ class StixBuilder(object):
for feature in course_of_action_object_keys:
if feature in attributes_dict:
setattr(course_of_action, feature, attributes_dict[feature])
self.ttps_from_objects[uuid] = course_of_action
self.append_course_of_action(course_of_action)
def parse_course_of_action_galaxy(self, galaxy):
galaxy_name = galaxy['name']
for cluster in galaxy['GalaxyCluster']:
uuid = cluster['collection_uuid']
course_of_action = CourseOfAction()
course_of_action.id_ = "{}:CourseOfAction-{}".format(self.namespace_prefix, uuid)
course_of_action.title = "{}: {}".format(galaxy_name, cluster['value'])
course_of_action.id_ = "{}:CourseOfAction-{}".format(self.namespace_prefix, cluster['uuid'])
course_of_action.title = cluster['value']
course_of_action.description = cluster['description']
self.galaxies['course_of_action'].append(course_of_action)
self.stix_package.add_course_of_action(course_of_action)
@staticmethod
def parse_credential_types(credential_types):
@ -825,8 +801,11 @@ class StixBuilder(object):
email_object.attachments = Attachments()
for attachment in attributes_dict['attachment']:
attachment_file = self.create_file_attachment(attachment['value'], attachment['uuid'])
email_object.add_related(attachment_file, "Contains", inline=True)
email_object.attachments.append(attachment_file.parent.id_)
related_file = RelatedObject(relationship='Contains', inline=True,
id_=attachment_file.parent.id_,
properties=attachment_file)
email_object.parent.related_objects.append(related_file)
email_object.attachments.append(related_file.id_)
uuid = misp_object['uuid']
email_object.header = email_header
email_object.parent.id_ = "{}:EmailMessageObject-{}".format(self.namespace_prefix, uuid)
@ -883,21 +862,20 @@ class StixBuilder(object):
def parse_malware_galaxy(self, galaxy):
galaxy_name = galaxy['name']
ttp = self.create_ttp_from_galaxy(galaxy['GalaxyCluster'][0]['collection_uuid'], galaxy_name)
behavior = Behavior()
for cluster in galaxy['GalaxyCluster']:
uuid = cluster['collection_uuid']
ttp = self.create_ttp_from_galaxy(uuid, galaxy_name, cluster['id'], cluster['type'])
malware = MalwareInstance()
malware.id_ = "{}:MalwareInstance-{}".format(self.namespace_prefix, uuid)
malware.title = "{}: {}".format(galaxy_name, cluster['value'])
malware.id_ = "{}:MalwareInstance-{}".format(self.namespace_prefix, cluster['uuid'])
malware.title = cluster['value']
if cluster.get('description'):
malware.description = cluster['description']
if cluster['meta'].get('synonyms'):
for synonym in cluster['meta']['synonyms']:
malware.add_name(synonym)
behavior = Behavior()
behavior.add_malware_instance(malware)
ttp.behavior = behavior
self.incident.add_leveraged_ttps(self.append_ttp(galaxy_name, ttp))
ttp.behavior = behavior
self.stix_package.add_ttp(ttp)
def parse_network_connection_object(self, misp_object):
to_ids, attributes_dict = self.create_attributes_dict(misp_object['Attribute'])
@ -999,12 +977,10 @@ class StixBuilder(object):
return to_ids, observable
def parse_threat_actor_galaxy(self, galaxy):
galaxy_name = galaxy['name']
for cluster in galaxy['GalaxyCluster']:
uuid = cluster['collection_uuid']
threat_actor = ThreatActor()
threat_actor.id_ = "{}:ThreatActor-{}".format(self.namespace_prefix, uuid)
threat_actor.title = "{}: {}".format(galaxy_name, cluster['value'])
threat_actor.id_ = "{}:ThreatActor-{}".format(self.namespace_prefix, cluster['uuid'])
threat_actor.title = cluster['value']
if cluster.get('description'):
threat_actor.description = cluster['description']
meta = cluster['meta']
@ -1015,25 +991,23 @@ class StixBuilder(object):
threat_actor.add_intended_effect(effect)
else:
threat_actor.add_intended_effect(meta['cfr-type-of-incident'])
self.galaxies['threat_actor'].append(threat_actor)
self.stix_package.add_threat_actor(threat_actor)
def parse_tool_galaxy(self, galaxy):
galaxy_name = galaxy['name']
ttp = self.create_ttp_from_galaxy(galaxy['GalaxyCluster'][0]['collection_uuid'], galaxy_name)
tools = Tools()
for cluster in galaxy['GalaxyCluster']:
uuid = cluster['collection_uuid']
ttp = self.create_ttp_from_galaxy(uuid, galaxy_name, cluster['id'], cluster['type'])
tool = ToolInformation()
tool.id_ = "{}:ToolInformation-{}".format(self.namespace_prefix, uuid)
name = "Mitre Tool" if galaxy['type'] == 'mitre-tool' else galaxy['name']
tool.name = "{}: {}".format(name, cluster['value'])
tool.id_ = "{}:ToolInformation-{}".format(self.namespace_prefix, cluster['uuid'])
tool.name = cluster['value']
if cluster.get('description'):
tool.description = cluster['description']
tools = Tools()
tools.tool = tool
resource = Resource()
resource.tools = tools
ttp.resources = resource
self.incident.add_leveraged_ttps(self.append_ttp(galaxy_name, ttp))
tools.append(tool)
resource = Resource()
resource.tools = tools
ttp.resources = resource
self.stix_package.add_ttp(ttp)
def parse_url_object(self, misp_object):
observables = []
@ -1091,15 +1065,15 @@ class StixBuilder(object):
ET.id_ = "{}:ExploitTarget-{}".format(self.orgname, uuid)
ET.add_vulnerability(vulnerability)
ttp.add_exploit_target(ET)
self.ttps_from_objects[uuid] = ttp
self.append_ttp(ttp, misp_object['meta-category'], uuid)
def parse_vulnerability_galaxy(self, galaxy):
galaxy_name = galaxy['name']
ttp = self.create_ttp_from_galaxy(galaxy['GalaxyCluster'][0]['collection_uuid'], galaxy_name)
exploit_target = ExploitTarget()
for cluster in galaxy['GalaxyCluster']:
uuid = cluster['collection_uuid']
ttp = self.create_ttp_from_galaxy(uuid, galaxy_name, cluster['id'], cluster['type'])
vulnerability = Vulnerability()
vulnerability.id_ = "{}:Vulnerability-{}".format(self.namespace_prefix, uuid)
vulnerability.id_ = "{}:Vulnerability-{}".format(self.namespace_prefix, cluster['uuid'])
vulnerability.title = cluster['value']
vulnerability.description = cluster['description']
if cluster['meta'].get('aliases'):
@ -1107,11 +1081,9 @@ class StixBuilder(object):
if cluster['meta'].get('refs'):
for reference in cluster['meta']['refs']:
vulnerability.add_reference(reference)
ET = ExploitTarget()
ET.id_ = "{}:ExploitTarget-{}".format(self.namespace_prefix, uuid)
ET.add_vulnerability(vulnerability)
ttp.add_exploit_target(ET)
self.incident.add_leveraged_ttps(self.append_ttp(galaxy_name, ttp))
exploit_target.add_vulnerability(vulnerability)
ttp.add_exploit_target(exploit_target)
self.stix_package.add_ttp(ttp)
def parse_weakness(self, misp_object):
ttp = self.create_ttp_from_object(misp_object)
@ -1128,7 +1100,7 @@ class StixBuilder(object):
ET.id_ = "{}:ExploitTarget-{}".format(self.orgname, uuid)
ET.add_weakness(weakness)
ttp.add_exploit_target(ET)
self.ttps_from_objects[uuid] = ttp
self.append_ttp(ttp, misp_object['meta-category'], uuid)
def parse_whois(self, misp_object):
to_ids, attributes_dict = self.create_attributes_dict_multiple(misp_object['Attribute'])
@ -1352,7 +1324,7 @@ class StixBuilder(object):
ciq_identity.specification = identity_spec
ciq_identity.id_ = "{}:Identity-{}".format(self.orgname, attribute['uuid'])
# is this a good idea?
ciq_identity.name = "{}: {} (MISP Attribute #{})".format(attribute_type, attribute_value, attribute['id'])
ciq_identity.name = "{}: {} (MISP Attribute)".format(attribute['category'], attribute_value)
return ciq_identity
def resolve_malware_sample(self, attribute):
@ -1440,11 +1412,25 @@ class StixBuilder(object):
except AttributeError:
self.incident.information_source.references = [reference]
def append_ttp(self, category, ttp):
self.ttps.append(ttp)
def append_course_of_action(self, course_of_action):
coa_taken = COATaken(course_of_action)
self.incident.add_coa_taken(coa_taken)
def append_threat_actor(self, threat_actor, category):
rta = ThreatActor(idref=threat_actor.id_, timestamp=threat_actor.timestamp)
related_ta = RelatedThreatActor(rta, relationship=category)
try:
self.attributed_threat_actors.append(related_ta)
except AttributeError:
self.attributed_threat_actors = AttributedThreatActors()
self.attributed_threat_actors.append(related_ta)
self.stix_package.add_threat_actor(threat_actor)
def append_ttp(self, ttp, category, uuid):
rttp = TTP(idref=ttp.id_, timestamp=ttp.timestamp)
related_ttp = RelatedTTP(rttp, relationship=category)
return related_ttp
self.incident.add_leveraged_ttps(related_ttp)
self.ttps[uuid] = ttp
def append_ttp_from_object(self, category, ttp):
rttp = TTP(idref=ttp.id_, timestamp=ttp.timestamp)
@ -1458,13 +1444,13 @@ class StixBuilder(object):
handling = self.set_handling(tags)
if handling is not None:
ttp.handling = handling
ttp.title = "{}: {} (MISP Attribute #{})".format(attribute['category'], attribute['value'], attribute['id'])
ttp.title = "{}: {} (MISP Attribute)".format(attribute['category'], attribute['value'])
return ttp
def create_ttp_from_galaxy(self, uuid, galaxy_name, cluster_id, cluster_type):
def create_ttp_from_galaxy(self, uuid, galaxy_name):
ttp = TTP()
ttp.id_ = "{}:TTP-{}".format(self.namespace_prefix, uuid)
ttp.title = "{}: {} (MISP GalaxyCluster #{})".format(galaxy_name, cluster_type, cluster_id)
ttp.title = "{} (MISP Galaxy)".format(galaxy_name)
return ttp
def create_ttp_from_object(self, misp_object):
@ -1474,7 +1460,7 @@ class StixBuilder(object):
handling = self.set_handling(tags)
if handling is not None:
ttp.handling = handling
ttp.title = "{}: {} (MISP Object #{})".format(misp_object['meta-category'], misp_object['name'], misp_object['id'])
ttp.title = "{}: {} (MISP Object)".format(misp_object['meta-category'], misp_object['name'])
return ttp
def create_attributes_dict(self, attributes, with_uuid=False):

View File

@ -21,11 +21,11 @@ misp_cybox_name = {"domain" : "DomainName", "hostname" : "Hostname", "url" : "UR
"named pipe" : "Pipe", "link" : "URI", "network-connection": "NetworkConnection", "windows-service-name": "WinService"}
cybox_name_attribute = {"DomainName" : "value", "Hostname" : "hostname_value", "URI" : "value", "AutonomousSystem" : "number",
"Pipe" : "name", "Mutex" : "name", "WinService": "name"}
misp_indicator_type = {"AS" : "", "mutex" : "Host Characteristics", "named pipe" : "Host Characteristics",
misp_indicator_type = {"mutex" : "Host Characteristics", "named pipe" : "Host Characteristics",
"email-attachment": "Malicious E-mail", "url" : "URL Watchlist"}
misp_indicator_type.update(dict.fromkeys(list(hash_type_attributes["single"]) + list(hash_type_attributes["composite"]) + ["filename"] + ["attachment"], "File Hash Watchlist"))
misp_indicator_type.update(dict.fromkeys(["email-src", "email-dst", "email-subject", "email-reply-to", "email-attachment"], "Malicious E-mail"))
misp_indicator_type.update(dict.fromkeys(["ip-src", "ip-dst", "ip-src|port", "ip-dst|port"], "IP Watchlist"))
misp_indicator_type.update(dict.fromkeys(["AS", "ip-src", "ip-dst", "ip-src|port", "ip-dst|port"], "IP Watchlist"))
misp_indicator_type.update(dict.fromkeys(["domain", "domain|ip", "hostname"], "Domain Watchlist"))
misp_indicator_type.update(dict.fromkeys(["regkey", "regkey|value"], "Host Characteristics"))
cybox_validation = {"AutonomousSystem": "isInt"}
@ -39,7 +39,7 @@ galaxy_types_mapping.update(dict.fromkeys(['mitre-course-of-action',
'mitre-mobile-attack-course-of-action'],
'parse_course_of_action_galaxy'))
galaxy_types_mapping.update(dict.fromkeys(['android', 'banker', 'stealer', 'backdoor', 'ransomware', 'mitre-malware',
'mitre-enterprise-attack-malware', 'mitre-mobile-attack-malware'],
'malpedia', 'mitre-enterprise-attack-malware', 'mitre-mobile-attack-malware'],
'parse_malware_galaxy'))
galaxy_types_mapping.update(dict.fromkeys(['threat-actor', 'microsoft-activity-group'],
'parse_threat_actor_galaxy'))

File diff suppressed because it is too large Load Diff

View File

@ -1,297 +1,140 @@
def attribute_data_observable(data):
return {'type': 'artifact', 'payload_bin': data}
misp_hash_types = ("authentihash", "ssdeep", "imphash", "md5", "sha1", "sha224",
"sha256", "sha384", "sha512", "sha512/224","sha512/256","tlsh")
attack_pattern_galaxies_list = ('mitre-attack-pattern', 'mitre-enterprise-attack-attack-pattern',
'mitre-mobile-attack-attack-pattern', 'mitre-pre-attack-attack-pattern')
course_of_action_galaxies_list = ('mitre-course-of-action', 'mitre-enterprise-attack-course-of-action',
'mitre-mobile-attack-course-of-action')
intrusion_set_galaxies_list = ('mitre-enterprise-attack-intrusion-set', 'mitre-mobile-attack-intrusion-set',
'mitre-pre-attack-intrusion-set', 'mitre-intrusion-set')
malware_galaxies_list = ('android', 'banker', 'stealer', 'backdoor', 'ransomware', 'mitre-malware',
'mitre-enterprise-attack-malware', 'mitre-mobile-attack-malware')
threat_actor_galaxies_list = ('threat-actor', 'microsoft-activity-group')
tool_galaxies_list = ('botnet', 'rat', 'exploit-kit', 'tds', 'tool', 'mitre-tool',
'mitre-enterprise-attack-tool', 'mitre-mobile-attack-tool')
def attribute_data_pattern(data):
return "artifact:payload_bin = '{}'".format(data)
def define_address_type(address):
if ':' in address:
return 'ipv6-addr'
return 'ipv4-addr'
def observable_as(_, attribute_value):
return {'0': {'type': 'autonomous-system', 'number': attribute_value}}
def pattern_as(_, attribute_value):
return "[autonomous-system:number = '{}']".format(attribute_value)
def observable_attachment(*args):
observable = observable_file(args[0], args[1])
if len(args) == 3:
observable['0']['content_ref'] = '0'
return {'0': attribute_data_observable(args[2]), '1': observable['0']}
return observable
def pattern_attachment(*args):
pattern = pattern_file(args[0], args[1])[1:-1]
if len(args) == 3:
pattern += " AND {}".format(attribute_data_pattern(args[2]))
return "[{}]".format(pattern)
def observable_domain(_, attribute_value):
return {'0': {'type': 'domain-name', 'value': attribute_value}}
def pattern_domain(_, attribute_value):
return "[domain-name:value = '{}']".format(attribute_value)
def observable_domain_ip(_, attribute_value):
address_type = define_address_type(attribute_value)
domain_value, ip_value = attribute_value.split('|')
domain = observable_domain(_, domain_value)
domain['0']['resolves_to_refs'] = '1'
domain['1'] = {'type': address_type, 'value': ip_value}
return domain
def pattern_domain_ip(_, attribute_value):
domain_value, ip_value = attribute_value.split('|')
domain = pattern_domain(_, domain_value)[1:-1]
domain += " AND domain-name:resolves_to_refs[*].value = '{}'".format(ip_value)
return "[{}]".format(domain)
def observable_email_address(attribute_type, attribute_value):
email_type = "from_ref" if 'src' in attribute_type else "to_refs"
return {'0': {'type': 'email-addr', 'value': attribute_value},
'1': {'type': 'email-message', email_type: '0', 'is_multipart': 'false'}}
def pattern_email_address(attribute_type, attribute_value):
email_type = "from_ref" if 'src' in attribute_type else "to_refs"
return "[email-message:{} = '{}']".format(email_type, attribute_value)
def observable_email_attachment(_, attribute_value):
observable = observable_file(_, attribute_value)
observable['1'] = {"type": "email-message", 'is_multipart': 'true',
"body_multipart": [{"content_disposition": "attachment; filename=''".format(attribute_value), "body_raw_ref": "0"}]}
return observable
def pattern_email_attachment(_, attribute_value):
return "[email-message:body_multipart[*].body_raw_ref.name = '{}']".format(attribute_value)
def observable_email_message(attribute_type, attribute_value):
email_type = attribute_type.split('-')[1]
return {'0': {'type': 'email-message', email_type: attribute_value, 'is_multipart': 'false'}}
def pattern_email_message(attribute_type, attribute_value):
email_type = attribute_type.split('-')[1]
return "[email-message:{} = '{}']".format(email_type, attribute_value)
def observable_file(_, attribute_value):
return {'0': {'type': 'file', 'name': attribute_value}}
def pattern_file(_, attribute_value):
return "[file:name = '{}']".format(attribute_value)
def observable_file_hash(attribute_type, attribute_value):
_, hash_type = attribute_type.split('|')
value1, value2 = attribute_value.split('|')
return {'0': {'type': 'file', 'name': value1, 'hashes': {hash_type: value2}}}
def pattern_file_hash(attribute_type, attribute_value):
_, hash_type = attribute_type.split('|')
value1, value2 = attribute_value.split('|')
return "[file:name = '{0}' AND file:hashes.'{1}' = '{2}']".format(value1, hash_type, value2)
def observable_hash(attribute_type, attribute_value):
return {'0': {'type': 'file', 'hashes': {attribute_type: attribute_value}}}
def pattern_hash(attribute_type, attribute_value):
return "[file:hashes.'{}' = '{}']".format(attribute_type, attribute_value)
def observable_hostname_port(_, attribute_value):
hostname, port = attribute_value.split('|')
hostname_port = observable_domain(_, hostname)
hostname_port[1] = observable_port(_, port)['0']
return hostname_port
def pattern_hostname_port(_, attribute_value):
hostname, port = attribute_value.split('|')
return "[{} AND {}]".format(pattern_domain(_, hostname)[1:-1], pattern_port(_, port)[1:-1])
def observable_ip(attribute_type, attribute_value):
ip_type = attribute_type.split('-')[1]
address_type = define_address_type(attribute_value)
return {'0': {'type': address_type, 'value': attribute_value},
'1': {'type': 'network-traffic', '{}_ref'.format(ip_type): '0',
'protocols': [address_type.split('-')[0]]}}
def pattern_ip(attribute_type, attribute_value):
ip_type = attribute_type.split('-')[1]
address_type = define_address_type(attribute_value)
return "[network-traffic:{0}_ref.type = '{1}' AND network-traffic:{0}_ref.value = '{2}']".format(ip_type, address_type, attribute_value)
def observable_ip_port(attribute_type, attribute_value):
ip_type, _ = attribute_type.split('|')
ip, port = attribute_value.split('|')
ip_port = observable_ip(ip_type, ip)
port_type = "{}_port".format(ip_type.split('-')[1])
ip_port['1'][port_type] = port
return ip_port
def pattern_ip_port(attribute_type, attribute_value):
ip_type, _ = attribute_type.split('|')
ip, port = attribute_value.split('|')
port_type = "{}_port".format(ip_type.split('-')[1])
return "[network-traffic:{} = '{}' AND {}]".format(port_type, port, pattern_ip(ip_type, ip)[1:-1])
def observable_mac_address(_, attribute_value):
return {'0': {'type': 'mac-addr', 'value': attribute_value.lower()}}
def pattern_mac_address(_, attribute_value):
return "[mac-addr:value = '{}']".format(attribute_value.lower())
def observable_malware_sample(*args):
observable = observable_file_hash("filename|md5", args[1])
if len(args) == 3:
observable['0']['content_ref'] = '0'
return {'0': attribute_data_observable(args[2]), '1': observable['0']}
return observable
def pattern_malware_sample(*args):
pattern = pattern_file_hash("filename|md5", args[1])[1:-1]
if len(args) == 3:
pattern += " AND {}".format(attribute_data_pattern(args[2]))
return "[{}]".format(pattern)
def observable_mutex(_, attribute_value):
return {'0': {'type': 'mutex', 'name': attribute_value}}
def pattern_mutex(_, attribute_value):
return "[mutex:name = '{}']".format(attribute_value)
def observable_port(_, attribute_value):
return {'0': {'type': 'network-traffic', 'dst_port': attribute_value, 'protocols': []}}
def pattern_port(_, attribute_value):
return "[network-traffic:dst_port = '{}']".format(attribute_value)
def observable_regkey(_, attribute_value):
return {'0': {'type': 'windows-registry-key', 'key': attribute_value.strip()}}
def pattern_regkey(_, attribute_value):
if '\\\\' not in attribute_value:
attribute_value = attribute_value.replace('\\', '\\\\')
return "[windows-registry-key:key = '{}']".format(attribute_value.strip())
def observable_regkey_value(_, attribute_value):
from stix2 import WindowsRegistryValueType
key, value = attribute_value.split('|')
regkey = observable_regkey(_, key)
regkey['0']['values'] = WindowsRegistryValueType(**{'name': value.strip()})
return regkey
def pattern_regkey_value(_, attribute_value):
key, value = attribute_value.split('|')
if '\\\\' not in value:
value = value.replace('\\', '\\\\')
regkey = pattern_regkey(_, key)[1:-1]
regkey += " AND windows-registry-key:values = '{}'".format(value.strip())
return "[{}]".format(regkey)
def observable_reply_to(_, attribute_value):
return {'0': {'type': 'email-addr', 'value': attribute_value},
'1': {'type': 'email-message', 'additional_header_fields': {'Reply-To': '0'}, 'is_multipart': 'false'}}
def pattern_reply_to(_, attribute_value):
return "[email-message:additional_header_fields.reply_to = '{}']".format(attribute_value)
def observable_url(_, attribute_value):
return {'0': {'type': 'url', 'value': attribute_value}}
def pattern_url(_, attribute_value):
return "[url:value = '{}']".format(attribute_value)
def observable_x509(_, attribute_value):
return {'0': {'type': 'x509-certificate', 'hashes': {'sha1': attribute_value}}}
def pattern_x509(_, attribute_value):
return "[x509-certificate:hashes = '{}']".format(attribute_value)
def return_vulnerability(name):
return {'source_name': 'cve', 'external_id': name}
galaxies_mapping = {'branded-vulnerability': ['vulnerability', 'add_vulnerability_from_galaxy']}
galaxies_mapping.update(dict.fromkeys(attack_pattern_galaxies_list, ['attack-pattern', 'add_attack_pattern']))
galaxies_mapping.update(dict.fromkeys(course_of_action_galaxies_list, ['course-of-action', 'add_course_of_action']))
galaxies_mapping.update(dict.fromkeys(intrusion_set_galaxies_list, ['intrusion-set', 'add_intrusion_set']))
galaxies_mapping.update(dict.fromkeys(malware_galaxies_list, ['malware', 'add_malware']))
galaxies_mapping.update(dict.fromkeys(threat_actor_galaxies_list, ['threat-actor', 'add_threat_actor']))
galaxies_mapping.update(dict.fromkeys(tool_galaxies_list, ['tool', 'add_tool']))
mispTypesMapping = {
'link': {'to_call': 'handle_link'},
'vulnerability': {'to_call': 'add_vulnerability', 'vulnerability_args': return_vulnerability},
'md5': {'to_call': 'handle_usual_type', 'observable': observable_hash, 'pattern': pattern_hash},
'sha1': {'to_call': 'handle_usual_type', 'observable': observable_hash, 'pattern': pattern_hash},
'sha256': {'to_call': 'handle_usual_type', 'observable': observable_hash, 'pattern': pattern_hash},
'filename': {'to_call': 'handle_usual_type', 'observable': observable_file, 'pattern': pattern_file},
'filename|md5': {'to_call': 'handle_usual_type', 'observable': observable_file_hash, 'pattern': pattern_file_hash},
'filename|sha1': {'to_call': 'handle_usual_type', 'observable': observable_file_hash, 'pattern': pattern_file_hash},
'filename|sha256': {'to_call': 'handle_usual_type', 'observable': observable_file_hash, 'pattern': pattern_file_hash},
'ip-src': {'to_call': 'handle_usual_type', 'observable': observable_ip, 'pattern': pattern_ip},
'ip-dst': {'to_call': 'handle_usual_type', 'observable': observable_ip, 'pattern': pattern_ip},
'hostname': {'to_call': 'handle_usual_type', 'observable': observable_domain, 'pattern': pattern_domain},
'domain': {'to_call': 'handle_usual_type', 'observable': observable_domain, 'pattern': pattern_domain},
'domain|ip': {'to_call': 'handle_usual_type', 'observable': observable_domain_ip, 'pattern': pattern_domain_ip},
'email-src': {'to_call': 'handle_usual_type', 'observable': observable_email_address, 'pattern': pattern_email_address},
'email-dst': {'to_call': 'handle_usual_type', 'observable': observable_email_address, 'pattern': pattern_email_address},
'email-subject': {'to_call': 'handle_usual_type', 'observable': observable_email_message, 'pattern': pattern_email_message},
'email-body': {'to_call': 'handle_usual_type', 'observable': observable_email_message, 'pattern': pattern_email_message},
'email-attachment': {'to_call': 'handle_usual_type', 'observable': observable_email_attachment, 'pattern': pattern_email_attachment},
'url': {'to_call': 'handle_usual_type', 'observable': observable_url, 'pattern': pattern_url},
'regkey': {'to_call': 'handle_usual_type', 'observable': observable_regkey, 'pattern': pattern_regkey},
'regkey|value': {'to_call': 'handle_usual_type', 'observable': observable_regkey_value, 'pattern': pattern_regkey_value},
'malware-sample': {'to_call': 'handle_usual_type', 'observable': observable_malware_sample, 'pattern': pattern_malware_sample},
'mutex': {'to_call': 'handle_usual_type', 'observable': observable_mutex, 'pattern': pattern_mutex},
'uri': {'to_call': 'handle_usual_type', 'observable': observable_url, 'pattern': pattern_url},
'authentihash': {'to_call': 'handle_usual_type', 'observable': observable_hash, 'pattern': pattern_hash},
'ssdeep': {'to_call': 'handle_usual_type', 'observable': observable_hash, 'pattern': pattern_hash},
'imphash': {'to_call': 'handle_usual_type', 'observable': observable_hash, 'pattern': pattern_hash},
'pehash': {'to_call': 'handle_usual_type', 'observable': observable_hash, 'pattern': pattern_hash},
'impfuzzy': {'to_call': 'handle_usual_type', 'observable': observable_hash, 'pattern': pattern_hash},
'sha224': {'to_call': 'handle_usual_type', 'observable': observable_hash, 'pattern': pattern_hash},
'sha384': {'to_call': 'handle_usual_type', 'observable': observable_hash, 'pattern': pattern_hash},
'sha512': {'to_call': 'handle_usual_type', 'observable': observable_hash, 'pattern': pattern_hash},
'sha512/224': {'to_call': 'handle_usual_type', 'observable': observable_hash, 'pattern': pattern_hash},
'sha512/256': {'to_call': 'handle_usual_type', 'observable': observable_hash, 'pattern': pattern_hash},
'tlsh': {'to_call': 'handle_usual_type', 'observable': observable_hash, 'pattern': pattern_hash},
'cdhash': {'to_call': 'handle_usual_type', 'observable': observable_hash, 'pattern': pattern_hash},
'filename|authentihash': {'to_call': 'handle_usual_type', 'observable': observable_file_hash, 'pattern': pattern_file_hash},
'filename|ssdeep': {'to_call': 'handle_usual_type', 'observable': observable_file_hash, 'pattern': pattern_file_hash},
'filename|imphash': {'to_call': 'handle_usual_type', 'observable': observable_file_hash, 'pattern': pattern_file_hash},
'filename|impfuzzy': {'to_call': 'handle_usual_type', 'observable': observable_file_hash, 'pattern': pattern_file_hash},
'filename|pehash': {'to_call': 'handle_usual_type', 'observable': observable_file_hash, 'pattern': pattern_file_hash},
'filename|sha224': {'to_call': 'handle_usual_type', 'observable': observable_file_hash, 'pattern': pattern_file_hash},
'filename|sha384': {'to_call': 'handle_usual_type', 'observable': observable_file_hash, 'pattern': pattern_file_hash},
'filename|sha512': {'to_call': 'handle_usual_type', 'observable': observable_file_hash, 'pattern': pattern_file_hash},
'filename|sha512/224': {'to_call': 'handle_usual_type', 'observable': observable_file_hash, 'pattern': pattern_file_hash},
'filename|sha512/256': {'to_call': 'handle_usual_type', 'observable': observable_file_hash, 'pattern': pattern_file_hash},
'filename|tlsh': {'to_call': 'handle_usual_type', 'observable': observable_file_hash, 'pattern': pattern_file_hash},
'x509-fingerprint-sha1': {'to_call': 'handle_usual_type', 'observable': observable_x509, 'pattern': pattern_x509},
'port': {'to_call': 'handle_usual_type', 'observable': observable_port, 'pattern': pattern_port},
'ip-dst|port': {'to_call': 'handle_usual_type', 'observable': observable_ip_port, 'pattern': pattern_ip_port},
'ip-src|port': {'to_call': 'handle_usual_type', 'observable': observable_ip_port, 'pattern': pattern_ip_port},
'hostname|port': {'to_call': 'handle_usual_type', 'observable': observable_hostname_port, 'pattern': pattern_hostname_port},
'email-reply-to': {'to_call': 'handle_usual_type', 'observable': observable_reply_to, 'pattern': pattern_reply_to},
'attachment': {'to_call': 'handle_usual_type', 'observable': observable_attachment, 'pattern': pattern_attachment},
'mac-address': {'to_call': 'handle_usual_type', 'observable': observable_mac_address, 'pattern': pattern_mac_address},
'AS': {'to_call': 'handle_usual_type', 'observable': observable_as, 'pattern': pattern_as}
'md5': {'observable': '_get_hash_observable', 'pattern': '_get_hash_pattern'},
'sha1': {'observable': '_get_hash_observable', 'pattern': '_get_hash_pattern'},
'sha256': {'observable': '_get_hash_observable', 'pattern': '_get_hash_pattern'},
'filename': {'observable': '_get_file_observable', 'pattern': '_get_file_pattern'},
'filename|md5': {'observable': '_get_file_hash_observable', 'pattern': '_get_file_hash_pattern'},
'filename|sha1': {'observable': '_get_file_hash_observable', 'pattern': '_get_file_hash_pattern'},
'filename|sha256': {'observable': '_get_file_hash_observable', 'pattern': '_get_file_hash_pattern'},
'ip-src': {'observable': '_get_ip_observable', 'pattern': '_get_ip_pattern'},
'ip-dst': {'observable': '_get_ip_observable', 'pattern': '_get_ip_pattern'},
'hostname': {'observable': '_get_domain_observable', 'pattern': '_get_domain_pattern'},
'domain': {'observable': '_get_domain_observable', 'pattern': '_get_domain_pattern'},
'domain|ip': {'observable': '_get_domain_ip_observable', 'pattern': '_get_domain_ip_pattern'},
'email-src': {'observable': '_get_email_address_observable', 'pattern': '_get_email_address_pattern'},
'email-dst': {'observable': '_get_email_address_observable', 'pattern': '_get_email_address_pattern'},
'email-subject': {'observable': '_get_email_message_observable', 'pattern': '_get_email_message_pattern'},
'email-body': {'observable': '_get_email_message_observable', 'pattern': '_get_email_message_pattern'},
'email-attachment': {'observable': '_get_email_attachment_observable', 'pattern': '_get_email_attachment_observable'},
'url': {'observable': '_get_url_observable', 'pattern': '_get_url_pattern'},
'regkey': {'observable': '_get_regkey_observable', 'pattern': '_get_regkey_pattern'},
'regkey|value': {'observable': '_get_regkey_value_observable', 'pattern': '_get_regkey_value_pattern'},
'malware-sample': {'observable': '_get_malware_sample_observable', 'pattern': '_get_malware_sample_pattern'},
'mutex': {'observable': '_get_mutex_observable', 'pattern': '_get_mutex_pattern'},
'uri': {'observable': '_get_url_observable', 'pattern': '_get_url_pattern'},
'authentihash': {'observable': '_get_hash_observable', 'pattern': '_get_hash_pattern'},
'ssdeep': {'observable': '_get_hash_observable', 'pattern': '_get_hash_pattern'},
'imphash': {'observable': '_get_hash_observable', 'pattern': '_get_hash_pattern'},
'pehash': {'observable': '_get_hash_observable', 'pattern': '_get_hash_pattern'},
'impfuzzy': {'observable': '_get_hash_observable', 'pattern': '_get_hash_pattern'},
'sha224': {'observable': '_get_hash_observable', 'pattern': '_get_hash_pattern'},
'sha384': {'observable': '_get_hash_observable', 'pattern': '_get_hash_pattern'},
'sha512': {'observable': '_get_hash_observable', 'pattern': '_get_hash_pattern'},
'sha512/224': {'observable': '_get_hash_observable', 'pattern': '_get_hash_pattern'},
'sha512/256': {'observable': '_get_hash_observable', 'pattern': '_get_hash_pattern'},
'tlsh': {'observable': '_get_hash_observable', 'pattern': '_get_hash_pattern'},
'cdhash': {'observable': '_get_hash_observable', 'pattern': '_get_hash_pattern'},
'filename|authentihash': {'observable': '_get_file_hash_observable', 'pattern': '_get_file_hash_pattern'},
'filename|ssdeep': {'observable': '_get_file_hash_observable', 'pattern': '_get_file_hash_pattern'},
'filename|imphash': {'observable': '_get_file_hash_observable', 'pattern': '_get_file_hash_pattern'},
'filename|impfuzzy': {'observable': '_get_file_hash_observable', 'pattern': '_get_file_hash_pattern'},
'filename|pehash': {'observable': '_get_file_hash_observable', 'pattern': '_get_file_hash_pattern'},
'filename|sha224': {'observable': '_get_file_hash_observable', 'pattern': '_get_file_hash_pattern'},
'filename|sha384': {'observable': '_get_file_hash_observable', 'pattern': '_get_file_hash_pattern'},
'filename|sha512': {'observable': '_get_file_hash_observable', 'pattern': '_get_file_hash_pattern'},
'filename|sha512/224': {'observable': '_get_file_hash_observable', 'pattern': '_get_file_hash_pattern'},
'filename|sha512/256': {'observable': '_get_file_hash_observable', 'pattern': '_get_file_hash_pattern'},
'filename|tlsh': {'observable': '_get_file_hash_observable', 'pattern': '_get_file_hash_pattern'},
'x509-fingerprint-md5': {'observable': '_get_x509_observable', 'pattern': '_get_x509_pattern'},
'x509-fingerprint-sha1': {'observable': '_get_x509_observable', 'pattern': '_get_x509_pattern'},
'x509-fingerprint-sha256': {'observable': '_get_x509_observable', 'pattern': '_get_x509_pattern'},
'port': {'observable': '_get_port_observable', 'pattern': '_get_port_pattern'},
'ip-dst|port': {'observable': '_get_ip_port_observable', 'pattern': '_get_ip_port_pattern'},
'ip-src|port': {'observable': '_get_ip_port_observable', 'pattern': '_get_ip_port_pattern'},
'hostname|port': {'observable': '_get_hostname_port_observable', 'pattern': '_get_hostname_port_pattern'},
'email-reply-to': {'observable': '_get_reply_to_observable', 'pattern': '_get_reply_to_pattern'},
'attachment': {'observable': '_get_attachment_observable', 'pattern': '_get_attachment_pattern'},
'mac-address': {'observable': '_get_mac_address_observable', 'pattern': '_get_mac_address_pattern'},
'AS': {'observable': '_get_as_observable', 'pattern': '_get_as_pattern'}
#'email-dst-display-name': {'observable': {'0': {'type': 'email-addr', 'display_name': ''}},
# 'pattern': 'email-addr:display_name = \'{0}\''},
#'email-src-display-name': {'observable': {'0': {'type': 'email-addr', 'display_name': ''}},
# 'pattern': 'email-addr:display_name = \'{0}\''}
}
objects_mapping = {
'asn': {'observable': 'resolve_asn_observable',
'pattern': 'resolve_asn_pattern'},
'credential': {'observable': 'resolve_credential_observable',
'pattern': 'resolve_credential_pattern'},
'domain-ip': {'observable': 'resolve_domain_ip_observable',
'pattern': 'resolve_domain_ip_pattern'},
'email': {'observable': 'resolve_email_object_observable',
'pattern': 'resolve_email_object_pattern'},
'file': {'observable': 'resolve_file_observable',
'pattern': 'resolve_file_pattern'},
'ip-port': {'observable': 'resolve_ip_port_observable',
'pattern': 'resolve_ip_port_pattern'},
'network-connection': {'observable': 'resolve_network_connection_observable',
'pattern': 'resolve_network_connection_pattern'},
'network-socket': {'observable': 'resolve_network_socket_observable',
'pattern': 'resolve_network_socket_pattern'},
'process': {'observable': 'resolve_process_observable',
'pattern': 'resolve_process_pattern'},
'registry-key': {'observable': 'resolve_regkey_observable',
'pattern': 'resolve_regkey_pattern'},
'stix2-pattern': {'pattern': 'resolve_stix2_pattern'},
'url': {'observable': 'resolve_url_observable',
'pattern': 'resolve_url_pattern'},
'user-account': {'observable': 'resolve_user_account_observable',
'pattern': 'resolve_user_account_pattern'},
'x509': {'observable': 'resolve_x509_observable',
'pattern': 'resolve_x509_pattern'}
}
network_traffic_pattern = "network-traffic:{0} = '{1}'"
network_traffic_src_ref = "src_ref.type = '{0}' AND network-traffic:src_ref.value"
network_traffic_dst_ref = "dst_ref.type = '{0}' AND network-traffic:dst_ref.value"
network_traffic_src_ref = "src_{0}.type = '{1}' AND network-traffic:src_{0}.value"
network_traffic_dst_ref = "dst_{0}.type = '{1}' AND network-traffic:dst_{0}.value"
network_traffic_reference_mapping = {'': ''}
objectsMapping = {'asn': {'to_call': 'handle_usual_object_name',
'observable': {'type': 'autonomous-system'},
'pattern': "autonomous-system:{0} = '{1}' AND "},
'pattern': "autonomous-system:{0} = '{1}'"},
'attack-pattern': {'to_call': 'add_attack_pattern_object'},
'course-of-action': {'to_call': 'add_course_of_action_from_object'},
'credential': {'to_call': 'handle_usual_object_name',
'observable': {'type': 'user-account'},
'pattern': "user-account:{0} = '{1}' AND "},
'pattern': "user-account:{0} = '{1}'"},
'domain-ip': {'to_call': 'handle_usual_object_name',
'pattern': "domain-name:{0} = '{1}'"},
'email': {'to_call': 'handle_usual_object_name',
'observable': {'0': {'type': 'email-message'}},
'pattern': "email-{0}:{1} = '{2}' AND "},
'pattern': "email-{0}:{1} = '{2}'"},
'file': {'to_call': 'handle_usual_object_name',
'observable': {'0': {'type': 'file', 'hashes': {}}},
'pattern': "file:{0} = '{1}' AND "},
'pattern': "file:{0} = '{1}'"},
'ip-port': {'to_call': 'handle_usual_object_name',
'pattern': network_traffic_pattern},
'network-connection': {'to_call': 'handle_usual_object_name',
@ -301,7 +144,7 @@ objectsMapping = {'asn': {'to_call': 'handle_usual_object_name',
'pe': {'to_call': 'populate_objects_to_parse'},
'pe-section': {'to_call': 'populate_objects_to_parse'},
'process': {'to_call': 'handle_usual_object_name',
'pattern': "process:{0} = '{1}' AND "},
'pattern': "process:{0} = '{1}'"},
'registry-key': {'to_call': 'handle_usual_object_name',
'observable': {'0': {'type': 'windows-registry-key'}},
'pattern': "windows-registry-key:{0} = '{1}'"},
@ -313,30 +156,37 @@ objectsMapping = {'asn': {'to_call': 'handle_usual_object_name',
'pattern': "user-account:{0} = '{1}'"},
'vulnerability': {'to_call': 'add_object_vulnerability'},
'x509': {'to_call': 'handle_usual_object_name',
'pattern': "x509-certificate:{0} = '{1}' AND "}
'pattern': "x509-certificate:{0} = '{1}'"}
}
asnObjectMapping = {'asn': 'number', 'description': 'name', 'subnet-announced': 'value'}
attackPatternObjectMapping = {'name': 'name', 'summary': 'description'}
attack_pattern_reference_mapping = {'id': ('capec', 'external_id'),
'references': ('mitre-attack', 'url')}
credentialObjectMapping = {'password': 'credential', 'username': 'user_id'}
domainIpObjectMapping = {'ip-dst': 'resolves_to_refs[*].value', 'domain': 'value'}
email_attachment = {'email_type': 'message', 'stix_type': 'body_multipart[{}].body_raw_ref.name'}
emailObjectMapping = {'email-body': {'email_type': 'message', 'stix_type': 'body'},
'subject': {'email_type': 'message', 'stix_type': 'subject'},
'to': {'email_type': 'message', 'stix_type': 'to_refs'}, 'cc': {'email_type': 'message', 'stix_type': 'cc_refs'},
'to': {'email_type': 'message', 'stix_type': 'to_refs'},
'cc': {'email_type': 'message', 'stix_type': 'cc_refs'},
'to-display-name': {'email_type': 'addr', 'stix_type': 'display_name'},
'from': {'email_type': 'message', 'stix_type': 'from_ref'},
'from-display-name': {'email_type': 'addr', 'stix_type': 'display_name'},
'reply-to': {'email_type': 'message', 'stix_type': 'additional_header_fields.reply_to'},
'attachment': {'email_type': 'message', 'stix_type': 'body_multipart[*].body_raw_ref.name'},
'attachment': email_attachment, 'screenshot': email_attachment,
'send-date': {'email_type': 'message', 'stix_type': 'date'},
'x-mailer': {'email_type': 'message', 'stix_type': 'additional_header_fields.x_mailer'}}
fileMapping = {'hashes': "hashes.'{0}'", 'size-in-bytes': 'size', 'filename': 'name',
'mime-type': 'mime_type', 'file-encoding': 'name_enc'}
fileMapping = {'size-in-bytes': 'size', 'mime-type': 'mime_type', 'file-encoding': 'name_enc'}
hash_types = ('MD5', 'SHA-1', 'SHA-256', 'SHA-224', 'SHA-384', 'SHA-512', 'ssdeep', 'tlsh')
fileMapping.update({hash_type.replace('-', '').lower(): hash_type for hash_type in hash_types})
hash_types = tuple(hash_type.replace('-', '').lower() for hash_type in hash_types)
ipPortObjectMapping = {'ip': network_traffic_dst_ref,
'src-port': 'src_port', 'dst-port': 'dst_port',
@ -352,7 +202,9 @@ peMapping = {'type': 'pe_type', 'number-sections': 'number_of_sections', 'imphas
peSectionMapping = {'name': 'name', 'size-in-bytes': 'size', 'entropy': 'entropy'}
processMapping = {'name': 'name', 'pid': 'pid', 'creation-time': 'created'}
processMapping = {'pid': 'pid', 'child-pid': 'child_refs[*].pid',
'name': 'name', 'parent-pid': 'parent_ref.pid',
'creation-time': 'created', 'image': 'binary_ref.name'}
regkeyMapping = {'data-type': 'data_type', 'data': 'data', 'name': 'name',
'last-modified': 'modified', 'key': 'key'}
@ -368,6 +220,8 @@ userAccountMapping = {'account-type': 'account_type', 'can_escalate_privs': 'can
unixAccountExtensionMapping = {'group': 'groups', 'group-id': 'gid', 'home_dir': 'home_dir', 'shell': 'shell'}
vulnerabilityMapping = {'id': 'name', 'summary': 'description'}
x509mapping = {'pubkey-info-algorithm': 'subject_public_key_algorithm', 'subject': 'subject',
'pubkey-info-exponent': 'subject_public_key_exponent', 'issuer': 'issuer',
'pubkey-info-modulus': 'subject_public_key_modulus', 'serial-number': 'serial_number',
@ -387,9 +241,6 @@ relationshipsSpecifications = {'attack-pattern': {'vulnerability': 'targets', 'i
'tool': 'uses'},
'course-of-action':{'attack-pattern': 'mitigates', 'malware': 'mitigates',
'tool': 'mitigates', 'vulnerability': 'mitigates'},
'indicator': {'attack-pattern': 'indicates', 'campaign': 'indicates',
'intrusion-set': 'indicates', 'malware': 'indicates',
'threat-actor': 'indicates', 'tool': 'indicates'},
'intrusion-set': {'threat-actor': 'attributed-to', 'identity': 'targets',
'vulnerability': 'targets', 'attack-pattern': 'uses',
'malware': 'uses', 'tool': 'uses'},
@ -400,3 +251,6 @@ relationshipsSpecifications = {'attack-pattern': {'vulnerability': 'targets', 'i
'tool': 'uses'},
'tool': {'identity': 'targets', 'vulnerability': 'targets'}
}
galaxy_types = ('attack-pattern', 'campaign', 'intrusion-set', 'malware', 'threat-actor', 'tool')
relationshipsSpecifications['indicator'] = {feature: 'indicates' for feature in galaxy_types}
relationshipsSpecifications['observed-data'] = {feature: 'observed-with' for feature in galaxy_types}

File diff suppressed because it is too large Load Diff

View File

@ -1,257 +1,367 @@
def define_observable_hash_type(h_type):
if 'sha' in h_type:
return 'SHA-{}'.format(h_type.split('sha')[1])
if h_type == 'md5':
return h_type.upper()
return h_type
################################################################################
# ATTRIBUTES AND OBJECTS MAPPING #
################################################################################
def parse_name(observable, _):
return observable['0'].get('name')
def parse_value(observable, _):
return observable['0'].get('value')
def parse_attachment(observable, _):
if len(observable) > 1:
return observable['1'].get('name'), observable['0'].get('payload_bin')
return observable['0'].get('name')
def parse_domain_ip(observable, _):
return "{}|{}".format(parse_value(observable, _), observable['1'].get('value'))
def parse_email_message(observable, attribute_type):
return observable['0'].get(attribute_type.split('-')[1])
def parse_hash(observable, attribute_type):
observable_type = define_observable_hash_type(attribute_type)
return observable['0']['hashes'].get(observable_type)
def parse_ip_port(observable, _):
try:
port = observable['1']['src_port']
except KeyError:
port = observable['1']['dst_port']
return '{}|{}'.format(parse_value(observable, _), port)
def parse_hostname_port(observable, _):
return '{}|{}'.format(parse_value(observable, _), observable['1'].get('dst_port'))
def parse_filename_hash(observable, attribute_type):
_, h = attribute_type.split('|')
return "{}|{}".format(parse_name(observable, _), parse_hash(observable, h))
def parse_malware_sample(observable, _):
if len(observable) > 1:
file_observable = observable['1']
filename = file_observable['name']
md5 = file_observable['hashes']['MD5']
return "{}|{}".format(filename, md5), observable['0'].get('payload_bin')
return parse_filename_hash(observable, 'filename|md5')
def parse_number(observable, _):
return observable['0'].get('number')
def parse_port(observable, _):
return observable
def parse_regkey(observable, _):
return observable['0'].get('key')
def parse_regkey_value(observable, _):
return '{}|{}'.format(parse_regkey(observable,_), parse_name(observable, _))
misp_types_mapping = {
'md5': parse_hash,
'sha1': parse_hash,
'sha256': parse_hash,
'filename': parse_name,
'filename|md5': parse_filename_hash,
'filename|sha1': parse_filename_hash,
'filename|sha256': parse_filename_hash,
'ip-src': parse_value,
'ip-dst': parse_value,
'hostname': parse_value,
'domain': parse_value,
'domain|ip': parse_domain_ip,
'email-src': parse_value,
'email-dst': parse_value,
'email-subject': parse_email_message,
'email-body': parse_email_message,
'email-attachment': parse_name,
'url': parse_value,
'regkey': parse_regkey,
'regkey|value': parse_regkey_value,
'malware-sample': parse_malware_sample,
'mutex': parse_name,
'uri': parse_value,
'authentihash': parse_hash,
'ssdeep': parse_hash,
'imphash': parse_hash,
'pehash': parse_hash,
'impfuzzy': parse_hash,
'sha224': parse_hash,
'sha384': parse_hash,
'sha512': parse_hash,
'sha512/224': parse_hash,
'sha512/256': parse_hash,
'tlsh': parse_hash,
'cdhash': parse_hash,
'filename|authentihash': parse_filename_hash,
'filename|ssdeep': parse_filename_hash,
'filename|imphash': parse_filename_hash,
'filename|impfuzzy': parse_filename_hash,
'filename|pehash': parse_filename_hash,
'filename|sha224': parse_filename_hash,
'filename|sha384': parse_filename_hash,
'filename|sha512': parse_filename_hash,
'filename|sha512/224': parse_filename_hash,
'filename|sha512/256': parse_filename_hash,
'filename|tlsh': parse_filename_hash,
'x509-fingerprint-sha1': parse_hash,
'port': parse_port,
'ip-dst|port': parse_ip_port,
'ip-src|port': parse_ip_port,
'hostname|port': parse_hostname_port,
'email-reply-to': parse_value,
'attachment': parse_attachment,
'mac-address': parse_value,
'AS': parse_number
attributes_mapping = {
'filename': '_parse_name',
'ip-src': '_parse_value',
'ip-dst': '_parse_value',
'hostname': '_parse_value',
'domain': '_parse_value',
'domain|ip': '_parse_domain_ip_attribute',
'email-src': '_parse_value',
'email-dst': '_parse_value',
'email-attachment': '_parse_name',
'url': '_parse_value',
'regkey': '_parse_regkey_attribute',
'regkey|value': '_parse_regkey_value',
'malware-sample': '_parse_malware_sample',
'mutex': '_parse_name',
'uri': '_parse_value',
'port': '_parse_port',
'ip-dst|port': '_parse_network_attribute',
'ip-src|port': '_parse_network_attribute',
'hostname|port': '_parse_network_attribute',
'email-reply-to': '_parse_email_reply_to',
'attachment': '_parse_attachment',
'mac-address': '_parse_value',
'AS': '_parse_number'
}
address_family_attribute_mapping = {'type': 'text','relation': 'address-family'}
as_number_attribute_mapping = {'type': 'AS', 'relation': 'asn'}
asn_description_attribute_mapping = {'type': 'text', 'relation': 'description'}
asn_subnet_attribute_mapping = {'type': 'ip-src', 'relation': 'subnet-announced'}
body_multipart_mapping = {'type': 'email-attachment', 'relation': 'attachment'}
cc_attribute_mapping = {'type': 'email-dst', 'relation': 'cc'}
data_attribute_mapping = {'type': 'text', 'relation': 'data'}
data_type_attribute_mapping = {'type': 'text', 'relation': 'data-type'}
domain_attribute_mapping = {'type': 'domain', 'relation': 'domain'}
domain_family_attribute_mapping = {'type': 'text', 'relation': 'domain-family'}
dst_port_attribute_mapping = {'type': 'port', 'relation': 'dst-port'}
email_date_attribute_mapping = {'type': 'datetime', 'relation': 'send-date'}
email_subject_attribute_mapping = {'type': 'email-subject', 'relation': 'subject'}
encoding_attribute_mapping = {'type': 'text', 'relation': 'file-encoding'}
end_datetime_attribute_mapping = {'type': 'datetime', 'relation': 'last-seen'}
entropy_mapping = {'type': 'float', 'relation': 'entropy'}
filename_attribute_mapping = {'type': 'filename', 'relation': 'filename'}
from_attribute_mapping = {'type': 'email-src', 'relation': 'from'}
imphash_mapping = {'type': 'imphash', 'relation': 'imphash'}
ip_attribute_mapping = {'type': 'ip-dst', 'relation': 'ip'}
issuer_attribute_mapping = {'type': 'text', 'relation': 'issuer'}
key_attribute_mapping = {'type': 'regkey', 'relation': 'key'}
mime_type_attribute_mapping = {'type': 'mime-type', 'relation': 'mimetype'}
modified_attribute_mapping = {'type': 'datetime', 'relation': 'last-modified'}
number_sections_mapping = {'type': 'counter', 'relation': 'number-sections'}
password_mapping = {'type': 'text', 'relation': 'password'}
path_attribute_mapping = {'type': 'text', 'relation': 'path'}
pe_type_mapping = {'type': 'text', 'relation': 'type'}
pid_attribute_mapping = {'type': 'text', 'relation': 'pid'}
process_command_line_mapping = {'type': 'text', 'relation': 'command-line'}
process_creation_time_mapping = {'type': 'datetime', 'relation': 'creation-time'}
process_name_mapping = {'type': 'text', 'relation': 'name'}
regkey_name_attribute_mapping = {'type': 'text', 'relation': 'name'}
reply_to_attribute_mapping = {'type': 'email-reply-to', 'relation': 'reply-to'}
section_name_mapping = {'type': 'text', 'relation': 'name'}
serial_number_attribute_mapping = {'type': 'text', 'relation': 'serial-number'}
size_attribute_mapping = {'type': 'size-in-bytes', 'relation': 'size-in-bytes'}
src_port_attribute_mapping = {'type': 'port', 'relation': 'src-port'}
start_datetime_attribute_mapping = {'type': 'datetime', 'relation': 'first-seen'}
state_attribute_mapping = {'type': 'text', 'relation': 'state'}
to_attribute_mapping = {'type': 'email-dst', 'relation': 'to'}
url_attribute_mapping = {'type': 'url', 'relation': 'url'}
url_port_attribute_mapping = {'type': 'port', 'relation': 'port'}
username_mapping = {'type': 'text', 'relation': 'username'}
x_mailer_attribute_mapping = {'type': 'email-x-mailer', 'relation': 'x-mailer'}
x509_md5_attribute_mapping = {'type': 'x509-fingerprint-md5', 'relation': 'x509-fingerprint-md5'}
x509_sha1_attribute_mapping = {'type': 'x509-fingerprint-sha1', 'relation': 'x509-fingerprint-sha1'}
x509_sha256_attribute_mapping = {'type': 'x509-fingerprint-sha256', 'relation': 'x509-fingerprint-sha256'}
x509_spka_attribute_mapping = {'type': 'text', 'relation': 'pubkey-info-algorithm'} # x509 subject public key algorithm
x509_spke_attribute_mapping = {'type': 'text', 'relation': 'pubkey-info-exponent'} # x509 subject public key exponent
x509_spkm_attribute_mapping = {'type': 'text', 'relation': 'pubkey-info-modulus'} # x509 subject public key modulus
x509_subject_attribute_mapping = {'type': 'text', 'relation': 'subject'}
x509_version_attribute_mapping = {'type': 'text', 'relation': 'version'}
x509_vna_attribute_mapping = {'type': 'datetime', 'relation': 'validity-not-after'} # x509 validity not after
x509_vnb_attribute_mapping = {'type': 'datetime', 'relation': 'validity-not-before'} # x509 validity not before
attributes_type_mapping = {
'md5': '_parse_hash',
'sha1': '_parse_hash',
'sha256': '_parse_hash',
'filename|md5': '_parse_filename_hash',
'filename|sha1': '_parse_filename_hash',
'filename|sha256': '_parse_filename_hash',
'email-subject': '_parse_email_message',
'email-body': '_parse_email_message',
'authentihash': '_parse_hash',
'ssdeep': '_parse_hash',
'imphash': '_parse_hash',
'pehash': '_parse_hash',
'impfuzzy': '_parse_hash',
'sha224': '_parse_hash',
'sha384': '_parse_hash',
'sha512': '_parse_hash',
'sha512/224': '_parse_hash',
'sha512/256': '_parse_hash',
'tlsh': '_parse_hash',
'cdhash': '_parse_hash',
'filename|authentihash': '_parse_filename_hash',
'filename|ssdeep': '_parse_filename_hash',
'filename|imphash': '_parse_filename_hash',
'filename|impfuzzy': '_parse_filename_hash',
'filename|pehash': '_parse_filename_hash',
'filename|sha224': '_parse_filename_hash',
'filename|sha384': '_parse_filename_hash',
'filename|sha512': '_parse_filename_hash',
'filename|sha512/224': '_parse_filename_hash',
'filename|sha512/256': '_parse_filename_hash',
'filename|tlsh': '_parse_filename_hash',
'x509-fingerprint-md5': '_parse_x509_attribute',
'x509-fingerprint-sha1': '_parse_x509_attribute',
'x509-fingerprint-sha256': '_parse_x509_attribute'
}
objects_mapping = {
'asn': {
'observable': 'parse_asn_observable',
'pattern': 'parse_asn_pattern'},
'credential': {
'observable': 'parse_credential_observable',
'pattern': 'parse_credential_pattern'},
'domain-ip': {
'observable': 'parse_domain_ip_observable',
'pattern': 'parse_domain_ip_pattern'},
'email': {
'observable': 'parse_email_observable',
'pattern': 'parse_email_pattern'},
'file': {
'observable': 'parse_file_observable',
'pattern': 'parse_file_pattern'},
'ip-port': {
'observable': 'parse_ip_port_observable',
'pattern': 'parse_ip_port_pattern'},
'network-connection': {
'observable': 'parse_network_connection_observable',
'pattern': 'parse_network_connection_pattern'},
'network-socket': {
'observable': 'parse_network_socket_observable',
'pattern': 'parse_network_socket_pattern'},
'process': {
'observable': 'parse_process_observable',
'pattern': 'parse_process_pattern'},
'registry-key': {
'observable': 'parse_regkey_observable',
'pattern': 'parse_regkey_pattern'},
'url': {
'observable': 'parse_url_observable',
'pattern': 'parse_url_pattern'},
'user-account': {
'observable': 'parse_user_account_observable',
'pattern': 'parse_user_account_pattern'},
'WindowsPEBinaryFile': {
'observable': 'parse_pe_observable',
'pattern': 'parse_pe_pattern'},
'x509': {
'observable': 'parse_x509_observable',
'pattern': 'parse_x509_pattern'}
}
observable_mapping = {
('artifact', 'file'): 'parse_file_observable',
('artifact', 'directory', 'file'): 'parse_file_observable',
('artifact', 'email-addr', 'email-message', 'file'): 'parse_email_observable',
('autonomous-system',): 'parse_asn_observable',
('autonomous-system', 'ipv4-addr'): 'parse_asn_observable',
('autonomous-system', 'ipv6-addr'): 'parse_asn_observable',
('autonomous-system', 'ipv4-addr', 'ipv6-addr'): 'parse_asn_observable',
('directory', 'file'): 'parse_file_observable',
('domain-name',): 'parse_domain_ip_observable',
('domain-name', 'ipv4-addr'): 'parse_domain_ip_observable',
('domain-name', 'ipv6-addr'): 'parse_domain_ip_observable',
('domain-name', 'ipv4-addr', 'ipv6-addr'): 'parse_domain_ip_observable',
('domain-name', 'ipv4-addr', 'network-traffic'): 'parse_domain_ip_network_traffic_observable',
('domain-name', 'ipv6-addr', 'network-traffic'): 'parse_domain_ip_network_traffic_observable',
('domain-name', 'ipv4-addr', 'ipv6-addr', 'network-traffic'): 'parse_domain_ip_network_traffic_observable',
('domain-name', 'network-traffic'): 'parse_domain_network_traffic_observable',
('domain-name', 'network-traffic', 'url'): 'parse_url_observable',
('email-addr',): 'parse_email_address_observable',
('email-addr', 'email-message'): 'parse_email_observable',
('email-addr', 'email-message', 'file'): 'parse_email_observable',
('email-message',): 'parse_email_observable',
('file',): 'parse_file_observable',
('file', 'process'): 'parse_process_observable',
('ipv4-addr',): 'parse_ip_address_observable',
('ipv6-addr',): 'parse_ip_address_observable',
('ipv4-addr', 'network-traffic'): 'parse_ip_network_traffic_observable',
('ipv6-addr', 'network-traffic'): 'parse_ip_network_traffic_observable',
('ipv4-addr', 'ipv6-addr', 'network-traffic'): 'parse_ip_network_traffic_observable',
('mac-addr',): 'parse_mac_address_observable',
('mutex',): 'parse_mutex_observable',
('process',): 'parse_process_observable',
('x509-certificate',): 'parse_x509_observable',
('url',): 'parse_url_observable',
('user-account',): 'parse_user_account_observable',
('windows-registry-key',): 'parse_regkey_observable'
}
pattern_mapping = {
('artifact', 'file'): 'parse_file_pattern',
('artifact', 'directory', 'file'): 'parse_file_pattern',
('autonomous-system', ): 'parse_as_pattern',
('autonomous-system', 'ipv4-addr'): 'parse_as_pattern',
('autonomous-system', 'ipv6-addr'): 'parse_as_pattern',
('autonomous-system', 'ipv4-addr', 'ipv6-addr'): 'parse_as_pattern',
('directory',): 'parse_file_pattern',
('directory', 'file'): 'parse_file_pattern',
('domain-name',): 'parse_domain_ip_port_pattern',
('domain-name', 'ipv4-addr'): 'parse_domain_ip_port_pattern',
('domain-name', 'ipv6-addr'): 'parse_domain_ip_port_pattern',
('domain-name', 'ipv4-addr', 'ipv6-addr'): 'parse_domain_ip_port_pattern',
('domain-name', 'ipv4-addr', 'url'): 'parse_url_pattern',
('domain-name', 'ipv6-addr', 'url'): 'parse_url_pattern',
('domain-name', 'ipv4-addr', 'ipv6-addr', 'url'): 'parse_url_pattern',
('domain-name', 'network-traffic'): 'parse_domain_ip_port_pattern',
('domain-name', 'network-traffic', 'url'): 'parse_url_pattern',
('email-addr',): 'parse_email_address_pattern',
('email-message',): 'parse_email_message_pattern',
('file',): 'parse_file_pattern',
('ipv4-addr',): 'parse_ip_address_pattern',
('ipv6-addr',): 'parse_ip_address_pattern',
('ipv4-addr', 'ipv6-addr'): 'parse_ip_address_pattern',
('mac-addr',): 'parse_mac_address_pattern',
('mutex',): 'parse_mutex_pattern',
('network-traffic',): 'parse_network_traffic_pattern',
('process',): 'parse_process_pattern',
('url',): 'parse_url_pattern',
('user-account',): 'parse_user_account_pattern',
('windows-registry-key',): 'parse_regkey_pattern',
('x509-certificate',): 'parse_x509_pattern'
}
pattern_forbidden_relations = (' LIKE ', ' FOLLOWEDBY ', ' MATCHES ', ' ISSUBSET ', ' ISSUPERSET ', ' REPEATS ')
single_attribute_fields = ('type', 'value', 'to_ids')
################################################################################
# OBSERVABLE OBJECTS AND PATTERNS MAPPING. #
################################################################################
address_family_attribute_mapping = {'type': 'text','object_relation': 'address-family'}
as_number_attribute_mapping = {'type': 'AS', 'object_relation': 'asn'}
description_attribute_mapping = {'type': 'text', 'object_relation': 'description'}
asn_subnet_attribute_mapping = {'type': 'ip-src', 'object_relation': 'subnet-announced'}
cc_attribute_mapping = {'type': 'email-dst', 'object_relation': 'cc'}
credential_attribute_mapping = {'type': 'text', 'object_relation': 'password'}
data_attribute_mapping = {'type': 'text', 'object_relation': 'data'}
data_type_attribute_mapping = {'type': 'text', 'object_relation': 'data-type'}
domain_attribute_mapping = {'type': 'domain', 'object_relation': 'domain'}
domain_family_attribute_mapping = {'type': 'text', 'object_relation': 'domain-family'}
dst_port_attribute_mapping = {'type': 'port', 'object_relation': 'dst-port'}
email_attachment_attribute_mapping = {'type': 'email-attachment', 'object_relation': 'attachment'}
email_date_attribute_mapping = {'type': 'datetime', 'object_relation': 'send-date'}
email_subject_attribute_mapping = {'type': 'email-subject', 'object_relation': 'subject'}
encoding_attribute_mapping = {'type': 'text', 'object_relation': 'file-encoding'}
end_datetime_attribute_mapping = {'type': 'datetime', 'object_relation': 'last-seen'}
entropy_mapping = {'type': 'float', 'object_relation': 'entropy'}
filename_attribute_mapping = {'type': 'filename', 'object_relation': 'filename'}
from_attribute_mapping = {'type': 'email-src', 'object_relation': 'from'}
imphash_mapping = {'type': 'imphash', 'object_relation': 'imphash'}
id_attribute_mapping = {'type': 'text', 'object_relation': 'id'}
ip_attribute_mapping = {'type': 'ip-dst', 'object_relation': 'ip'}
issuer_attribute_mapping = {'type': 'text', 'object_relation': 'issuer'}
key_attribute_mapping = {'type': 'regkey', 'object_relation': 'key'}
malware_sample_attribute_mapping = {'type': 'malware-sample', 'object_relation': 'malware-sample'}
mime_type_attribute_mapping = {'type': 'mime-type', 'object_relation': 'mimetype'}
modified_attribute_mapping = {'type': 'datetime', 'object_relation': 'last-modified'}
name_attribute_mapping = {'type': 'text', 'object_relation': 'name'}
network_traffic_ip = {'type': 'ip-{}', 'object_relation': 'ip-{}'}
number_sections_mapping = {'type': 'counter', 'object_relation': 'number-sections'}
password_mapping = {'type': 'text', 'object_relation': 'password'}
path_attribute_mapping = {'type': 'text', 'object_relation': 'path'}
pe_type_mapping = {'type': 'text', 'object_relation': 'type'}
pid_attribute_mapping = {'type': 'text', 'object_relation': 'pid'}
process_command_line_mapping = {'type': 'text', 'object_relation': 'command-line'}
process_creation_time_mapping = {'type': 'datetime', 'object_relation': 'creation-time'}
process_image_mapping = {'type': 'filename', 'object_relation': 'image'}
process_name_mapping = {'type': 'text', 'object_relation': 'name'}
regkey_name_attribute_mapping = {'type': 'text', 'object_relation': 'name'}
references_attribute_mapping = {'type': 'link', 'object_relation': 'references'}
reply_to_attribute_mapping = {'type': 'email-reply-to', 'object_relation': 'reply-to'}
screenshot_attribute_mapping = {'type': 'attachment', 'object_relation': 'screenshot'}
section_name_mapping = {'type': 'text', 'object_relation': 'name'}
serial_number_attribute_mapping = {'type': 'text', 'object_relation': 'serial-number'}
size_attribute_mapping = {'type': 'size-in-bytes', 'object_relation': 'size-in-bytes'}
src_port_attribute_mapping = {'type': 'port', 'object_relation': 'src-port'}
start_datetime_attribute_mapping = {'type': 'datetime', 'object_relation': 'first-seen'}
state_attribute_mapping = {'type': 'text', 'object_relation': 'state'}
summary_attribute_mapping = {'type': 'text', 'object_relation': 'summary'}
to_attribute_mapping = {'type': 'email-dst', 'object_relation': 'to'}
url_attribute_mapping = {'type': 'url', 'object_relation': 'url'}
url_port_attribute_mapping = {'type': 'port', 'object_relation': 'port'}
user_id_mapping = {'type': 'text', 'object_relation': 'username'}
x_mailer_attribute_mapping = {'type': 'email-x-mailer', 'object_relation': 'x-mailer'}
x509_md5_attribute_mapping = {'type': 'x509-fingerprint-md5', 'object_relation': 'x509-fingerprint-md5'}
x509_sha1_attribute_mapping = {'type': 'x509-fingerprint-sha1', 'object_relation': 'x509-fingerprint-sha1'}
x509_sha256_attribute_mapping = {'type': 'x509-fingerprint-sha256', 'object_relation': 'x509-fingerprint-sha256'}
x509_spka_attribute_mapping = {'type': 'text', 'object_relation': 'pubkey-info-algorithm'} # x509 subject public key algorithm
x509_spke_attribute_mapping = {'type': 'text', 'object_relation': 'pubkey-info-exponent'} # x509 subject public key exponent
x509_spkm_attribute_mapping = {'type': 'text', 'object_relation': 'pubkey-info-modulus'} # x509 subject public key modulus
x509_subject_attribute_mapping = {'type': 'text', 'object_relation': 'subject'}
x509_version_attribute_mapping = {'type': 'text', 'object_relation': 'version'}
x509_vna_attribute_mapping = {'type': 'datetime', 'object_relation': 'validity-not-after'} # x509 validity not after
x509_vnb_attribute_mapping = {'type': 'datetime', 'object_relation': 'validity-not-before'} # x509 validity not before
asn_mapping = {'number': as_number_attribute_mapping,
'autonomous-system:number': as_number_attribute_mapping,
'name': asn_description_attribute_mapping,
'autonomous-system:name': asn_description_attribute_mapping,
'value': asn_subnet_attribute_mapping,
'name': description_attribute_mapping,
'autonomous-system:name': description_attribute_mapping,
'ipv4-addr': asn_subnet_attribute_mapping,
'ipv6-addr': asn_subnet_attribute_mapping,
'ipv4-addr:value': asn_subnet_attribute_mapping,
'ipv6-addr:value': asn_subnet_attribute_mapping}
credential_mapping = {'credential': {'type': 'text', 'relation': 'password'},
'user_id': {'type': 'text', 'relation': 'username'}}
attack_pattern_mapping = {'name': name_attribute_mapping,
'description': summary_attribute_mapping}
attack_pattern_references_mapping = {'mitre-attack': references_attribute_mapping,
'capec': id_attribute_mapping}
course_of_action_mapping = {'description': description_attribute_mapping,
'name': name_attribute_mapping}
credential_mapping = {'credential': credential_attribute_mapping,
'user-account:credential': credential_attribute_mapping,
'user_id': user_id_mapping,
'user-account:user_id': user_id_mapping}
domain_ip_mapping = {'domain-name': domain_attribute_mapping,
'domain-name:value': domain_attribute_mapping,
'ipv4-addr': ip_attribute_mapping,
'ipv6-addr': ip_attribute_mapping,
'domain-name:resolves_to_refs[*].value': ip_attribute_mapping}
'ipv4-addr:value': ip_attribute_mapping,
'ipv6-addr:value': ip_attribute_mapping,
'domain-name:resolves_to_refs[*].value': ip_attribute_mapping,
'network-traffic:dst_port': dst_port_attribute_mapping,
'network-traffic:src_port': src_port_attribute_mapping}
email_mapping = {'date': email_date_attribute_mapping,
'email-message:date': email_date_attribute_mapping,
'to_refs': to_attribute_mapping,
'email-message:to_refs': to_attribute_mapping,
'cc_refs': cc_attribute_mapping,
'email-message:cc_refs': cc_attribute_mapping,
'email-message:to_refs[*].value': to_attribute_mapping,
'email-message:cc_refs[*].value': cc_attribute_mapping,
'subject': email_subject_attribute_mapping,
'email-message:subject': email_subject_attribute_mapping,
'X-Mailer': x_mailer_attribute_mapping,
'email-message:additional_header_fields.x_mailer': x_mailer_attribute_mapping,
'Reply-To': reply_to_attribute_mapping,
'email-message:additional_header_fields.reply_to': reply_to_attribute_mapping,
'from_ref': from_attribute_mapping,
'email-message:from_ref': from_attribute_mapping,
'body_multipart': body_multipart_mapping,
'email-message:body_multipart[*].body_raw_ref.name': body_multipart_mapping,
'email-message:from_ref.value': from_attribute_mapping,
'email-addr:value': to_attribute_mapping}
file_mapping = {'mime_type': mime_type_attribute_mapping,
email_references_mapping = {'attachment': email_attachment_attribute_mapping,
'cc_refs': cc_attribute_mapping,
'from_ref': from_attribute_mapping,
'screenshot': screenshot_attribute_mapping,
'to_refs': to_attribute_mapping}
file_mapping = {'artifact:mime_type': mime_type_attribute_mapping,
'file:content_ref.mime_type': mime_type_attribute_mapping,
'mime_type': mime_type_attribute_mapping,
'file:mime_type': mime_type_attribute_mapping,
'name': filename_attribute_mapping,
'file:name': filename_attribute_mapping,
'name_enc': encoding_attribute_mapping,
'file:name_enc': encoding_attribute_mapping,
'path': path_attribute_mapping,
'file:parent_directory_ref.path': path_attribute_mapping,
'directory:path': path_attribute_mapping,
'size': size_attribute_mapping,
'file:size': size_attribute_mapping}
network_traffic_mapping = {'src_port': src_port_attribute_mapping,
'network-traffic:src_port': src_port_attribute_mapping,
'dst_port': dst_port_attribute_mapping,
network_traffic_mapping = {'dst_port':dst_port_attribute_mapping,
'src_port': src_port_attribute_mapping,
'network-traffic:dst_port': dst_port_attribute_mapping,
'start': start_datetime_attribute_mapping,
'network-traffic:start': start_datetime_attribute_mapping,
'end': end_datetime_attribute_mapping,
'network-traffic:end': end_datetime_attribute_mapping,
'value': domain_attribute_mapping,
'domain-name:value': domain_attribute_mapping,
'network-traffic:dst_ref.value': {'type': 'ip-dst', 'relation': 'ip-dst'},
'network-traffic:src_red.value': {'type': 'ip-src', 'relation': 'ip-src'},
'address_family': address_family_attribute_mapping,
"network-traffic:extensions.'socket-ext'.address_family": address_family_attribute_mapping,
'protocol_family': domain_family_attribute_mapping,
"network-traffic:extensions.'socket-ext'.protocol_family": domain_family_attribute_mapping,
'is_blocking': state_attribute_mapping,
"network-traffic:extensions.'socket-ext'.is_blocking": state_attribute_mapping,
'is_listening': state_attribute_mapping,
"network-traffic:extensions.'socket-ext'.is_listening": state_attribute_mapping}
'network-traffic:src_port': src_port_attribute_mapping}
network_traffic_extensions = {'socket-ext': 'network-socket'}
ip_port_mapping = {'value': domain_attribute_mapping,
'domain-name:value': domain_attribute_mapping,
'network-traffic:dst_ref.value': {'type': 'ip-dst', 'object_relation': 'ip-dst'},
'network-traffic:src_ref.value': {'type': 'ip-src', 'object_relation': 'ip-src'}}
ip_port_mapping.update(network_traffic_mapping)
network_traffic_ip = ('ip-{}', 'ip-{}')
ip_port_types = {'domain-name': ('domain', 'domain'), 'ipv4-addr': network_traffic_ip, 'ipv6-addr': network_traffic_ip}
network_socket_types = {'domain-name': ('hostname', 'hostname-{}'), 'ipv4-addr': network_traffic_ip, 'ipv6-addr': network_traffic_ip}
network_traffic_references_mapping = {'with_extensions': network_socket_types, 'without_extensions': ip_port_types}
ip_port_references_mapping = {'domain-name': domain_attribute_mapping,
'ipv4-addr': network_traffic_ip,
'ipv6-addr': network_traffic_ip}
network_socket_extension_mapping = {'address_family': address_family_attribute_mapping,
"network-traffic:extensions.'socket-ext'.address_family": address_family_attribute_mapping,
'protocol_family': domain_family_attribute_mapping,
"network-traffic:extensions.'socket-ext'.protocol_family": domain_family_attribute_mapping,
'is_blocking': state_attribute_mapping,
"network-traffic:extensions.'socket-ext'.is_blocking": state_attribute_mapping,
'is_listening': state_attribute_mapping,
"network-traffic:extensions.'socket-ext'.is_listening": state_attribute_mapping}
network_traffic_references_mapping = {'domain-name': {'type': 'hostname', 'object_relation': 'hostname-{}'},
'ipv4-addr': network_traffic_ip,
'ipv6-addr': network_traffic_ip}
pe_mapping = {'pe_type': pe_type_mapping, 'number_of_sections': number_sections_mapping, 'imphash': imphash_mapping}
pe_section_mapping = {'name': section_name_mapping, 'size': size_attribute_mapping, 'entropy': entropy_mapping}
hash_types = ('MD5', 'SHA-1', 'SHA-256', 'SHA-224', 'SHA-384', 'SHA-512', 'ssdeep', 'tlsh')
for hash_type in hash_types:
misp_hash_type = hash_type.replace('-', '').lower()
attribute = {'type': misp_hash_type, 'object_relation': misp_hash_type}
file_mapping[hash_type] = attribute
file_mapping.update({f"file:hashes.'{feature}'": attribute for feature in (hash_type, misp_hash_type)})
file_mapping.update({f"file:hashes.{feature}": attribute for feature in (hash_type, misp_hash_type)})
pe_section_mapping[hash_type] = attribute
pe_section_mapping[misp_hash_type] = attribute
process_mapping = {'name': process_name_mapping,
'process:name': process_name_mapping,
'pid': pid_attribute_mapping,
@ -260,8 +370,15 @@ process_mapping = {'name': process_name_mapping,
'process:created': process_creation_time_mapping,
'command_line': process_command_line_mapping,
'process:command_line': process_command_line_mapping,
'process:parent_ref': {'type': 'text', 'relation': 'parent-pid'},
'process:child_refs': {'type': 'text', 'relation': 'child-pid'}}
'process:parent_ref.pid': {'type': 'text', 'object_relation': 'parent-pid'},
'process:child_refs[*].pid': {'type': 'text', 'object_relation': 'child-pid'},
'process:binary_ref.name': process_image_mapping}
child_process_reference_mapping = {'pid': {'type': 'text', 'object_relation': 'child-pid'}}
parent_process_reference_mapping = {'command_line': {'type': 'text', 'object_relation': 'parent-command-line'},
'pid': {'type': 'text', 'object_relation': 'parent-pid'},
'process-name': {'type': 'text', 'object_relation': 'parent-process-name'}}
regkey_mapping = {'data': data_attribute_mapping,
'windows-registry-key:values.data': data_attribute_mapping,
@ -273,7 +390,7 @@ regkey_mapping = {'data': data_attribute_mapping,
'windows-registry-key:values.name': regkey_name_attribute_mapping,
'key': key_attribute_mapping,
'windows-registry-key:key': key_attribute_mapping,
'windows-registry-key:value': {'type': 'text', 'relation': 'hive'}
'windows-registry-key:value': {'type': 'text', 'object_relation': 'hive'}
}
url_mapping = {'url': url_attribute_mapping,
@ -281,27 +398,32 @@ url_mapping = {'url': url_attribute_mapping,
'domain-name': domain_attribute_mapping,
'domain-name:value': domain_attribute_mapping,
'network-traffic': url_port_attribute_mapping,
'network-traffic:dst_port': url_port_attribute_mapping
'network-traffic:dst_port': url_port_attribute_mapping,
'ipv4-addr:value': ip_attribute_mapping,
'ipv6-addr:value': ip_attribute_mapping
}
user_account_mapping = {'account_created': {'type': 'datetime', 'object_relation': 'created', 'disable_correlation': True},
'account_expires': {'type': 'datetime', 'object_relation': 'expires', 'disable_correlation': True},
'account_first_login': {'type': 'datetime', 'object_relation': 'first_login', 'disable_correlation': True},
'account_last_login': {'type': 'datetime', 'object_relation': 'last_login', 'disable_correlation': True},
'account_login': {'type': 'text', 'object_relation': 'username'},
user_account_mapping = {'account_created': {'type': 'datetime', 'object_relation': 'created'},
'account_expires': {'type': 'datetime', 'object_relation': 'expires'},
'account_first_login': {'type': 'datetime', 'object_relation': 'first_login'},
'account_last_login': {'type': 'datetime', 'object_relation': 'last_login'},
'account_login': user_id_mapping,
'account_type': {'type': 'text', 'object_relation': 'account-type'},
'can_escalate_privs': {'type': 'boolean', 'object_relation': 'can_escalate_privs', 'disable_correlation': True},
'credential': {'type': 'text', 'object_relation': 'password'},
'credential_last_changed': {'type': 'datetime', 'object_relation': 'password_last_changed', 'disable_correlation': True},
'can_escalate_privs': {'type': 'boolean', 'object_relation': 'can_escalate_privs'},
'credential': credential_attribute_mapping,
'credential_last_changed': {'type': 'datetime', 'object_relation': 'password_last_changed'},
'display_name': {'type': 'text', 'object_relation': 'display-name'},
'gid': {'type': 'text', 'object_relation': 'group-id', 'disable_correlation': True},
'home_dir': {'type': 'text', 'object_relation': 'home_dir', 'disable_correlation': True},
'is_disabled': {'type': 'boolean', 'object_relation': 'disabled', 'disable_correlation': True},
'is_privileged': {'type': 'boolean', 'object_relation': 'privileged', 'disable_correlation': True},
'is_service_account': {'type': 'boolean', 'object_relation': 'is_service_account', 'disable_correlation': True},
'shell': {'type': 'text', 'object_relation': 'shell', 'disable_correlation': True},
'gid': {'type': 'text', 'object_relation': 'group-id'},
'home_dir': {'type': 'text', 'object_relation': 'home_dir'},
'is_disabled': {'type': 'boolean', 'object_relation': 'disabled'},
'is_privileged': {'type': 'boolean', 'object_relation': 'privileged'},
'is_service_account': {'type': 'boolean', 'object_relation': 'is_service_account'},
'shell': {'type': 'text', 'object_relation': 'shell'},
'user_id': {'type': 'text', 'object_relation': 'user-id'}}
vulnerability_mapping = {'name': id_attribute_mapping,
'description': summary_attribute_mapping}
x509_mapping = {'issuer': issuer_attribute_mapping,
'x509-certificate:issuer': issuer_attribute_mapping,
'serial_number': serial_number_attribute_mapping,
@ -328,17 +450,10 @@ x509_mapping = {'issuer': issuer_attribute_mapping,
"x509-certificate:hashes.'md5'": x509_md5_attribute_mapping,
}
domain_pattern_mapping = {'value': {'type': 'domain'}}
ip_pattern_mapping = {'value': {'type': 'ip-dst'}}
external_pattern_mapping = {'domain-name': domain_pattern_mapping,
'email-addr': {'value': {'type': 'email-dst'}},
'file': file_mapping,
'ipv4-addr': ip_pattern_mapping,
'ipv6-addr': ip_pattern_mapping,
'url': {'value':{'type': 'url'}},
'x509-certificate': x509_mapping
}
attachment_types = ('file:content_ref.name', 'file:content_ref.payload_bin',
'artifact:x_misp_text_name', 'artifact:payload_bin',
"file:hashes.'MD5'", "file:content_ref.hashes.'MD5'",
'file:name')
connection_protocols = {"IP": "3", "ICMP": "3", "ARP": "3",
"TCP": "4", "UDP": "4",

View File

@ -54,8 +54,8 @@ class StixParser():
def __init__(self):
super(StixParser, self).__init__()
self.misp_event = MISPEvent()
self.galaxies = defaultdict(list)
self.references = defaultdict(list)
self.galaxies = set()
################################################################################
## LOADING & UTILITY FUNCTIONS USED BY BOTH SUBCLASSES. ##
@ -78,6 +78,9 @@ class StixParser():
attribute_distribution = int(attribute_distribution) if attribute_distribution.isdigit() else 5
except IndexError:
attribute_distribution = 5
synonyms_to_tag_names = args[2] if len(args) > 2 else '/var/www/MISP/app/files/scripts/synonymsToTagNames.json'
with open(synonyms_to_tag_names, 'rt', encoding='utf-8') as f:
self.synonyms_to_tag_names = json.loads(f.read())
self.misp_event.distribution = event_distribution
self.__attribute_distribution = attribute_distribution
self.from_misp = from_misp
@ -88,7 +91,8 @@ class StixParser():
if self.references:
self.build_references()
if self.galaxies:
self.build_galaxies()
for galaxy in self.galaxies:
self.misp_event.add_tag(galaxy)
# Convert the MISP event we create from the STIX document into json format
# and write it in the output file
@ -157,17 +161,6 @@ class StixParser():
for attribute in misp_object.attributes:
attribute.distribution = self.__attribute_distribution
def build_galaxies(self):
galaxies = []
for name, clusters in self.galaxies.items():
galaxy_type = stix2misp_mapping._galaxy_mapping[name]
galaxy = {'type': galaxy_type, 'name': name, 'GalaxyCluster': []}
for cluster in clusters:
galaxy['GalaxyCluster'].append(cluster)
self.misp_event.add_tag(cluster['tag_name'])
galaxies.append(galaxy)
self.misp_event['Galaxy'] = galaxies
# Make references between objects
def build_references(self):
for misp_object in self.misp_event.objects:
@ -633,6 +626,30 @@ class StixParser():
file_type, file_value, _ = self.handle_file(properties, False)
return file_type, file_value, pe_uuid
# Parse a course of action and add a MISP object to the event
def parse_course_of_action(self, course_of_action):
misp_object = MISPObject('course-of-action', misp_objects_path_custom=_MISP_objects_path)
misp_object.uuid = self.fetch_uuid(course_of_action.id_)
if course_of_action.title:
attribute = {'type': 'text', 'object_relation': 'name',
'value': course_of_action.title}
misp_object.add_attribute(**attribute)
for prop, properties_key in stix2misp_mapping._coa_mapping.items():
if getattr(course_of_action, prop):
attribute = {'type': 'text', 'object_relation': prop.replace('_', ''),
'value': attrgetter('{}.{}'.format(prop, properties_key))(course_of_action)}
misp_object.add_attribute(**attribute)
if course_of_action.parameter_observables:
for observable in course_of_action.parameter_observables.observables:
properties = observable.object_.properties
attribute = MISPAttribute()
attribute.type, attribute.value, _ = self.handle_attribute_type(properties)
referenced_uuid = str(uuid.uuid4())
attribute.uuid = referenced_uuid
self.misp_event.add_attribute(**attribute)
misp_object.add_reference(referenced_uuid, 'observable', None, **attribute)
self.misp_event.add_object(**misp_object)
# Parse attributes of a portable executable, create the corresponding object,
# and return its uuid to build the reference for the file object generated at the same time
def parse_pe(self, properties):
@ -733,8 +750,8 @@ class StixParser():
# The value returned by the indicators or observables parser is a list of dictionaries
# These dictionaries are the attributes we add in an object, itself added in the MISP event
def handle_object_case(self, attribute_type, attribute_value, compl_data, to_ids=False, object_uuid=None):
misp_object = MISPObject(attribute_type, misp_objects_path_custom=_MISP_objects_path)
def handle_object_case(self, name, attribute_value, compl_data, to_ids=False, object_uuid=None):
misp_object = MISPObject(name, misp_objects_path_custom=_MISP_objects_path)
if object_uuid:
misp_object.uuid = object_uuid
for attribute in attribute_value:
@ -750,6 +767,63 @@ class StixParser():
misp_object.add_reference(uuid, 'connected-to')
self.misp_event.add_object(**misp_object)
################################################################################
## GALAXY PARSING FUNCTIONS USED BY BOTH SUBCLASSES ##
################################################################################
@staticmethod
def _get_galaxy_name(galaxy, feature):
if hasattr(galaxy, feature) and getattr(galaxy, feature):
return getattr(galaxy, feature)
for name in ('name', 'names'):
if hasattr(galaxy, name) and getattr(galaxy, name):
return list(value.value for value in getattr(galaxy, name))
return
def _parse_courses_of_action(self, courses_of_action):
for course_of_action in courses_of_action:
self.parse_galaxy(course_of_action, 'title', 'mitre-course-of-action')
def _parse_threat_actors(self, threat_actors):
for threat_actor in threat_actors:
self.parse_galaxy(threat_actor, 'title', 'threat-actor')
def _parse_ttp(self, ttp):
if ttp.behavior:
if ttp.behavior.attack_patterns:
for attack_pattern in ttp.behavior.attack_patterns:
self.parse_galaxy(attack_pattern, 'title', 'misp-attack-pattern')
if ttp.behavior.malware_instances:
for malware_instance in ttp.behavior.malware_instances:
if not malware_instance._XSI_TYPE or 'stix-maec' not in malware_instance._XSI_TYPE:
self.parse_galaxy(malware_instance, 'title', 'ransomware')
elif ttp.exploit_targets:
if ttp.exploit_targets.exploit_target:
for exploit_target in ttp.exploit_targets.exploit_target:
if exploit_target.item.vulnerabilities:
for vulnerability in exploit_target.item.vulnerabilities:
self.parse_galaxy(vulnerability, 'title', 'branded_vulnerability')
elif ttp.resources:
if ttp.resources.tools:
for tool in ttp.resources.tools:
self.parse_galaxy(tool, 'name', 'tool')
def parse_galaxy(self, galaxy, feature, default_value):
names = self._get_galaxy_name(galaxy, feature)
if names:
if isinstance(names, list):
for name in names:
self._resolve_galaxy(name, default_value)
else:
self._resolve_galaxy(names, default_value)
def _resolve_galaxy(self, name, default_value):
if name in self.synonyms_to_tag_names:
self.galaxies.update(self.synonyms_to_tag_names[name])
else:
self.galaxies.add('misp-galaxy:{}="{}"'.format(default_value, name))
################################################################################
## UTILITY FUNCTIONS USED BY PARSING FUNCTION ABOVE ##
################################################################################
@ -840,49 +914,35 @@ class StixFromMISPParser(StixParser):
if self.event.information_source and self.event.information_source.references:
for reference in self.event.information_source.references:
self.misp_event.add_attribute(**{'type': 'link', 'value': reference})
if package.courses_of_action:
self._parse_courses_of_action(package.courses_of_action)
if package.threat_actors:
self._parse_threat_actors(package.threat_actors)
if package.ttps:
for ttp in package.ttps.ttp:
ttp_id = '-'.join((part for part in ttp.id_.split('-')[-5:]))
ttp_type = 'galaxy' if ttp_id in self.galaxies_references else 'object'
ttp_type = 'galaxy' if ttp_id in self.object_references else 'object'
self.parse_ttp(ttp, ttp_type, ttp_id)
# if ttp.handling:
# self.parse_tlp_marking(ttp.handling)
self.set_event_fields()
def parse_ttp(self, ttp, ttp_type, ttp_id):
if ttp.behavior:
if ttp.behavior.attack_patterns:
to_call = 'parse_attack_pattern_{}'.format(ttp_type)
for attack_pattern in ttp.behavior.attack_patterns:
getattr(self, to_call)(attack_pattern, ttp_id)
if ttp.behavior.malware_instances:
for malware in ttp.behavior.malware_instances:
self.parse_malware_galaxy(malware, ttp_id)
elif ttp.exploit_targets:
if ttp.exploit_targets.exploit_target:
for et in ttp.exploit_targets.exploit_target:
if et.item.vulnerabilities:
to_call = 'parse_vulnerability_{}'.format(ttp_type)
for vulnerability in et.item.vulnerabilities:
getattr(self, to_call)(vulnerability, ttp_id)
elif et.item.weaknesses:
for weakness in et.item.weaknesses:
if ttp_type == 'object':
if ttp.behavior:
if ttp.behavior.attack_patterns:
for attack_pattern in ttp.behavior.attack_patterns:
self.parse_attack_pattern_object(attack_pattern, ttp_id)
elif ttp.exploit_targets and ttp.exploit_targets.exploit_target:
for exploit_target in ttp.exploit_targets.exploit_target:
if exploit_target.item.vulnerabilities:
for vulnerability in exploit_target.item.vulnerabilities:
self.parse_vulnerability_object(vulnerability, ttp_id)
if exploit_target.item.weaknesses:
for weakness in exploit_target.item.weaknesses:
self.parse_weakness_object(weakness, ttp_id)
elif ttp.resources:
if ttp.resources.tools:
for tool in ttp.resources.tools:
self.parse_tool_galaxy(tool, ttp_id)
def parse_attack_pattern_galaxy(self, attack_pattern, uuid):
name, value = attack_pattern.title.split(': ')
cluster_type = stix2misp_mapping._galaxy_mapping[name]
cluster = {'type': cluster_type, 'name': name, 'value': value,
'tag_name': 'misp-galaxy:{}="{}"'.format(cluster_type, value)}
if attack_pattern.description:
cluster['description'] = attack_pattern.description.value
if attack_pattern.capec_id:
cluster['meta'] = {'external_id': [attack_pattern.capec_id]}
self.galaxies[name].append(cluster)
else:
self._parse_ttp(ttp)
def parse_attack_pattern_object(self, attack_pattern, uuid):
attribute_type = 'text'
@ -899,42 +959,6 @@ class StixFromMISPParser(StixParser):
attack_pattern_object.add_attribute(**attribute)
self.misp_event.add_object(**attack_pattern_object)
def parse_malware_galaxy(self, malware, uuid):
name, value = malware.title.split(': ')
cluster_type = stix2misp_mapping._galaxy_mapping[name]
cluster = {'type': cluster_type, 'name': name, 'value': value,
'tag_name': 'misp-galaxy:{}="{}"'.format(cluster_type, value)}
if malware.description:
cluster['description'] = malware.description.value
if malware.names:
cluster['meta'] = {'synonyms': [name.value for name in malware.names]}
self.galaxies[name].append(cluster)
def parse_tool_galaxy(self, tool, uuid):
name, value = tool.name.split(': ')
cluster_type = stix2misp_mapping._galaxy_mapping[name]
cluster = {'type': cluster_type, 'name': name, 'value': value,
'tag_name': 'misp-galaxy:{}="{}"'.format(cluster_type, value)}
if tool.description:
cluster['description'] = tool.description.value
self.galaxies[name].append(cluster)
def parse_vulnerability_galaxy(self, vulnerability, uuid):
name = 'Branded Vulnerability'
value = vulnerability.title
cluster = {'type': 'branded-vulnerability', 'name': name, 'value': value,
'tag_name': 'misp-galaxy:branded-vulnerability="{}"'.format(value)}
if vulnerability.description:
cluster['description'] = vulnerability.description.value
meta = {}
if vulnerability.cve_id:
meta['aliases'] = [vulnerability.cve_id]
if vulnerability.references:
meta['refs'] = [reference for reference in vulnerability.references]
if meta:
cluster['meta'] = meta
self.galaxies[name].append(cluster)
def parse_vulnerability_object(self, vulnerability, uuid):
attributes = []
for key, mapping in stix2misp_mapping._vulnerability_object_mapping.items():
@ -1064,29 +1088,15 @@ class StixFromMISPParser(StixParser):
# Parse STIX object that we know will give MISP objects
def parse_misp_object_indicator(self, indicator):
item = indicator.item
name = item.title.split(': ')[0]
name = self._define_name(indicator.item.observable, indicator.relationship)
if name not in ('passive-dns'):
self.fill_misp_object(item, name, to_ids=True)
self.fill_misp_object(indicator.item, name, to_ids=True)
else:
if str(indicator.relationship) != "misc":
print("Unparsed Object type: {}".format(name), file=sys.stderr)
print(f"Unparsed Object type: {name}\n{indicator.to_json()}", file=sys.stderr)
def parse_misp_object_observable(self, observable):
object_type = str(observable.relationship)
observable = observable.item
observable_id = observable.id_
if object_type == "file":
name = "registry-key" if "WinRegistryKey" in observable_id else "file"
elif "Custom" in observable_id:
name = observable_id.split("Custom")[0].split(":")[1]
elif object_type == "network":
if "ObservableComposition" in observable_id:
name = observable_id.split("_")[0].split(":")[1]
else:
name = cybox_to_misp_object[observable_id.split('-')[0].split(':')[1]]
else:
name = cybox_to_misp_object[observable_id.split('-')[0].split(':')[1]]
name = self._define_name(observable.item, observable.relationship)
try:
self.fill_misp_object(observable, name)
except Exception:
@ -1152,16 +1162,27 @@ class StixFromMISPParser(StixParser):
self.misp_event.add_attribute(**{'type': 'vulnerability', 'value': vulnerability.cve_id})
def parse_related_galaxies(self):
galaxies_references = []
for feature in ('coa_taken', 'coa_requested'):
coas = getattr(self.event, feature)
if coas:
galaxies_references.extend([coa.course_of_action.idref for coa in coas])
object_references = []
for coa_taken in self.event.coa_taken:
self.parse_course_of_action(coa_taken.course_of_action)
if self.event.attributed_threat_actors:
galaxies_references.extend([ta.item.idref for ta in self.event.attributed_threat_actors.threat_actor])
object_references.extend([ta.item.idref for ta in self.event.attributed_threat_actors.threat_actor])
if self.event.leveraged_ttps and self.event.leveraged_ttps.ttp:
galaxies_references.extend([ttp.item.idref for ttp in self.event.leveraged_ttps.ttp])
self.galaxies_references = tuple('-'.join((r for r in ref.split('-')[-5:])) for ref in galaxies_references)
object_references.extend([ttp.item.idref for ttp in self.event.leveraged_ttps.ttp])
self.object_references = tuple('-'.join((r for r in ref.split('-')[-5:])) for ref in object_references if ref is not None)
@staticmethod
def _define_name(observable, relationship):
observable_id = observable.id_
if relationship == "file":
return "registry-key" if "WinRegistryKey" in observable_id else "file"
if "Custom" in observable_id:
return observable_id.split("Custom")[0].split(":")[1]
if relationship == "network":
if "ObservableComposition" in observable_id:
return observable_id.split("_")[0].split(":")[1]
return cybox_to_misp_object[observable_id.split('-')[0].split(':')[1]]
return cybox_to_misp_object[observable_id.split('-')[0].split(':')[1]]
def fetch_event_info(self):
info = self.get_event_info()
@ -1204,10 +1225,13 @@ class ExternalStixParser(StixParser):
self.parse_external_indicators(self.event.indicators)
if self.event.observables:
self.parse_external_observable(self.event.observables.observables)
if self.event.ttps:
self.parse_ttps(self.event.ttps.ttp)
if self.event.courses_of_action:
self.parse_coa(self.event.courses_of_action)
if any(getattr(self.event, feature) for feature in ('ttps', 'courses_of_action', 'threat_actors')):
if self.event.ttps:
self.parse_ttps(self.event.ttps.ttp)
if self.event.courses_of_action:
self.parse_coa(self.event.courses_of_action)
if self.event.threat_actors:
self._parse_threat_actors(self.event.threat_actors)
if self.dns_objects:
self.resolve_dns_objects()
self.set_distribution()
@ -1235,26 +1259,7 @@ class ExternalStixParser(StixParser):
# Parse the courses of action field of an external STIX document
def parse_coa(self, courses_of_action):
for coa in courses_of_action:
misp_object = MISPObject('course-of-action', misp_objects_path_custom=_MISP_objects_path)
if coa.title:
attribute = {'type': 'text', 'object_relation': 'name',
'value': coa.title}
misp_object.add_attribute(**attribute)
for prop, properties_key in stix2misp_mapping._coa_mapping.items():
if getattr(coa, prop):
attribute = {'type': 'text', 'object_relation': prop.replace('_', ''),
'value': attrgetter('{}.{}'.format(prop, properties_key))(coa)}
misp_object.add_attribute(**attribute)
if coa.parameter_observables:
for observable in coa.parameter_observables.observables:
properties = observable.object_.properties
attribute = MISPAttribute()
attribute.type, attribute.value, _ = self.handle_attribute_type(properties)
referenced_uuid = str(uuid.uuid4())
attribute.uuid = referenced_uuid
self.misp_event.add_attribute(**attribute)
misp_object.add_reference(referenced_uuid, 'observable', None, **attribute)
self.misp_event.add_object(**misp_object)
self.parse_course_of_action(coa)
# Parse description of an external indicator or observable and add it in the MISP event as an attribute
def parse_description(self, stix_object):
@ -1342,21 +1347,8 @@ class ExternalStixParser(StixParser):
# Parse the ttps field of an external STIX document
def parse_ttps(self, ttps):
galaxy_types = {value: key for key, value in stix2misp_mapping._galaxy_mapping.items() if 'malware' in value}
for ttp in ttps:
if ttp.behavior and ttp.behavior.malware_instances:
mi = ttp.behavior.malware_instances[0]
mi_types = mi.types
mi_name = mi_types[0].value if mi_types and mi_types[0].value in galaxy_types else "Malware"
cluster_type = stix2misp_mapping._galaxy_mapping[mi_name]
value = ttp.title
cluster = {'name': mi_name, 'type': cluster_type, 'value': value,
'tag_name': 'misp-galaxy:{}="{}"'.format(cluster_type, value)}
if mi.description:
cluster['description'] = mi.description.value
if mi.names:
cluster['meta'] = {'synonyms': [name.value for name in mi.names]}
self.galaxies[mi_name].append(cluster)
self._parse_ttp(ttp)
# Parse a DNS object
def resolve_dns_objects(self):

View File

@ -9,6 +9,7 @@ class Comparer():
event1.load_file(filename1)
event2 = MISPEvent()
event2.load_file(filename2)
self.__jq_misp_event(filename2)
self.tags1 = defaultdict(set)
self.tags2 = defaultdict(set)
self.galaxies1 = defaultdict(set)
@ -154,6 +155,13 @@ class Comparer():
if object['name'] == 'pe' and uuid1 in getattr(self, f'references{first}'):
self._iterate_through_pe_and_sections(uuid1, first, second, uuid2)
@staticmethod
def __jq_misp_event(filename):
with open(filename, 'rt', encoding='utf-8') as f:
json_event = json.loads(f.read())
with open(filename, 'wt', encoding='utf-8') as f:
f.write(json.dumps(json_event, indent=4))
def main(args):
comparer = Comparer(*args[1:])
comparer.compare_attributes()

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -45,29 +45,29 @@
xmlns:xal="urn:oasis:names:tc:ciq:xal:3"
xmlns:xnl="urn:oasis:names:tc:ciq:xnl:3"
xmlns:xpil="urn:oasis:names:tc:ciq:xpil:3"
xmlns:ORGNAME="http://127.0.0.1"
xmlns:ORGNAME="https://localhost"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
id="ORGNAME:Package-0e21f8ad-949e-4d51-b81a-d64b8b79a365" version="1.1.1" timestamp="2019-09-24T11:08:05.255008">
id="ORGNAME:Package-2c3ea3a1-be16-4853-b751-2af5a210a828" version="1.1.1" timestamp="2020-03-23T12:34:17.787525">
<stix:STIX_Header>
<stix:Title>Export from ORGNAME MISP</stix:Title>
<stix:Package_Intent xsi:type="stixVocabs:PackageIntentVocab-1.0">Threat Report</stix:Package_Intent>
</stix:STIX_Header>
<stix:Related_Packages>
<stix:Related_Package>
<stix:Package id="ORGNAME:STIXPackage-5ac4db18-0c58-4436-a3fa-01ef0a00020f" version="1.1.1" timestamp="2019-09-23T16:23:16">
<stix:Package id="ORGNAME:STIXPackage-5ac4db18-0c58-4436-a3fa-01ef0a00020f" version="1.1.1" timestamp="2020-03-23T10:18:50">
<stix:STIX_Header>
<stix:Title>Export from ORGNAME MISP</stix:Title>
<stix:Package_Intent xsi:type="stixVocabs:PackageIntentVocab-1.0">Threat Report</stix:Package_Intent>
</stix:STIX_Header>
<stix:TTPs>
<stix:TTP id="ORGNAME:TTP-dcb864dc-775f-11e7-9fbb-1f41b4996683" timestamp="2019-09-24T09:08:05.529834+00:00" xsi:type='ttp:TTPType'>
<ttp:Title>Attack Pattern: mitre-attack-pattern (MISP GalaxyCluster #7454)</ttp:Title>
<stix:TTP id="ORGNAME:TTP-dcb864dc-775f-11e7-9fbb-1f41b4996683" timestamp="2020-03-23T11:34:18.043819+00:00" xsi:type='ttp:TTPType'>
<ttp:Title>Attack Pattern (MISP Galaxy)</ttp:Title>
<ttp:Behavior>
<ttp:Attack_Patterns>
<ttp:Attack_Pattern capec_id="CAPEC-471" id="ORGNAME:AttackPattern-dcb864dc-775f-11e7-9fbb-1f41b4996683">
<ttp:Title>Attack Pattern: DLL Search Order Hijacking - T1038</ttp:Title>
<ttp:Attack_Pattern capec_id="CAPEC-471" id="ORGNAME:AttackPattern-46944654-fcc1-4f63-9dad-628102376586">
<ttp:Title>DLL Search Order Hijacking - T1038</ttp:Title>
<ttp:Description>Windows systems use a common method to look for required DLLs to load into a program. (Citation: Microsoft DLL Search) Adversaries may take advantage of the Windows DLL search order and programs that ambiguously specify DLLs to gain privilege escalation and persistence.
Adversaries may perform DLL preloading, also called binary planting attacks, (Citation: OWASP Binary Planting) by placing a malicious DLL with the same name as an ambiguously specified DLL in a location that Windows searches before the legitimate DLL. Often this location is the current working directory of the program. Remote DLL preloading attacks occur when a program sets its current directory to a remote location such as a Web share before loading a DLL. (Citation: Microsoft 2269637) Adversaries may use this behavior to cause the program to load a malicious DLL.
@ -81,34 +81,34 @@
</ttp:Attack_Patterns>
</ttp:Behavior>
</stix:TTP>
<stix:TTP id="ORGNAME:TTP-d752161c-78f6-11e7-a0ea-bfa79b407ce4" timestamp="2019-09-24T09:08:05.530979+00:00" xsi:type='ttp:TTPType'>
<ttp:Title>Malware: mitre-malware (MISP GalaxyCluster #6734)</ttp:Title>
<stix:TTP id="ORGNAME:TTP-d752161c-78f6-11e7-a0ea-bfa79b407ce4" timestamp="2020-03-23T11:34:18.044606+00:00" xsi:type='ttp:TTPType'>
<ttp:Title>Malware (MISP Galaxy)</ttp:Title>
<ttp:Behavior>
<ttp:Malware>
<ttp:Malware_Instance id="ORGNAME:MalwareInstance-d752161c-78f6-11e7-a0ea-bfa79b407ce4">
<ttp:Malware_Instance id="ORGNAME:MalwareInstance-7551188b-8f91-4d34-8350-0d0c57b2b913">
<ttp:Name>Elise</ttp:Name>
<ttp:Name>BKDR_ESILE</ttp:Name>
<ttp:Name>Page</ttp:Name>
<ttp:Title>Malware: Elise - S0081</ttp:Title>
<ttp:Title>Elise - S0081</ttp:Title>
<ttp:Description>[Elise](https://attack.mitre.org/software/S0081) is a custom backdoor Trojan that appears to be used exclusively by [Lotus Blossom](https://attack.mitre.org/groups/G0030). It is part of a larger group of
tools referred to as LStudio, ST Group, and APT0LSTU. (Citation: Lotus Blossom Jun 2015)(Citation: Accenture Dragonfish Jan 2018)</ttp:Description>
</ttp:Malware_Instance>
</ttp:Malware>
</ttp:Behavior>
</stix:TTP>
<stix:TTP id="ORGNAME:TTP-d700dc5c-78f6-11e7-a476-5f748c8e4fe0" timestamp="2019-09-24T09:08:05.531213+00:00" xsi:type='ttp:TTPType'>
<ttp:Title>Tool: mitre-tool (MISP GalaxyCluster #7242)</ttp:Title>
<stix:TTP id="ORGNAME:TTP-d700dc5c-78f6-11e7-a476-5f748c8e4fe0" timestamp="2020-03-23T11:34:18.044807+00:00" xsi:type='ttp:TTPType'>
<ttp:Title>Tool (MISP Galaxy)</ttp:Title>
<ttp:Resources>
<ttp:Tools>
<ttp:Tool id="ORGNAME:ToolInformation-d700dc5c-78f6-11e7-a476-5f748c8e4fe0">
<cyboxCommon:Name>Mitre Tool: ifconfig - S0101</cyboxCommon:Name>
<ttp:Tool id="ORGNAME:ToolInformation-362dc67f-4e85-4562-9dac-1b6b7f3ec4b5">
<cyboxCommon:Name>ifconfig - S0101</cyboxCommon:Name>
<cyboxCommon:Description>[ifconfig](https://attack.mitre.org/software/S0101) is a Unix-based utility used to gather information about and interact with the TCP/IP settings on a system. (Citation: Wikipedia Ifconfig)</cyboxCommon:Description>
</ttp:Tool>
</ttp:Tools>
</ttp:Resources>
</stix:TTP>
<stix:TTP id="ORGNAME:TTP-5ac4db18-0150-4435-b016-01ef0a00020f" timestamp="2019-08-12T11:50:38" xsi:type='ttp:TTPType'>
<ttp:Title>External analysis: CVE-2017-11774 (MISP Attribute #253573)</ttp:Title>
<ttp:Title>External analysis: CVE-2017-11774 (MISP Attribute)</ttp:Title>
<ttp:Exploit_Targets>
<ttp:Exploit_Target>
<stixCommon:Exploit_Target id="ORGNAME:ExploitTarget-5ac4db18-0150-4435-b016-01ef0a00020f" timestamp="2019-08-12T11:50:38" xsi:type='et:ExploitTargetType'>
@ -127,51 +127,8 @@
</marking:Marking>
</ttp:Handling>
</stix:TTP>
<stix:TTP id="ORGNAME:TTP-6c14bf61-c388-4d10-aeb6-9a0881550a39" timestamp="2019-08-12T12:20:12" xsi:type='ttp:TTPType'>
<ttp:Title>vulnerability: vulnerability (MISP Object #16309)</ttp:Title>
<ttp:Exploit_Targets>
<ttp:Exploit_Target>
<stixCommon:Exploit_Target id="ORGNAME:ExploitTarget-6c14bf61-c388-4d10-aeb6-9a0881550a39" timestamp="2019-08-12T12:20:12" xsi:type='et:ExploitTargetType'>
<et:Vulnerability>
<et:Description>Microsoft Outlook 2010 SP2, Outlook 2013 SP1 and RT SP1, and Outlook 2016 allow an attacker to execute arbitrary commands, due to how Microsoft Office handles objects in memory, aka "Microsoft Outlook Security Feature Bypass Vulnerability."</et:Description>
<et:CVE_ID>CVE-2017-11774</et:CVE_ID>
<et:CVSS_Score>
<et:Overall_Score>6.8</et:Overall_Score>
</et:CVSS_Score>
<et:Published_DateTime precision="second">2017-10-13T09:29:00.427000</et:Published_DateTime>
<et:References>
<stixCommon:Reference>http://www.securityfocus.com/bid/101098</stixCommon:Reference>
<stixCommon:Reference>http://www.securitytracker.com/id/1039542</stixCommon:Reference>
<stixCommon:Reference>https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2017-11774</stixCommon:Reference>
<stixCommon:Reference>https://sensepost.com/blog/2017/outlook-home-page-another-ruler-vector/</stixCommon:Reference>
</et:References>
</et:Vulnerability>
</stixCommon:Exploit_Target>
</ttp:Exploit_Target>
</ttp:Exploit_Targets>
<ttp:Related_TTPs>
<ttp:Related_TTP>
<stixCommon:Relationship>related-to </stixCommon:Relationship>
<stixCommon:TTP idref="ORGNAME:TTP-5ac4db18-0150-4435-b016-01ef0a00020f" timestamp="2019-08-12T11:50:38" xsi:type='ttp:TTPType'/>
</ttp:Related_TTP>
<ttp:Related_TTP>
<stixCommon:Relationship>weakened-by </stixCommon:Relationship>
<stixCommon:TTP idref="ORGNAME:TTP-13f1e6e7-87ab-4ced-bb84-d9461b3eb36a" timestamp="2019-08-12T12:20:13" xsi:type='ttp:TTPType'/>
</ttp:Related_TTP>
<ttp:Related_TTP>
<stixCommon:Relationship>targeted-by </stixCommon:Relationship>
<stixCommon:TTP idref="ORGNAME:TTP-7edfed6f-7b06-46ea-8aba-9c4075df40d1" timestamp="2019-08-12T12:20:14" xsi:type='ttp:TTPType'/>
</ttp:Related_TTP>
</ttp:Related_TTPs>
<ttp:Handling>
<marking:Marking>
<marking:Controlled_Structure>../../../descendant-or-self::node()</marking:Controlled_Structure>
<marking:Marking_Structure xsi:type='tlpMarking:TLPMarkingStructureType' color="WHITE"/>
</marking:Marking>
</ttp:Handling>
</stix:TTP>
<stix:TTP id="ORGNAME:TTP-13f1e6e7-87ab-4ced-bb84-d9461b3eb36a" timestamp="2019-08-12T12:20:13" xsi:type='ttp:TTPType'>
<ttp:Title>vulnerability: weakness (MISP Object #16310)</ttp:Title>
<ttp:Title>vulnerability: weakness (MISP Object)</ttp:Title>
<ttp:Exploit_Targets>
<ttp:Exploit_Target>
<stixCommon:Exploit_Target id="ORGNAME:ExploitTarget-13f1e6e7-87ab-4ced-bb84-d9461b3eb36a" timestamp="2019-08-12T12:20:13" xsi:type='et:ExploitTargetType'>
@ -190,10 +147,10 @@
</ttp:Handling>
</stix:TTP>
<stix:TTP id="ORGNAME:TTP-7edfed6f-7b06-46ea-8aba-9c4075df40d1" timestamp="2019-08-12T12:20:14" xsi:type='ttp:TTPType'>
<ttp:Title>vulnerability: attack-pattern (MISP Object #16321)</ttp:Title>
<ttp:Title>vulnerability: attack-pattern (MISP Object)</ttp:Title>
<ttp:Behavior>
<ttp:Attack_Patterns>
<ttp:Attack_Pattern capec_id="9" id="ORGNAME:AttackPattern-7edfed6f-7b06-46ea-8aba-9c4075df40d1">
<ttp:Attack_Pattern capec_id="CAPEC-9" id="ORGNAME:AttackPattern-7edfed6f-7b06-46ea-8aba-9c4075df40d1">
<ttp:Title>Buffer Overflow in Local Command-Line Utilities</ttp:Title>
<ttp:Description>This attack targets command-line utilities available in a number of shells. An attacker can leverage a vulnerability found in a command-line utility to escalate privilege to root.</ttp:Description>
</ttp:Attack_Pattern>
@ -206,14 +163,53 @@
</marking:Marking>
</ttp:Handling>
</stix:TTP>
<stix:TTP id="ORGNAME:TTP-5e57b76e-e974-451a-b893-1440a964451a" timestamp="2020-03-23T10:18:50" xsi:type='ttp:TTPType'>
<ttp:Title>vulnerability: vulnerability (MISP Object)</ttp:Title>
<ttp:Exploit_Targets>
<ttp:Exploit_Target>
<stixCommon:Exploit_Target id="ORGNAME:ExploitTarget-5e57b76e-e974-451a-b893-1440a964451a" timestamp="2020-03-23T10:18:50" xsi:type='et:ExploitTargetType'>
<et:Vulnerability>
<et:Description>Microsoft Outlook 2010 SP2, Outlook 2013 SP1 and RT SP1, and Outlook 2016 allow an attacker to execute arbitrary commands, due to how Microsoft Office handles objects in memory, aka "Microsoft Outlook Security Feature Bypass Vulnerability."</et:Description>
<et:CVE_ID>CVE-2017-11774</et:CVE_ID>
<et:CVSS_Score>
<et:Overall_Score>6.8</et:Overall_Score>
</et:CVSS_Score>
<et:Published_DateTime precision="second">2017-10-13T07:29:00.427000+00:00</et:Published_DateTime>
<et:References>
<stixCommon:Reference>http://www.securityfocus.com/bid/101098</stixCommon:Reference>
<stixCommon:Reference>http://www.securitytracker.com/id/1039542</stixCommon:Reference>
<stixCommon:Reference>https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2017-11774</stixCommon:Reference>
<stixCommon:Reference>https://sensepost.com/blog/2017/outlook-home-page-another-ruler-vector/</stixCommon:Reference>
</et:References>
</et:Vulnerability>
</stixCommon:Exploit_Target>
</ttp:Exploit_Target>
</ttp:Exploit_Targets>
<ttp:Related_TTPs>
<ttp:Related_TTP>
<stixCommon:Relationship>targeted-by</stixCommon:Relationship>
<stixCommon:TTP idref="ORGNAME:TTP-7edfed6f-7b06-46ea-8aba-9c4075df40d1" timestamp="2019-08-12T12:20:14" xsi:type='ttp:TTPType'/>
</ttp:Related_TTP>
<ttp:Related_TTP>
<stixCommon:Relationship>weakened-by</stixCommon:Relationship>
<stixCommon:TTP idref="ORGNAME:TTP-13f1e6e7-87ab-4ced-bb84-d9461b3eb36a" timestamp="2019-08-12T12:20:13" xsi:type='ttp:TTPType'/>
</ttp:Related_TTP>
</ttp:Related_TTPs>
<ttp:Handling>
<marking:Marking>
<marking:Controlled_Structure>../../../descendant-or-self::node()</marking:Controlled_Structure>
<marking:Marking_Structure xsi:type='tlpMarking:TLPMarkingStructureType' color="WHITE"/>
</marking:Marking>
</ttp:Handling>
</stix:TTP>
</stix:TTPs>
<stix:Incidents>
<stix:Incident id="ORGNAME:Incident-5ac4db18-0c58-4436-a3fa-01ef0a00020f" timestamp="2019-09-23T16:23:25" xsi:type='incident:IncidentType'>
<stix:Incident id="ORGNAME:Incident-5ac4db18-0c58-4436-a3fa-01ef0a00020f" timestamp="2020-03-23T10:20:55" xsi:type='incident:IncidentType'>
<incident:Title>STIX observables test event</incident:Title>
<incident:External_ID source="MISP Event">1256</incident:External_ID>
<incident:Time>
<incident:Incident_Discovery precision="second">2018-03-28T00:00:00</incident:Incident_Discovery>
<incident:Incident_Reported precision="second">2019-09-23T16:23:25</incident:Incident_Reported>
<incident:Incident_Reported precision="second">2020-03-23T10:20:55</incident:Incident_Reported>
</incident:Time>
<incident:Reporter>
<stixCommon:Identity>
@ -283,22 +279,6 @@
</cybox:Object>
</stixCommon:Observable>
</incident:Related_Observable>
<incident:Related_Observable>
<stixCommon:Relationship>Payload delivery</stixCommon:Relationship>
<stixCommon:Observable id="ORGNAME:File-5ac4db18-fc8c-4b50-9ad0-01ef0a00020f">
<cybox:Object id="ORGNAME:FileObject-5ac4db18-fc8c-4b50-9ad0-01ef0a00020f">
<cybox:Properties xsi:type="FileObj:FileObjectType">
<FileObj:File_Name condition="Equals">oui</FileObj:File_Name>
<FileObj:Hashes>
<cyboxCommon:Hash>
<cyboxCommon:Type condition="Equals" xsi:type="cyboxVocabs:HashNameVocab-1.0">MD5</cyboxCommon:Type>
<cyboxCommon:Simple_Hash_Value condition="Equals">b2a5abfeef9e36964281a31e17b57c97</cyboxCommon:Simple_Hash_Value>
</cyboxCommon:Hash>
</FileObj:Hashes>
</cybox:Properties>
</cybox:Object>
</stixCommon:Observable>
</incident:Related_Observable>
<incident:Related_Observable>
<stixCommon:Relationship>Network activity</stixCommon:Relationship>
<stixCommon:Observable id="ORGNAME:Address-5ac4db18-62f0-4f12-906d-01ef0a00020f">
@ -454,16 +434,6 @@
</cybox:Object>
</stixCommon:Observable>
</incident:Related_Observable>
<incident:Related_Observable>
<stixCommon:Relationship>External analysis</stixCommon:Relationship>
<stixCommon:Observable id="ORGNAME:File-5ac4db18-7328-4665-93b2-01ef0a00020f">
<cybox:Object id="ORGNAME:FileObject-5ac4db18-7328-4665-93b2-01ef0a00020f">
<cybox:Properties xsi:type="FileObj:FileObjectType">
<FileObj:File_Name>oui.oui</FileObj:File_Name>
</cybox:Properties>
</cybox:Object>
</stixCommon:Observable>
</incident:Related_Observable>
<incident:Related_Observable>
<stixCommon:Relationship>Network activity</stixCommon:Relationship>
<stixCommon:Observable id="ORGNAME:System-5ac4db18-ddfc-4fde-b936-01ef0a00020f">
@ -527,6 +497,17 @@
</cybox:Object>
</stixCommon:Observable>
</incident:Related_Observable>
<incident:Related_Observable>
<stixCommon:Relationship>Payload delivery</stixCommon:Relationship>
<stixCommon:Observable id="ORGNAME:Artifact-5e384a51-9868-4cba-b7c0-3c7ba964451a">
<cybox:Title>oui.oui</cybox:Title>
<cybox:Object id="ORGNAME:ArtifactObject-5e384a51-9868-4cba-b7c0-3c7ba964451a">
<cybox:Properties xsi:type="ArtifactObj:ArtifactObjectType">
<ArtifactObj:Raw_Artifact><![CDATA[ZWNobyAiREFOR0VST1VTIE1BTFdBUkUiIAoK]]></ArtifactObj:Raw_Artifact>
</cybox:Properties>
</cybox:Object>
</stixCommon:Observable>
</incident:Related_Observable>
<incident:Related_Observable>
<stixCommon:Relationship>file</stixCommon:Relationship>
<stixCommon:Observable id="ORGNAME:WinRegistryKey-5ac4db18-7ba4-4c60-ae41-01ef0a00020f">
@ -608,56 +589,6 @@
</cybox:Observable_Composition>
</stixCommon:Observable>
</incident:Related_Observable>
<incident:Related_Observable>
<stixCommon:Relationship>network</stixCommon:Relationship>
<stixCommon:Observable id="ORGNAME:EmailMessage-5ac4db18-1c74-4220-a942-01ef0a00020f">
<cybox:Object id="ORGNAME:EmailMessageObject-5ac4db18-1c74-4220-a942-01ef0a00020f">
<cybox:Properties xsi:type="EmailMessageObj:EmailMessageObjectType">
<EmailMessageObj:Header>
<EmailMessageObj:To>
<EmailMessageObj:Recipient xsi:type="AddressObj:AddressObjectType" category="e-mail">
<AddressObj:Address_Value>oui@to.lu</AddressObj:Address_Value>
</EmailMessageObj:Recipient>
</EmailMessageObj:To>
<EmailMessageObj:CC>
<EmailMessageObj:Recipient xsi:type="AddressObj:AddressObjectType" category="e-mail">
<AddressObj:Address_Value>oui1@cc.com</AddressObj:Address_Value>
</EmailMessageObj:Recipient>
<EmailMessageObj:Recipient xsi:type="AddressObj:AddressObjectType" category="e-mail">
<AddressObj:Address_Value>oui2@cc.com</AddressObj:Address_Value>
</EmailMessageObj:Recipient>
</EmailMessageObj:CC>
<EmailMessageObj:From xsi:type="AddressObj:AddressObjectType" category="e-mail">
<AddressObj:Address_Value condition="Equals">oui@source.com</AddressObj:Address_Value>
</EmailMessageObj:From>
<EmailMessageObj:Subject condition="Equals">Le Oui</EmailMessageObj:Subject>
<EmailMessageObj:Reply_To xsi:type="AddressObj:AddressObjectType" category="e-mail">
<AddressObj:Address_Value condition="Equals">oui@reply.com</AddressObj:Address_Value>
</EmailMessageObj:Reply_To>
<EmailMessageObj:X_Mailer condition="Equals">oui_X-mailer</EmailMessageObj:X_Mailer>
</EmailMessageObj:Header>
<EmailMessageObj:Attachments>
<EmailMessageObj:File object_reference="ORGNAME:File-aaac3122-2010-43fa-b793-b875e1a92623"/>
<EmailMessageObj:File object_reference="ORGNAME:File-5e8dc4f6-b808-423d-be18-65a46a7edcb2"/>
</EmailMessageObj:Attachments>
</cybox:Properties>
<cybox:Related_Objects>
<cybox:Related_Object id="ORGNAME:File-aaac3122-2010-43fa-b793-b875e1a92623">
<cybox:Properties xsi:type="FileObj:FileObjectType">
<FileObj:File_Name condition="Equals">oui.jpg</FileObj:File_Name>
</cybox:Properties>
<cybox:Relationship xsi:type="cyboxVocabs:ObjectRelationshipVocab-1.1">Contains</cybox:Relationship>
</cybox:Related_Object>
<cybox:Related_Object id="ORGNAME:File-5e8dc4f6-b808-423d-be18-65a46a7edcb2">
<cybox:Properties xsi:type="FileObj:FileObjectType">
<FileObj:File_Name condition="Equals">oui.png</FileObj:File_Name>
</cybox:Properties>
<cybox:Relationship xsi:type="cyboxVocabs:ObjectRelationshipVocab-1.1">Contains</cybox:Relationship>
</cybox:Related_Object>
</cybox:Related_Objects>
</cybox:Object>
</stixCommon:Observable>
</incident:Related_Observable>
<incident:Related_Observable>
<stixCommon:Relationship>network</stixCommon:Relationship>
<stixCommon:Observable id="ORGNAME:ip-port_ObservableComposition-5ac4db19-f4d0-460f-94c8-01ef0a00020f">
@ -729,7 +660,7 @@
<stixCommon:Observable id="ORGNAME:NetworkSocket-5afb332f-1fbc-4028-be0d-02070a00020f">
<cybox:Object id="ORGNAME:NetworkSocketObject-5afb332f-1fbc-4028-be0d-02070a00020f">
<cybox:Properties xsi:type="NetworkSocketObj:NetworkSocketObjectType" is_blocking="false" is_listening="true">
<NetworkSocketObj:Address_Family>AF_UNSPEC</NetworkSocketObj:Address_Family>
<NetworkSocketObj:Address_Family>AF_SECURITY</NetworkSocketObj:Address_Family>
<NetworkSocketObj:Domain>PF_UNSPEC</NetworkSocketObj:Domain>
<NetworkSocketObj:Local_Address xsi:type="SocketAddressObj:SocketAddressObjectType">
<SocketAddressObj:IP_Address xsi:type="AddressObj:AddressObjectType" category="ipv4-addr" is_source="true" is_destination="false">
@ -744,6 +675,9 @@
<SocketAddressObj:IP_Address xsi:type="AddressObj:AddressObjectType" category="ipv4-addr" is_source="false" is_destination="true">
<AddressObj:Address_Value condition="Equals">8.8.8.8</AddressObj:Address_Value>
</SocketAddressObj:IP_Address>
<SocketAddressObj:Hostname xsi:type="HostnameObj:HostnameObjectType">
<HostnameObj:Hostname_Value condition="Equals">google.com</HostnameObj:Hostname_Value>
</SocketAddressObj:Hostname>
<SocketAddressObj:Port xsi:type="PortObj:PortObjectType">
<PortObj:Port_Value condition="Equals">443</PortObj:Port_Value>
</SocketAddressObj:Port>
@ -752,17 +686,6 @@
</cybox:Object>
</stixCommon:Observable>
</incident:Related_Observable>
<incident:Related_Observable>
<stixCommon:Relationship>misc</stixCommon:Relationship>
<stixCommon:Observable id="ORGNAME:Process-5afbed53-7eb0-4a86-ac84-021d0a00020f">
<cybox:Object id="ORGNAME:ProcessObject-5afbed53-7eb0-4a86-ac84-021d0a00020f">
<cybox:Properties xsi:type="ProcessObj:ProcessObjectType">
<ProcessObj:PID>2510</ProcessObj:PID>
<ProcessObj:Name>Ça_process</ProcessObj:Name>
</cybox:Properties>
</cybox:Object>
</stixCommon:Observable>
</incident:Related_Observable>
<incident:Related_Observable>
<stixCommon:Relationship>network</stixCommon:Relationship>
<stixCommon:Observable id="ORGNAME:Whois-5b0d1bd1-7508-4002-85d0-026b0a00020f">
@ -838,6 +761,99 @@
</cybox:Object>
</stixCommon:Observable>
</incident:Related_Observable>
<incident:Related_Observable>
<stixCommon:Relationship>file</stixCommon:Relationship>
<stixCommon:Observable id="ORGNAME:file_ObservableComposition-5e384a61-41f4-4345-ab87-3ccda964451a">
<cybox:Observable_Composition operator="AND">
<cybox:Observable id="ORGNAME:Artifact-5e384a61-44d8-448c-9d3e-3ccda964451a">
<cybox:Title>oui</cybox:Title>
<cybox:Object id="ORGNAME:ArtifactObject-5e384a61-44d8-448c-9d3e-3ccda964451a">
<cybox:Properties xsi:type="ArtifactObj:ArtifactObjectType">
<ArtifactObj:Hashes>
<cyboxCommon:Hash>
<cyboxCommon:Type condition="Equals" xsi:type="cyboxVocabs:HashNameVocab-1.0">MD5</cyboxCommon:Type>
<cyboxCommon:Simple_Hash_Value condition="Equals">8764605c6f388c89096b534d33565802</cyboxCommon:Simple_Hash_Value>
</cyboxCommon:Hash>
</ArtifactObj:Hashes>
<ArtifactObj:Raw_Artifact><![CDATA[UEsDBAoACQAAAKuLQ1AvUbiwLwAAACMAAAAgABwAODc2NDYwNWM2ZjM4OGM4OTA5NmI1MzRkMzM1NjU4MDJVVAkAA2FKOF5hSjhedXgLAAEEIQAAAAQhAAAAr8yRUrxnnbTmZ+TfII2jzPn2bHzIVP86IGSVrTVNrA3vHFfKU+ESaEcVKbOyOXdQSwcIL1G4sC8AAAAjAAAAUEsDBAoACQAAAKuLQ1BAAezaDwAAAAMAAAAtABwAODc2NDYwNWM2ZjM4OGM4OTA5NmI1MzRkMzM1NjU4MDIuZmlsZW5hbWUudHh0VVQJAANhSjheYUo4XnV4CwABBCEAAAAEIQAAAGWLK6zSjg7CdftqyJII31BLBwhAAezaDwAAAAMAAABQSwECHgMKAAkAAACri0NQL1G4sC8AAAAjAAAAIAAYAAAAAAABAAAApIEAAAAAODc2NDYwNWM2ZjM4OGM4OTA5NmI1MzRkMzM1NjU4MDJVVAUAA2FKOF51eAsAAQQhAAAABCEAAABQSwECHgMKAAkAAACri0NQQAHs2g8AAAADAAAALQAYAAAAAAABAAAApIGZAAAAODc2NDYwNWM2ZjM4OGM4OTA5NmI1MzRkMzM1NjU4MDIuZmlsZW5hbWUudHh0VVQFAANhSjhedXgLAAEEIQAAAAQhAAAAUEsFBgAAAAACAAIA2QAAAB8BAAAAAA==]]></ArtifactObj:Raw_Artifact>
</cybox:Properties>
</cybox:Object>
</cybox:Observable>
<cybox:Observable id="ORGNAME:File-5e384a61-41f4-4345-ab87-3ccda964451a">
<cybox:Object id="ORGNAME:FileObject-5e384a61-41f4-4345-ab87-3ccda964451a">
<cybox:Properties xsi:type="FileObj:FileObjectType">
<FileObj:File_Name condition="Equals">oui</FileObj:File_Name>
<FileObj:Size_In_Bytes condition="Equals">35</FileObj:Size_In_Bytes>
<FileObj:Hashes>
<cyboxCommon:Hash>
<cyboxCommon:Type condition="Equals" xsi:type="cyboxVocabs:HashNameVocab-1.0">MD5</cyboxCommon:Type>
<cyboxCommon:Simple_Hash_Value condition="Equals">8764605c6f388c89096b534d33565802</cyboxCommon:Simple_Hash_Value>
</cyboxCommon:Hash>
<cyboxCommon:Hash>
<cyboxCommon:Type condition="Equals" xsi:type="cyboxVocabs:HashNameVocab-1.0">SHA1</cyboxCommon:Type>
<cyboxCommon:Simple_Hash_Value condition="Equals">46aba99aa7158e4609aaa72b50990842fd22ae86</cyboxCommon:Simple_Hash_Value>
</cyboxCommon:Hash>
<cyboxCommon:Hash>
<cyboxCommon:Type condition="Equals" xsi:type="cyboxVocabs:HashNameVocab-1.0">SHA256</cyboxCommon:Type>
<cyboxCommon:Simple_Hash_Value condition="Equals">ec5aedf5ecc6bdadd4120932170d1b10f6cfa175cfda22951dfd882928ab279b</cyboxCommon:Simple_Hash_Value>
</cyboxCommon:Hash>
</FileObj:Hashes>
</cybox:Properties>
</cybox:Object>
</cybox:Observable>
</cybox:Observable_Composition>
</stixCommon:Observable>
</incident:Related_Observable>
<incident:Related_Observable>
<stixCommon:Relationship>network</stixCommon:Relationship>
<stixCommon:Observable id="ORGNAME:EmailMessage-5e3967f3-b870-4638-b366-22fda964451a">
<cybox:Object id="ORGNAME:EmailMessageObject-5e3967f3-b870-4638-b366-22fda964451a">
<cybox:Properties xsi:type="EmailMessageObj:EmailMessageObjectType">
<EmailMessageObj:Header>
<EmailMessageObj:To>
<EmailMessageObj:Recipient xsi:type="AddressObj:AddressObjectType" category="e-mail">
<AddressObj:Address_Value>oui@to.lu</AddressObj:Address_Value>
</EmailMessageObj:Recipient>
</EmailMessageObj:To>
<EmailMessageObj:CC>
<EmailMessageObj:Recipient xsi:type="AddressObj:AddressObjectType" category="e-mail">
<AddressObj:Address_Value>oui1@cc.com</AddressObj:Address_Value>
</EmailMessageObj:Recipient>
<EmailMessageObj:Recipient xsi:type="AddressObj:AddressObjectType" category="e-mail">
<AddressObj:Address_Value>oui2@cc.com</AddressObj:Address_Value>
</EmailMessageObj:Recipient>
</EmailMessageObj:CC>
<EmailMessageObj:From xsi:type="AddressObj:AddressObjectType" category="e-mail">
<AddressObj:Address_Value condition="Equals">oui@source.com</AddressObj:Address_Value>
</EmailMessageObj:From>
<EmailMessageObj:Subject condition="Equals">Le Oui</EmailMessageObj:Subject>
<EmailMessageObj:Reply_To xsi:type="AddressObj:AddressObjectType" category="e-mail">
<AddressObj:Address_Value condition="Equals">oui@reply.com</AddressObj:Address_Value>
</EmailMessageObj:Reply_To>
<EmailMessageObj:X_Mailer condition="Equals">oui_X-mailer</EmailMessageObj:X_Mailer>
</EmailMessageObj:Header>
<EmailMessageObj:Attachments>
<EmailMessageObj:File object_reference="ORGNAME:FileObject-5e3967f3-43ac-4486-a5b6-22fda964451a"/>
<EmailMessageObj:File object_reference="ORGNAME:FileObject-5e3967f3-0e50-44cc-ab7a-22fda964451a"/>
</EmailMessageObj:Attachments>
</cybox:Properties>
<cybox:Related_Objects>
<cybox:Related_Object id="ORGNAME:FileObject-5e3967f3-43ac-4486-a5b6-22fda964451a">
<cybox:Properties xsi:type="FileObj:FileObjectType">
<FileObj:File_Name condition="Equals">oui.jpg</FileObj:File_Name>
</cybox:Properties>
<cybox:Relationship xsi:type="cyboxVocabs:ObjectRelationshipVocab-1.1">Contains</cybox:Relationship>
</cybox:Related_Object>
<cybox:Related_Object id="ORGNAME:FileObject-5e3967f3-0e50-44cc-ab7a-22fda964451a">
<cybox:Properties xsi:type="FileObj:FileObjectType">
<FileObj:File_Name condition="Equals">oui.png</FileObj:File_Name>
</cybox:Properties>
<cybox:Relationship xsi:type="cyboxVocabs:ObjectRelationshipVocab-1.1">Contains</cybox:Relationship>
</cybox:Related_Object>
</cybox:Related_Objects>
</cybox:Object>
</stixCommon:Observable>
</incident:Related_Observable>
<incident:Related_Observable>
<stixCommon:Relationship>file</stixCommon:Relationship>
<stixCommon:Observable id="ORGNAME:WinExecutableFile-5ac4db19-c620-4b8d-a5a7-01ef0a00020f">
@ -904,27 +920,35 @@
</incident:Related_Observables>
<incident:Leveraged_TTPs>
<incident:Leveraged_TTP>
<stixCommon:Relationship>Attack Pattern</stixCommon:Relationship>
<stixCommon:TTP idref="ORGNAME:TTP-dcb864dc-775f-11e7-9fbb-1f41b4996683" timestamp="2019-09-24T09:08:05.529834+00:00" xsi:type='ttp:TTPType'/>
<stixCommon:Relationship>vulnerability</stixCommon:Relationship>
<stixCommon:TTP idref="ORGNAME:TTP-13f1e6e7-87ab-4ced-bb84-d9461b3eb36a" timestamp="2019-08-12T12:20:13" xsi:type='ttp:TTPType'/>
</incident:Leveraged_TTP>
<incident:Leveraged_TTP>
<stixCommon:Relationship>Malware</stixCommon:Relationship>
<stixCommon:TTP idref="ORGNAME:TTP-d752161c-78f6-11e7-a0ea-bfa79b407ce4" timestamp="2019-09-24T09:08:05.530979+00:00" xsi:type='ttp:TTPType'/>
<stixCommon:Relationship>vulnerability</stixCommon:Relationship>
<stixCommon:TTP idref="ORGNAME:TTP-7edfed6f-7b06-46ea-8aba-9c4075df40d1" timestamp="2019-08-12T12:20:14" xsi:type='ttp:TTPType'/>
</incident:Leveraged_TTP>
<incident:Leveraged_TTP>
<stixCommon:Relationship>Tool</stixCommon:Relationship>
<stixCommon:TTP idref="ORGNAME:TTP-d700dc5c-78f6-11e7-a476-5f748c8e4fe0" timestamp="2019-09-24T09:08:05.531213+00:00" xsi:type='ttp:TTPType'/>
<stixCommon:Relationship>vulnerability</stixCommon:Relationship>
<stixCommon:TTP idref="ORGNAME:TTP-5e57b76e-e974-451a-b893-1440a964451a" timestamp="2020-03-23T10:18:50" xsi:type='ttp:TTPType'/>
</incident:Leveraged_TTP>
</incident:Leveraged_TTPs>
<incident:Attributed_Threat_Actors>
<incident:Threat_Actor>
<stixCommon:Relationship>ThreatActor</stixCommon:Relationship>
<stixCommon:Threat_Actor idref="ORGNAME:ThreatActor-7cdff317-a673-4474-84ec-4f1754947823" timestamp="2019-09-24T09:08:05.531710+00:00" xsi:type='ta:ThreatActorType'>
</stixCommon:Threat_Actor>
</incident:Threat_Actor>
</incident:Attributed_Threat_Actors>
<incident:COA_Taken>
<incident:Course_Of_Action idref="ORGNAME:CourseOfAction-a8825ae8-6dea-11e7-8d57-7728f3cfe086" timestamp="2019-09-24T09:08:05.531515+00:00" xsi:type='coa:CourseOfActionType'/>
<incident:Course_Of_Action id="ORGNAME:CourseOfAction-5d515039-9a68-468b-9c78-3affa964451a" timestamp="2020-03-23T11:34:18.051910+00:00" xsi:type='coa:CourseOfActionType'>
<coa:Title>Block traffic to PIVY C2 Server (10.10.10.10)</coa:Title>
<coa:Stage xsi:type="stixVocabs:COAStageVocab-1.0">Response</coa:Stage>
<coa:Objective>
<coa:Description>Block communication between the PIVY agents and the C2 Server</coa:Description>
</coa:Objective>
<coa:Impact timestamp="2020-03-23T11:34:18.052015+00:00">
<stixCommon:Value>Low</stixCommon:Value>
</coa:Impact>
<coa:Cost timestamp="2020-03-23T11:34:18.051997+00:00">
<stixCommon:Value>Low</stixCommon:Value>
</coa:Cost>
<coa:Efficacy timestamp="2020-03-23T11:34:18.052037+00:00">
<stixCommon:Value>High</stixCommon:Value>
</coa:Efficacy>
</incident:Course_Of_Action>
</incident:COA_Taken>
<incident:History>
<incident:History_Item>
@ -990,36 +1014,20 @@
</stix:Incident>
</stix:Incidents>
<stix:Courses_Of_Action>
<stix:Course_Of_Action id="ORGNAME:CourseOfAction-a8825ae8-6dea-11e7-8d57-7728f3cfe086" timestamp="2019-09-24T09:08:05.531515+00:00" xsi:type='coa:CourseOfActionType'>
<coa:Title>Course of Action: Access Token Manipulation Mitigation - T1134</coa:Title>
<stix:Course_Of_Action id="ORGNAME:CourseOfAction-c61fee9f-16fb-4f8c-bbf0-869093fcd4a6" timestamp="2020-03-23T11:34:18.045047+00:00" xsi:type='coa:CourseOfActionType'>
<coa:Title>Access Token Manipulation Mitigation - T1134</coa:Title>
<coa:Description>Access tokens are an integral part of the security system within Windows and cannot be turned off. However, an attacker must already have administrator level access on the local system to make full use of this technique; be sure to restrict users and accounts to the least privileges they require to do their job.
Any user can also spoof access tokens if they have legitimate credentials. Follow mitigation guidelines for preventing adversary use of [Valid Accounts](https://attack.mitre.org/techniques/T1078). Limit permissions so that users and user groups cannot create tokens. This setting should be defined for the local system account only. GPO: Computer Configuration &gt; [Policies] &gt; Windows Settings &gt; Security Settings &gt; Local Policies &gt; User Rights Assignment: Create a token object. (Citation: Microsoft Create Token) Also define who can create a process level token to only the local and network service through GPO: Computer Configuration &gt; [Policies] &gt; Windows Settings &gt; Security Settings &gt; Local Policies &gt; User Rights Assignment: Replace a process level token. (Citation: Microsoft Replace Process Token)
Also limit opportunities for adversaries to increase privileges by limiting Privilege Escalation opportunities.</coa:Description>
</stix:Course_Of_Action>
<stix:Course_Of_Action id="ORGNAME:CourseOfAction-5d515039-9a68-468b-9c78-3affa964451a" timestamp="2019-09-24T09:08:05.540804+00:00" xsi:type='coa:CourseOfActionType'>
<coa:Title>Block traffic to PIVY C2 Server (10.10.10.10)</coa:Title>
<coa:Stage xsi:type="stixVocabs:COAStageVocab-1.0">Response</coa:Stage>
<coa:Objective>
<coa:Description>Block communication between the PIVY agents and the C2 Server</coa:Description>
</coa:Objective>
<coa:Impact timestamp="2019-09-24T09:08:05.540895+00:00">
<stixCommon:Value>Low</stixCommon:Value>
</coa:Impact>
<coa:Cost timestamp="2019-09-24T09:08:05.540878+00:00">
<stixCommon:Value>Low</stixCommon:Value>
</coa:Cost>
<coa:Efficacy timestamp="2019-09-24T09:08:05.540912+00:00">
<stixCommon:Value>High</stixCommon:Value>
</coa:Efficacy>
</stix:Course_Of_Action>
</stix:Courses_Of_Action>
<stix:Threat_Actors>
<stix:Threat_Actor id="ORGNAME:ThreatActor-7cdff317-a673-4474-84ec-4f1754947823" timestamp="2019-09-24T09:08:05.531710+00:00" xsi:type='ta:ThreatActorType'>
<ta:Title>Threat Actor: APT 16</ta:Title>
<stix:Threat_Actor id="ORGNAME:ThreatActor-1f73e14f-b882-4032-a565-26dc653b0daf" timestamp="2020-03-23T11:34:18.045253+00:00" xsi:type='ta:ThreatActorType'>
<ta:Title>APT 16</ta:Title>
<ta:Description>Between November 26, 2015, and December 1, 2015, known and suspected China-based APT groups launched several spear-phishing attacks targeting Japanese and Taiwanese organizations in the high-tech, government services, media and financial services industries. Each campaign delivered a malicious Microsoft Word document exploiting the aforementioned EPS dict copy use-after-free vulnerability, and the local Windows privilege escalation vulnerability CVE-2015-1701. The successful exploitation of both vulnerabilities led to the delivery of either a downloader that we refer to as IRONHALO, or a backdoor that we refer to as ELMER.</ta:Description>
<ta:Intended_Effect timestamp="2019-09-24T09:08:05.532164+00:00">
<ta:Intended_Effect timestamp="2020-03-23T11:34:18.045606+00:00">
<stixCommon:Value>Espionage</stixCommon:Value>
</ta:Intended_Effect>
</stix:Threat_Actor>

View File

@ -29,39 +29,49 @@ def get_internal(event):
return StixFromMISPParser(), stix2.parse(event, allow_custom=True, interoperability=True)
def query_import(filename, externalise):
with open(filename, 'rt', encoding='utf-8') as f:
event = json.loads(f.read())
stix_parser, event = get_external(event) if externalise else get_internal(event)
stix_parser.handler(event, filename, [0, 5])
stix_parser.save_file()
if __name__ == '__main__':
parser = ArgumentParser(description='Full process of querying data from MISP and comparing the results after a STIX export then import')
parser.add_argument('--setup', default='setup.json', help='Path to the file containing the required setup to connect to the MISP server.')
parser.add_argument('--eventid', nargs='+', help='Filter on Event id')
parser.add_argument('--withAttachments', type=int, help='Export Attributes with the attachments')
parser.add_argument('-o', '--output', type=str, required=True, help='Name of the output file to save the result of the query in')
parser.add_argument('-i', '--input', type=str, help='Name of the input file to use instead of requesting MISP to gather an event.')
parser.add_argument('-o', '--output', type=str, help='Name of the output file to save the result of the query in')
parser.add_argument('-d', '--delete', action='store_true', help='Delete all the files generated')
parser.add_argument('-x', '--externalise', action='store_true', help='Make the STIX file look like it has been generated from an external source')
args = parser.parse_args()
output = args.output
args.relative_path = 'events/restSearch'
filenames = []
for return_type in ('json', 'stix2'):
args.output = f"test_{return_type}_{output}.json"
args.returnFormat = return_type
query_misp(args)
filenames.append(args.output)
to_delete = [filename for filename in filenames]
stix_analyse = validate_file(filenames[1])
print_results(stix_analyse)
with open(filenames[1], 'rt', encoding='utf-8') as f:
event = json.loads(f.read())
stix_parser, event = get_external(event) if args.externalise else get_internal(event)
stix_parser.handler(event, filenames[1], [0, 5])
stix_parser.save_file()
filenames[1] = f'{filenames[1]}.stix2'
to_delete.append(filenames[1])
if args.input:
filenames = (f'test_json_{args.input}.json', f'test_stix2_{args.input}.json.stix2')
query_import(f'test_stix2_{args.input}.json', args.externalise)
else:
if not args.output:
sys.exit('Please provide an output name for the test files.')
output = args.output
filenames = []
for return_type in ('json', 'stix2'):
args.output = f"test_{return_type}_{output}.json"
args.returnFormat = return_type
query_misp(args)
filenames.append(args.output)
to_delete = [filename for filename in filenames]
stix_analyse = validate_file(filenames[1])
print_results(stix_analyse)
query_import(filenames[1], args.externalise)
filenames[1] = f'{filenames[1]}.stix2'
to_delete.append(filenames[1])
comparer = Comparer(*filenames)
comparer.compare_attributes()
comparer.compare_objects()
comparer.compare_tags()
comparer.compare_galaxies()
comparer.compare_references()
if args.delete:
if args.delete and not args.input:
for filename in to_delete:
os.remove(filename)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long