chg: Refactor of the Bro export

pull/1544/head
Iglocska 2016-09-15 17:44:59 +02:00
parent 85879e735c
commit 59ecf40f42
7 changed files with 209 additions and 109 deletions

View File

@ -317,7 +317,8 @@ class EventShell extends AppShell
} else {
$zipname = DS . 'misp.bro.' . $user['Organisation']['name'] . '.intel.zip';
}
$tmpZipname = DS . "bro_export_tmp.zip";
$random_component = $this->Event->generateRandomFileName();
$tmpZipname = DS . "bro_export_tmp_" . $random_component . ".zip";
$zip = new File($dir->pwd() . $tmpZipname);
foreach ($types as $k => $type) {
$final = $this->Attribute->bro($user, $type);
@ -332,7 +333,7 @@ class EventShell extends AppShell
$execRetval = '';
$execOutput = array();
exec('zip -gj ' . $zip->path . ' ' . $dir->pwd() . '/' . $filename,
exec('zip -gj ' . $zip->path . ' ' . $dir->pwd() . '/' . $filename,
$execOutput, $execRetval);
if ($execRetval != 0) { // not EXIT_SUCCESS
throw new Exception('An error has occured while attempting to zip the intel files.');

View File

@ -1871,7 +1871,11 @@ class AttributesController extends AppController {
}
}
$this->response->type('txt'); // set the content type
$this->header('Content-Disposition: download; filename="misp.' . $type . '.intel"');
$filename = 'misp.' . $type . '.intel';
if ($eventId) {
$filename = 'misp.' . $type . '.event_' . $eventId . '.intel';
}
$this->header('Content-Disposition: download; filename="' . $filename . '"');
$this->layout = 'text/default';
$attributes = array("#fields indicator\tindicator_type\tmeta.source\tmeta.url\tmeta.do_notice\tmeta.if_in");
$attributes = array_merge($attributes, $this->Attribute->bro($this->Auth->user(), $type, $tags, $eventId, $allowNonIDS, $from, $to, $last));

View File

@ -1719,7 +1719,6 @@ class EventsController extends AppController {
if ($last) $last = $this->Event->resolveTimeDelta($last);
// backwards compatibility, swap key and format
if ($format != 'snort' && $format != 'suricata') {
$key = $format;
$format = 'suricata'; // default format
}
$this->response->type('txt'); // set the content type
@ -3142,10 +3141,10 @@ class EventsController extends AppController {
'checkbox' => false,
),
'bro' => array(
'url' => '/events/bro/download/' . $id,
'text' => 'Download Bro rules',
'requiresPublished' => true,
'checkbox' => false,
'url' => '/attributes/bro/download/all/false/' . $id,
'text' => 'Download Bro rules',
'requiresPublished' => true,
'checkbox' => false
),
'text' => array(
'url' => '/attributes/text/download/all/false/' . $id,

View File

@ -0,0 +1,176 @@
<?php
class BroExport {
public $rules = array();
// mapping from misp attribute type to the bro intel type
// alternative mechanisms are:
// - alternate: array containing a detection regex and a replacement bro type
// - composite: for composite misp attributes (domain|ip), use the provided bro type if the second value is queried
// - replace: run a replacement regex on the value before generating the bro rule
private $mapping = array(
'ip-dst' => array('brotype' => 'ADDR', 'alternate' => array('#/#', 'SUBNET')),
'ip-src' => array('brotype' => 'ADDR', 'alternate' => array('#/#', 'SUBNET')),
'email-src' => array('brotype' => 'EMAIL'),
'email-dst' => array('brotype' => 'EMAIL'),
'email-attachment' => array('brotype' => 'FILE_NAME'),
'filename' => array('brotype' => 'FILE_NAME'),
'hostname' => array('brotype' => 'DOMAIN'),
'domain' => array('brotype' => 'DOMAIN'),
'domain|ip' => array('brotype' => 'DOMAIN', 'composite' => 'ADDR'),
'url' => array('brotype' => 'URL', 'replace' => array('#^https?://#', '')),
'user-agent' => array('brotype' => 'SOFTWARE'),
'md5' => array('brotype' => 'FILE_HASH'),
'malware-sample' => array('brotype' => 'FILE_NAME', 'composite' => 'FILE_HASH'),
'filename|md5' => array('brotype' => 'FILE_NAME', 'composite' => 'FILE_HASH'),
'sha1' => array('brotype' => 'FILE_HASH'),
'filename|sha1' => array('brotype' => 'FILE_NAME', 'composite' => 'FILE_HASH'),
'sha256' => array('brotype' => 'FILE_HASH'),
'filename|sha256' => array('brotype' => 'FILE_NAME', 'composite' => 'FILE_HASH'),
'x509-fingerprint-sha1' => array('brotype' => 'CERT_HASH'),
);
// export group to misp type mapping
// the mapped type is in an array format, first value being the misp type, second being the value field used
private $__mispTypes = array(
'ip' => array(
array('ip-src', 1),
array('ip-dst', 1),
array('domain|ip', 2)
),
'url' => array(
array('url', 1)
),
'domain' => array(
array('hostname', 1),
array('domain', 1),
array('domain|ip', 1)
),
'email' => array(
array('email-src', 1),
array('email-dst', 1)
),
'filename' => array(
array('filename', 1),
array('email-attachment', 1),
array('attachment', 1),
array('filename|md5', 1),
array('filename|sha1', 1),
array('filename|sha256', 1),
array('malware-sample', 1)
),
'filehash' => array(
array('md5', 1),
array('sha1', 1),
array('sha256', 1),
array('filename|md5', 2),
array('filename|sha1', 2),
array('filename|sha256', 2),
array('malware-sample', 2)
),
'certhash' => array(
array('x509-fingerprint-sha1', 1)
),
'software' => array(
array('user-agent', 1)
)
);
private $whitelist = null;
public function export($items, $orgs, $valueField, $intel) {
$this->Whitelist = ClassRegistry::init('Whitelist');
$this->whitelist = $this->Whitelist->getBlockedValues();
//For bro format organisation
$orgsName = array();
// generate the rules
foreach ($items as $item) {
if (!isset($orgs[$item['Event']['orgc_id']])) {
continue;
} else {
$orgName = $orgs[$item['Event']['orgc_id']];
}
$ruleFormatReference = Configure::read('MISP.baseurl') . '/events/view/' . $item['Event']['id'];
$ruleFormat = "%s\t%s\t" . $orgName . "\t" . $ruleFormatReference . "\t%s\t%s";
$rule = $this->__generateRule($item['Attribute'], $ruleFormat, $valueField);
if (!empty($rule)) {
if (!in_array($rule, $intel)) {
$intel[] = $rule;
}
}
}
return $intel;
}
private function __generateRule($attribute, $ruleFormat, $valueField) {
if (isset($this->mapping[$attribute['type']])) {
$brotype = $this->mapping[$attribute['type']]['brotype'];
$overruled = $this->checkWhitelist($attribute['value']);
if (isset($this->mapping[$attribute['type']]['alternate'])) {
if (preg_match($this->mapping[$attribute['type']]['alternate'][0], $attribute['value'])) {
$brotype = $this->mapping[$attribute['type']]['alternate'][1];
}
}
if ($valueField == 2 && isset($this->mapping[$attribute['type']]['composite'])) {
$brotype = $this->mapping[$attribute['type']]['composite'];
}
$attribute['value'] = $this->replaceIllegalChars($attribute['value']); // substitute chars not allowed in rule
if (isset($this->mapping[$attribute['type']]['replace'])) {
$attribute['value'] = preg_replace(
$this->mapping[$attribute['type']]['replace'][0],
$this->mapping[$attribute['type']]['replace'][1],
$attribute['value']
);
}
return sprintf($ruleFormat,
($overruled) ? '#OVERRULED BY WHITELIST# ' :
$attribute['value'], // value - for composite values only the relevant element is taken
'Intel:' . $brotype, // type
'T', // meta.do_notice
'-' // meta.if_in
);
}
return false;
}
/**
* Replaces characters that are not allowed in a signature.
* example: " is converted to |22|
* @param unknown_type $value
*/
public static function replaceIllegalChars($value) {
$replace_pairs = array(
'|' => '|7c|', // Needs to stay on top !
'"' => '|22|',
';' => '|3b|',
':' => '|3a|',
'\\' => '|5c|',
'0x' => '|30 78|'
);
return strtr($value, $replace_pairs);
}
public function checkWhitelist($value) {
foreach ($this->whitelist as $wlitem) {
if (preg_match($wlitem, $value)) {
return true;
}
}
return false;
}
public function getMispTypes($type) {
$mispTypes = array();
if ($type !== 'all') {
if (isset($this->__mispTypes[$type])) {
$mispTypes = $this->__mispTypes[$type];
}
} else {
foreach ($this->__mispTypes as $mispType) {
$mispTypes = array_merge($mispTypes, $mispType);
}
}
return $mispTypes;
}
}

View File

@ -1412,7 +1412,7 @@ class Attribute extends AppModel {
return $values;
}
function bro($user, $type, $tags = false, $eventId = false, $from = false, $to = false, $last = false){
function bro($user, $type, $tags = false, $eventId = false, $from = false, $to = false, $last = false) {
//restricting to non-private or same org if the user is not a site-admin.
$conditions['AND'] = array('Attribute.to_ids =' => 1, 'Event.published =' => 1);
if ($from) $conditions['AND']['Event.date >='] = $from;
@ -1438,113 +1438,31 @@ class Attribute extends AppModel {
$conditions['AND'][] = $temp;
}
$mispTypes = $this->getMispTypes($type);
App::uses('BroExport', 'Export');
$export = new BroExport();
$mispTypes = $export->getMispTypes($type);
$intel = array();
foreach($mispTypes as $mispType) {
$conditions['AND']['Attribute.type'] = $mispType;
if ($type === 'filehash') {
$intel = array_merge($intel, $this->brohids($user, $conditions, $this->getValueField($type, $mispType), $mispType));
} else {
$intel = array_merge($intel, $this->bronids($user, $conditions,$this->getValueField($type, $mispType)));
}
$conditions['AND']['Attribute.type'] = $mispType[0];
$intel = $this->__bro($intel, $user, $conditions, $mispType[1], $export);
}
return $intel;
}
private function getMispTypes($type)
{
$mispTypes = array();
if ($type !== 'all') {
switch ($type) {
case 'ip':
$mispTypes = array('ip-src', 'ip-dst', 'domain|ip');
break;
case 'url':
$mispTypes = array('url');
break;
case 'domain':
$mispTypes = array('hostname', 'domain', 'domain|ip');
break;
case 'email':
$mispTypes = array('email-src', 'email-dst');
break;
case 'filename':
$mispTypes = array('filename', 'email-attachment', 'filename|md5', 'filename|sha1', 'filename|sha256');
break;
case 'filehash':
$mispTypes = array('md5', 'sha1', 'sha256', 'filename|md5', 'filename|sha1', 'filename|sha256');
break;
case 'certhash':
$mispTypes = array('x509-fingerprint-sha1');
break;
case 'software':
$mispTypes = array('user-agent');
break;
}
return $mispTypes;
}
return $mispTypes;
}
private function getValueField($type, $mispType){
$valueField = "value1";
switch($type){
case 'ip':
if($mispType == 'domain|ip'){
$valueField = "value2";
}
break;
case 'filehash':
if($mispType == 'filename|md5' || $mispType == 'filename|sha1' || $mispType == 'filename|sha256'){
$valueField = "value2";
}
break;
}
return $valueField;
}
private function brohids($user, $conditions, $valueField, $mispType)
{
$intel = array();
App::uses('HidsBroExport', 'Export');
$export = new HidsBroExport();
$attributes = $this->fetchAttributes($user, array(
'conditions' => $conditions,
'order' => 'Attribute.'.$valueField.' ASC',
'fields' => array('Attribute.id', 'Attribute.event_id', 'Attribute.type',
'Attribute.'.$valueField." as value"),
'contain' => array('Event' => array(
'fields' => array(
'Event.id', 'Event.published', 'Event.date',
'Event.publish_timestamp', 'Event.orgc_id'),
),
)
)
);
$intel = array_merge($intel, $export->export($attributes, strtoupper($mispType), true));
return $intel;
}
private function bronids($user, $conditions, $valueField)
{
$intel = array();
App::uses('NidsBroExport', 'Export');
$export = new NidsBroExport();
private function __bro($intel, $user, $conditions, $valueField, $export) {
$attributes = $this->fetchAttributes($user, array(
'conditions' => $conditions, // array of conditions
'order' => 'Attribute.'.$valueField.' ASC',
'order' => 'Attribute.value' . $valueField . ' ASC',
'recursive' => -1, // int
'fields' => array('Attribute.id', 'Attribute.event_id', 'Attribute.type', 'Attribute.'.$valueField." as value"),
'contain' => array('Event' => array('fields' => array('Event.id', 'Event.threat_level_id'))),
'group' => array('Attribute.type', 'Attribute.'.$valueField), // fields to GROUP BY
'fields' => array('Attribute.id', 'Attribute.event_id', 'Attribute.type', 'Attribute.value' . $valueField . " as value"),
'contain' => array('Event' => array('fields' => array('Event.id', 'Event.threat_level_id', 'Event.orgc_id'))),
'group' => array('Attribute.type', 'Attribute.value' . $valueField), // fields to GROUP BY
)
);
$intel = array_merge($intel, $export->export($attributes, $user['nids_sid'], 'bro', true));
return $intel;
$orgs = $this->Event->Orgc->find('list', array(
'fields' => array('Orgc.id', 'Orgc.name')
));
return $export->export($attributes, $orgs, $valueField, $intel);
}
public function generateCorrelation($jobId = false, $startPercentage = 0) {

View File

@ -98,9 +98,11 @@ class Event extends AppModel {
'description' => 'Click this to download all network related attributes that you have access to under the Snort rule format. Only published events and attributes marked as IDS Signature are exported. Administration is able to maintain a whitelist containing host, domain name and IP numbers to exclude from the NIDS export.',
),
'bro' => array(
'extension' => '.intel.zip',
'type' => 'Bro',
'description' => 'Click this to download all network related attributes that you have access to under the Bro rule format. Only published events and attributes marked as IDS Signature are exported. Administration is able to maintain a whitelist containing host, domain name and IP numbers to exclude from the NIDS export.',
'extension' => '.intel.zip',
'type' => 'Bro',
'requiresPublished' => 1,
'canHaveAttachments' => false,
'description' => 'Click this to download all network related attributes that you have access to under the Bro rule format. Only published events and attributes marked as IDS Signature are exported. Administration is able to maintain a whitelist containing host, domain name and IP numbers to exclude from the NIDS export.',
),
'rpz' => array(
'extension' => '.txt',

View File

@ -40,7 +40,7 @@
</div>
<div class="row bottom-buffer">
<div class="span3">
<?php echo $this->Html->link('Download Bro signatures', array('action' => 'nids', 'bro', 'download'), array('class' => 'btn btn-block full-width')); ?>
<?php echo $this->Html->link('Download Bro signatures', array('controller' => 'attributes', 'action' => 'bro', 'download'), array('class' => 'btn btn-block full-width')); ?>
</div>
<div class="span9">Click these to download all network related attributes that you
have access to under the Bro rule format. Only <em>published</em>