From e60a1e435748a052865f45003073989709dc5440 Mon Sep 17 00:00:00 2001 From: reyesj2 <94730068+reyesj2@users.noreply.github.com> Date: Thu, 9 Jan 2025 16:06:10 -0600 Subject: [PATCH] zeek ldap & ldap_search parsing Signed-off-by: reyesj2 <94730068+reyesj2@users.noreply.github.com> --- salt/elasticsearch/files/ingest/zeek.ldap | 25 ++++++ .../files/ingest/zeek.ldap_search | 9 +++ .../templates/component/ecs/zeek.json | 81 +++++++++++++++++++ salt/soc/defaults.yaml | 34 ++++++++ 4 files changed, 149 insertions(+) create mode 100644 salt/elasticsearch/files/ingest/zeek.ldap create mode 100644 salt/elasticsearch/files/ingest/zeek.ldap_search diff --git a/salt/elasticsearch/files/ingest/zeek.ldap b/salt/elasticsearch/files/ingest/zeek.ldap new file mode 100644 index 000000000..b7fef825a --- /dev/null +++ b/salt/elasticsearch/files/ingest/zeek.ldap @@ -0,0 +1,25 @@ +{ + "description": "zeek.ldap", + "processors": [ + {"set": {"field": "event.dataset", "value": "ldap"}}, + {"json": {"field": "message", "target_field": "message2", "ignore_failure": true}}, + {"rename": {"field": "message2.message_id", "target_field": "ldap.message_id", "ignore_missing": true}}, + {"rename": {"field": "message2.opcode", "target_field": "ldap.opcode", "ignore_missing": true}}, + {"rename": {"field": "message2.result", "target_field": "ldap.result", "ignore_missing": true}}, + {"rename": {"field": "message2.diagnostic_message", "target_field": "ldap.diagnostic_message", "ignore_missing": true}}, + {"rename": {"field": "message2.version", "target_field": "ldap.version", "ignore_missing": true}}, + {"rename": {"field": "message2.object", "target_field": "ldap.object", "ignore_missing": true}}, + {"rename": {"field": "message2.argument", "target_field": "ldap.argument", "ignore_missing": true}}, + {"rename": {"field": "message2.scope", "target_field": "ldap_search.scope", "ignore_missing":true}}, + {"rename": {"field": "message2.deref_aliases", "target_field": "ldap_search.deref_aliases", "ignore_missing":true}}, + {"rename": {"field": "message2.base_object", "target_field": "ldap.object", "ignore_missing":true}}, + {"rename": {"field": "message2.result_count", "target_field": "ldap_search.result_count", "ignore_missing":true}}, + {"rename": {"field": "message2.filter", "target_field": "ldap_search.filter", "ignore_missing":true}}, + {"rename": {"field": "message2.attributes", "target_field": "ldap_search.attributes", "ignore_missing":true}}, + {"script": {"source": "if (ctx.containsKey('ldap') && ctx.ldap.containsKey('diagnostic_message') && ctx.ldap.diagnostic_message != null) {\n String message = ctx.ldap.diagnostic_message;\n\n // get user and property from SASL success\n if (message.toLowerCase().contains(\"sasl(0): successful result\")) {\n Pattern pattern = /user:\\s*([^ ]+)\\s*property:\\s*([^ ]+)/i;\n Matcher matcher = pattern.matcher(message);\n if (matcher.find()) {\n ctx.ldap.user_email = matcher.group(1); // Extract user email\n ctx.ldap.property = matcher.group(2); // Extract property\n }\n }\n if (message.toLowerCase().contains(\"ldaperr:\")) {\n Pattern pattern = /comment:\\s*([^,]+)/i;\n Matcher matcher = pattern.matcher(message);\n\n if (matcher.find()) {\n ctx.ldap.comment = matcher.group(1);\n }\n }\n }","ignore_failure": true}}, + {"script": {"source": "if (ctx.containsKey('ldap') && ctx.ldap.containsKey('object') && ctx.ldap.object != null) {\n String message = ctx.ldap.object;\n\n // parse common name from ldap object\n if (message.toLowerCase().contains(\"cn=\")) {\n Pattern pattern = /cn=([^,]+)/i;\n Matcher matcher = pattern.matcher(message);\n if (matcher.find()) {\n ctx.ldap.common_name = matcher.group(1); // Extract CN\n }\n }\n // build domain from ldap object\n if (message.toLowerCase().contains(\"dc=\")) {\n Pattern dcPattern = /dc=([^,]+)/i;\n Matcher dcMatcher = dcPattern.matcher(message);\n\n StringBuilder domainBuilder = new StringBuilder();\n while (dcMatcher.find()) {\n if (domainBuilder.length() > 0 ){\n domainBuilder.append(\".\");\n }\n domainBuilder.append(dcMatcher.group(1));\n }\n if (domainBuilder.length() > 0) {\n ctx.ldap.domain = domainBuilder.toString();\n }\n }\n // create list of any organizational units from ldap object\n if (message.toLowerCase().contains(\"ou=\")) {\n Pattern ouPattern = /ou=([^,]+)/i;\n Matcher ouMatcher = ouPattern.matcher(message);\n ctx.ldap.organizational_unit = [];\n\n while (ouMatcher.find()) {\n ctx.ldap.organizational_unit.add(ouMatcher.group(1));\n }\n if(ctx.ldap.organizational_unit.isEmpty()) {\n ctx.remove(\"ldap.organizational_unit\");\n }\n }\n}\n","ignore_failure": true}}, + {"remove": {"field": "message2.tags","ignore_failure": true}}, + {"remove": {"field": ["host"],"ignore_failure": true}}, + {"pipeline": {"name": "zeek.common"}} + ] +} \ No newline at end of file diff --git a/salt/elasticsearch/files/ingest/zeek.ldap_search b/salt/elasticsearch/files/ingest/zeek.ldap_search new file mode 100644 index 000000000..2a625c319 --- /dev/null +++ b/salt/elasticsearch/files/ingest/zeek.ldap_search @@ -0,0 +1,9 @@ +{ + "description":"zeek.ldap_search", + "processors":[ + {"pipeline": {"name": "zeek.ldap", "ignore_missing_pipeline":true,"ignore_failure":true}}, + {"set": {"field": "event.dataset", "value":"ldap_search"}}, + {"remove": {"field": "tags", "ignore_missing":true}}, + {"pipeline": {"name": "zeek.common"}} + ] +} \ No newline at end of file diff --git a/salt/elasticsearch/templates/component/ecs/zeek.json b/salt/elasticsearch/templates/component/ecs/zeek.json index 0b2d7dc37..b0617305e 100644 --- a/salt/elasticsearch/templates/component/ecs/zeek.json +++ b/salt/elasticsearch/templates/component/ecs/zeek.json @@ -834,6 +834,81 @@ } } }, + "ldap": { + "type": "object", + "properties": { + "message_id": { + "type": "short" + }, + "opcode": { + "ignore_above": 1024, + "type": "keyword" + }, + "result": { + "ignore_above": 1024, + "type": "keyword" + }, + "diagnostic_message": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "type": "short" + }, + "object": { + "ignore_above": 1024, + "type": "keyword" + }, + "argument": { + "ignore_above": 1024, + "type": "keyword" + }, + "user_email": { + "ignore_above": 1024, + "type": "keyword" + }, + "property": { + "ignore_above": 1024, + "type": "keyword" + }, + "common_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "organizational_unit": { + "ignore_above": 1024, + "type": "keyword" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ldap_search": { + "type": "object", + "properties": { + "scope": { + "ignore_above": 1024, + "type": "keyword" + }, + "deref_aliases": { + "ignore_above": 1024, + "type": "keyword" + }, + "result_count": { + "type": "long" + }, + "filter": { + "ignore_above": 1024, + "type": "keyword" + }, + "attributes": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, "modbus": { "properties": { "exception": { @@ -1176,24 +1251,30 @@ "type": "object", "properties": { "server_name": { + "ignore_above": 1024, "type": "keyword" }, "version": { "type": "short" }, "client_initial_dcid": { + "ignore_above": 1024, "type": "keyword" }, "client_scid": { + "ignore_above": 1024, "type": "keyword" }, "server_scid": { + "ignore_above": 1024, "type": "keyword" }, "client_protocol": { + "ignore_above": 1024, "type": "keyword" }, "history": { + "ignore_above": 1024, "type": "keyword" } } diff --git a/salt/soc/defaults.yaml b/salt/soc/defaults.yaml index 813b54223..66affd7e1 100644 --- a/salt/soc/defaults.yaml +++ b/salt/soc/defaults.yaml @@ -284,6 +284,27 @@ soc: - kerberos.service - kerberos.request_type - log.id.uid + '::ldap': + - soc_timestamp + - event.dataset + - source.ip + - source.port + - destination.ip + - destination.port + - ldap.result + - ldap.common_name + - ldap.object + - ldap.opcode + '::ldap_search': + - soc_timestamp + - event.dataset + - source.ip + - source.port + - destination.ip + - destination.port + - ldap.result + - ldap.object + - ldap_search.filter '::modbus': - soc_timestamp - event.dataset @@ -1722,6 +1743,13 @@ soc: description: KERBEROS grouped by service query: 'tags:kerberos | groupby kerberos.service' showSubtitle: true + - name: LDAP + description: LDAP grouped by source ip and result + query: 'tags:ldap | groupby source.ip ldap.result' + - name: LDAP_SEARCH + description: LDAP_SEARCH grouped by source.ip and filter + query: 'tags:ldap_search | groupby source.ip | groupby ldap_search.filter' + showSubtitle: true - name: MODBUS description: MODBUS grouped by function query: 'tags:modbus | groupby modbus.function' @@ -1952,6 +1980,12 @@ soc: - name: Kerberos description: Kerberos network metadata query: 'tags:kerberos | groupby kerberos.service | groupby -sankey kerberos.service source.ip | groupby source.ip | groupby -sankey source.ip destination.ip | groupby destination.ip | groupby destination.port | groupby kerberos.client | groupby kerberos.request_type' + - name: LDAP + description: LDAP (Lightweight Directory Access Protocol) network metadata + query: 'tags:ldap | groupby source.ip | groupby destination.ip | groupby destination.port | groupby ldap.user_email | groupby ldap.property | groupby ldap.result | groupby ldap.common_name | groupby ldap.organizational_unit | groupby ldap.domain | groupby ldap.version | groupby ldap.object' + - name: LDAP_SEARCH + description: LDAP_SEARCH (Lightweight Directory Access Protocol) Search network metadata + query: 'tags:ldap_search | groupby source.ip | groupby destination.ip | groupby destination.port | groupby ldap_search.scope | groupby ldap.object | groupby ldap.domain | groupby ldap_search.filter' - name: MySQL description: MySQL network metadata query: 'tags:mysql | groupby mysql.command | groupby -sankey mysql.command source.ip | groupby source.ip | groupby -sankey source.ip destination.ip | groupby destination.ip | groupby destination.port | groupby mysql.argument | groupby mysql.success | groupby mysql.response | groupby mysql.rows'