mirror of https://github.com/MISP/MISP
Merge branch 'develop' into 2.4
commit
af516ed534
2
PyMISP
2
PyMISP
|
@ -1 +1 @@
|
|||
Subproject commit 9a7adb2e0d60d2edee9f541db808652875bae20e
|
||||
Subproject commit be755277b85cd6b06a1c2d6bb793f47ac891cc66
|
|
@ -1 +1 @@
|
|||
{"major":2, "minor":4, "hotfix":168}
|
||||
{"major":2, "minor":4, "hotfix":169}
|
||||
|
|
|
@ -213,12 +213,13 @@ $config = array(
|
|||
// Warning: The following is a 3rd party contribution and still untested (including security) by the MISP-project team.
|
||||
// Feel free to enable it and report back to us if you run into any issues.
|
||||
//
|
||||
// Uncomment the following to enable Kerberos authentication
|
||||
// Uncomment the following to enable Kerberos/LDAP authentication
|
||||
// needs PHP LDAP support enabled (e.g. compile flag --with-ldap or Debian package php5-ldap)
|
||||
/*
|
||||
'ApacheSecureAuth' => array( // Configuration for kerberos authentication
|
||||
'ApacheSecureAuth' => array( // Configuration for kerberos/LDAP authentication
|
||||
'apacheEnv' => 'REMOTE_USER', // If proxy variable = HTTP_REMOTE_USER, If BasicAuth ldap = PHP_AUTH_USER
|
||||
'ldapServer' => 'ldap://example.com', // FQDN or IP
|
||||
'ldapServer' => 'ldap://example.com', // FQDN or IP, ldap:// for LDAP or LDAP+STARTTLS, ldaps:// for LDAPS
|
||||
'starttls' => true, // true for STARTTLS, ignored for LDAPS
|
||||
'ldapProtocol' => 3,
|
||||
'ldapNetworkTimeout' => -1, // use -1 for unlimited network timeout
|
||||
'ldapReaderUser' => 'cn=userWithReadAccess,ou=users,dc=example,dc=com', // DN ou RDN LDAP with reader user right
|
||||
|
|
|
@ -29,6 +29,8 @@ class Ls22Shell extends AppShell
|
|||
|
||||
public function getOptionParser()
|
||||
{
|
||||
$this->stdout->styles('green', array('text' => 'green'));
|
||||
|
||||
$parser = parent::getOptionParser();
|
||||
$parser->addSubcommand('enableTaxonomy', [
|
||||
'help' => __('Enable a taxonomy with all its tags.'),
|
||||
|
@ -448,6 +450,8 @@ class Ls22Shell extends AppShell
|
|||
}
|
||||
$time_range[] = $this->param['to'];
|
||||
}
|
||||
$event_extended_uuids = [];
|
||||
$event_uuid_per_org = [];
|
||||
foreach ($org_mapping as $org_name => $org_id) {
|
||||
$time_range = [];
|
||||
$params = [
|
||||
|
@ -462,6 +466,7 @@ class Ls22Shell extends AppShell
|
|||
$results[$org_name] = [
|
||||
'attribute_count' => 0,
|
||||
'object_count' => 0,
|
||||
'event_count' => count($events['response']),
|
||||
'connected_elements' => 0,
|
||||
'event_tags' => 0,
|
||||
'attribute_tags' => 0,
|
||||
|
@ -470,9 +475,16 @@ class Ls22Shell extends AppShell
|
|||
'attribute_attack' => 0,
|
||||
'attribute_other' => 0,
|
||||
'score' => 0,
|
||||
'warnings' => 0
|
||||
'warnings' => 0,
|
||||
'events_extended' => 0,
|
||||
'extending_events' => 0,
|
||||
];
|
||||
foreach ($events['response'] as $event) {
|
||||
$event_uuid_per_org[$event['Event']['uuid']] = $org_name;
|
||||
if (!empty($event['Event']['extends_uuid'])) {
|
||||
$event_extended_uuids[$org_name] = $event['Event']['extends_uuid'];
|
||||
}
|
||||
|
||||
if (!empty($event['Event']['Tag'])) {
|
||||
foreach ($event['Event']['Tag'] as $tag) {
|
||||
if (substr($tag['name'], 0, 32) === 'misp-galaxy:mitre-attack-pattern') {
|
||||
|
@ -505,7 +517,7 @@ class Ls22Shell extends AppShell
|
|||
}
|
||||
}
|
||||
if (!empty($attribute['warnings'])) {
|
||||
$result[$org_name]['warnings'] += 1;
|
||||
$results[$org_name]['warnings'] += 1;
|
||||
}
|
||||
}
|
||||
$results[$org_name]['attribute_count'] += count($event['Event']['Attribute']);
|
||||
|
@ -532,6 +544,18 @@ class Ls22Shell extends AppShell
|
|||
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($event_extended_uuids as $orgc => $uuid) {
|
||||
$org_name = $event_uuid_per_org[$uuid];
|
||||
if ($orgc != $org_name) {
|
||||
// Add point for org extending another event
|
||||
$results[$orgc]['extending_events'] += 1;
|
||||
// Add point for org getting their event extended
|
||||
$results[$org_name]['events_extended'] += 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$scores = [];
|
||||
foreach ($results as $k => $result) {
|
||||
$totalCount = $result['attribute_count'] + $result['object_count'];
|
||||
|
@ -546,8 +570,9 @@ class Ls22Shell extends AppShell
|
|||
$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']);
|
||||
$results[$k]['metrics']['collaboration'] = 100 * ((2*$result['events_extended'] + $result['extending_events']) / $result['event_count']);
|
||||
}
|
||||
foreach (['connectedness', 'attack_weight', 'other_weight', 'warnings'] as $metric) {
|
||||
foreach (['connectedness', 'attack_weight', 'other_weight', 'warnings', 'collaboration'] as $metric) {
|
||||
if (empty($results[$k]['metrics'][$metric])) {
|
||||
$results[$k]['metrics'][$metric] = 0;
|
||||
}
|
||||
|
@ -559,13 +584,15 @@ class Ls22Shell extends AppShell
|
|||
20 * $results[$k]['metrics']['warnings'] +
|
||||
20 * $results[$k]['metrics']['connectedness'] +
|
||||
40 * $results[$k]['metrics']['attack_weight'] +
|
||||
20 * $results[$k]['metrics']['other_weight']
|
||||
10 * $results[$k]['metrics']['other_weight'] +
|
||||
10 * $results[$k]['metrics']['collaboration']
|
||||
) / 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']);
|
||||
$scores[$k]['other_weight'] = round(10 * $results[$k]['metrics']['other_weight']);
|
||||
$scores[$k]['collaboration'] = round(10 * $results[$k]['metrics']['collaboration']);
|
||||
}
|
||||
arsort($scores, SORT_DESC);
|
||||
$this->out(str_repeat('=', 128), 1, Shell::NORMAL);
|
||||
|
@ -581,15 +608,17 @@ class Ls22Shell extends AppShell
|
|||
$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));
|
||||
$score_string[4] = str_repeat('█', round($score['collaboration']/100));
|
||||
$this->out(sprintf(
|
||||
'| %s | %s | %s |',
|
||||
str_pad($org, 10, ' ', STR_PAD_RIGHT),
|
||||
sprintf(
|
||||
'<error>%s</error><warning>%s</warning><question>%s</question><info>%s</info>%s',
|
||||
'<error>%s</error><warning>%s</warning><question>%s</question><info>%s</info><green>%s</green>%s',
|
||||
$score_string[0],
|
||||
$score_string[1],
|
||||
$score_string[2],
|
||||
$score_string[3],
|
||||
$score_string[4],
|
||||
str_repeat(' ', 100 - mb_strlen(implode('', $score_string)))
|
||||
),
|
||||
str_pad($score['total'] . '%', 8, ' ', STR_PAD_RIGHT)
|
||||
|
@ -602,6 +631,7 @@ class Ls22Shell extends AppShell
|
|||
'<warning>█: Connectedness</warning>',
|
||||
'<question>█: ATT&CK context</question>',
|
||||
'<info>█: Other Context</info>',
|
||||
'<green>█: Collaboration</green>',
|
||||
str_repeat(' ', 52)
|
||||
), 1, Shell::NORMAL);
|
||||
$this->out(str_repeat('=', 128), 1, Shell::NORMAL);
|
||||
|
|
|
@ -34,7 +34,7 @@ class AppController extends Controller
|
|||
public $helpers = array('OrgImg', 'FontAwesome', 'UserName');
|
||||
|
||||
private $__queryVersion = '147';
|
||||
public $pyMispVersion = '2.4.168';
|
||||
public $pyMispVersion = '2.4.169';
|
||||
public $phpmin = '7.2';
|
||||
public $phprec = '7.4';
|
||||
public $phptoonew = '8.0';
|
||||
|
@ -170,7 +170,7 @@ class AppController extends Controller
|
|||
throw new MethodNotAllowedException('You are using an unsecure and outdated version of IE, please download Google Chrome, Mozilla Firefox or update to a newer version of IE. If you are running IE9 or newer and still receive this error message, please make sure that you are not running your browser in compatibility mode. If you still have issues accessing the site, get in touch with your administration team at ' . Configure::read('MISP.contact'));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// For fresh installation (salt empty) generate a new salt
|
||||
if (!Configure::read('Security.salt')) {
|
||||
$this->User->Server->serverSettingsSaveValue('Security.salt', $this->User->generateRandomPassword(32));
|
||||
|
@ -200,7 +200,7 @@ class AppController extends Controller
|
|||
} else {
|
||||
$this->Auth->authenticate[AuthComponent::ALL]['userFields'] = $authUserFields;
|
||||
}
|
||||
|
||||
|
||||
$userLoggedIn = false;
|
||||
if (Configure::read('Plugin.CustomAuth_enable')) {
|
||||
$userLoggedIn = $this->__customAuthentication($_SERVER);
|
||||
|
@ -310,7 +310,7 @@ class AppController extends Controller
|
|||
$this->__accessMonitor($user);
|
||||
|
||||
} else {
|
||||
$preAuthActions = array('login', 'register', 'getGpgPublicKey');
|
||||
$preAuthActions = array('login', 'register', 'getGpgPublicKey', 'logout401');
|
||||
if (!empty(Configure::read('Security.email_otp_enabled'))) {
|
||||
$preAuthActions[] = 'email_otp';
|
||||
}
|
||||
|
|
|
@ -750,6 +750,7 @@ class ACLComponent extends Component
|
|||
'initiatePasswordReset' => ['AND' => ['perm_admin', 'password_change_enabled']],
|
||||
'login' => array('*'),
|
||||
'logout' => array('*'),
|
||||
'logout401' => array('*'),
|
||||
'notificationSettings' => ['*'],
|
||||
'register' => array('*'),
|
||||
'registrations' => array(),
|
||||
|
|
|
@ -38,10 +38,10 @@ class ApacheAuthenticate extends BaseAuthenticate
|
|||
}
|
||||
return $returnCode;
|
||||
}
|
||||
|
||||
|
||||
private function getEmailAddress($ldapEmailField, $ldapUserData)
|
||||
{
|
||||
// return the email address of an LDAP user if one of the fields in $ldapEmaiLField exists
|
||||
// return the email address of an LDAP user if one of the fields in $ldapEmaiLField exists
|
||||
foreach($ldapEmailField as $field) {
|
||||
if (isset($ldapUserData[0][$field][0])) {
|
||||
return $ldapUserData[0][$field][0];
|
||||
|
@ -73,6 +73,14 @@ class ApacheAuthenticate extends BaseAuthenticate
|
|||
ldap_set_option($ldapconn, LDAP_OPT_PROTOCOL_VERSION, Configure::read('ApacheSecureAuth.ldapProtocol'));
|
||||
ldap_set_option($ldapconn, LDAP_OPT_REFERRALS, Configure::read('ApacheSecureAuth.ldapAllowReferrals', true));
|
||||
|
||||
if (Configure::read('ApacheSecureAuth.starttls', false) == true) {
|
||||
# Default is false, sine STARTTLS support is a new feature
|
||||
# Ignored on ldaps://, but can trigger problems for orgs
|
||||
# using unencrypted LDAP. Loose comparison allows users to
|
||||
# use # true / 1 / etc.
|
||||
ldap_start_tls($ldapconn);
|
||||
}
|
||||
|
||||
if ($ldapconn) {
|
||||
// LDAP bind
|
||||
$ldapbind = ldap_bind($ldapconn, $ldaprdn, $ldappass);
|
||||
|
@ -105,7 +113,6 @@ class ApacheAuthenticate extends BaseAuthenticate
|
|||
} else {
|
||||
die("User not found in LDAP");
|
||||
}
|
||||
|
||||
// close LDAP connection
|
||||
ldap_close($ldapconn);
|
||||
}
|
||||
|
|
|
@ -2324,6 +2324,9 @@ class EventsController extends AppController
|
|||
if ($this->request->is('post')) {
|
||||
$results = array();
|
||||
if (!empty($this->request->data)) {
|
||||
if (empty($this->request->data['Event'])) {
|
||||
$this->request->data['Event'] = $this->request->data;
|
||||
}
|
||||
if (!empty($this->request->data['Event']['filecontent'])) {
|
||||
$data = $this->request->data['Event']['filecontent'];
|
||||
$isXml = $data[0] === '<';
|
||||
|
@ -2557,7 +2560,7 @@ class EventsController extends AppController
|
|||
}
|
||||
}
|
||||
|
||||
public function populate($id)
|
||||
public function populate($id, $regenerateUUIDs=false)
|
||||
{
|
||||
if ($this->request->is('get') && $this->_isRest()) {
|
||||
return $this->RestResponse->describe('Events', 'populate', false, $this->response->type());
|
||||
|
@ -2579,15 +2582,25 @@ class EventsController extends AppController
|
|||
}
|
||||
if ($this->request->is('post') || $this->request->is('put')) {
|
||||
if (isset($this->request->data['Event'])) {
|
||||
$regenerateUUIDs = $this->request->data['Event']['regenerate_uuids'] ?? false;
|
||||
$this->request->data = $this->request->data['Event'];
|
||||
}
|
||||
if (isset($this->request->data['json'])) {
|
||||
$this->request->data = $this->_jsonDecode($this->request->data['json']);
|
||||
}
|
||||
if (isset($this->request->data['Event'])) {
|
||||
$this->request->data = $this->request->data['Event'];
|
||||
}
|
||||
$eventToSave = $event;
|
||||
$capturedObjects = ['Attribute', 'Object', 'Tag', 'Galaxy', 'EventReport'];
|
||||
foreach ($capturedObjects as $objectType) {
|
||||
if (!empty($this->request->data[$objectType])) {
|
||||
if (!empty($regenerateUUIDs)) {
|
||||
foreach ($this->request->data[$objectType] as $i => $obj) {
|
||||
unset($this->request->data[$objectType][$i]['id']);
|
||||
unset($this->request->data[$objectType][$i]['uuid']);
|
||||
}
|
||||
}
|
||||
$eventToSave['Event'][$objectType] = $this->request->data[$objectType];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ class UsersController extends AppController
|
|||
parent::beforeFilter();
|
||||
|
||||
// what pages are allowed for non-logged-in users
|
||||
$allowedActions = array('login', 'logout', 'getGpgPublicKey');
|
||||
$allowedActions = array('login', 'logout', 'getGpgPublicKey', 'logout401');
|
||||
if(!empty(Configure::read('Security.email_otp_enabled'))) {
|
||||
$allowedActions[] = 'email_otp';
|
||||
}
|
||||
|
@ -2896,4 +2896,11 @@ class UsersController extends AppController
|
|||
$this->render('/genericTemplates/confirm');
|
||||
}
|
||||
}
|
||||
public function logout401() {
|
||||
# You should read the documentation in docs/CONFIG.ApacheSecureAuth.md
|
||||
# before using this endpoint. It is not useful without webserver config
|
||||
# changes.
|
||||
# To use this, set Plugin.CustomAuth_custom_logout to /users/logout401
|
||||
$this->response->statusCode(401);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -527,7 +527,6 @@ class DefaultCorrelationBehavior extends ModelBehavior
|
|||
$correlation[$prefix . 'object_id'] &&
|
||||
(
|
||||
$correlation[$prefix . 'object_distribution'] == 0 ||
|
||||
$correlation[$prefix . 'object_distribution'] == 5 ||
|
||||
(
|
||||
$correlation[$prefix . 'object_distribution'] == 4 &&
|
||||
!in_array($correlation[$prefix . 'object_sharing_group_id'], $sgids)
|
||||
|
|
|
@ -3996,21 +3996,29 @@ class Server extends AppModel
|
|||
try {
|
||||
$composer = FileAccessTool::readJsonFromFile(APP . DS . 'composer.json');
|
||||
$extensions = [];
|
||||
$dependencies = [];
|
||||
foreach ($composer['require'] as $require => $foo) {
|
||||
if (substr($require, 0, 4) === 'ext-') {
|
||||
$extensions[substr($require, 4)] = true;
|
||||
}
|
||||
else if (mb_strpos($require, '/') !== false) { // external dependencies have namespaces, so a /
|
||||
$dependencies[$require] = true;
|
||||
}
|
||||
}
|
||||
foreach ($composer['suggest'] as $suggest => $reason) {
|
||||
if (substr($suggest, 0, 4) === 'ext-') {
|
||||
$extensions[substr($suggest, 4)] = $reason;
|
||||
}
|
||||
else if (mb_strpos($suggest, '/') !== false) { // external dependencies have namespaces, so a /
|
||||
$dependencies[$suggest] = $reason;
|
||||
}
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$this->logException('Could not load extensions from composer.json', $e, LOG_NOTICE);
|
||||
$extensions = ['redis' => '', 'gd' => '', 'ssdeep' => '', 'zip' => '', 'intl' => '']; // Default extensions
|
||||
}
|
||||
|
||||
// check PHP extensions
|
||||
$results = ['cli' => false];
|
||||
foreach ($extensions as $extension => $reason) {
|
||||
$results['extensions'][$extension] = [
|
||||
|
@ -4022,9 +4030,9 @@ class Server extends AppModel
|
|||
'info' => $reason === true ? null : $reason,
|
||||
];
|
||||
}
|
||||
if (is_readable(APP . '/files/scripts/selftest.php')) {
|
||||
if (is_readable(APP . DS . 'files' . DS . 'scripts' . DS . 'selftest.php')) {
|
||||
try {
|
||||
$execResult = ProcessTool::execute(['php', APP . '/files/scripts/selftest.php', json_encode(array_keys($extensions))]);
|
||||
$execResult = ProcessTool::execute(['php', APP . DS . 'files' . DS . 'scripts' . DS . 'selftest.php', json_encode(array_keys($extensions))]);
|
||||
} catch (Exception $e) {
|
||||
// pass
|
||||
}
|
||||
|
@ -4037,6 +4045,7 @@ class Server extends AppModel
|
|||
}
|
||||
}
|
||||
|
||||
// version check
|
||||
$minimalVersions = [
|
||||
'redis' => '2.2.8', // because of sAddArray method
|
||||
];
|
||||
|
@ -4052,6 +4061,24 @@ class Server extends AppModel
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check PHP dependencies, installed in the Vendor directory, just check presence of the folder
|
||||
if (class_exists('\Composer\InstalledVersions')) {
|
||||
foreach ($dependencies as $dependency => $reason) {
|
||||
try {
|
||||
$version = \Composer\InstalledVersions::getVersion($dependency);
|
||||
} catch (Exception $e) {
|
||||
$version = false;
|
||||
}
|
||||
$results['dependencies'][$dependency] = [
|
||||
'version' => $version,
|
||||
'version_outdated' => false,
|
||||
'required' => $reason === true,
|
||||
'info' => $reason === true ? null : $reason,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
|
|
|
@ -36,7 +36,6 @@ class WorkflowBaseModule
|
|||
{
|
||||
$fullIndexedParams = [];
|
||||
foreach ($this->params as $param) {
|
||||
$param['value'] = $nodeParamByID[$param['id']]['value'] ?? null;
|
||||
$param['value'] = $node['data']['indexed_params'][$param['id']] ?? null;
|
||||
$fullIndexedParams[$param['id']] = $param;
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ class Module_ms_teams_webhook extends Module_webhook
|
|||
{
|
||||
public $id = 'ms-teams-webhook';
|
||||
public $name = 'MS Teams Webhook';
|
||||
public $version = '0.2';
|
||||
public $description = 'Perform callbacks to the MS Teams webhook provided by the "Incoming Webhook" connector';
|
||||
public $icon_path = 'MS_Teams.png';
|
||||
|
||||
|
@ -37,7 +38,7 @@ class Module_ms_teams_webhook extends Module_webhook
|
|||
];
|
||||
}
|
||||
|
||||
protected function doRequest($url, $contentType, $data)
|
||||
protected function doRequest($url, $contentType, $data, $headers = [], $serverConfig = null)
|
||||
{
|
||||
$data = '{"text":"' . implode($data) . '"}';
|
||||
return parent::doRequest($url, $contentType, $data);
|
||||
|
|
|
@ -0,0 +1,167 @@
|
|||
<?php
|
||||
include_once APP . 'Model/WorkflowModules/WorkflowBaseModule.php';
|
||||
|
||||
App::uses('SyncTool', 'Tools');
|
||||
App::uses('JsonTool', 'Tools');
|
||||
|
||||
class Module_splunk_hec_export extends Module_webhook
|
||||
{
|
||||
public $id = 'splunk-hec-export';
|
||||
public $name = 'Splunk HEC export';
|
||||
public $version = '0.2';
|
||||
public $description = 'Export Event Data to Splunk HTTP Event Collector. Due to the potential high amount of requests, it\'s recommanded to put this module after a `concurrent_task` logic module.';
|
||||
public $icon_path = 'Splunk.png';
|
||||
public $support_filters = false;
|
||||
public $expect_misp_core_format = true;
|
||||
public $params = [];
|
||||
public $outputs = 0;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->params = [
|
||||
[
|
||||
'id' => 'url',
|
||||
'label' => __('HEC URL'),
|
||||
'type' => 'input',
|
||||
'placeholder' => 'https://splunk:8088/services/collector/event',
|
||||
],
|
||||
[
|
||||
'id' => 'verify_tls',
|
||||
'label' => __('Verify HTTPS Certificate'),
|
||||
'type' => 'select',
|
||||
'options' => [
|
||||
'1' => __('True'),
|
||||
'0' => __('False'),
|
||||
],
|
||||
'default' => 1,
|
||||
],
|
||||
[
|
||||
'id' => 'hec_token',
|
||||
'label' => __('HEC Token'),
|
||||
'type' => 'select',
|
||||
'type' => 'input',
|
||||
'placeholder' => '00000000-0000-0000-000000000000'
|
||||
],
|
||||
[
|
||||
'id' => 'source_type',
|
||||
'label' => __('Source Type'),
|
||||
'type' => 'select',
|
||||
'type' => 'input',
|
||||
'default' => '',
|
||||
'placeholder' => 'misp:event'
|
||||
],
|
||||
[
|
||||
'id' => 'event_per_attribute',
|
||||
'label' => __('Create one Splunk Event per Attribute'),
|
||||
'type' => 'select',
|
||||
'options' => [
|
||||
'1' => __('True'),
|
||||
'0' => __('False'),
|
||||
],
|
||||
'default' => 0,
|
||||
],
|
||||
[
|
||||
'id' => 'data_extraction_model',
|
||||
'label' => __('Data extraction model (JSON)'),
|
||||
'type' => 'textarea',
|
||||
'default' => '',
|
||||
'placeholder' => '{ "EventInfo": "Event.info", "AttributeValue": "Event.Attribute.{n}.value"}',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function exec(array $node, WorkflowRoamingData $roamingData, array &$errors = []): bool
|
||||
{
|
||||
if (empty(Configure::read('Security.rest_client_enable_arbitrary_urls'))) {
|
||||
$errors[] = __('`Security.rest_client_enable_arbitrary_urls` is turned off');
|
||||
return false;
|
||||
}
|
||||
$params = $this->getParamsWithValues($node);
|
||||
if (empty($params['url']['value'])) {
|
||||
$errors[] = __('URL not provided.');
|
||||
return false;
|
||||
}
|
||||
if (empty($params['hec_token']['value'])) {
|
||||
$errors[] = __('Authorization token not provided.');
|
||||
return false;
|
||||
}
|
||||
|
||||
$rData = $roamingData->getData();
|
||||
$event_without_attributes = $rData['Event'];
|
||||
unset($event_without_attributes['Attribute']);
|
||||
unset($event_without_attributes['_AttributeFlattened']);
|
||||
|
||||
$splunk_events = [];
|
||||
if (!empty($params['event_per_attribute']['value'])) {
|
||||
foreach ($rData['Event']['Attribute'] as $attribute) {
|
||||
$splunk_events[] = [
|
||||
'Attribute' => $attribute,
|
||||
'Event' => $event_without_attributes
|
||||
];
|
||||
}
|
||||
} else {
|
||||
$splunk_events[] = $rData;
|
||||
}
|
||||
|
||||
if (!empty($params['data_extraction_model']['value'])) {
|
||||
$data_extraction_model = JsonTool::decode($params['data_extraction_model']['value']);
|
||||
$extracted_events = [];
|
||||
foreach ($splunk_events as $splunk_event) {
|
||||
$event = [];
|
||||
foreach ($data_extraction_model as $field => $path) {
|
||||
$field_data = $this->extractData($splunk_event, $path);
|
||||
$event[$field] = count($field_data) == 1 ? $field_data[0] : $field_data; // unpack if only one element
|
||||
}
|
||||
$extracted_events[] = $event;
|
||||
}
|
||||
$splunk_events = $extracted_events;
|
||||
}
|
||||
|
||||
return $this->sendToSplunk($splunk_events, $params['hec_token']['value'], $params['url']['value'], $params['source_type']['value']);
|
||||
}
|
||||
|
||||
protected function sendToSplunk(array $splunk_events, $token, $url, $source_type): bool
|
||||
{
|
||||
foreach ($splunk_events as $splunk_event) {
|
||||
try {
|
||||
$headers = [
|
||||
'Authorization' => "Splunk {$token}",
|
||||
];
|
||||
$serverConfig = [
|
||||
'Server' => ['self_signed' => empty($params['verify_tls']['value'])]
|
||||
];
|
||||
|
||||
$hec_event = [
|
||||
'event' => $splunk_event
|
||||
];
|
||||
if (!empty($source_type)) {
|
||||
$hec_event['sourcetype'] = $source_type;
|
||||
}
|
||||
|
||||
$response = $this->doRequest(
|
||||
$url,
|
||||
'json',
|
||||
$hec_event,
|
||||
$headers,
|
||||
$serverConfig
|
||||
);
|
||||
if (!$response->isOk()) {
|
||||
if ($response->code === 403 || $response->code === 401) {
|
||||
$errors[] = __('Authentication failed.');
|
||||
return false;
|
||||
}
|
||||
$errors[] = __('Something went wrong with the request or the remote side is having issues. Body returned: %s', $response->body);
|
||||
return false;
|
||||
}
|
||||
} catch (SocketException $e) {
|
||||
$errors[] = __('Something went wrong while sending the request. Error returned: %s', $e->getMessage());
|
||||
return false;
|
||||
} catch (Exception $e) {
|
||||
$errors[] = __('Something went wrong. Error returned: %s', $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -8,7 +8,7 @@ class Module_webhook extends WorkflowBaseActionModule
|
|||
{
|
||||
public $id = 'webhook';
|
||||
public $name = 'Webhook';
|
||||
public $version = '0.2';
|
||||
public $version = '0.3';
|
||||
public $description = 'Allow to perform custom callbacks to the provided URL';
|
||||
public $icon_path = 'webhook.png';
|
||||
public $inputs = 1;
|
||||
|
@ -106,21 +106,22 @@ class Module_webhook extends WorkflowBaseActionModule
|
|||
return false;
|
||||
}
|
||||
|
||||
protected function doRequest($url, $contentType, $data)
|
||||
protected function doRequest($url, $contentType, $data, $headers = [], $serverConfig = null)
|
||||
{
|
||||
$this->Event = ClassRegistry::init('Event'); // We just need a model to use AppModel functions
|
||||
$version = implode('.', $this->Event->checkMISPVersion());
|
||||
$commit = $this->Event->checkMIPSCommit();
|
||||
|
||||
$request = [
|
||||
'header' => [
|
||||
'header' => array_merge([
|
||||
'Accept' => 'application/json',
|
||||
'Content-Type' => 'application/json',
|
||||
'User-Agent' => 'MISP ' . $version . (empty($commit) ? '' : ' - #' . $commit),
|
||||
]
|
||||
], $headers)
|
||||
];
|
||||
$syncTool = new SyncTool();
|
||||
$HttpSocket = $syncTool->setupHttpSocket(null, $this->timeout);
|
||||
$serverConfig = !empty($serverConfig['Server']) ? $serverConfig : ['Server' => $serverConfig];
|
||||
$HttpSocket = $syncTool->setupHttpSocket($serverConfig, $this->timeout);
|
||||
if ($contentType == 'form') {
|
||||
$request['header']['Content-Type'] = 'application/x-www-form-urlencoded';
|
||||
$response = $HttpSocket->post($url, $data, $request);
|
||||
|
|
|
@ -32,11 +32,12 @@
|
|||
foreach ($relatedData as $k => $v) {
|
||||
$popover .= '<b class="black">' . h($k) . '</b>: <span class="blue">' . h($v) . '</span><br>';
|
||||
}
|
||||
$relevantId = !isset($relatedAttribute['attribute_id']) ? $relatedAttribute['Event']['id'] : $relatedAttribute['id'];
|
||||
$link = $this->Html->link(
|
||||
$relatedAttribute['id'],
|
||||
$relevantId,
|
||||
$withPivot ?
|
||||
['controller' => 'events', 'action' => 'view', $relatedAttribute['id'], true, $event['Event']['id']] :
|
||||
['controller' => 'events', 'action' => 'view', $relatedAttribute['id']],
|
||||
['controller' => 'events', 'action' => 'view', $relevantId, true, $event['Event']['id']] :
|
||||
['controller' => 'events', 'action' => 'view', $relevantId],
|
||||
['class' => ($relatedAttribute['org_id'] == $me['org_id']) ? $linkColour : 'blue']
|
||||
);
|
||||
echo sprintf(
|
||||
|
|
|
@ -159,9 +159,9 @@ $type_mapper = [
|
|||
.addClass(jqXHR.status == 200 ? 'label-success' : 'label-important')
|
||||
if (typeof result === 'object') {
|
||||
$executionResultText.text(JSON.stringify(result, '', 4));
|
||||
$('#executionResultHtml').empty();
|
||||
} else {
|
||||
$('#executionResultHtml').html(result);
|
||||
// $executionResultText.text(result);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -75,6 +75,9 @@
|
|||
'tag_display_style' => 2
|
||||
]);
|
||||
}
|
||||
if (empty($hTags)) {
|
||||
$hTags = sprintf('<span class="grey">-%s-</span>', __('none'));
|
||||
}
|
||||
|
||||
$highlightedTagsString .= sprintf(
|
||||
'<tr><td style="font-weight: bold;text-transform: uppercase;">%s</td></td><td>%s</td><td>%s</td></tr>',
|
||||
|
|
|
@ -238,6 +238,40 @@ $humanReadableFilesize = function ($bytes, $dec = 2) {
|
|||
</tbody>
|
||||
</table>
|
||||
|
||||
<h4><?= __('PHP Dependencies') ?></h4>
|
||||
<p><?= _("Dependencies located in the Vendor folder. You can use composer to install them: 'php composer.phar help' ") ?></p>
|
||||
<table class="table table-condensed table-bordered" style="width: 40vw">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><?= __('Dependency') ?></th>
|
||||
<th><?= __('Required') ?></th>
|
||||
<th><?= __('Why to install') ?></th>
|
||||
<th><?= __('Installed') ?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($extensions['dependencies'] as $dependency => $info): ?>
|
||||
<tr>
|
||||
<td class="bold"><?= h($dependency) ?></td>
|
||||
<td><?= $info['required'] ? '<i class="black fa fa-check" role="img" aria-label="' . __('Yes') . '"></i>' : '<i class="black fa fa-times" role="img" aria-label="' . __('No') . '"></i>' ?></td>
|
||||
<td><?= $info['info'] ?></td>
|
||||
<td><?php
|
||||
$version = $info["version"];
|
||||
$outdated = $info["version_outdated"];
|
||||
if ($version && !$outdated) {
|
||||
echo '<i class="green fa fa-check" role="img" aria-label="' . __('Yes') . '"></i> (' . h($version) .')';
|
||||
} else {
|
||||
echo '<i class="red fa fa-times" role="img" aria-label="' . __('No') . '"></i>';
|
||||
if ($outdated) {
|
||||
echo '<br>' . __("Version %s installed, but required at least %s", h($version), h($info['required_version']));
|
||||
}
|
||||
}
|
||||
?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div style="width:400px;">
|
||||
<?= $this->element('/genericElements/IndexTable/index_table', array(
|
||||
'data' => array(
|
||||
|
|
|
@ -10,7 +10,13 @@
|
|||
[
|
||||
'field' => 'json',
|
||||
'class' => 'input-big-chungus',
|
||||
'type' => 'textarea'
|
||||
'type' => 'textarea',
|
||||
],
|
||||
[
|
||||
'field' => 'regenerate_uuids',
|
||||
'type' => 'checkbox',
|
||||
'label' => __('Regenerate UUIDs'),
|
||||
'title' => __('test'),
|
||||
],
|
||||
],
|
||||
'submit' => [
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
<div style="width:100%;">
|
||||
<script>
|
||||
// Chrome/Edge will log you out once it sees the HTTP401.
|
||||
// We need to be extra hacky to properly log out on i.e. Firefox.
|
||||
<?php
|
||||
$split_baseurl = array();
|
||||
# We split the baseurl, since we need to add data between the
|
||||
# schema and hostname. We *could* use parse_url here, but then
|
||||
# we would need a lot of code to rebuild it
|
||||
if (preg_match("/(https?:\/\/)(.*)/", $baseurl, $split_baseurl)):
|
||||
?>
|
||||
// The following call has to be done in the users browser to properly make
|
||||
// Firefox forget HTTP Basic auth credentials. The login with user set to
|
||||
// "logout" will be captured by webserver configuration, and not be sendt
|
||||
// to LDAP, but will invalidate the old, cached login in the browser.
|
||||
// If this is not working, make sure you have configured the webserver
|
||||
// as described in docs/CONFIG.ApacheSecureAuth.md Logout => LDAP => Option 2.
|
||||
let logoutxhr401 = new XMLHttpRequest()
|
||||
logoutxhr401.open("GET", "<?php echo $split_baseurl[1]; ?>logout:@<?php echo $split_baseurl[2]; ?>/users/login")
|
||||
logoutxhr401.send()
|
||||
<?php
|
||||
else:
|
||||
echo "// We failed to parse baseurl";
|
||||
endif;
|
||||
?>
|
||||
</script>
|
||||
<table style="margin-left:auto;margin-right:auto;">
|
||||
<tr>
|
||||
<td style="width:460px">
|
||||
<br /><br />
|
||||
<div>
|
||||
<?php if (Configure::read('MISP.main_logo') && file_exists(APP . '/webroot/img/custom/' . Configure::read('MISP.main_logo'))): ?>
|
||||
<img src="<?php echo $baseurl?>/img/custom/<?php echo h(Configure::read('MISP.main_logo'));?>" style=" display:block; margin-left: auto; margin-right: auto;" />
|
||||
<?php else: ?>
|
||||
<img src="<?php echo $baseurl?>/img/misp-logo-s-u.png" style="display:block; margin-left: auto; margin-right: auto;"/>
|
||||
<?php endif;?>
|
||||
</div>
|
||||
<br>
|
||||
<?php
|
||||
echo sprintf('<h5>%s</h5>',
|
||||
__('You have been successfully logged out.')
|
||||
);
|
||||
?>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
|
@ -1 +1 @@
|
|||
Subproject commit f605f041d99057b767231831d7f5970be6c1fcb9
|
||||
Subproject commit 963a389216ae0c1a829a2e3fdb80e4cb3fe0650c
|
|
@ -1 +1 @@
|
|||
Subproject commit fd603be3283953b68ed48ede7afd2e19f43577ac
|
||||
Subproject commit 1da4760dcc99502a2dd5da02cba212b42068fcb8
|
|
@ -1 +1 @@
|
|||
Subproject commit a51a9adc6c693bd5d2bf83f49920df7247ced298
|
||||
Subproject commit 677563239ef64e6895796fc42df2bae8ca12da7b
|
Binary file not shown.
After Width: | Height: | Size: 7.6 KiB |
|
@ -715,7 +715,7 @@ class EventGraph {
|
|||
if ( node.node_type == 'object' ) {
|
||||
var group = 'object';
|
||||
var label = dataHandler.generate_label(node);
|
||||
var labelHtml = label + '</br><i>' + escapeHtml(node.comment) + '</i>'
|
||||
var labelHtml = escapeHtml(label) + '</br><i>' + escapeHtml(node.comment) + '</i>'
|
||||
label += ' ' + escapeHtml(node.comment)
|
||||
var striped_value = that.strip_text_value(label);
|
||||
node_conf = {
|
||||
|
@ -742,7 +742,7 @@ class EventGraph {
|
|||
id: node.id,
|
||||
uuid: node.uuid,
|
||||
label: label,
|
||||
title: label,
|
||||
title: escapeHtml(label),
|
||||
group: group,
|
||||
mass: 20,
|
||||
color: {
|
||||
|
@ -766,15 +766,15 @@ class EventGraph {
|
|||
node_conf = {
|
||||
id: node.id,
|
||||
label: striped_value,
|
||||
title: label,
|
||||
title: escapeHtml(label),
|
||||
group: group
|
||||
};
|
||||
dataHandler.mapping_value_to_nodeID.set(label, node.id);
|
||||
} else {
|
||||
group = 'attribute';
|
||||
label = node.type + ': ' + node.label;
|
||||
label = escapeHtml(node.type) + ': ' + node.label;
|
||||
label += ' ' + escapeHtml(node.comment)
|
||||
var labelHtml = label + '</br><i>' + escapeHtml(node.comment) + '</i>'
|
||||
var labelHtml = escapeHtml(label) + '</br><i>' + escapeHtml(node.comment) + '</i>'
|
||||
var striped_value = that.strip_text_value(label);
|
||||
node_conf = {
|
||||
id: node.id,
|
||||
|
@ -822,7 +822,7 @@ class EventGraph {
|
|||
from: rel.from,
|
||||
to: rel.to,
|
||||
label: rel.type,
|
||||
title: rel.comment,
|
||||
title: escapeHtml(rel.comment),
|
||||
color: {
|
||||
opacity: 1.0,
|
||||
}
|
||||
|
@ -1053,7 +1053,7 @@ class EventGraph {
|
|||
x: parent_pos.x,
|
||||
y: parent_pos.y,
|
||||
label: attr.object_relation + ': ' + striped_value,
|
||||
title: attr.object_relation + ': ' + attr.value,
|
||||
title: escapeHtml(attr.object_relation) + ': ' + escapeHtml(attr.value),
|
||||
group: 'obj_relation',
|
||||
color: {
|
||||
background: parent_color
|
||||
|
|
|
@ -7349,11 +7349,11 @@
|
|||
{
|
||||
"column_name": "baseurl",
|
||||
"is_nullable": "NO",
|
||||
"data_type": "int",
|
||||
"character_maximum_length": null,
|
||||
"numeric_precision": "10",
|
||||
"collation_name": null,
|
||||
"column_type": "int(11)",
|
||||
"data_type": "varchar",
|
||||
"character_maximum_length": "191",
|
||||
"numeric_precision": null,
|
||||
"collation_name": "utf8mb4_unicode_ci",
|
||||
"column_type": "varchar(191)",
|
||||
"column_default": "0",
|
||||
"extra": ""
|
||||
},
|
||||
|
@ -9472,5 +9472,5 @@
|
|||
"uuid": false
|
||||
}
|
||||
},
|
||||
"db_version": "105"
|
||||
"db_version": "106"
|
||||
}
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
# ApacheSecureAuth
|
||||
<!---
|
||||
Ugly diff hack to render text as red using Github's markdown parser
|
||||
-->
|
||||
```diff
|
||||
- BE AWARE: The ApacheSecureAuth / LDAP login code is a
|
||||
- 3rd party contribution and untested (including security)
|
||||
- by the MISP-project team.
|
||||
```
|
||||
|
||||
However, you are free to enable it and report back to the developers if you run into any issues.
|
||||
|
||||
## Configuration
|
||||
### MISP configuration
|
||||
See the commented sections of [config.default.php](../app/Config/config.default.php) for an example of the MISP configuration variables that the ApacheSecureAuth module requires.
|
||||
|
||||
### Webserver configuration
|
||||
`TODO`
|
||||
|
||||
## Logout
|
||||
### Kerberos
|
||||
If you have configured you webserver to authenticate users using Kerberos/SPNEGO/Negotiate,
|
||||
there is no "log out", other than invalidating the user's Kerberos tickets.
|
||||
You can hide the GUI "Log out" link by setting `Plugin.CustomAuth_disable_logout` to `true`.
|
||||
|
||||
If you just want to log in as another user, you should be able to do this in an ingonito window.
|
||||
Most browser will not allow Kerberos/SPNEGO/Negotiate authentification when in ingognito mode,
|
||||
and i.e. Apache will fall back to having the user input his credentials in a HTTP Basic Auth
|
||||
popup, for then to authenticate the user with AD using these credentials.
|
||||
|
||||
### LDAP
|
||||
If you are capturing the user's credentials using HTTP Basic Auth, it can be difficult to make
|
||||
the browser forget these.
|
||||
There is no common or properly defined way of "logging out" after logging in with HTTP Basic Auth.
|
||||
|
||||
If the user presses the GUI "Log out" link, this can result in a logout-login loop, where the user
|
||||
is logged out, but then immediately loggged back in by means of the browsers cached HTTP Basic Auth
|
||||
credentials. This can be observed when a user presses "Log out", for then to be returned to the
|
||||
events view with two flash messages - one about a successful logout, and one "Welcome back" login-message.
|
||||
|
||||
There are two options to improve the user experience:
|
||||
|
||||
#### Option 1 (simple): Hide GUI "Log Out"
|
||||
As with Kerberos, the admin can hide the GUI "Log out" link by setting `Plugin.CustomAuth_disable_logout` to `true`.
|
||||
This is sufficient for many organizations.
|
||||
|
||||
#### Options 2 (complicated): Trick the browser into forgetting cached HTTP Basic Auth credentials
|
||||
The internal path `/users/logout401` in combination with webserver configuration
|
||||
can trick most browsers into forgetting cached HTTP Basic Auth credentials.
|
||||
|
||||
1. Set `Plugin.CustomAuth_custom_logout` to the internal path `/users/logout401`
|
||||
2. Modify your webserver configuration. Below is an example for Apache2
|
||||
|
||||
````
|
||||
# Only requiring LDAP auth for the /users/login path will improve the user experience.
|
||||
#<Location "/">
|
||||
<Location "/users/login">
|
||||
# This block will catch the Ajax logout from /users/logout401 that is required for
|
||||
# some browsers, i.e. Firefox. 'Basic bG9nb3V0Og==' equals 'Basic logout:' as
|
||||
# used buy the `/users/logout401` endpoint. This will prevent extraneous failed
|
||||
# logins a "logout" user on the LDAP server.
|
||||
<If "-n %{HTTP:Authorization} && %{HTTP:Authorization} == 'Basic bG9nb3V0Og==' ">
|
||||
AuthType Basic
|
||||
AuthName "MISP" # Must be same as in LDAP block
|
||||
AuthUserFile /dev/null
|
||||
Require valid-user
|
||||
</If>
|
||||
AuthType Basic
|
||||
AuthName "MISP"
|
||||
AuthBasicProvider ldap
|
||||
...
|
||||
</Else>
|
||||
</Location>
|
||||
````
|
|
@ -167,10 +167,10 @@ with open('../../misp-book/categories-and-types/README.md', 'w') as f:
|
|||
# Find the offset of the start header: "### MISP default attributes and categories"
|
||||
# Find the offset of the end/next header: "## MISP objects"
|
||||
# Replace our new content in between
|
||||
print("Updating MISP website - ../../misp-website/_pages/datamodels.md")
|
||||
print("Updating MISP website - ../../misp-website/content/datamodels.md")
|
||||
misp_website = []
|
||||
store_lines = True
|
||||
with open('../../misp-website/_pages/datamodels.md', 'r') as f:
|
||||
with open('../../misp-website/content/datamodels.md', 'r') as f:
|
||||
for line in f:
|
||||
# start marker
|
||||
if store_lines:
|
||||
|
@ -183,7 +183,7 @@ with open('../../misp-website/_pages/datamodels.md', 'r') as f:
|
|||
elif line.startswith('## MISP objects'):
|
||||
store_lines = True
|
||||
misp_website.append(line)
|
||||
with open('../../misp-website/_pages/datamodels.md', 'w') as f:
|
||||
with open('../../misp-website/content/datamodels.md', 'w') as f:
|
||||
f.write(''.join(misp_website))
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue