Kibana KQL search common value on single column

Hello,

I'm new on Kibana and I can't find answers to my problem, hopefully someone can help :slight_smile: .

Here is my problem : I have a table with some value like (Column A = Port number, Column B = IP Address, Column C = OS, etc...).

I would like to count the number of Host that have for example port 22, and 3389 open and not just port 22 or port 3389 open, but cannot find a way, it says everytime "0".

Here is what I tried with KQL : Port.keyword : 22 and Port.keyword : 3389.

Anyone can help please ?

Thank you !!!

Which is the your document structure. (1) Port number is a single value and each Host has multiple document per Port. (2) Each Host has single document and it contains Port number as an array in Column A field.

As you got 0 by the query, I suppose you have (1). If so, you have to query terms aggregation on Host and use bucket selecter aggregation for it.

Hi Tomo and thanks for answer,

Here is an example of my document structure. I would like Kibana to tell me : How many Host(IP) have port number 22 AND 3389 and even if it's possible says AND only Windows, or Linux, or ...etc.

Kibana should tell me "1" but can't figure out.

Port IP OS
22 192.168.1.1 Windows 10
53 192.168.1.1 Windows 10
67 192.168.1.1 Windows 10
69 192.168.1.1 Windows 10
80 192.168.1.1 Windows 10
23 192.168.1.1 Windows 10
3389 192.168.1.1 Windows 10
443 192.168.1.1 Windows 10
389 192.168.1.2 Windows 10
110 192.168.1.2 Windows 2008 R2
119 192.168.1.2 Windows 2008 R2
22 192.168.1.2 Windows 2008 R2

Thanks for your help.

I have some more question to go ahead. Why are you using kibana? How do you want to show the count?
Is there any other fields or something to prohibit you from changing the structure to contain ports as an array?

Hi Tomo,

I use kibana because I'm trying to make dashboard from Nessus data. For that I use Nessus report .csv and inject them with logstash. So know I'd like to make modules that give me this information like : Number of Windows server with port 22 and 3389 opened : XXX. So unfortunately I can't change structure of the document.

I suppose use transform and create another index is a best way. I will explain.

To know IP 192.168.1.1 has both Port 22 and 3389, you need to aggregate by IP. Even in Elasticsearch, to query or filter on aggregated buckets are really restricted. The only way is bucket selector aggregation. It is not as free as query / filter on documents.

If you want to know the count on Elasticsearch, this query:

GET /test_bucket_selecter/_search?filter_path=aggregations.stats.count,aggregations.hosts.buckets.key
{
  "query":{
    "bool":{
      "should":[
        {"term":{"Port":"22"}},
        {"term":{"Port":"3389"}}
      ]
    }
  },
  "size":0,
  "aggs":{
    "hosts":{
      "multi_terms":{
        "terms":[
          {"field":"IP"},
          {"field":"OS"}
        ]
      },
      "aggs":{
        "port_cardinality":{
          "cardinality":{
            "field":"Port"
          }
        },
        "cardinality_filter":{
          "bucket_selector": {
            "buckets_path": {
              "portCardinality": "port_cardinality"},
            "script": "params.portCardinality >= 2"
          }
        }
      }
    },
    "stats":{
      "stats_bucket": {
        "buckets_path": "hosts>port_cardinality"
      }
    }
  }
}

gives you:

{
  "aggregations" : {
    "hosts" : {
      "buckets" : [
        {
          "key" : [
            "192.168.1.1",
            "Windows 10"
          ]
        }
      ]
    },
    "stats" : {
      "count" : 1
    }
  }
}

but such complex query is not achivable with "aggregation based > Data Table".

As my recent post, you can create transform to get an index containing documents like:

{
      "IP" : "192.168.1.1"
      "OS" : [
        "Windows 10"
      ],
      "Port" : [
        22,
        3389
      ]
    }

With this structure documents, your query is easily achieved and more free to visualize in dashboard.

Sample transform is:

PUT  _transform/test_ip_transform
{
  "pivot":{
    "group_by":{
      "IP":{
        "terms":{
          "field":"IP"
        }
      }
    },
    "aggs":{
      "Port":{
        "scripted_metric": {
         "init_script": "state.set = new HashSet()",
          "map_script": "if (params['_source'].containsKey(params.field)) {state.set.add(params['_source'][params.field])}",
          "combine_script": "return state.set",
          "reduce_script": "def ret = new HashSet(); for (s in states) {for (k in s) {ret.add(k);}} return ret",
          "params":{
            "field": "Port"
          }
        }
      },
      "OS":{
        "scripted_metric": {
         "init_script": "state.set = new HashSet()",
          "map_script": "if (params['_source'].containsKey(params.field)) {state.set.add(params['_source'][params.field])}",
          "combine_script": "return state.set",
          "reduce_script": "def ret = new HashSet(); for (s in states) {for (k in s) {ret.add(k);}} return ret",
          "params":{
            "field": "OS"
          }
        }
      }
    }
  },
  "source": {
    "index":"test_ip"
  },
  "dest":{
    "index": "test_ip_transformed"
  },
  "frequency": "1m"
}

POST _transform/test_ip_transform/_start

Then you get:

GET /test_ip_transformed/_search?filter_path=hits.hits._source

{
  "hits" : {
    "hits" : [
      {
        "_source" : {
          "OS" : [
            "Windows 10"
          ],
          "IP" : "192.168.1.1",
          "Port" : [
            22,
            3389
          ]
        }
      },
      {
        "_source" : {
          "OS" : [
            "Windows 2008 R2"
          ],
          "IP" : "192.168.1.2",
          "Port" : [
            22,
            110
          ]
        }
      }
    ]
  }
}

With this structure, you can easily query and visualize what you want.

Take care about transform limitations.

Hello Tomo,

Thanks a lot for your help !! I think this could work even if it's High level for me, but I'll try !! :slight_smile:

But I'm afraid that the bucket limit is a problem. With my example, this could work, but in real life I have to do that with very big file (20.000 line, maybe 1000 or 2000 Host and lot of used port).

Do you thing that could still work ?

Transform exists exactly for such use case that you need complete set of buckets beyond the max_bucket limit of usual aggregation!

To see when to use transforms: the first use case is that you want to search all aggregation results.

This topic was automatically closed 28 days after the last reply. New replies are no longer allowed.