mirror of https://github.com/MISP/MISP
new: [AttackMatrix] added Mobile/Pre-Attack Matrix support, UI
improvements and code refactopull/3347/head
parent
95e694f054
commit
bc156ab13a
|
@ -4564,46 +4564,90 @@ class EventsController extends AppController {
|
|||
if (!$this->request->is('ajax')) {
|
||||
throw new MethodNotAllowedException('Invalid method.');
|
||||
}
|
||||
$killChainOrder = array('initial-access', 'execution', 'persistence', 'privilege-escalation', 'defense-evasion', 'credential-access', 'discovery', 'lateral-movement', 'collection', 'exfiltration', 'command-and-control');
|
||||
|
||||
$event = $this->Event->fetchEvent($this->Auth->user(), array('eventid' => $eventId));
|
||||
if (empty($event)) throw new NotFoundException('Event not found or you are not authorised to view it.');
|
||||
$event = $event[0];
|
||||
$mitreAttackMatrix = $this->Event->GalaxyCluster->Galaxy->getMitreAttackMatrix();
|
||||
$attackClusters = $mitreAttackMatrix['attackClusters'];
|
||||
$attackGalaxyId = $mitreAttackMatrix['attackGalaxyId'];
|
||||
$killChainOrderEnterprise = array(
|
||||
'initial-access',
|
||||
'execution',
|
||||
'persistence',
|
||||
'privilege-escalation',
|
||||
'defense-evasion',
|
||||
'credential-access',
|
||||
'discovery',
|
||||
'lateral-movement',
|
||||
'collection',
|
||||
'exfiltration',
|
||||
'command-and-control'
|
||||
);
|
||||
$killChainOrderMobile = array(
|
||||
'persistence',
|
||||
'privilege-escalation',
|
||||
'defense-evasion',
|
||||
'credential-access',
|
||||
'discovery',
|
||||
'lateral-movement',
|
||||
'effects', 'collection',
|
||||
'exfiltration',
|
||||
'command-and-control',
|
||||
'general-network-based',
|
||||
'cellular-network-based',
|
||||
'could-based'
|
||||
);
|
||||
$killChainOrderPre = array(
|
||||
'priority-definition-planning',
|
||||
'priority-definition-direction',
|
||||
'target-selection',
|
||||
'technical-information-gathering',
|
||||
'people-information-gathering',
|
||||
'organizational-information-gathering',
|
||||
'technical-weakness-identification',
|
||||
'people-weakness-identification',
|
||||
'organizational-weakness-identification',
|
||||
'adversary-opsec',
|
||||
'establish-&-maintain-infrastructure',
|
||||
'persona-development',
|
||||
'build-capabilities',
|
||||
'test-capabilities',
|
||||
'stage-capabilities',
|
||||
'app-delivery-via-authorized-app-store',
|
||||
'app-delivery-via-other-means',
|
||||
'exploit-via-cellular-network',
|
||||
'exploit-via-internet',
|
||||
);
|
||||
|
||||
$type = "mitre-enterprise-attack-attack-pattern";
|
||||
$killChainOrders = array(
|
||||
'mitre-enterprise-attack-attack-pattern' => $killChainOrderEnterprise,
|
||||
'mitre-mobile-attack-attack-pattern' => $killChainOrderMobile,
|
||||
'mitre-pre-attack-attack-pattern' => $killChainOrderPre,
|
||||
);
|
||||
|
||||
$eventTags = $this->Event->EventTag->find('list', array(
|
||||
'recursive' => -1,
|
||||
'conditions' => array('event_id' => $eventId),
|
||||
'fields' => array('Tag.name'),
|
||||
'contain' => 'Tag',
|
||||
));
|
||||
$attributeTags = $this->Event->Attribute->AttributeTag->find('list', array(
|
||||
'recursive' => -1,
|
||||
'conditions' => array('event_id' => $eventId),
|
||||
'fields' => array('Tag.name'),
|
||||
'contain' => array(
|
||||
'Tag' => array(
|
||||
'fields' => array('name')
|
||||
),
|
||||
)
|
||||
));
|
||||
$tags = array('eventTags' => $eventTags, 'attributeTags' => $attributeTags);
|
||||
$this->loadModel('GalaxyCluster');
|
||||
$attackTactic = $this->GalaxyCluster->Galaxy->getMitreAttackMatrix();
|
||||
|
||||
// get score of galaxy
|
||||
$db = $this->Event->getDataSource();
|
||||
// tag along with its occurence in the event
|
||||
$subQuery = $db->buildStatement(
|
||||
array(
|
||||
'fields' => array('attr_tag.tag_id as id', 'count(attr_tag.tag_id) as value'),
|
||||
'table' => $db->fullTableName($this->Event->Attribute->AttributeTag),
|
||||
'alias' => 'attr_tag',
|
||||
'conditions' => array('event_id' => $eventId),
|
||||
'group' => 'tag_id'
|
||||
),
|
||||
$this->Event
|
||||
);
|
||||
$subQueryExpression = $db->expression($subQuery)->value;
|
||||
// get related galaxies
|
||||
$attributeTagScores = $this->Event->query("SELECT name, value FROM (" . $subQueryExpression . ") AS score, tags WHERE tags.id=score.id;");
|
||||
|
||||
// arrange data
|
||||
$scores = array();
|
||||
$maxScore = 0;
|
||||
foreach ($attributeTags as $name) {
|
||||
if (strpos($name, $type) === false) { // do not belong to mitre attack
|
||||
continue;
|
||||
}
|
||||
if (!isset($scores[$name])) {
|
||||
$scores[$name] = 0;
|
||||
}
|
||||
$scores[$name]++;
|
||||
$maxScore = $scores[$name] > $maxScore ? $scores[$name] : $maxScore;
|
||||
foreach($attributeTagScores as $item) {
|
||||
$score = $item['score']['value'];
|
||||
$name = $item['tags']['name'];
|
||||
$maxScore = $score > $maxScore ? $score : $maxScore;
|
||||
$scores[$name] = $score;
|
||||
}
|
||||
|
||||
App::uses('ColourGradientTool', 'Tools');
|
||||
|
@ -4611,10 +4655,8 @@ class EventsController extends AppController {
|
|||
$colours = $gradientTool->createGradientFromValues($scores);
|
||||
|
||||
$this->set('target_type', $itemType);
|
||||
$this->set('killChainOrder', $killChainOrder);
|
||||
$this->set('killChainNames', $killChainOrder);
|
||||
$this->set('attackGalaxyId', $attackGalaxyId);
|
||||
$this->set('attackClusters', $attackClusters);
|
||||
$this->set('killChainOrders', $killChainOrders);
|
||||
$this->set('attackTactic', $attackTactic);
|
||||
$this->set('scores', $scores);
|
||||
$this->set('maxScore', $maxScore);
|
||||
$this->set('colours', $colours);
|
||||
|
|
|
@ -219,57 +219,55 @@ class Galaxy extends AppModel{
|
|||
}
|
||||
|
||||
public function getMitreAttackMatrix($type="mitre-enterprise-attack-attack-pattern") {
|
||||
$conditions = array('Galaxy.type' => $type);
|
||||
$expectedDescription = 'ATT&CK Tactic';
|
||||
$expectedNamespace = 'mitre-attack';
|
||||
$conditions = array('Galaxy.description' => $expectedDescription, 'Galaxy.namespace' => $expectedNamespace);
|
||||
$contains = array(
|
||||
//'GalaxyCluster.GalaxyElement' => function($q) {
|
||||
// return $q->where(['GalaxyElement.key' => 'kill_chain']);
|
||||
//}
|
||||
//'GalaxyCluster' => array('GalaxyElement'),
|
||||
//'GalaxyCluster.GalaxyElement' => array(
|
||||
// 'conditions' => array('GalaxyElement.key' => 'kill_chain')
|
||||
//),
|
||||
'GalaxyCluster' => array('GalaxyElement'),
|
||||
//'GalaxyCluster' => array('GalaxyElement' => function ($q) {
|
||||
// return $q->where(array('GalaxyElement.key' => 'kill_chain'));
|
||||
//}),
|
||||
|
||||
);
|
||||
|
||||
$galaxy = $this->find('first', array(
|
||||
$galaxies = $this->find('all', array(
|
||||
'recursive' => -1,
|
||||
'contain' => $contains,
|
||||
'conditions' => $conditions,
|
||||
));
|
||||
|
||||
if (empty($galaxy)) {
|
||||
throw new NotFoundException('Galaxy not found.');
|
||||
if (empty($galaxies)) {
|
||||
throw new NotFoundException('Galaxies not found.');
|
||||
}
|
||||
if (empty($galaxy['GalaxyCluster'])) {
|
||||
throw new NotFoundException('Galaxy not found.');
|
||||
}
|
||||
$clusters = $galaxy['GalaxyCluster'];
|
||||
$attackClusters = array();
|
||||
$attackTactic = array();
|
||||
|
||||
foreach ($clusters as $cluster) {
|
||||
if (empty($cluster['GalaxyElement'])) {
|
||||
continue;
|
||||
}
|
||||
$toBeAdded = false;
|
||||
$galaxyElements = $cluster['GalaxyElement'];
|
||||
foreach ($galaxyElements as $element) {
|
||||
if ($element['key'] == 'kill_chain') {
|
||||
$kc = explode(":", $element['value'])[2];
|
||||
$toBeAdded = true;
|
||||
foreach ($galaxies as $galaxy) {
|
||||
$galaxyType = $galaxy['Galaxy']['type'];
|
||||
$clusters = $galaxy['GalaxyCluster'];
|
||||
$attackClusters = array();
|
||||
// add cluster if kill_chain is present
|
||||
foreach ($clusters as $cluster) {
|
||||
if (empty($cluster['GalaxyElement'])) {
|
||||
continue;
|
||||
}
|
||||
if ($element['key'] == 'external_id') {
|
||||
$cluster['external_id'] = $element['value'];
|
||||
$toBeAdded = false;
|
||||
$clusterType = $cluster['type'];
|
||||
$galaxyElements = $cluster['GalaxyElement'];
|
||||
foreach ($galaxyElements as $element) {
|
||||
if ($element['key'] == 'kill_chain') {
|
||||
$kc = explode(":", $element['value'])[2];
|
||||
$toBeAdded = true;
|
||||
}
|
||||
if ($element['key'] == 'external_id') {
|
||||
$cluster['external_id'] = $element['value'];
|
||||
}
|
||||
}
|
||||
if ($toBeAdded) {
|
||||
$attackClusters[$kc][] = $cluster;
|
||||
}
|
||||
}
|
||||
if ($toBeAdded) {
|
||||
$attackClusters[$kc][] = $cluster;
|
||||
}
|
||||
$attackTactic[$galaxyType] = array(
|
||||
'clusters' => $attackClusters,
|
||||
'galaxy' => $galaxy['Galaxy']
|
||||
);
|
||||
}
|
||||
|
||||
return array('attackClusters' => $attackClusters, 'attackGalaxyId' => $galaxy['Galaxy']['id']);
|
||||
return $attackTactic;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,15 @@
|
|||
<div class="attack-matrix-options" style="right: initial; background: transparent;">
|
||||
<ul id="attack-matrix-tabscontroller" class="nav nav-tabs" style="margin-bottom: 2px;">
|
||||
<?php
|
||||
$enterpriseTag = "mitre-enterprise-attack-attack-pattern";
|
||||
foreach($attackTactic as $tactic) {
|
||||
$galaxy = $tactic['galaxy'];
|
||||
?>
|
||||
<li class="tactic <?php echo $galaxy['type']==$enterpriseTag ? "active" : ""; ?>"><span href="#tabMatrix-<?php echo $galaxy['type']; ?>" data-toggle="tab" style="padding-top: 3px; padding-bottom: 3px;"><?php echo($galaxy['name']); ?></span></li>
|
||||
<?php } ?>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="attack-matrix-options">
|
||||
<span id="matrix-heatmap-legend-caret">
|
||||
<span id="matrix-heatmap-legend-caret-value">0</span>
|
||||
|
@ -21,22 +33,27 @@
|
|||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div id="matrix_container" class="fixed-table-container-inner" style="height: 670px;" data-picking-mode="<?php echo $pickingMode ? 'true' : 'false'; ?>">
|
||||
<div id="matrix_container" class="fixed-table-container-inner" style="max-height: 670px;" data-picking-mode="<?php echo $pickingMode ? 'true' : 'false'; ?>">
|
||||
<div class="tab-content">
|
||||
<?php foreach($attackTactic as $galaxy):
|
||||
$galaxyType = $galaxy['galaxy']['type'];
|
||||
?>
|
||||
<div class="tab-pane <?php echo $galaxyType==$enterpriseTag ? "active" : ""; ?>" id="tabMatrix-<?php echo $galaxyType; ?>">
|
||||
<div class="header-background"></div>
|
||||
<div class="fixed-table-container-inner" style="height: 670px;">
|
||||
<div class="fixed-table-container-inner" style="max-height: 670px;">
|
||||
<table class="table table-condensed matrix-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<?php
|
||||
foreach($killChainNames as $kc) {
|
||||
foreach($killChainOrders[$galaxyType] as $kc):
|
||||
$name = str_replace("-", " ", $kc);
|
||||
echo '<th>
|
||||
<div class="extra-wrap">
|
||||
<div class="th-inner">'.ucfirst($name).'</div>
|
||||
</div>
|
||||
</th>';
|
||||
}
|
||||
?>
|
||||
<th>
|
||||
<?php echo ucfirst($name); ?>
|
||||
<div class="th-inner"><?php echo ucfirst($name); ?></div>
|
||||
</th>
|
||||
|
||||
<?php endforeach; ?>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody style="overflow-y: scroll;">
|
||||
|
@ -46,28 +63,34 @@
|
|||
do {
|
||||
$added = false;
|
||||
echo '<tr>';
|
||||
$killChainOrder = $killChainOrders[$galaxyType];
|
||||
$attackClusters = $galaxy['clusters'];
|
||||
foreach($killChainOrder as $kc) {
|
||||
$clusters = $attackClusters[$kc];
|
||||
$td = '<td ';
|
||||
if ($i < count($clusters)) {
|
||||
$clusterId = $clusters[$i]['id'];
|
||||
$tagName = $clusters[$i]['tag_name'];
|
||||
$score = empty($scores[$tagName]) ? 0 : $scores[$tagName];
|
||||
$name = join(" ", array_slice(explode(" ", $clusters[$i]['value']), 0, -2)); // remove " - external_id"
|
||||
$td .= ' class="heatCell matrix-interaction ' . ($pickingMode ? 'cell-picking"' : '"');
|
||||
$td .= isset($colours[$tagName]) ? ' style="background: ' . $colours[$tagName] . '; color: ' . $this->TextColour->getTextColour($colours[$tagName]) . '"' : '' ;
|
||||
$td .= ' data-score="'.h($score).'"';
|
||||
$td .= ' data-tag_name="'.h($tagName).'"';
|
||||
if ($pickingMode) {
|
||||
$td .= ' data-target-type="attribute"';
|
||||
$td .= ' data-target-id="'.h($target_id).'"';
|
||||
$td .= ' data-cluster-id="'.h($clusterId).'"';
|
||||
}
|
||||
$td .= ' title="'.h($clusters[$i]['external_id']).'"';
|
||||
$td .= '>' . h($name);
|
||||
$added = true;
|
||||
if(!isset($attackClusters[$kc])) { // undefined index
|
||||
$td = '<td class="">';
|
||||
} else {
|
||||
$td .= 'class="">';
|
||||
$clusters = $attackClusters[$kc];
|
||||
$td = '<td ';
|
||||
if ($i < count($clusters)) {
|
||||
$clusterId = $clusters[$i]['id'];
|
||||
$tagName = $clusters[$i]['tag_name'];
|
||||
$score = empty($scores[$tagName]) ? 0 : $scores[$tagName];
|
||||
$name = join(" ", array_slice(explode(" ", $clusters[$i]['value']), 0, -2)); // remove " - external_id"
|
||||
$td .= ' class="heatCell matrix-interaction ' . ($pickingMode ? 'cell-picking"' : '"');
|
||||
$td .= isset($colours[$tagName]) ? ' style="background: ' . $colours[$tagName] . '; color: ' . $this->TextColour->getTextColour($colours[$tagName]) . '"' : '' ;
|
||||
$td .= ' data-score="'.h($score).'"';
|
||||
$td .= ' data-tag_name="'.h($tagName).'"';
|
||||
if ($pickingMode) {
|
||||
$td .= ' data-target-type="attribute"';
|
||||
$td .= ' data-target-id="'.h($target_id).'"';
|
||||
$td .= ' data-cluster-id="'.h($clusterId).'"';
|
||||
}
|
||||
$td .= ' title="'.h($clusters[$i]['external_id']).'"';
|
||||
$td .= '>' . h($name);
|
||||
$added = true;
|
||||
} else {
|
||||
$td .= 'class="">';
|
||||
}
|
||||
}
|
||||
$td .= '</td>';
|
||||
echo $td;
|
||||
|
@ -79,6 +102,9 @@
|
|||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if($pickingMode): ?>
|
||||
|
|
|
@ -4,6 +4,11 @@
|
|||
|
||||
.matrix-table th {
|
||||
padding: 0px 5px;
|
||||
color: transparent;
|
||||
line-height: 12px;
|
||||
}
|
||||
|
||||
.matrix-table thead > tr {
|
||||
}
|
||||
|
||||
.matrix-table thead {
|
||||
|
@ -16,6 +21,7 @@
|
|||
|
||||
table.matrix-table {
|
||||
table-layout: fixed;
|
||||
margin-bottom: unset;
|
||||
}
|
||||
|
||||
td.matrix-interaction {
|
||||
|
@ -41,15 +47,23 @@ td.matrix-interaction:hover {
|
|||
color: white;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
line-height: 30px;
|
||||
text-align: left;
|
||||
padding-left: 5px;
|
||||
margin-left: -5px;
|
||||
/*line-height: 30px;*/
|
||||
line-height: 12px;
|
||||
}
|
||||
|
||||
div.th-inner {
|
||||
min-height: 30px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.header-background {
|
||||
background-color: #363636;
|
||||
height: 30px;
|
||||
min-height: 30px;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
|
@ -64,7 +78,7 @@ td.matrix-interaction:hover {
|
|||
.attack-matrix-options {
|
||||
position: absolute;
|
||||
right: -1px;
|
||||
top: -18px;
|
||||
top: -22px;
|
||||
background: #363636;
|
||||
color: white;
|
||||
padding: 1px 5px;
|
||||
|
@ -106,3 +120,23 @@ td.matrix-interaction:hover {
|
|||
position: relative;
|
||||
display: block;
|
||||
}
|
||||
|
||||
li.tactic {
|
||||
color: #555555;
|
||||
cursor: default;
|
||||
background-color: #ffffff;
|
||||
border: 1px solid #ddd;
|
||||
border-bottom-color: transparent;
|
||||
padding: 2px 8px 2px 8px;
|
||||
}
|
||||
|
||||
li.tactic:hover {
|
||||
background-color: #6f6f6f;
|
||||
color: white;
|
||||
}
|
||||
|
||||
li.tactic.active {
|
||||
background-color: #363636;
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
(function () {
|
||||
$(document).ready(function() {
|
||||
$('#attack-matrix-tabscontroller span').click(function (e) {
|
||||
$(this).tab('show');
|
||||
})
|
||||
|
||||
var pickingMode = $('#matrix_container').data('picking-mode');
|
||||
if (pickingMode) {
|
||||
$('.ajax_popover_form .cell-picking').click(function() {
|
||||
|
@ -24,20 +28,45 @@
|
|||
$('#checkbox_attackMatrix_showAll').click(function() { toggleAttackMatrixCells('.info_container_eventgraph_network'); });
|
||||
}
|
||||
|
||||
|
||||
scoredCells.tooltip({
|
||||
container: 'body',
|
||||
placement: 'top',
|
||||
});
|
||||
|
||||
scoredCells.hover(enteringScoredCell, leavingScoredCell);
|
||||
|
||||
$('span[data-toggle="tab"]').on('shown', function (e) {
|
||||
var tabId = $(e.target).attr('href');
|
||||
resizeHeader(tabId);
|
||||
})
|
||||
|
||||
toggleAttackMatrixCells();
|
||||
});
|
||||
|
||||
function toggleAttackMatrixCells(jfilter) {
|
||||
function resizeHeader(tabId) {
|
||||
if (tabId === undefined) {
|
||||
tabId = '';
|
||||
}
|
||||
// resize fixed header div based on dimension of th cell
|
||||
$(tabId + ' .matrix-table').each(function() {
|
||||
var max_height = 0;
|
||||
var div = $(this).find('thead > tr > th > div');
|
||||
var cell = $(this).find('thead > tr > th');
|
||||
for(var i=0; i<cell.length; i++) {
|
||||
var cellH = $(cell[i]).css('height')
|
||||
console.log(cellH);
|
||||
max_height = $(cell[i]).height() > max_height ? $(cell[i]).height() : max_height;
|
||||
$(div[i]).css({
|
||||
width: $(cell[i]).css('width'),
|
||||
height: cellH,
|
||||
});
|
||||
}
|
||||
console.log(max_height);
|
||||
$(tabId + ' .header-background').css('height', max_height+'px');
|
||||
});
|
||||
}
|
||||
|
||||
function toggleAttackMatrixCells(jfilterOrig) {
|
||||
// get active tab
|
||||
var activeTableId = $('#attack-matrix-tabscontroller > li.active > span').attr('href');
|
||||
jfilter = jfilterOrig === undefined ? activeTableId : jfilterOrig+' '+activeTableId;
|
||||
|
||||
var visibilityVal, displayVal;
|
||||
if($(jfilter+' #checkbox_attackMatrix_showAll').prop('checked')) {
|
||||
if($(jfilterOrig+' #checkbox_attackMatrix_showAll').prop('checked')) {
|
||||
visibilityVal = 'visible';
|
||||
displayVal = 'table-cell';
|
||||
displayVal = '';
|
||||
|
@ -53,6 +82,8 @@
|
|||
});
|
||||
var rowNum = $(jfilter+' .matrix-table > tbody > tr').length;
|
||||
var colNum = $(jfilter+' .matrix-table > thead > tr > th').length;
|
||||
|
||||
// hide empty row
|
||||
for (var i=1; i<=rowNum; i++) {
|
||||
var cellNoValues = $(jfilter+' .matrix-table > tbody > tr:nth-child('+i+') > td').filter(function() {
|
||||
return $(this).attr('data-score') == 0 || $(this).attr('data-score') === undefined;
|
||||
|
@ -62,6 +93,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
// hide empty column
|
||||
for (var i=1; i<=colNum; i++) {
|
||||
var cellNoValues = $(jfilter+' .matrix-table tr td:nth-child('+i+')').filter(function() {
|
||||
return $(this).attr('data-score') == 0 || $(this).attr('data-score') === undefined;
|
||||
|
|
Loading…
Reference in New Issue