Calculation of DNS Query Entropy Directly Within Zeek
Hi !
I have previously discussed the value of calculating the entropy (in the “Shannon” sense) of DNS queries. I had previously used R scripts to perform the calculations, since the underlying idea was to graph what the tool was observing.
I’ve since had another idea: why not have Zeek perform the calculation directly and add the resulting value to the “dns.log” file? This would allow more people to read it and process the information as they wish.
Enough talk, let’s get to the script (as usual, I’ll try to document each line as best as possible to make the code more readable)..
# Defines the namespace for this script named DNS_Entropy. This prevents variable or function name collisions with other Zeek scripts.
module DNS_Entropy;
# An export block used to make variables, types, or structure modifications accessible globally throughout the Zeek engine.
export {
# Uses the redef (redefinition) keyword to modify Zeek's built-in DNS::Info record structure (which generates dns.log).
# The '+=' operator specifies that we are appending a new field to the existing structure without overwriting it.
redef record DNS::Info += {
# Declares the new field named entropy:
# double: The data type (floating-point number).
# &optional: Indicates the field is not required for every record (prevents runtime errors if entropy isn't computed).
# &log: Critical attribute telling Zeek to write this value as a dedicated column in dns.log.
entropy: double &optional &log;
};
}
# Declares an event handler that triggers whenever Zeek observes a DNS request in the network traffic:
# 'c': The object representing the network connection (IP addresses, ports, metadata).
# query: The string containing the requested domain name (e.g., "google.com").
event dns_request(c: connection, msg: dns_msg, query: string, qtype: count, qclass: count)
{
# A safety check using the ?$ operator.
# It verifies whether the DNS state record ($dns) is attached to the connection c in memory before trying to access it.
if ( c?$dns )
{
# Declares a local variable e and stores the result of the built-in "find_entropy(query)" function.
# This one returns a structure containing the Shannon entropy calculation for the query string.
local e = find_entropy(query);
# Extracts the numerical entropy value (e$entropy) and assigns it to the entropy field of the active DNS session (c$dns).
# This populates the column in 'dns.log'.
c$dns$entropy = e$entropy;
}
}
Let’s see what this shows in the ‘dns.log’ file.
#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p proto trans_id rtt query qclass qclass_name qtype qtype_name rcode rcode_name AA
TC RD RA Z answers TTLs rejected opcode opcode_name entropy
#types time string addr port addr port enum count interval string count string count string count string bool bool bool bool count vector[string] vector[interval] bool count string double
1789581627.186833 CgmXXK38WUAlu9S8D3 162.212.157.188 19637 8.8.8.8 53 udp 49781 0.012198 27.in-addr.arpa 1 C_INTERNET 48 DNSKEY 0 NOERROR F F
T T 1 DNSKEY 13,DNSKEY 13,RRSIG 48 27.in-addr.arpa 3119.000000,3119.000000,3119.000000 F 0 query 3.189898
1789581627.177670 CKIdZH1b7y4SDL7GHj 162.212.157.188 41821 194.0.1.18 53 udp 34191 - vietel.com.vn 1 C_INTERNET 1 A 0 NOERROR F F
F F 1 - - F 0 query 3.238901
Cheers.