ELK + Bro: Building a passive DNS database

Posted by Binor on 21/04/2018

Passive DNS data is a good source of threat intelligence. We show in this post how to build such database for your environment using Bro IDS and the ELK stack. The only requirement here is that your bro sensor is seeing all the DNS traffic originating from your local network. You can use Brostash to deploy a Bro based sensor.

The Bro logs for DNS traffic combine in one single entry the queried domain and the corresponding answer. If the query is successful and the domain exists, the log entry includes the IP associated with the domain and the corresponding TTL.

# Bro DNS log
1515995103.332099 CV4PAq1OLVM2ZhyKC1 10.1.20.100 57357 10.1.20.1 53 udp 15608 3.910198 s1.lemde.fr 1 C_INTERNET 1 A 0 NOERROR F F T T 0 93.184.220.239 60.000000 F

In our Logstash pipeline for processing the Bro logs, we are parsing the DNS logs and storing the resolved IP in the field domain_ips. In building the passive DNS database, we start first by using the Logstash clone filter plugin. We apply this filter to any DNS event going through our processing pipeline and where the field domain_ips exists. Next, we change the logtype field value and then apply the split filter on the list of domain answer IP. The output of this filter is a list of new events where we have a one to one association between a domain and an IP. Following the application of this filter, we use the ruby filter to extract from the original event the DNS answer TTL associated with the given domain and IP.

filter {
  if ![is_dns_clone] and [logtype] == "brodns" and [domain_ips] {
    clone {
      clones => ["logstash-bro"]
      add_field => { "is_dns_clone" => "true" }
    }
  }
  ...
  split {
    field => "domain_ips"
    target => "ip"
  }
  if [ip] {
    ruby {
      code => "
        aa_list = event.get('dns_answers')
        at_list = event.get('dns_answers_ttls')
        ll = aa_list.length
        x = event.get('ip')
        event.set('ips', [x])
        (1..ll).each do |i|
          vv = aa_list[i-1]
          if vv == x
            tt_l = at_list[i-1]
            event.set('ttl', tt_l)
            break
          end
        end
      "
    }
  }
  ...
}

In the final step of the log pipeline processing, we use the Logstash mutate filter to convert the ttl field to an integer, replace the value of the field dst_ip with the domain’s IP and change the document type to logstash-passive. With the document type change, our passive DNS database will be stored in a new index.

mutate {
  convert => { "ttl" => "integer" }
  replace => [ "dst_ip", "%{ip}" ]
  replace => [ "type", "logstash-passive" ]

}

With the configuration above, we have now our own Passive DNS database. This database associates with every domain seen an IP, a TTL and a timestamp. The same approach we showed here can be used to build a Passive X509 certificates database. The needed bro scripts and the Logstash filters/patterns are available in our github repositories.

Let's Get In Touch!


+222 45 29 00 29

+222 45 29 85 40