diff --git a/salt/common/tools/sbin/so-user b/salt/common/tools/sbin/so-user index cf9fc91c0..e47da4369 100755 --- a/salt/common/tools/sbin/so-user +++ b/salt/common/tools/sbin/so-user @@ -147,7 +147,7 @@ function updatePassword() { # Generate password hash passwordHash=$(hashPassword "$password") # Update DB with new hash - echo "update identity_credentials set config=CAST('{\"hashed_password\":\"$passwordHash\"}' as BLOB) where identity_id='${identityId}';" | sqlite3 "$databasePath" + echo "update identity_credentials set config=CAST('{\"hashed_password\":\"$passwordHash\"}' as BLOB), updated_at=datetime('now') where identity_id='${identityId}';" | sqlite3 "$databasePath" [[ $? != 0 ]] && fail "Unable to update password" fi } @@ -310,7 +310,7 @@ function listUsers() { users=$(echo "${response}" | jq -r ".[] | .verifiable_addresses[0].value" | sort) for user in $users; do - roles=$(grep "$user" "$elasticRolesFile" | cut -d: -f1 | tr '\n' ' ') + roles=$(grep ":$user\$" "$elasticRolesFile" | cut -d: -f1 | tr '\n' ' ') echo "$user: $roles" done } @@ -341,7 +341,7 @@ function adjustUserRole() { filename="$socRolesFile" hasRole=0 - grep "$role:" "$socRolesFile" | grep -q "$identityId" && hasRole=1 + grep "^$role:" "$socRolesFile" | grep -q "$identityId" && hasRole=1 if [[ "$op" == "add" ]]; then if [[ "$hasRole" == "1" ]]; then echo "User '$email' already has the role: $role" diff --git a/salt/soc/files/soc/cases.eventfields.json b/salt/soc/files/soc/cases.eventfields.json new file mode 100644 index 000000000..901c34345 --- /dev/null +++ b/salt/soc/files/soc/cases.eventfields.json @@ -0,0 +1,3 @@ +{ + "default": ["soc_timestamp", "case.title", "case.status", "case.severity", "case.createTime"] +} \ No newline at end of file diff --git a/salt/soc/files/soc/cases.queries.json b/salt/soc/files/soc/cases.queries.json new file mode 100644 index 000000000..6d49a89e1 --- /dev/null +++ b/salt/soc/files/soc/cases.queries.json @@ -0,0 +1,5 @@ +[ + { "name": "Open Cases", "query": "!case.status:Closed AND !case.category:Template" }, + { "name": "Closed Cases", "query": "case.status:Closed AND !case.category:Template" }, + { "name": "Templates", "query": "case.category:Template" } +] \ No newline at end of file diff --git a/salt/soc/files/soc/presets.artifacttype.json b/salt/soc/files/soc/presets.artifacttype.json new file mode 100644 index 000000000..4afa16c28 --- /dev/null +++ b/salt/soc/files/soc/presets.artifacttype.json @@ -0,0 +1,20 @@ +{ + "labels": [ + "autonomous-system", + "domain", + "file", + "filename", + "fqdn", + "hash", + "ip", + "mail", + "mail_subject", + "other", + "regexp", + "registry", + "uri_path", + "url", + "user-agent" + ], + "customEnabled": true +} diff --git a/salt/soc/files/soc/presets.category.json b/salt/soc/files/soc/presets.category.json new file mode 100644 index 000000000..0f48a8e82 --- /dev/null +++ b/salt/soc/files/soc/presets.category.json @@ -0,0 +1,7 @@ +{ + "labels": [ + "General", + "Template" + ], + "customEnabled": true +} \ No newline at end of file diff --git a/salt/soc/files/soc/presets.pap.json b/salt/soc/files/soc/presets.pap.json new file mode 100644 index 000000000..f1e8570dd --- /dev/null +++ b/salt/soc/files/soc/presets.pap.json @@ -0,0 +1,9 @@ +{ + "labels": [ + "White", + "Green", + "Amber", + "Red" + ], + "customEnabled": false +} \ No newline at end of file diff --git a/salt/soc/files/soc/presets.severity.json b/salt/soc/files/soc/presets.severity.json new file mode 100644 index 000000000..f04574787 --- /dev/null +++ b/salt/soc/files/soc/presets.severity.json @@ -0,0 +1,9 @@ +{ + "labels": [ + "Low", + "Medium", + "High", + "Critical" + ], + "customEnabled": false +} \ No newline at end of file diff --git a/salt/soc/files/soc/presets.status.json b/salt/soc/files/soc/presets.status.json new file mode 100644 index 000000000..06bab7e94 --- /dev/null +++ b/salt/soc/files/soc/presets.status.json @@ -0,0 +1,8 @@ +{ + "labels": [ + "New", + "In Progress", + "Closed" + ], + "customEnabled": false +} \ No newline at end of file diff --git a/salt/soc/files/soc/presets.tag.json b/salt/soc/files/soc/presets.tag.json new file mode 100644 index 000000000..545b513f8 --- /dev/null +++ b/salt/soc/files/soc/presets.tag.json @@ -0,0 +1,8 @@ +{ + "labels": [ + "false-positive", + "confirmed", + "pending" + ], + "customEnabled": true +} \ No newline at end of file diff --git a/salt/soc/files/soc/presets.tlp.json b/salt/soc/files/soc/presets.tlp.json new file mode 100644 index 000000000..f1e8570dd --- /dev/null +++ b/salt/soc/files/soc/presets.tlp.json @@ -0,0 +1,9 @@ +{ + "labels": [ + "White", + "Green", + "Amber", + "Red" + ], + "customEnabled": false +} \ No newline at end of file diff --git a/salt/soc/files/soc/soc.json b/salt/soc/files/soc/soc.json index 02128fd3c..064a781df 100644 --- a/salt/soc/files/soc/soc.json +++ b/salt/soc/files/soc/soc.json @@ -16,8 +16,17 @@ {%- import_json "soc/files/soc/alerts.eventfields.json" as alerts_eventfields %} {%- import_json "soc/files/soc/hunt.queries.json" as hunt_queries %} {%- import_json "soc/files/soc/hunt.eventfields.json" as hunt_eventfields %} +{%- import_json "soc/files/soc/cases.queries.json" as cases_queries %} +{%- import_json "soc/files/soc/cases.eventfields.json" as cases_eventfields %} {%- import_json "soc/files/soc/menu.actions.json" as menu_actions %} {%- import_json "soc/files/soc/tools.json" as tools %} +{%- import_json "soc/files/soc/presets.artifacttype.json" as presets_artifacttype %} +{%- import_json "soc/files/soc/presets.category.json" as presets_category %} +{%- import_json "soc/files/soc/presets.pap.json" as presets_pap %} +{%- import_json "soc/files/soc/presets.severity.json" as presets_severity %} +{%- import_json "soc/files/soc/presets.status.json" as presets_status %} +{%- import_json "soc/files/soc/presets.tag.json" as presets_tag %} +{%- import_json "soc/files/soc/presets.tlp.json" as presets_tlp %} {%- set DNET = salt['pillar.get']('global:dockernet', '172.17.0.0') %} {%- if salt['pillar.get']('elasticsearch:auth:enabled') is sameas true %} {%- set ES_USER = salt['pillar.get']('elasticsearch:auth:users:so_elastic_user:user', '') %} @@ -27,6 +36,8 @@ {%- set ES_PASS = '' %} {%- endif %} {%- set ES_INDEX_PATTERNS = salt['pillar.get']('soc:es_index_patterns', '*:so-*') %} +{%- set CASE_MODULE = salt['pillar.get']('soc:case_module', 'soc') %} +{%- set GENERIC_CASE_CONFIG = salt['pillar.get']('soc:generic_case_config', '') %} { "logFilename": "/opt/sensoroni/logs/sensoroni-server.log", "server": { @@ -57,9 +68,10 @@ {%- endif %} "username": "{{ ES_USER }}", "password": "{{ ES_PASS }}", - "index": "{{ ES_INDEX_PATTERNS }}", + "index": "{{ ES_INDEX_PATTERNS }}", "cacheMs": {{ ES_FIELDCAPS_CACHE }}, "verifyCert": false, + "casesEnabled": {{ 'true' if CASE_MODULE == 'soc' else 'false' }}, "timeoutMs": {{ API_TIMEOUT }} }, "influxdb": { @@ -77,12 +89,22 @@ "refreshIntervalMs": 30000, "offlineThresholdMs": 900000 }, -{% if THEHIVEKEY != '' %} +{% if CASE_MODULE == 'thehive' and THEHIVEKEY != '' %} "thehive": { "hostUrl": "http://{{ MANAGERIP }}:9000/thehive", "key": "{{ THEHIVEKEY }}", "verifyCert": false }, +{% elif CASE_MODULE == 'elasticcases' %} + "elasticcases": { + "hostUrl": "https://{{ MANAGERIP }}:5601", + "username": "{{ ES_USER }}", + "password": "{{ ES_PASS }}", + }, +{% elif CASE_MODULE == 'generichttp' %} + "generichttp": { + {{ GENERIC_CASE_CONFIG }} + }, {% endif %} "statickeyauth": { "anonymousCidr": "{{ DNET }}/24", @@ -113,6 +135,7 @@ "webSocketTimeoutMs": {{ WEBSOCKET_TIMEOUT }}, "tipTimeoutMs": {{ TIP_TIMEOUT }}, "cacheExpirationMs": {{ CACHE_EXPIRATION }}, + "casesEnabled": {{ 'true' if CASE_MODULE == 'soc' else 'false' }}, "inactiveTools": [ {%- if PLAYBOOK == 0 %} "toolPlaybook", @@ -139,7 +162,8 @@ "relativeTimeUnit": 30, "mostRecentlyUsedLimit": 5, "ackEnabled": false, - "escalateEnabled": {{ 'true' if THEHIVEKEY != '' else 'false' }}, + "escalateEnabled": true, + "escalateRelatedEventsEnabled": {{ 'true' if CASE_MODULE == 'soc' else 'false' }}, "eventFields": {{ hunt_eventfields | json }}, "queryBaseFilter": "", "queryToggleFilters": [], @@ -159,7 +183,8 @@ "relativeTimeUnit": 30, "mostRecentlyUsedLimit": 5, "ackEnabled": true, - "escalateEnabled": {{ 'true' if THEHIVEKEY != '' else 'false' }}, + "escalateEnabled": true, + "escalateRelatedEventsEnabled": {{ 'true' if CASE_MODULE == 'soc' else 'false' }}, "eventFields": {{ alerts_eventfields | json }}, "queryBaseFilter": "event.dataset:alert", "queryToggleFilters": [ @@ -167,8 +192,40 @@ { "name": "escalated", "filter": "event.escalated:true", "enabled": false, "exclusive": true, "enablesToggles":["acknowledged"] } ], "queries": {{ alerts_queries | json }}, - "actions": {{ menu_actions | json }} - } + "actions": {{ menu_actions | json }} + }, + "cases": { + "advanced": false, + "groupItemsPerPage": 50, + "groupFetchLimit": 100, + "eventItemsPerPage": 50, + "eventFetchLimit": 500, + "relativeTimeValue": 12, + "relativeTimeUnit": 60, + "mostRecentlyUsedLimit": 5, + "ackEnabled": false, + "escalateEnabled": false, + "escalateRelatedEventsEnabled": false, + "viewEnabled": true, + "eventFields": {{ cases_eventfields | json }}, + "queryBaseFilter": "_index:so-case AND kind:case", + "queryToggleFilters": [ + ], + "queries": {{ cases_queries | json }}, + "actions": {{ menu_actions | json }} + }, + "case": { + "mostRecentlyUsedLimit": 5, + "presets": { + "artifactType": {{ presets_artifacttype | json }}, + "category": {{ presets_category | json }}, + "pap": {{ presets_pap | json }}, + "severity": {{ presets_severity | json }}, + "status": {{ presets_status | json }}, + "tags": {{ presets_tag | json }}, + "tlp": {{ presets_tlp | json }} + } + } } } }