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

pull/2547/head
iglocska 2017-10-08 15:32:57 +02:00
commit f5bcd37944
15 changed files with 582 additions and 20 deletions

View File

@ -36,6 +36,7 @@ $config = array(
'take_ownership_xml_import' => false,
'unpublishedprivate' => false,
'disable_emailing' => false,
'Attributes_Values_Filter_In_Event' => 'id, uuid, value, comment, type, category, Tag.name',
),
'GnuPG' =>
array(

View File

@ -46,7 +46,7 @@ class AppController extends Controller {
public $helpers = array('Utility');
private $__queryVersion = '20';
private $__queryVersion = '21';
public $pyMispVersion = '2.4.80';
public $phpmin = '5.6.5';
public $phprec = '7.0.16';

View File

@ -714,6 +714,51 @@ class EventsController extends AppController {
$this->layout = 'ajax';
}
/*
* Search for a value on an attribute level for a specific field.
* $attribute : (array) an attribute
* $fields : (array) list of keys in attribute to search in
* $searchValue : Value to search
* returns true on match
*/
private function __valueInFieldAttribute($attribute, $fields, $searchValue) {
foreach ($attribute as $k => $v){ // look in attributes line
if (is_string($v)) {
foreach ($fields as $field){
if (strpos(".", $field) === false) { // check sub array after
// check for key in attribut
if (isset($attribute[$field])) {
$temp_value = strtolower($attribute[$field]);
$temp_search = strtolower($searchValue);
if(strpos($temp_value, $temp_search) !==false) {
return true;
}
}
}
}
} else {
// check for tag in attribut maybe for other thing later
if($k === 'AttributeTag'){
foreach ($v as $tag) {
foreach ($fields as $field) {
if (strpos(strtolower($field), "tag.") !== false) { // check sub array
$tagKey = explode('tag.', strtolower($field))[1];
if (isset($tag['Tag'][$tagKey])) {
$temp_value = strtolower($tag['Tag'][$tagKey]);
$temp_search = strtolower($searchValue);
if (strpos($temp_value, $temp_search) !==false) {
return true;
}
}
}
}
}
}
}
}
return false;
}
public function viewEventAttributes($id, $all = false) {
if (isset($this->params['named']['focus'])) {
$this->set('focus', $this->params['named']['focus']);
@ -725,6 +770,50 @@ class EventsController extends AppController {
$results = $this->Event->fetchEvent($this->Auth->user(), $conditions);
if (empty($results)) throw new NotFoundException('Invalid event');
$event = $results[0];
if (!empty($this->params['named']['searchFor'])) {
$filterColumns = empty(Configure::read('MISP.event_view_filter_fields')) ? 'id, uuid, value, comment, type, category, Tag.name' : Configure::read('MISP.event_view_filter_fields');
$filterValue = array_map('trim', explode(",", $filterColumns));
$validFilters = array('id', 'uuid', 'value', 'comment', 'type', 'category', 'Tag.name');
foreach ($filterValue as $k => $v) {
if (!in_array($v, $validFilters)) {
unset($filterValue[$k]);
}
}
// search in all attributes
foreach ($event['Attribute'] as $k => $attribute) {
if (!$this->__valueInFieldAttribute($attribute, $filterValue, $this->params['named']['searchFor'])) {
unset($event['Attribute'][$k]);
}
}
$event['Attribute'] = array_values($event['Attribute']);
// search in all attributes
foreach ($event['ShadowAttribute'] as $k => $proposals) {
if (!$this->__valueInFieldAttribute($proposals, $filterValue, $this->params['named']['searchFor'])) {
unset($event['ShadowAttribute'][$k]);
}
}
$event['ShadowAttribute'] = array_values($event['ShadowAttribute']);
// search for all attributes in object
foreach ($event['Object'] as $k => $object) {
foreach ($object['Attribute'] as $k2 => $attribute){
if (!$this->__valueInFieldAttribute($attribute, $filterValue, $this->params['named']['searchFor'])) {
unset($event['Object'][$k]['Attribute'][$k2]);
}
}
if (count($event['Object'][$k]['Attribute']) == 0){
// remove object if empty
unset($event['Object'][$k]);
} else {
$event['Object'][$k]['Attribute'] = array_values($event['Object'][$k]['Attribute']);
}
}
$event['Object'] = array_values($event['Object']);
$this->set('passedArgsArray', array('all' => $this->params['named']['searchFor']));
}
$emptyEvent = (empty($event['Object']) && empty($event['Attribute']));
$this->set('emptyEvent', $emptyEvent);
$params = $this->Event->rearrangeEventForView($event, $this->passedArgs, $all);
@ -2654,7 +2743,7 @@ class EventsController extends AppController {
$attributeLevelFilters = array('value', 'type', 'category', 'uuid');
$preFilterLevel = 'event';
foreach ($parameters as $k => $param) {
if (${$parameters[$k]} !== false) {
if (${$parameters[$k]} !== null && ${$parameters[$k]} !== false) {
if (in_array($param, $attributeLevelFilters)) {
$preFilterLevel = 'attribute';
}
@ -2689,9 +2778,16 @@ class EventsController extends AppController {
);
$attributes = $this->Event->Attribute->fetchAttributes($this->Auth->user(), $params);
$eventIds = array();
// Add event ID if specifically specified to allow for the export of an empty event
if (isset($eventid) && $eventid) $eventIds[] = $eventid;
$eventIds = array_merge($eventIds, array_values($attributes));
if (!empty($attributes)) {
$eventIds = array_values($attributes);
}
if (is_array($eventid)) {
foreach ($eventid as $temp_id) {
if (!in_array($temp_id, $eventIds)) $eventIds[] = $temp_id;
}
} else {
if (!in_array($eventid, $eventIds)) $eventIds[] = $eventid;
}
unset($attributes);
}
}
@ -2741,6 +2837,7 @@ class EventsController extends AppController {
$extension = '.ioc';
}
if (isset($eventid) && $eventid) {
if (is_array($eventid)) $eventid = 'list';
$final_filename="misp.event." . $eventid . "." . $result[0]['Event']['uuid'] . '.' . $extension;
} else {
$final_filename="misp.search.events.results." . $extension;

View File

@ -40,6 +40,30 @@ class ObjectsController extends AppController {
if (empty($event) || (!$this->_isSiteAdmin() && $event['Event']['orgc_id'] != $this->Auth->user('org_id'))) {
throw new NotFoundException('Invalid event.');
}
$sharing_groups = array();
if ($this->request->data['Object']['distribution'] == 4) {
$sharing_groups[$this->request->data['Object']['sharing_group_id']] = false;
}
foreach ($this->request->data['Attribute'] as $attribute) {
if ($attribute['distribution'] == 4) {
$sharing_groups[$attribute['sharing_group_id']] = false;
}
}
if (!empty($sharing_groups)) {
$sgs = $this->MispObject->SharingGroup->find('all', array(
'conditions' => array('SharingGroup.id' => array_keys($sharing_groups)),
'recursive' => -1,
'fields' => array('SharingGroup.id', 'SharingGroup.name'),
'order' => false
));
foreach ($sgs as $sg) {
$sharing_groups[$sg['SharingGroup']['id']] = $sg;
}
foreach ($sharing_groups as $k => $sg) {
if (empty($sg)) throw new NotFoundException('Invalid sharing group.');
}
$this->set('sg', $sharing_groups);
}
if ($this->request->data['Object']['distribution'] == 4) {
$sg = $this->MispObject->SharingGroup->find('first', array(
'conditions' => array('SharingGroup.id' => $this->request->data['Object']['sharing_group_id']),

View File

@ -50,11 +50,11 @@ class TaxonomiesController extends AppController {
App::uses('CustomPaginationTool', 'Tools');
$filter = isset($this->passedArgs['filter']) ? $this->passedArgs['filter'] : false;
$taxonomy = $this->Taxonomy->getTaxonomy($id, array('full' => true, 'filter' => $filter));
if (empty($taxonomy)) throw new NotFoundException('Taxonomy not found.');
foreach ($taxonomy['entries'] as $key => $value) {
$taxonomy['entries'][$key]['events'] = count($value['existing_tag']['EventTag']);
}
$this->set('filter', $filter);
if (empty($taxonomy)) throw new NotFoundException('Taxonomy not found.');
$customPagination = new CustomPaginationTool();
$params = $customPagination->createPaginationRules($taxonomy['entries'], $this->passedArgs, 'TaxonomyEntry');
if ($params['sort'] == 'id') $params['sort'] = 'tag';

View File

@ -269,7 +269,7 @@ class Attribute extends AppModel {
'Payload delivery' => array(
'desc' => 'Information about how the malware is delivered',
'formdesc' => 'Information about the way the malware payload is initially delivered, for example information about the email or web-site, vulnerability used, originating IP etc. Malware sample itself should be attached here.',
'types' => array('md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512', 'sha512/224', 'sha512/256', 'ssdeep', 'imphash', 'impfuzzy','authentihash', 'pehash', 'tlsh', 'filename', 'filename|md5', 'filename|sha1', 'filename|sha224', 'filename|sha256', 'filename|sha384', 'filename|sha512', 'filename|sha512/224', 'filename|sha512/256', 'filename|authentihash', 'filename|ssdeep', 'filename|tlsh', 'filename|imphash','filename|impfuzzy', 'filename|pehash', 'ip-src', 'ip-dst', 'ip-dst|port', 'ip-src|port', 'hostname', 'domain', 'email-src', 'email-dst', 'email-subject', 'email-attachment', 'email-body', 'url', 'user-agent', 'AS', 'pattern-in-file', 'pattern-in-traffic', 'yara', 'sigma', 'attachment', 'malware-sample', 'link', 'malware-type', 'comment', 'text', 'hex', 'vulnerability', 'x509-fingerprint-sha1', 'other', 'ip-dst|port', 'ip-src|port', 'hostname|port', 'email-dst-display-name', 'email-src-display-name', 'email-header', 'email-reply-to', 'email-x-mailer', 'email-mime-boundary', 'email-thread-index', 'email-message-id', 'mobile-application-id', 'whois-registrant-email')
'types' => array('md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512', 'sha512/224', 'sha512/256', 'ssdeep', 'imphash', 'impfuzzy','authentihash', 'pehash', 'tlsh', 'filename', 'filename|md5', 'filename|sha1', 'filename|sha224', 'filename|sha256', 'filename|sha384', 'filename|sha512', 'filename|sha512/224', 'filename|sha512/256', 'filename|authentihash', 'filename|ssdeep', 'filename|tlsh', 'filename|imphash','filename|impfuzzy', 'filename|pehash', 'ip-src', 'ip-dst', 'ip-dst|port', 'ip-src|port', 'hostname', 'domain', 'email-src', 'email-dst', 'email-subject', 'email-attachment', 'email-body', 'url', 'user-agent', 'AS', 'pattern-in-file', 'pattern-in-traffic', 'yara', 'sigma', 'attachment', 'malware-sample', 'link', 'malware-type', 'comment', 'text', 'hex', 'vulnerability', 'x509-fingerprint-sha1', 'other', 'hostname|port', 'email-dst-display-name', 'email-src-display-name', 'email-header', 'email-reply-to', 'email-x-mailer', 'email-mime-boundary', 'email-thread-index', 'email-message-id', 'mobile-application-id', 'whois-registrant-email')
),
'Artifacts dropped' => array(
'desc' => 'Any artifact (files, registry keys etc.) dropped by the malware or other modifications to the system',
@ -310,7 +310,7 @@ class Attribute extends AppModel {
),
'Support Tool' => array(
'desc' => 'Tools supporting analysis or detection of the event',
'types' => array('link', 'text', 'attachment', 'comment', 'text', 'other', 'hex')
'types' => array('link', 'text', 'attachment', 'comment', 'other', 'hex')
),
'Social network' => array(
'desc' => 'Social networks and platforms',

View File

@ -792,6 +792,14 @@ class Server extends AppModel {
'test' => null,
'type' => 'string',
'redacted' => true
),
'event_view_filter_fields' => array(
'level' => 2,
'description' => 'Specify which fields to filter on when you search on the event view. Default values are : "id, uuid, value, comment, type, category, Tag.name"',
'value' => 'id, uuid, value, comment, type, category, Tag.name',
'errorMessage' => '',
'test' => null,
'type' => 'string',
)
),
'GnuPG' => array(

View File

@ -132,7 +132,8 @@ class CertificateAuthenticate extends BaseAuthenticate
self::$user = self::$client;
// If $sync is true, allow the creation of the user from the certificate
$sync = Configure::read('CertAuth.syncUser');
if ($sync) {
$url = Configure::read('CertAuth.restApi.url');
if ($sync && $url) {
if (!self::getRestUser()) return false;
}

View File

@ -33,6 +33,12 @@
}
}
}
$filtered = false;
if(isset($passedArgsArray)){
if (count($passedArgsArray) > 0) {
$filtered = true;
}
}
?>
<div class="pagination">
<ul>
@ -132,6 +138,13 @@
<div id="filter_deleted" title="Include deleted attributes" role="button" tabindex="0" aria-label="Include deleted attributes" class="attribute_filter_text<?php if ($deleted) echo '_active'; ?>" onClick="toggleDeletedAttributes('<?php echo Router::url( $this->here, true );?>');">Include deleted attributes</div>
<?php endif; ?>
<div id="show_context" title="Show attribute context fields" role="button" tabindex="0" aria-label="Show attribute context fields" class="attribute_filter_text" onClick="toggleContextFields();">Show context fields</div>
<div title="input filter" tabindex="0" aria-label="input filter" class="attribute_filter_text" style="padding-top:0px;">
<input type="text" id="attributesFilterField" style="height:20px;padding:0px;margin:0px;" class="form-control" data-eventid="<?php echo h($event['Event']['id']); ?>"></input>
<span id="attributesFilterButton" role="button" class="icon-search" tabindex="0" aria-label="Filter on attributes value" onClick="filterAttributes('value', '<?php echo h($event['Event']['id']); ?>');"></span>
<?php if ($filtered):?>
<span tabindex="0" aria-label="Show all attributes" title="Remove filters" role="button" onClick="filterAttributes('all', '<?php echo h($event['Event']['id']); ?>');" class='icon-remove'></span>
<?php endif;?>
</div>
</div>
<table class="table table-striped table-condensed">
@ -319,6 +332,12 @@ attributes or the appropriate distribution level. If you think there is a mistak
genericPopup(url, '#screenshot_box');
});
});
$('#attributesFilterField').bind("keydown", function(e) {
var eventid = $('#attributesFilterField').data("eventid");
if ((e.keyCode == 13 || e.keyCode == 10)) {
filterAttributes('value', eventid);
}
});
$('.hex-value-convert').click(function() {
var val = $(this).parent().children(':first-child').text();
if ($(this).parent().children(':first-child').attr('data-original-title') == 'Hexadecimal representation') {

View File

@ -20,17 +20,24 @@
if ($data['Object']['distribution'] != 4) {
echo $distributionLevels[$data['Object']['distribution']];
} else {
echo h($sg['SharingGroup']['name']);
echo h($sg[$data['Object']['sharing_group_id']]['SharingGroup']['name']);
}
?><br />
<span class="bold">Comment</span>: <?php echo h($data['Object']['comment']); ?><br />
<span class="bold">Attributes</span>:<br />
<?php
$attributeFields = array('category', 'type', 'value', 'to_ids' , 'comment', 'uuid', 'distribution', 'sharing_group_id');
$attributeFields = array('category', 'type', 'value', 'to_ids' , 'comment', 'uuid', 'distribution');
if (!empty($data['Attribute'])):
foreach ($data['Attribute'] as $attribute):
echo '<span class="bold" style="margin-left:2em;">' . h($attribute['object_relation']) . ':</span><br />';
foreach ($attributeFields as $field):
if ($field == 'distribution') {
if ($attribute['distribution'] != 4) {
$attribute[$field] = $distributionLevels[$attribute['distribution']];
} else {
$attribute[$field] = $sg[$attribute['sharing_group_id']]['SharingGroup']['name'];
}
}
if ($field == 'to_ids') $attribute[$field] = $attribute[$field] ? 'Yes' : 'No';
if (isset($attribute[$field])):
?>

View File

@ -1,6 +1,7 @@
from cybox.core import Object, Observable, ObservableComposition
from cybox.objects.file_object import File
from cybox.objects.address_object import Address
from cybox.objects.port_object import Port
from cybox.objects.hostname_object import Hostname
from cybox.objects.uri_object import URI
from cybox.objects.pipe_object import Pipe
@ -23,7 +24,8 @@ hash_type_attributes = {"single":["md5", "sha1", "sha224", "sha256", "sha384", "
simple_type_to_method = {}
simple_type_to_method.update(dict.fromkeys(hash_type_attributes["single"] + hash_type_attributes["composite"] + ["attachment"], "resolveFileObservable"))
simple_type_to_method.update(dict.fromkeys(["ip-src", "ip-dst"], "generateIPObservable"))
simple_type_to_method.update(dict.fromkeys(["ip-src", "ip-dst", "ip-src|port", "ip-dst|port"], "generateIPObservable"))
simple_type_to_method.update(dict.fromkeys(["port"], "generatePortObservable"))
simple_type_to_method.update(dict.fromkeys(["domain|ip"], "generateDomainIPObservable"))
simple_type_to_method.update(dict.fromkeys(["regkey", "regkey|value"], "generateRegkeyObservable"))
simple_type_to_method.update(dict.fromkeys(["hostname", "domain", "url", "AS", "mutex", "named pipe", "link"], "generateSimpleObservable"))
@ -64,9 +66,6 @@ misp_reghive = {
def generateObservable(indicator, attribute):
if (attribute["type"] in ("snort", "yara")):
generateTM(indicator, attribute)
elif (attribute["type"] == "domain|ip"):
observable = generateDomainIPObservable(indicator, attribute)
indicator.add_observable(observable)
else:
observable = None;
if (attribute["type"] in simple_type_to_method.keys()):
@ -75,6 +74,8 @@ def generateObservable(indicator, attribute):
property = action(indicator, attribute)
if property is False:
return False
if isinstance(property, Observable):
return indicator.add_observable(property)
property.condition = "Equals"
object = Object(property)
object.id_ = cybox.utils.idgen.__generator.namespace.prefix + ":" + property.__class__.__name__ + "-" + attribute["uuid"]
@ -129,6 +130,8 @@ def generateFileObservable(filenameValue, hashValue, fuzzy):
def resolveIPType(attribute_value, attribute_type):
address_object = Address()
cidr = False
if ("|" in attribute_value):
attribute_value = attribute_value.split('|')[0]
if ("/" in attribute_value):
ip = attribute_value.split('/')[0]
cidr = True
@ -141,15 +144,21 @@ def resolveIPType(attribute_value, attribute_type):
ipv4 = False
if (cidr == True):
address_object.category = "cidr"
condition = "Contains"
elif (ipv4 == True):
address_object.category = "ipv4-addr"
condition = "Equals"
else:
address_object.category = "ipv6-addr"
if (attribute_type == "ip-src") or (attribute_type == "domain|ip"):
condition = "Equals"
if attribute_type.startswith("ip-src"):
address_object.is_source = True
else:
address_object.is_destination = False
if attribute_type.startswith("ip-dst"):
address_object.is_source = False
address_object.is_destination = True
address_object.address_value = attribute_value
address_object.condition = condition
return address_object
def generateDomainIPObservable(indicator, attribute):
@ -158,10 +167,11 @@ def generateDomainIPObservable(indicator, attribute):
ip = attribute["value"].split('|')[1]
address_object = resolveIPType(ip, attribute["type"])
address_object.parent.id_ = cybox.utils.idgen.__generator.namespace.prefix + ":AddressObject-" + attribute["uuid"]
address_observable=Observable(address_object)
address_observable = Observable(address_object)
address_observable.id_ = cybox.utils.idgen.__generator.namespace.prefix + ":Address-" + attribute["uuid"]
domain_object = DomainName()
domain_object.value = domain
domain_object.value.condition = "Equals"
domain_object.parent.id_ = cybox.utils.idgen.__generator.namespace.prefix + ":DomainNameObject-" + attribute["uuid"]
domain_observable = Observable(domain_object)
domain_observable.id_ = cybox.utils.idgen.__generator.namespace.prefix + ":DomainName-" + attribute["uuid"]
@ -174,7 +184,31 @@ def generateDomainIPObservable(indicator, attribute):
def generateIPObservable(indicator, attribute):
indicator.add_indicator_type("IP Watchlist")
address_object = resolveIPType(attribute["value"], attribute["type"])
return address_object
address_object.parent.id_ = cybox.utils.idgen.__generator.namespace.prefix + ":AddressObject-" + attribute["uuid"]
if ("|" in attribute["value"]):
port = attribute["value"].split('|')[1]
address_observable = Observable(address_object)
address_observable.id_ = cybox.utils.idgen.__generator.namespace.prefix + ":Address-" + attribute["uuid"]
port_object = Port()
port_object.port_value = attribute["value"].split('|')[1]
port_object.port_value.condition = "Equals"
port_object.parent.id_ = cybox.utils.idgen.__generator.namespace.prefix + ":PortObject-" + attribute["uuid"]
port_observable = Observable(port_object)
port_observable.id_ = cybox.utils.idgen.__generator.namespace.prefix + ":Port-" + attribute["uuid"]
compositeObject = ObservableComposition(observables = [address_observable, port_observable])
compositeObject.operator = "AND"
observable = Observable(id_ = cybox.utils.idgen.__generator.namespace.prefix + ":ObservableComposition-" + attribute["uuid"])
observable.observable_composition = compositeObject
return observable
else:
return address_object
def generatePortObservable(indicator, attribute):
port_object = Port()
port_object.port_value = attribute["value"]
port_object.port_value.condition = "Equals"
port_object.parent.id_ = cybox.utils.idgen.__generator.namespace.prefix + ":PortObject-" + attribute["uuid"]
return port_object
def generateRegkeyObservable(indicator, attribute):
indicator.add_indicator_type("Host Characteristics")
@ -254,6 +288,7 @@ def resolveHTTPObservable(indicator, attribute):
else:
line = HTTPRequestLine()
line.http_method = attribute["value"]
line.http_method.condition = "Equals"
client_request.http_request_line = line
request_response.http_client_request = client_request
new_object = HTTPSession()

View File

@ -99,6 +99,7 @@ def generateSTIXObjects(event):
setOrg(incident, event["Org"]["name"])
setTag(incident, event["Tag"])
resolveAttributes(incident, ttps, event["Attribute"])
resolveObjects(incident, ttps, event["Object"])
return [incident, ttps]
@ -111,6 +112,26 @@ def setDates(incident, date, published):
incident_time.incident_reported = timestamp
incident.time = incident_time
# decide what to do with the objects, as not all of them will become indicators
def resolveObjects(incident, ttps, objects):
for obj in objects:
tmp_incident = Incident()
resolveAttributes(tmp_incident, ttps, obj["Attribute"])
indicator = Indicator(timestamp=getDateFromTimestamp(int(obj["timestamp"])))
indicator.id_= namespace[1] + ":MispObject-" + obj["uuid"]
if obj["comment"] != "":
indicator.description = obj["comment"]
setTLP(indicator, obj["distribution"])
indicator.title = obj["name"] + " (MISP Object #" + obj["id"] + ")"
indicator.description = indicator.title
indicator.add_indicator_type("Malware Artifacts")
indicator.add_valid_time_position(ValidTime())
indicator.observable_composition_operator = "AND"
for rindicator in tmp_incident.related_indicators:
indicator.add_observable(rindicator.item.observable)
relatedIndicator = RelatedIndicator(indicator, relationship=obj["meta-category"])
incident.related_indicators.append(relatedIndicator)
# decide what to do with the attribute, as not all of them will become indicators
def resolveAttributes(incident, ttps, attributes):
for attribute in attributes:

View File

@ -31,6 +31,7 @@ NS_DICT = {
"http://cybox.mitre.org/default_vocabularies-2" : 'cyboxVocabs',
"http://cybox.mitre.org/objects#ASObject-1" : 'ASObj',
"http://cybox.mitre.org/objects#AddressObject-2" : 'AddressObj',
"http://cybox.mitre.org/objects#PortObject-2" : 'PortObj',
"http://cybox.mitre.org/objects#DomainNameObject-1" : 'DomainNameObj',
"http://cybox.mitre.org/objects#EmailMessageObject-2" : 'EmailMessageObj',
"http://cybox.mitre.org/objects#FileObject-2" : 'FileObj',

View File

@ -0,0 +1,345 @@
#!/usr/bin/env python3
# Copyright (C) 2017 CIRCL Computer Incident Response Center Luxembourg (smile gie)
# Copyright (C) 2017 Christian Studer
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import sys, json, os, time, datetime, re
from stix2 import *
from mixbox import idgen
from cybox.utils import Namespace
namespace = ['https://github.com/MISP/MISP', 'MISP']
not_implemented_attributes = ['yara', 'pattern-in-traffic', 'pattern-in-memory']
non_indicator_attributes = ['text', 'comment', 'other', 'link', 'target-user', 'target-email',
'target-machine', 'target-org', 'target-location', 'target-external',
'vulnerability', 'attachment']
def loadEvent(args, pathname):
try:
filename = args[1]
tempFile = open(filename, 'r')
events = json.loads(tempFile.read())
return events
except:
print(json.dumps({'success' : 0, 'message' : 'The temporary MISP export file could not be read'}))
sys.exit(1)
def saveFile(args, pathname, package):
# print(package)
# try:
tab_args = args[1].split('.')
filename = "{}/tmp/misp.stix.{}{}.{}".format(pathname, tab_args[-4], tab_args[-3], tab_args[-1])
d = os.path.dirname(filename)
if not os.path.exists(d):
os.makedirs(d)
with open(filename, 'w') as f:
f.write(str(package))
# except:
# print(json.dumps({'success' : 0, 'message' : 'The STIX file could not be written'}))
# sys.exit(1)
# converts timestamp to the format used by STIX
def getDateFromTimestamp(timestamp):
return datetime.datetime.utcfromtimestamp(timestamp).isoformat() + "+00:00"
def setIdentity(event):
org = event["Orgc"]
identity = Identity(type="identity", id="identity--{}".format(org["uuid"]),
name=org["name"], identity_class="organization")
return identity
def readAttributes(event, identity, object_refs, external_refs):
attributes = []
for attribute in event["Attribute"]:
attr_type = attribute['type']
if attr_type in non_indicator_attributes:
if attr_type == "link":
handleLink(attribute, external_refs)
else:
handleNonIndicatorAttribute(object_refs, attributes, attribute, identity)
else:
if attribute['to_ids'] == 'false':
handleIndicatorAttribute(object_refs, attributes, attribute, identity)
else:
addObservedData(object_refs, attributes, attribute, identity)
# if event['Galaxy']:
# galaxies = event['Galaxy']
# for galaxy in galaxies:
# galaxyType = galaxy['type']
# if 'ware' in galaxyType:
# addMalware(object_refs, attributes, galaxy, identity)
# elif 'intrusion' in galaxyType:
# addIntrusionSet(object_refs, attributes, galaxy, identity)
# elif 'exploit' in galaxyType:
# addCampaign(object_refs, attributes, galaxy, identity)
# elif 'threat-actor' in galaxyType:
# addThreatActor(object_refs, attributes, galaxy, identity)
# elif 'rat' in galaxyType or 'tool' in galaxyType:
# addTool(object_refs, attributes, galaxy, identity)
return attributes
def handleLink(attribute, external_refs):
url = attribute['value']
source = 'url'
if 'comment' in attribute:
source += ' - {}'.format(attribute['comment'])
link = {'source_name': source, 'url': url}
external_refs.append(link)
def addAttackPattern(object_refs, attributes, galaxy, identity):
attack_id = "attack-pattern--{}".format(galaxy['uuid'])
attackPattern = AttackPattern()
attributes.append(attackPattern)
object_refs.append(attack_id)
def addCampaign(object_refs, attributes, galaxy, identity):
cluster = galaxy['GalaxyCluster'][0]
campaign_id = "campaign--{}".format(cluster['uuid'])
name = cluster['value']
description = cluster['description']
campaign_args = {'id': campaign_id, 'type': 'campaign', 'name': name, 'description': description,
'created_by_ref': identity}
meta = cluster['meta']
addAliases(meta, campaign_args)
campaign = Campaign(**campaign_args)
attributes.append(campaign)
object_refs.append(campaign_id)
def addCourseOfAction(object_refs, attributes, galaxy, identity):
courseOfAction_id = "course-of-action--{}".format(galaxy['uuid'])
courseOfAction = CourseOfAction()
attributes.append(courseOfAction)
object_refs.append(courseOfAction_id)
def addIntrusionSet(object_refs, attributes, galaxy, identity):
cluster = galaxy['GalaxyCluster'][0]
intrusionSet_id = "intrusion-set--{}".format(cluster['uuid'])
name = cluster['value']
description = cluster['description']
intrusion_args = {'id': intrusionSet_id, 'type': 'intrusion-set', 'name': name, 'description': description,
'created_by_ref': identity}
meta = cluster['meta']
addAliases(meta, intrusion_args)
intrusionSet = IntrusionSet(**intrusion_args)
attributes.append(intrusionSet)
object_refs.append(intrusionSet_id)
def addMalware(object_refs, attributes, galaxy, identity):
malware_id = "malware--{}".format(galaxy['uuid'])
malware_args = {}
malware = Malware(**malware_args)
attributes.append(malware)
object_refs.append(malware_id)
def addObservedData(object_refs, attributes, attribute, identity):
observedData_id = "observed-data--{}".format(attribute['uuid'])
timestamp = getDateFromTimestamp(int(attribute['timestamp']))
object0 = defineObservableType(attribute['type'], attribute['value'])
# OBSERVABLE TYPES ARE CRAP
objects = {'0': object0}
observedData_args = {'id': observedData_id, 'type': 'observed-data', 'number_observed': 1,
'first_observed': timestamp, 'last_observed': timestamp, 'objects': objects,
'created_by_ref': identity}
observedData = ObservedData(**observedData_args)
attributes.append(observedData)
object_refs.append(observedData_id)
def addThreatActor(object_refs, attributes, galaxy, identity):
cluster = galaxy['GalaxyCluster'][0]
threatActor_id = "threat-actor--{}".format(cluster['uuid'])
name = cluster['value']
description = cluster['description']
labels = ['crime-syndicate'] # Arbitrary value as a first test
threatActor_args = {'id': threatActor_id, 'type': 'threat-actor', 'name': name, 'description': description,
'labels': labels, 'created_by_ref': identity}
meta = cluster['meta']
addAliases(meta, threatActor_args)
threatActor = ThreatActor(**threatActor_args)
attributes.append(threatActor)
object_refs.append(threatActor_id)
def addTool(object_refs, attributes, galaxy, identity):
tool_id = "tool--{}".format(galaxy['uuid'])
tool_args = {}
tool = Tool(**tool_args)
attributes.append(tool)
object_refs.append(tool_id)
def addVulnerability(object_refs, attributes, attribute, identity):
vuln_id = "vulnerability--{}".format(attribute['uuid'])
name = attribute['value']
ext_refs = [{'source_name': 'cve',
'external_id': name}]
vuln_args = {'type': 'vulnerability', 'id': vuln_id, 'external_references': ext_refs, 'name': name,
'created_by_ref': identity}
vulnerability = Vulnerability(**vuln_args)
attributes.append(vulnerability)
object_refs.append(vuln_id)
def addAliases(meta, argument):
if meta['synonyms']:
aliases = []
for a in meta['synonyms']:
aliases.append(a)
argument['aliases'] = aliases
def defineObservableType(dtype, val):
object0 = {}
# if dtype == '':
# datatype = 'artifact'
# elif dtype == '':
# datatype = 'autonomous-system'
# elif dtype == '':
# datatype = 'directory'
# elif dtype == '':
# datatype = 'domain-name'
# el
if 'email' in dtype and 'name' not in dtype and ('src' in dtype or 'dst' in dtype or 'target' in dtype):
object0['type'] = 'email-address'
object0['value'] = val
elif 'email' in dtype and ('body' in dtype or 'subject' in dtype or 'header' in dtype or 'reply' in dtype):
object0['type'] = 'email-message'
object0['subject'] = val
object0['is_multipart'] = 'false'
elif 'attachment' in dtype:
object0['type'] = 'file'
object0['name'] = val
# elif dtype == '':
# datatype = 'ipv4-addr'
# elif dtype == '':
# datatype = 'ipv6-addr'
# elif dtype == '':
# datatype = 'mac-addr'
# elif dtype == 'mutex':
# datatype = 'mutex'
# elif dtype == '':
# datatype = 'network-traffic'
# elif dtype == '':
# datatype = 'process'
# elif dtype == '':
# datatype = 'software'
elif dtype == 'url':
object0['type'] = 'url'
object0['value'] = val
# elif dtype == '':
# datatype = 'user-account'
elif 'regkey' in dtype:
object0['type'] = 'windows-registry-key'
object0['key'] = val
elif 'x509' in dtype:
object0['type'] = 'x509-certificate'
elif 'md5' in dtype or 'sha' in dtype:
object0['type'] = 'file'
object0['hashes'] = {dtype: val}
else:
object0['type'] = 'file' # CRAP BEFORE FINDING HOW TO HANDLE ALL THE CASES \o/
object0['name'] = val
return object0
def handleNonIndicatorAttribute(object_refs, attributes, attribute, identity):
attr_type = attribute['type']
if attr_type == "vulnerability":
addVulnerability(object_refs, attributes, attribute, identity)
# elif "target" in attr_type or attr_type == "attachment":
else:
addObservedData(object_refs, attributes, attribute, identity)
def handleIndicatorAttribute(object_refs, attributes, attribute, identity):
indic_id = "indicator--{}".format(attribute['uuid'])
category = attribute['category']
killchain = [{'kill_chain_name': 'misp-category',
'phase_name': category}]
args_indicator = {'valid_from': getDateFromTimestamp(int(attribute['timestamp'])), 'type': 'indicator',
'labels': ['malicious activity'], 'pattern': definePattern(attribute), 'id': indic_id,
'created_by_ref': identity}
args_indicator['kill_chain_phases'] = killchain
indicator = Indicator(**args_indicator)
# indicator = Indicator(valid_from=getDateFromTimestamp(int(attribute["timestamp"])), type='indicator',
# labels=['malicious activity'], pattern="{}".format(definePattern(attribute)),
# id=indic_id, kill_chain_phases=killchain, created_by_ref=identity)
# indicator.id = indic_id
# indicator.labels = ['malicious activity']
# indicator.pattern = "{}".format(definePattern(attribute))
attributes.append(indicator)
object_refs.append(indic_id)
def buildRelationships():
return
def definePattern(attribute):
attr_type = attribute['type']
pattern =""
if 'md5' in attr_type or 'sha' in attr_type:
pattern += 'file:hashes.{} = \'{}\''.format(attr_type, attribute['value'])
return [pattern]
def eventReport(event, identity, object_refs, external_refs):
timestamp = getDateFromTimestamp(int(event["publish_timestamp"]))
name = event["info"]
tags = event['Tag']
labels = []
for tag in tags:
labels.append(tag['name'])
report = Report(type="report", id="report--{}".format(event["uuid"]), created_by_ref=identity["id"],
name=name, published=timestamp, labels=labels, object_refs=object_refs,
external_references=external_refs)
return report
def generateEventPackage(event, SDOs):
bundle_id = event['uuid']
bundle = Bundle(type="bundle", spec_version="2.0", id="bundle--{}".format(bundle_id), objects=SDOs)
return bundle
def main(args):
pathname = os.path.dirname(sys.argv[0])
if len(sys.argv) > 3:
namespace[0] = sys.argv[3]
if len(sys.argv) > 4:
namespace[1] = sys.argv[4].replace(" ", "_")
namespace[1] = re.sub('[\W]+', '', namespace[1])
try:
idgen.set_id_namespace({namespace[0]: namespace[1]})
except ValueError:
try:
idgen.set_id_namespace(Namespace(namespace[0], namespace[1]))
except TypeError:
idgen.set_id_namespace(Namespace(namespace[0], namespace[1], "MISP"))
event = loadEvent(args, pathname)
if 'response' in event:
event = event['response'][0]['Event']
else:
event = event['Event']
# print(event['Galaxy'])
# sys.exit(0)
SDOs = []
object_refs = []
external_refs = []
identity = setIdentity(event)
SDOs.append(identity)
attributes = readAttributes(event, identity, object_refs, external_refs)
report = eventReport(event, identity, object_refs, external_refs)
SDOs.append(report)
for attribute in attributes:
SDOs.append(attribute)
stix_package = generateEventPackage(event, SDOs)
saveFile(args, pathname, stix_package)
# print(stix_package)
if __name__ == "__main__":
main(sys.argv)

View File

@ -2558,6 +2558,10 @@ function syncUserSelected() {
function filterAttributes(filter, id) {
url = "/events/viewEventAttributes/" + id + "/attributeFilter:" + filter;
if(filter === 'value'){
filter = $('#attributesFilterField').val().trim();
url += "/searchFor:" + filter;
}
if (deleted) url += '/deleted:true';
$.ajax({
type:"get",
@ -3218,7 +3222,6 @@ $('.add_object_attribute_row').click(function() {
$('.quickToggleCheckbox').toggle(function() {
var url = $(this).data('checkbox-url');
});
(function(){