Closed CarlosRS9 closed 1 year ago
I was able to reproduce the scenario with:
Using the command:
root@wazuh:/var/ossec# /var/ossec/wodles/aws/aws-s3 -b wazuh-aws-wodle-waf -t cloudtrail -s 2022-Aug-17 -p dev -d2
DEBUG: +++ Debug mode on - Level: 2
DEBUG: +++ Table does not exist; create
DEBUG: +++ Working on XXXXXXXXXXXX - us-west-1
DEBUG: +++ Marker: AWSLogs/XXXXXXXXXXXX/CloudTrail/us-west-1/2022/08/17
DEBUG: ++ Found new log: AWSLogs/XXXXXXXXXXXX/CloudTrail/us-west-1/2022/08/17/XXXXXXXXXXXX_CloudTrail_us-west-1_20220817T0000Z_HASDoKlxgfdkdIOa.json.txt
DEBUG: +++ DB Maintenance
As a result, we can see that data.aws.requestParameters.rules
field is not correctly showing in the dashboard.
In the discovery section we can see as raw JSON.
I was able to configure requestParameters.rules
as a nested field
, using the base template. Adding the section to into the mapping
...
"requestParameters": {
"properties": {
"rules": {
"type": "nested"
}
}
}
...
And data.aws.requestParameters.rules.name
to the index.query.default_field
list.
Load the new configuration and refresh the field list in Stack Management > Index Patterns > wazuh-alerts-*
.
curl -XPUT -k -u admin:SecretPassword 'https://localhost:9200/_template/wazuh' -H 'Content-Type: application/json' -d @wazuh-template.json
After that we have the object field listed.
Now, as we can see in the discovery section the field requestParameters.rules
is treated as a NestedField
.
But, still i have some trouble filtering by the object fields.
And the field in the security events dashboard isn't showing correctly.
I will continue my research.
Also i try mapping all the fields contained in the rule object, with the same results as before.
"requestParameters": {
"properties": {
"rules": {
"type": "nested",
"properties": {
"name": {
"type": "keyword"
},
"priority": {
"type": "integer"
},
"statement": {
"type": "object"
},
"action": {
"type": "object"
},
"ruleLabels": {
"type": "nested"
},
"visibilityConfig": {
"type": "object"
}
}
}
}
}
I do some changes in the WAF log, split into two records with 4 objects into rules
array each. And i ran the the module to ingest the alerts.
/var/ossec/wodles/aws/aws-s3 -b wazuh-aws-wodle-waf -t cloudtrail -s 2022-Aug-19 -p dev -d2
After that i did some queries through the elastic API:
curl -XGET -k -u admin:SecretPassword 'https://localhost:9200/wazuh-alerts-4.x-*/_search?pretty' -H 'Content-Type: application/json' -d \
'{
"query": {
"term": {
"data.aws.log_info.s3bucket": "wazuh-aws-wodle-waf"
}
}
}'
With this the result was the expected, two documents returned.
...
{
"value": 2,
"relation": "eq"
}
...
rules
arraycurl -XGET -k -u admin:SecretPassword 'https://localhost:9200/wazuh-alerts-4.x-*/_search?pretty' -H 'Content-Type: application/json' -d \
'{
"query": {
"nested": {
"path": "data.aws.requestParameters.rules",
"query": {
"bool": {
"must": [
{ "match": { "data.aws.requestParameters.rules.name": "X-Application-ID" }}
]
}
}
}
}
}
'
The result was the expected, only one document returned.
...
{
"value": 1,
"relation": "eq"
}
...
Is possible filter by rules
array objects through the elastic API REST. The next step is try to do the same using the dashboard.
I was able to search from the security events view, using the sentence data.aws.requestParameters.rules:{name:X-Application-ID}
.
But still is not possible use a nested field as a filter.
Also i found that nested fields
have a limited support https://github.com/elastic/kibana/issues/1084#issuecomment-585178079
I have also replicated this use case. First of all, I have ingested the AWS log just like Nico did following the steps detailed in https://github.com/wazuh/wazuh/issues/14547#issuecomment-1218414156.
As Nico said, the data.aws.requestParameters.rules
field is not correctly shown in the dashboard:
Modules > Security events > Dashboard
And it is correctly shown in the events or discover section:
Modules > Security events > Events
Discover
Before updating and loading the modified wazuh-template.json
, I realized that the field is appearing in the field list of **Stack Management > Index Patterns > wazuh-alerts-** even if the template is not modified (only refreshing the `wazuh-alerts-`'s field list).
BUT the nested field configuration does need to be applied to avoid this error:
# curl -XGET -k -u admin:admin 'https://localhost:9200/wazuh-alerts-4.x-*/_search?pretty' -H 'Content-Type: application/json' -d \
'{
"query": {
"nested": {
"path": "data.aws.requestParameters.rules",
"query": {
"bool": {
"must": [
{ "match": { "data.aws.requestParameters.rules.name": "X-Application-ID" }}
]
}
}
}
}
}
'
{
"error" : {
"root_cause" : [
{
"type" : "query_shard_exception",
"reason" : "failed to create query: [nested] nested object under path [data.aws.requestParameters.rules] is not of nested type",
"index" : "wazuh-alerts-4.x-2022.08.22",
"index_uuid" : "x3hwpf7pSBKRUPtYHpY1Sg"
}
],
"type" : "search_phase_execution_exception",
"reason" : "all shards failed",
"phase" : "query",
"grouped" : true,
"failed_shards" : [
{
"shard" : 0,
"index" : "wazuh-alerts-4.x-2022.08.22",
"node" : "C5rMLicuSbe_tKGEiWA06A",
"reason" : {
"type" : "query_shard_exception",
"reason" : "failed to create query: [nested] nested object under path [data.aws.requestParameters.rules] is not of nested type",
"index" : "wazuh-alerts-4.x-2022.08.22",
"index_uuid" : "x3hwpf7pSBKRUPtYHpY1Sg",
"caused_by" : {
"type" : "illegal_state_exception",
"reason" : "[nested] nested object under path [data.aws.requestParameters.rules] is not of nested type"
}
}
}
]
},
"status" : 400
}
Healthcheck:
# curl -X GET -k -u admin:password 'https://localhost:9200/?pretty'
{
"name" : "wazuh-indexer-1",
"cluster_name" : "wazuh-cluster",
"cluster_uuid" : "hOScwD_hQ2exKf6lh8C1DA",
"version" : {
"number" : "7.10.2",
"build_type" : "rpm",
"build_hash" : "e505b10357c03ae8d26d675172402f2f2144ef0f",
"build_date" : "2022-01-14T03:38:06.881862Z",
"build_snapshot" : false,
"lucene_version" : "8.10.1",
"minimum_wire_compatibility_version" : "6.8.0",
"minimum_index_compatibility_version" : "6.0.0-beta1"
},
"tagline" : "The OpenSearch Project: https://opensearch.org/"
}
I have made data.aws.requestParameters.rules
nested by following the Elasticsearch documentation https://www.elastic.co/guide/en/elasticsearch/reference/current/nested.html and https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-put-mapping.html. IMPORTANT: This step should be done before refreshing the index (the index is refreshed automatically when ingesting the AWS WAF logs).
Making the field nested in the Kibana dev tools:
PUT wazuh-alerts-4.x-2022.08.22/_mapping
{
"properties": {
"data":{
"properties":{
"aws": {
"properties": {
"requestParameters": {
"properties": {
"rules": {
"type": "nested"
}
}
}
}
}
}
}
}
}
With this update, the rules
field is considered nested and the filter can be applied with the Elastic API, as Nico said:
# curl -XGET -k -u admin:admin 'https://localhost:9200/wazuh-alerts-4.x-*/_search?pretty' -H 'Content-Type: application/json' -d \
'{
"query": {
"nested": {
"path": "data.aws.requestParameters.rules",
"query": {
"bool": {
"must": [
{ "match": { "data.aws.requestParameters.rules.name": "X-Application-ID" }}
]
}
}
}
}
}
'
I have also tried using the Kibana filters (User interface) and they are working.
For instance, filtering by rules when one of them has its name equal to MS-Internos and when it is RL_ALL (2 different events).
What is not working is the filter in the dashboard section (modules > security events > dashboard). The filter does work in the events section (modules > security events > events).
In the events section, the request is a POST request done to /localhost/internal/search/opensearch
with the following query in its payload:
{
"bool": {
"must": [],
"filter": [
{
"match_all": {}
},
{
"match_phrase": {
"cluster.name": {
"query": "wazuh"
}
}
},
{
"nested": {
"path": "data.aws.requestParameters.rules",
"query": {
"match_phrase": {
"data.aws.requestParameters.rules.name": "some-bus-rl"
}
}
}
},
{
"range": {
"timestamp": {
"gte": "2022-08-21T11:22:45.411Z",
"lte": "2022-08-22T11:22:45.411Z",
"format": "strict_date_optional_time"
}
}
}
],
"should": [],
"must_not": []
}
}
In the dashboard section, the request is a POST request done to /localhost/elastic/alerts
with the following query in its payload:
{
"bool": {
"must": [
{
"range": {
"timestamp": {
"gte": "now-24h",
"lte": "now",
"format": "epoch_millis"
}
}
}
],
"filter": [
{
"match_all": {}
},
{
"match_phrase": {
"cluster.name": {
"query": "wazuh"
}
}
},
{
"match_phrase": {
"data.aws.requestParameters.rules.name": "some-bus-rl"
}
},
{
"match_phrase": {
"cluster.name": {
"query": "wazuh"
}
}
},
{
"match_phrase": {
"data.aws.requestParameters.rules.name": "some-bus-rl"
}
}
],
"should": [],
"must_not": []
}
}
As we can see, the query used in the events section to filter by a nested field uses the nested
keyword whereas the one of the dashboard section uses the match_phrase
keyword. This match_phrase
keyword is the one used to filter by standard fields (non-nested). This is why the dashboard is not showing the events properly.
If we have a look at the discover section (outside the Wazuh plugin), we can also see that the filter is working:
In this section, the query used was the following:
{
"bool": {
"must": [],
"filter": [
{
"match_all": {}
},
{
"nested": {
"path": "data.aws.requestParameters.rules",
"query": {
"match_phrase": {
"data.aws.requestParameters.rules.name": "MS-ECOM-PARTNER"
}
}
}
},
{
"range": {
"timestamp": {
"gte": "2022-08-21T11:39:29.362Z",
"lte": "2022-08-22T11:39:29.362Z",
"format": "strict_date_optional_time"
}
}
}
],
"should": [],
"must_not": []
}
}
As we can see, the nested keyword is used, and the query follows the same format used in the events section of the Wazuh plugin.
The conclusion is that we can filter AWS WAF events using fields present in items inside an item list. What appears to be wrong is the way we are making the requests to the Elastic API in the Dashboard section of the Wazuh plugin when a nested field is present.
I was able to index documents with the requested format and these were dynamically mapped without any extra action. The filters and search returned the same results as before.
After send event to indexer get the created mapping.
curl -XGET -k -u admin:SecretPassword 'https://localhost:9200/wazuh-alerts-4.x-2022.08.29?pretty'
As we can see the requestParameters.rules
was corretly mapped.
...
"rules" : {
"type" : "nested",
"properties" : {
"action" : {
"properties" : {
"block" : {
"type" : "object"
},
"count" : {
"type" : "object"
}
}
},
"name" : {
"type" : "keyword"
},
"overrideAction" : {
"properties" : {
"none" : {
"type" : "object"
}
}
},
"priority" : {
"type" : "long"
},
"ruleLabels" : {
"properties" : {
"name" : {
"type" : "keyword"
}
}
},
"statement" : {
"properties" : {
"andStatement" : {
"properties" : {
"statements" : {
"properties" : {
"byteMatchStatement" : {
"properties" : {
"fieldToMatch" : {
"properties" : {
"uriPath" : {
"type" : "object"
}
}
},
"positionalConstraint" : {
"type" : "keyword"
},
"searchString" : {
"properties" : {
"address" : {
"type" : "long"
},
"bigEndian" : {
"type" : "boolean"
},
"capacity" : {
"type" : "long"
},
"hb" : {
"type" : "long"
},
"isReadOnly" : {
"type" : "boolean"
},
"limit" : {
"type" : "long"
},
"mark" : {
"type" : "long"
},
"nativeByteOrder" : {
"type" : "boolean"
},
"offset" : {
"type" : "long"
},
"position" : {
"type" : "long"
}
}
},
"textTransformations" : {
"properties" : {
"priority" : {
"type" : "long"
},
"type" : {
"type" : "keyword"
}
}
}
}
},
"notStatement" : {
"properties" : {
"statement" : {
"properties" : {
"byteMatchStatement" : {
"properties" : {
"fieldToMatch" : {
"properties" : {
"singleHeader" : {
"properties" : {
"name" : {
"type" : "keyword"
}
}
}
}
},
"positionalConstraint" : {
"type" : "keyword"
},
"searchString" : {
"properties" : {
"address" : {
"type" : "long"
},
"bigEndian" : {
"type" : "boolean"
},
"capacity" : {
"type" : "long"
},
"hb" : {
"type" : "long"
},
"isReadOnly" : {
"type" : "boolean"
},
"limit" : {
"type" : "long"
},
"mark" : {
"type" : "long"
},
"nativeByteOrder" : {
"type" : "boolean"
},
"offset" : {
"type" : "long"
},
"position" : {
"type" : "long"
}
}
},
"textTransformations" : {
"properties" : {
"priority" : {
"type" : "long"
},
"type" : {
"type" : "keyword"
}
}
}
}
},
"geoMatchStatement" : {
"properties" : {
"countryCodes" : {
"type" : "keyword"
},
"forwardedIPConfig" : {
"properties" : {
"fallbackBehavior" : {
"type" : "keyword"
},
"headerName" : {
"type" : "keyword"
}
}
}
}
}
}
}
}
},
"sizeConstraintStatement" : {
"properties" : {
"comparisonOperator" : {
"type" : "keyword"
},
"fieldToMatch" : {
"properties" : {
"singleHeader" : {
"properties" : {
"name" : {
"type" : "keyword"
}
}
}
}
},
"size" : {
"type" : "long"
},
"textTransformations" : {
"properties" : {
"priority" : {
"type" : "long"
},
"type" : {
"type" : "keyword"
}
}
}
}
}
}
}
}
},
"byteMatchStatement" : {
"properties" : {
"fieldToMatch" : {
"properties" : {
"singleHeader" : {
"properties" : {
"name" : {
"type" : "keyword"
}
}
},
"uriPath" : {
"type" : "object"
}
}
},
"positionalConstraint" : {
"type" : "keyword"
},
"searchString" : {
"properties" : {
"address" : {
"type" : "long"
},
"bigEndian" : {
"type" : "boolean"
},
"capacity" : {
"type" : "long"
},
"hb" : {
"type" : "long"
},
"isReadOnly" : {
"type" : "boolean"
},
"limit" : {
"type" : "long"
},
"mark" : {
"type" : "long"
},
"nativeByteOrder" : {
"type" : "boolean"
},
"offset" : {
"type" : "long"
},
"position" : {
"type" : "long"
}
}
},
"textTransformations" : {
"properties" : {
"priority" : {
"type" : "long"
},
"type" : {
"type" : "keyword"
}
}
}
}
},
"rateBasedStatement" : {
"properties" : {
"aggregateKeyType" : {
"type" : "keyword"
},
"forwardedIPConfig" : {
"properties" : {
"fallbackBehavior" : {
"type" : "keyword"
},
"headerName" : {
"type" : "keyword"
}
}
},
"limit" : {
"type" : "long"
},
"scopeDownStatement" : {
"properties" : {
"byteMatchStatement" : {
"properties" : {
"fieldToMatch" : {
"properties" : {
"uriPath" : {
"type" : "object"
}
}
},
"positionalConstraint" : {
"type" : "keyword"
},
"searchString" : {
"properties" : {
"address" : {
"type" : "long"
},
"bigEndian" : {
"type" : "boolean"
},
"capacity" : {
"type" : "long"
},
"hb" : {
"type" : "long"
},
"isReadOnly" : {
"type" : "boolean"
},
"limit" : {
"type" : "long"
},
"mark" : {
"type" : "long"
},
"nativeByteOrder" : {
"type" : "boolean"
},
"offset" : {
"type" : "long"
},
"position" : {
"type" : "long"
}
}
},
"textTransformations" : {
"properties" : {
"priority" : {
"type" : "long"
},
"type" : {
"type" : "keyword"
}
}
}
}
},
"regexPatternSetReferenceStatement" : {
"properties" : {
"aRN" : {
"type" : "keyword"
},
"fieldToMatch" : {
"properties" : {
"uriPath" : {
"type" : "object"
}
}
},
"textTransformations" : {
"properties" : {
"priority" : {
"type" : "long"
},
"type" : {
"type" : "keyword"
}
}
}
}
}
}
}
}
},
"ruleGroupReferenceStatement" : {
"properties" : {
"aRN" : {
"type" : "keyword"
}
}
}
}
},
"visibilityConfig" : {
"properties" : {
"cloudWatchMetricsEnabled" : {
"type" : "boolean"
},
"metricName" : {
"type" : "keyword"
},
"sampledRequestsEnabled" : {
"type" : "boolean"
}
}
}
}
}
...
Then in Security Events > Events we got the message.
And the field is displayed as nested
.
Description
To determine the best way to address #14226 we need to determine if it is possible to filter the alerts in the UI using the data from the contents of the
requestParameters.rules
of a WAF log without having to apply major changes to our AWS integration. In particular, we want to be able to filter by thename
andaction
keys contained in the different items present in therequestParameters.rules
list.The issue here is that the
requestParameters.rules
contains a list of items, each one corrresponding to a different application, which prevents us from being able to filter by these fields. In addition to that, these items can contain different fields each one.We need to investigate how the contents of
requestParameters.rules
are currently being indexed and displayed in theDiscover
page of the UI. To do this, we should:requestParameters.rules
to the analysis engine without applying any modification to them.Discover
page.requestParameters.rules
contents are rendered in the interface.One possible solution could be the usage of Elastic's nested fields to alter the way
requestParameters.rules
are being indexed and determine if this allows us to index them in a way that those items can be queried independently of each other.We need to determine if #14226 can be achieved by means of the nested field or any other solution involving Elasticsearch configurations.