diff --git a/app/Console/Command/Ls22Shell.php b/app/Console/Command/Ls22Shell.php
index 63a945bbe..e5791abcb 100644
--- a/app/Console/Command/Ls22Shell.php
+++ b/app/Console/Command/Ls22Shell.php
@@ -52,6 +52,55 @@ class Ls22Shell extends AppShell
),
),
]);
+ $parser->addSubcommand('checkSyncConnections', [
+ 'help' => __('Check the given sync connection(s) for the given server(s).'),
+ 'parser' => array(
+ 'options' => array(
+ 'instances' => [
+ 'help' => 'Path to the instance file, by default "instances.csv" from the local directory',
+ 'short' => 'i',
+ 'required' => true
+ ],
+ 'misp_url_filter' => [
+ 'help' => 'The url of the instance to execute changes on. If not set, all are updated.',
+ 'short' => 'm',
+ 'required' => false
+ ],
+ 'synced_misp_url_filter' => [
+ 'help' => 'The sync connection to modify on each valid instance (as selected by the misp_url_filter). If not set, all sync connections on the selected instances will be updated.',
+ 'short' => 's',
+ 'required' => false
+ ]
+ ),
+ ),
+ ]);
+ $parser->addSubcommand('modifySyncConnection', [
+ 'help' => __('Modify sync connection(s).'),
+ 'parser' => array(
+ 'options' => array(
+ 'instances' => [
+ 'help' => 'Path to the instance file, by default "instances.csv" from the local directory',
+ 'short' => 'i',
+ 'required' => true
+ ],
+ 'misp_url_filter' => [
+ 'help' => 'The url of the instance to execute changes on. If not set, all are updated.',
+ 'short' => 'm',
+ 'required' => false
+ ],
+ 'synced_misp_url_filter' => [
+ 'help' => 'The sync connection to modify on each valid instance (as selected by the misp_url_filter). If not set, all sync connections on the selected instances will be updated.',
+ 'short' => 's',
+ 'required' => false
+ ],
+ 'json' => [
+ 'help' => 'JSON delta to push (such as \'{"push": 1}\').',
+ 'short' => 'j',
+ 'required' => true
+ ]
+ ),
+ ),
+ ]);
$parser->addSubcommand('addWarninglist', [
'help' => __('Inject warninglist'),
'parser' => array(
@@ -104,6 +153,11 @@ class Ls22Shell extends AppShell
'help' => 'Upper bound of the date. Accepts timestamp or date distance (such as 1d or 5h). Defaults to unbounded.',
'short' => 't',
'required' => false
+ ],
+ 'org' => [
+ 'help' => 'Name the org that should be evaluated. If not set, all will be included.',
+ 'short' => 'o',
+ 'required' => false
]
),
),
@@ -111,6 +165,103 @@ class Ls22Shell extends AppShell
return $parser;
}
+ public function checkSyncConnections()
+ {
+ $this->__getInstances($this->param('instances'));
+ $results = [];
+ $instanceFilter = $this->param('misp_url_filter');
+ $syncedInstanceFilter = $this->param('synced_misp_url_filter');
+ foreach ($this->__servers as $server) {
+ if (!empty($instanceFilter) && strtolower(trim($server['Server']['url'])) !== strtolower(trim($instanceFilter))) {
+ continue;
+ }
+ $HttpSocket = $this->Server->setupHttpSocket($server, null);
+ $request = $this->Server->setupSyncRequest($server, 'Server');
+ $start_time = microtime(true);
+ $response = $HttpSocket->get($server['Server']['url'] . '/servers/index', false, $request);
+ $baseline = round((microtime(true) - $start_time) * 1000);
+ if (!$response->isOk()) {
+ $this->out($server['Server']['url'] . ': ' . 'Connection or auth failed', 1, Shell::NORMAL);
+ continue;
+ }
+ $synced_servers = json_decode($response->body, true);
+ foreach ($synced_servers as $synced_server) {
+ $success = false;
+ if (empty($syncedInstanceFilter) || strtolower($synced_server['Server']['url']) === strtolower($syncedInstanceFilter)) {
+ $start_time = microtime(true);
+ $response = $HttpSocket->get($server['Server']['url'] . '/servers/testConnection/' . $synced_server['Server']['id'], '{}', $request);
+ $execution_time = round((microtime(true) - $start_time) * 1000) - $baseline;
+ if ($response->isOk()) {
+ $success = true;
+ }
+ $this->out(
+ sprintf(
+ '%s connection to %s: %s (%sms)',
+ $server['Server']['url'],
+ $synced_server['Server']['url'],
+ sprintf(
+ '<%s>%s%s>',
+ $success ? 'info' : 'error',
+ $success ? 'Success' : 'Failed',
+ $success ? 'info' : 'error'
+ ),
+ $execution_time
+ ),
+ 1,
+ Shell::NORMAL
+ );
+ }
+ }
+ }
+ }
+
+ public function modifySyncConnection()
+ {
+ $this->__getInstances($this->param('instances'));
+ $results = [];
+ $instanceFilter = $this->param('misp_url_filter');
+ $syncedInstanceFilter = $this->param('synced_misp_url_filter');
+ $json = $this->param('json');
+ foreach ($this->__servers as $server) {
+ if (!empty($instanceFilter) && strtolower(trim($server['Server']['url'])) !== strtolower(trim($instanceFilter))) {
+ continue;
+ }
+ $HttpSocket = $this->Server->setupHttpSocket($server, null);
+ $request = $this->Server->setupSyncRequest($server, 'Server');
+ $response = $HttpSocket->get($server['Server']['url'] . '/servers/index', false, $request);
+ if (!$response->isOk()) {
+ $this->out($server['Server']['url'] . ': ' . 'Connection or auth failed', 1, Shell::NORMAL);
+ }
+ $synced_servers = json_decode($response->body, true);
+ $success = false;
+ foreach ($synced_servers as $synced_server) {
+ if (empty($syncedInstanceFilter) || strtolower($synced_server['Server']['url']) === strtolower($syncedInstanceFilter)) {
+ debug($json);
+ $response = $HttpSocket->post($server['Server']['url'] . '/servers/edit/' . $synced_server['Server']['id'], $json, $request);
+ debug($response->body);
+ if ($response->isOk()) {
+ $success = true;
+ }
+ $this->out(
+ sprintf(
+ '%s connection to %s: %s',
+ $server['Server']['url'],
+ $synced_server['Server']['url'],
+ sprintf(
+ '<%s>%s%s>',
+ $success ? 'info' : 'error',
+ $success ? 'Success' : 'Failed',
+ $success ? 'info' : 'error'
+ )
+ ),
+ 1,
+ Shell::NORMAL
+ );
+ }
+ }
+ }
+ }
+
public function enableTaxonomy()
{
$taxonomyToEnable = $this->param('taxonomy');
@@ -162,16 +313,29 @@ class Ls22Shell extends AppShell
$HttpSocket = $this->Server->setupHttpSocket($server, null);
$request = $this->Server->setupSyncRequest($server, 'Server');
$start_time = microtime(true);
- $response = $HttpSocket->get($server['Server']['url'] . '/users/view/me', false, $request);
- $execution_time = round((microtime(true) - $start_time) * 1000);
- $statusWrapped = sprintf(
- '<%s>%s%s>',
- $response->isOk() ? 'info' : 'error',
- $response->isOk() ? 'OK (' . $execution_time . 'ms)' : 'Failed. (' . $response->code . ')',
- $response->isOk() ? 'info' : 'error'
- );
+ $fatal_error = false;
+ try {
+ $response = $HttpSocket->get($server['Server']['url'] . '/users/view/me', false, $request);
+ } catch (Exception $e) {
+ $fatal_error = true;
+ echo "\x07";
+ $statusWrapped = sprintf(
+ '%s %s: %s',
+ 'Something went wrong while trying to reach',
+ $server['Server']['url'],
+ $e->getMessage()
+ );
+ }
+ if (!$fatal_error) {
+ $execution_time = round((microtime(true) - $start_time) * 1000);
+ $statusWrapped = sprintf(
+ '<%s>%s%s>',
+ $response->isOk() ? 'info' : 'error',
+ $response->isOk() ? 'OK (' . $execution_time . 'ms)' : 'Failed. (' . $response->code . ')',
+ $response->isOk() ? 'info' : 'error'
+ );
+ }
$this->out($server['Server']['url'] . ': ' . $statusWrapped, 1, Shell::NORMAL);
- $results[$server['Server']['url']] = $response->isOk() ? $execution_time : false;
}
}
@@ -215,28 +379,30 @@ class Ls22Shell extends AppShell
}
$HttpSocket = $this->Server->setupHttpSocket($server, null);
$request = $this->Server->setupSyncRequest($server);
- $response = $HttpSocket->get($server['Server']['url'] . '/organisations/index', false, $request);
+ $response = $HttpSocket->get($server['Server']['url'] . '/organisations/index/scope:all', false, $request);
$orgs = json_decode($response->body(), true);
$this->out(__('Organisations fetched. %d found.', count($orgs)), 1, Shell::VERBOSE);
$org_mapping = [];
foreach ($orgs as $org) {
- $name = explode(' ', $org['Organisation']['name']);
- if ($name[0] !== 'BT') {
+ if (!empty($this->param('org')) && $org['Organisation']['name'] !== $this->param('org')) {
+ continue;
+ }
+ if ($org['Organisation']['name'] === 'YT') {
continue;
}
$org_mapping[$org['Organisation']['name']] = $org['Organisation']['id'];
}
+ if (!empty($this->param['from'])) {
+ $time_range[] = $this->param['from'];
+ }
+ if (!empty($this->param['to'])) {
+ if (empty($time_range)) {
+ $time_range[] = '365d';
+ }
+ $time_range[] = $this->param['to'];
+ }
foreach ($org_mapping as $org_name => $org_id) {
$time_range = [];
- if (!empty($this->param['from'])) {
- $time_range[] = $this->param['from'];
- }
- if (!empty($this->param['to'])) {
- if (empty($time_range)) {
- $time_range[] = '365d';
- }
- $time_range[] = $this->param['to'];
- }
$params = [
'org' => $org_id
];
@@ -256,7 +422,8 @@ class Ls22Shell extends AppShell
'other' => 0,
'attribute_attack' => 0,
'attribute_other' => 0,
- 'score' => 0
+ 'score' => 0,
+ 'warnings' => 0
];
foreach ($events['response'] as $event) {
if (!empty($event['Event']['Tag'])) {
@@ -290,6 +457,9 @@ class Ls22Shell extends AppShell
}
}
}
+ if (!empty($attribute['warnings'])) {
+ $result[$org_name]['warnings'] += 1;
+ }
}
$results[$org_name]['attribute_count'] += count($event['Event']['Attribute']);
if (!empty($event['Event']['Object'])) {
@@ -319,11 +489,18 @@ class Ls22Shell extends AppShell
foreach ($results as $k => $result) {
$totalCount = $result['attribute_count'] + $result['object_count'];
if ($totalCount) {
+ if (empty($result['warnings'])) {
+ $results[$k]['metrics']['warnings'] = 100;
+ } else if (100 * $result['warnings'] < $result['attribute_count']) {
+ $results[$k]['metrics']['warnings'] = 50;
+ } else {
+ $results[$k]['metrics']['warnings'] = 0;
+ }
$results[$k]['metrics']['connectedness'] = 100 * ($result['connected_elements'] / ($result['attribute_count'] + $result['object_count']));
$results[$k]['metrics']['attack_weight'] = 100 * (2*($result['attack']) + $result['attribute_attack']) / ($result['attribute_count'] + $result['object_count']);
$results[$k]['metrics']['other_weight'] = 100 * (2*($result['other']) + $result['attribute_other']) / ($result['attribute_count'] + $result['object_count']);
}
- foreach (['connectedness', 'attack_weight', 'other_weight'] as $metric) {
+ foreach (['connectedness', 'attack_weight', 'other_weight', 'warnings'] as $metric) {
if (empty($results[$k]['metrics'][$metric])) {
$results[$k]['metrics'][$metric] = 0;
}
@@ -331,8 +508,17 @@ class Ls22Shell extends AppShell
$results[$k]['metrics'][$metric] = 100;
}
}
- $results[$k]['score'] = round(40 * $results[$k]['metrics']['connectedness'] + 40 * $results[$k]['metrics']['attack_weight'] + 20 * $results[$k]['metrics']['other_weight']) / 100;
- $scores[$k] = $results[$k]['score'];
+ $results[$k]['score'] = round(
+ 20 * $results[$k]['metrics']['warnings'] +
+ 20 * $results[$k]['metrics']['connectedness'] +
+ 40 * $results[$k]['metrics']['attack_weight'] +
+ 20 * $results[$k]['metrics']['other_weight']
+ ) / 100;
+ $scores[$k]['total'] = $results[$k]['score'];
+ $scores[$k]['warnings'] = round(20 * $results[$k]['metrics']['warnings']);
+ $scores[$k]['connectedness'] = round(20 * $results[$k]['metrics']['connectedness']);
+ $scores[$k]['attack_weight'] = round(40 * $results[$k]['metrics']['attack_weight']);
+ $scores[$k]['other_weight'] = round(20 * $results[$k]['metrics']['other_weight']);
}
arsort($scores, SORT_DESC);
$this->out(str_repeat('=', 128), 1, Shell::NORMAL);
@@ -344,18 +530,34 @@ class Ls22Shell extends AppShell
), 1, Shell::NORMAL);
$this->out(str_repeat('=', 128), 1, Shell::NORMAL);
foreach ($scores as $org => $score) {
- $score_string = str_repeat('█', round($score));
+ $score_string[0] = str_repeat('█', round($score['warnings']/100));
+ $score_string[1] = str_repeat('█', round($score['connectedness']/100));
+ $score_string[2] = str_repeat('█', round($score['attack_weight']/100));
+ $score_string[3] = str_repeat('█', round($score['other_weight']/100));
$this->out(sprintf(
'| %s | %s | %s |',
str_pad($org, 10, ' ', STR_PAD_RIGHT),
sprintf(
- '%s%s',
- $score_string,
- str_repeat(' ', 100 - mb_strlen($score_string))
+ '%s%s%s%s%s',
+ $score_string[0],
+ $score_string[1],
+ $score_string[2],
+ $score_string[3],
+ str_repeat(' ', 100 - mb_strlen(implode('', $score_string)))
),
- str_pad($score . '%', 8, ' ', STR_PAD_RIGHT)
+ str_pad($score['total'] . '%', 8, ' ', STR_PAD_RIGHT)
), 1, Shell::NORMAL);
}
$this->out(str_repeat('=', 128), 1, Shell::NORMAL);
+ $this->out(sprintf(
+ '| Legend: %s %s %s %s %s |',
+ '█: Warnings',
+ '█: Connectedness',
+ '█: ATT&CK context',
+ '█: Other Context',
+ str_repeat(' ', 52)
+ ), 1, Shell::NORMAL);
+ $this->out(str_repeat('=', 128), 1, Shell::NORMAL);
+ file_put_contents(APP . 'tmp/report.json', json_encode($results, JSON_PRETTY_PRINT));
}
}