In a recent sysadvent article I described how to configure BIND with a Response Policy Zone. Using an RPZ can efficiently thwart outbound network traffic based on one’s own preferences, and it can be extended to import and/or subscribe to externally provided DNS zones.

Configuring BIND

My local BIND server has been configured with two RPZs. One is maintained manually, mostly for reaching internal resources behind a NAT firewall by their official DNS names. The other one is a dedicated malware RPZ, automatically updated once a day from the freely available files provided by the Malware Domain Blocklist (note their terms of use). When downloaded, the list of domains associated with malicious activity is converted to an RPZ compatible zone file. The resulting file is saved as /etc/bind/db.malware. Below are the first few lines of this file after conversion.

$TTL 60
@               IN      SOA     primary-dns.example.com. root.example.com.  (
                        2015120911      ; serial 
                        3H              ; refresh 
                        1H              ; retry 
                        1W              ; expiry 
                        1H)             ; minimum 
                IN      NS      primary-dns.example.com.
                IN      NS      secondary-dns.example.com.

jnvzpp.sellclassics.com IN      A       127.0.0.1
                        IN      AAAA    ::1
iebar.t2t2.com          IN      A       127.0.0.1
                        IN      AAAA    ::1
27simn888.com           IN     	A       127.0.0.1
                        IN      AAAA    ::1
74203s040.edusite.ru    IN      A       127.0.0.1
                        IN      AAAA    ::1
adfrut.cl               IN      A       127.0.0.1
                        IN      AAAA    ::1
byxlujke.ru             IN      A       127.0.0.1
                        IN      AAAA    ::1
; [...]

The malware zone

BIND allows up to 32 different Response Policy Zone files, so I’ve separated the two, naming the manual zone “rpz” and the malware domains zone “malware”. Configuring them is done in named.conf or a supporting file (for instance /etc/named.conf.local if you’re on a Debian based platform).

zone "rpz" {
  ; manually maintained zone
  type master;
  file "/etc/bind/db.rpz";
};
zone "malware" {
  ; automatically updated zone
  type master;
  file "/etc/bind/db.malwaredomains";
};

Both zones are added to the response-policy {} clause. Add this inside the options {} section in named.conf (or /etc/bind/named.conf.options on Debian and derivates).

options {
  response-policy {
    zone "rpz";
    zone "malware";
  };
};

Log configuration

The zones defined in the above response-policy {} setting will automatically log to the rpz category. In Debian, logging is configured in /etc/bind/named.conf.local.

logging {
  ; Define a logging target (channel)
  channel named-rpz {
    file "/var/log/bind/rpz.log" versions 3 size 250k;
    severity info;
  };
  
  ; Direct every rpz event to the defined channel
  category rpz {
    named-rpz;
  };
};

Paydirt!

When properly configured, requests for domains in the malware zone are logged in the RPZ log file, suffixed with the zone reference, namely malware. The following extract shows successful logging of a request for a domain listed in the malware zone.

client 127.0.0.1#53547 (byxlujke.ru): rpz QNAME Local-Data rewrite
byxlujke.ru via byxlujke.ru.malware

Requests picked up by the other RPZ file, simply named “rpz”, will have a .rpz suffix.

Adding OSSEC to the mix

After testing that attempts to reach malware sites are indeed logged by the DNS server, I set up OSSEC to tail BIND’s malware query log. For this I had to write a decoder and define logging rules in OSSEC, shown below. Both of these could probably be drastically improved by someone more fluent in OSSEC. That said, I haven’t seen a single false positive - yet…

OSSEC master

The log parsing takes place on the OSSEC master. There are two files to modify, and one to add.

Add the following content to /var/ossec/etc/local_decoder.xml:

<decoder name="malware-dns">
    <prematch>^client </prematch>
</decoder>

<decoder name="malware-dns-lookup">
    <parent>malware-dns</parent>
    <regex offset="after_parent">^(\.+)#\d+ \((\.+)\): \.+.malware$</regex>
    <order>srcip, extra_data</order>
</decoder>

Add a new file /var/ossec/rules/malware_dns_rules.xml with the following content:

<group name="syslog,bind">
  <!-- Malware DNS Log Types -->
  <!-- level=0 for not generating alerts by default -->
  <rule id="110201" level="0">
    <decoded_as>malware-dns</decoded_as>
    <description>Malware DNS group</description>
  </rule>

  <!-- Malware DNS Event Chains -->
  <!-- Fire an alert (level=8) if the log line ends with "malware" -->
  <rule id="110202" level="8">
    <if_sid>110201</if_sid>
    <match>malware$</match>
    <description>Malware DNS lookup</description>
  </rule>
</group>

Last step on the OSSEC master is to include the above rules file. Add an <include> line in /var/ossec/etc/ossec.conf:

<ossec_config>
  [...]
  <rules>
    [...]
    <include>malware_dns_rules.xml</include>
  </rules>
  [...]
</ossec_config>

OSSEC agent

The name of the file to tail needs to be defined on every OSSEC agent. The configuration file for this is /var/ossec/etc/ossec.conf.

<ossec_config>
  [...]
  <localfile>
    <log_format>syslog</log_format>
    <location>/var/log/bind/rpz.log</location>
  </localfile>
</ossec_config>

Testing the parser

If everything has been set up correctly, OSSEC parsing should work as expected. To be on the safe side, test the parser with OSSEC’s ossec-logtest.

root@server:~# /var/ossec/bin/ossec-logtest
2015/12/09 08:49:48 ossec-testrule: INFO: Reading local decoder file.
2015/12/09 08:49:48 ossec-testrule: INFO: Started (pid: 2772).
ossec-testrule: Type one log per line.

client 127.0.0.1#3558 (byxlujke.ru): rpz QNAME Local-Data rewrite
byxlujke.ru via byxlujke.ru.malware


**Phase 1: Completed pre-decoding.
       full event: 'client 127.0.0.1#3558 (byxlujke.ru): rpz QNAME
       Local-Data rewrite byxlujke.ru via byxlujke.ru.malware'
       hostname: 'server'
       program_name: '(null)'
       log: 'client 127.0.0.1#3558
       (byxlujke.ru): rpz QNAME Local-Data rewrite byxlujke.ru via
       byxlujke.ru.malware'

**Phase 2: Completed decoding.
       decoder: 'malware-dns'
       srcip: '127.0.0.1'
       extra_data: 'byxlujke.ru'

**Phase 3: Completed filtering (rules).
       Rule id: '110202'
       Level: '8'
       Description: 'Malware DNS lookup'
       **Alert to be generated.

The result

Now, if someone (or something) on my network is trying to reach a resource on a domain registered as affiliated with malware, OSSEC will react and alert by email, raise an alarm in your SIEM, or whatever else you want OSSEC to do.

Below is a sample email alert from OSSEC, when attempting to browse a malware domain from a client.

OSSEC HIDS Notification.
2015 Dec 09 15:07:06

Received From: server->/var/log/bind/rpz.log
Rule: 110202 fired (level 8) -> "Malware DNS lookup"
Portion of the log(s):

client 192.168.42.42#52162 (byxlujke.ru):
rpz QNAME Local-Data rewrite byxlujke.ru via byxlujke.ru.malware

And an extract from OSSEC’s alert log:

** Alert 1449670026.3728841: mail  - syslog,bind
2015 Dec 09 15:07:06 server->/var/log/bind/rpz.log
Rule: 110202 (level 8) -> 'Malware DNS lookup'
Src IP: 192.168.42.42
client 192.168.42.42#52162 (byxlujke.ru): rpz QNAME Local-Data rewrite byxlujke.ru via byxlujke.ru.malware