Windows DNS Server log collection is essential yet complex, primarily because Windows DNS Server provides logs in various places in different forms containing a vast amount of information. Nevertheless, we all know that DNS Server log collection is paramount in IT security. Getting it right can be challenging.
The Windows DNS Server section in the NXLog user guide offers a comprehensive guide on collecting log records from a Windows DNS Server. In this post, I will elaborate on capturing the resolved address from logs you can collect from Event Tracing for Windows (ETW) providers.
The resolved address can be critical in identifying malicious DNS activity; however, its importance is often overlooked, as it is challenging to collect. Thankfully, NXLog’s advanced data capturing and parsing capabilities allow us to gather the resolved address and present it in a form that can be easily understood and further processed.
Capturing the DNS resolved address
The starting point of this post was a customer query about how to capture the resolved address. However, we need some more corresponding data (metadata) to see the resolved address' context and to make well-informed conclusions, so I added the most obvious ones.
The most critical information for incident response is the following:
-
Timestamp
-
Endpoint hostname
-
Domain
-
Account name
-
The query
-
What that query resolved to
-
When the query was resolved
Collecting the above gives you a better overview of how a query looks and what it resolves to. The resolved address is the most interesting piece of data to gather because the original Windows event is a payload in binary data with hexadecimal representation. Being able to interpret the resolved address is a unique feature in NXLog. NXLog can decode this data and retrieve it.
What is the expected output?
So far, we know what data we would like to gather. Still, I would like to lay down some expectations on how I imagine the output. It is worth solidifying your thoughts before so you can ingest a particular configuration into your log collection strategy.
I’ve engineered a real-life scenario of what you, as the reader, would consider a suitable and informative output log:
-
The output must be human-readable
-
It must be easy to process by machines/software (if you want to send the data to a SIEM solution, for example)
-
The log must only contain what we need (noise free)
-
It must have appropriate naming (depends on your individual need, but we do it to our needs here)
-
The output must be as small as possible (to help with our SIEM and storage costs)
-
Should only contain specific EventIDs (only what we need)
This is the wish list. I would also like some additional background information here:
-
First of all, you need to enable the Analytic trace channel on your Windows host (instructions on the Microsoft website)
-
Our input module im_etw will then collect events and parse them into fields on the fly. The field names that will be populated with data can be found (here)
-
The DNS user queries can be traced in the Microsoft-Windows-DNSServer provider with Event IDs 256-259 (reference, '256' is undocumented) In this post we will only focus on Event ID 256 and Event ID 257
Start with the basics - default ETW configuration
In the first configuration, I would like to show a basic setup using the im_etw module, as it is interesting to see how much value you can get from a little extra effort.
Microsoft-Windows-DNSServer
This configuration uses the im_etw module to collect all logs from the Microsoft-Windows-DNSServer ETW provider, then outputs it in pretty printed JSON format.
<Extension json>
Module xm_json
PrettyPrint TRUE
</Extension>
<Input etw>
Module im_etw
Provider Microsoft-Windows-DNSServer
Exec to_json();
</Input>
The resulting nested JSON file contains a wealth of data about a single event. It collected and parsed the following fields for an example DNS response event (EventID: 257) with lookup "National Aeronautics and Space Administration" presented in JSON pretty print format.
Although it contains a wealth of data, it also contains a lot of noise. It is never a good idea to collect every single log from a system, and it is rarely a good idea to parse and send every part of a single log for further processing. It is always worth looking at the log file and thinking about what you need. As usual, there is no silver bullet. Every scenario is different, and yours is too.
{
"SourceName": "Microsoft-Windows-DNSServer",
"ProviderGuid": "{EB79061A-A566-4698-9119-3ED2807060E7}",
"EventID": 257,
"Version": 0,
"ChannelID": 16,
"Channel": "Microsoft-Windows-DNS-Server/Analytical ",
"LevelValue": 4,
"Level": "Information ",
"OpcodeValue": 0,
"TaskValue": 1,
"Category": "LOOK_UP ",
"Keywords": "9223372036854775810",
"EventTime": "2022-01-04T14:46:56.932456+00:00",
"ExecutionProcessID": 1848,
"ExecutionThreadID": 3632,
"EventType": "INFO",
"SeverityValue": 2,
"Severity": "INFO",
"Hostname": "WIN-VO0N2RBUVE4",
"Domain": "NT AUTHORITY",
"AccountName": "SYSTEM",
"UserID": "S-1-5-18",
"AccountType": "User",
"Flags": "33152",
"TCP": "0",
"InterfaceIP": "127.0.0.1",
"Destination": "127.0.0.1",
"AA": "0",
"AD": "0",
"QNAME": "nasa.gov.",
"QTYPE": "1",
"XID": "28411",
"DNSSEC": "0",
"RCODE": "0",
"Port": "60346",
"Scope": "Default",
"Zone": "..Cache",
"PolicyName": "NULL",
"PacketData": "0x6EFB81800001000200000000046E61736103676F760000010001C00C0001000100000258000434000E74C00C0001000100000258000417162778",
"ParsedPacketData": {
"dns.id": 28411,
"dns.flags.recursion_desired": "true",
"dns.flags.truncated_response": "false",
"dns.flags.authoritative": "false",
"dns.opcode": "QUERY",
"dns.flags.query_or_response": "true",
"dns.response.code": "NOERROR",
"dns.flags.checking_disabled": "false",
"dns.flags.authentic_data": "false",
"dns.flags.recursion_available": "true",
"dns.query": [
{
"dns.query.name": "nasa.gov",
"dns.query.type": "A",
"dns.query.class": "IN"
}
],
"dns.answer": [
{
"dns.answer.name": "nasa.gov",
"dns.answer.type": "A",
"dns.answer.class": "IN",
"dns.answer.ttl": 600,
"dns.answer.data": "52.0.14.116"wp-admin/https://kb.nxlog.co
},
{
"dns.answer.name": "nasa.gov",
"dns.answer.type": "A",
"dns.answer.class": "IN",
"dns.answer.ttl": 600,
"dns.answer.data": "23.22.39.120"
}
]
},
"AdditionalInfo": "VirtualizationInstance:.",
"EventReceivedTime": "2022-01-04T14:46:57.912861+00:00",
"SourceModuleName": "ETW",
"SourceModuleType": "im_etw"
}
If you look at this JSON file, it contains a nested section: ParsePacketData
.
I will use that to parse the resolved address in the following example.
Taking the configuration to the next level
In this section, I will build on the basic configuration above and tune it to all the needs we set out at the beginning of the post.
Microsoft-Windows-DNSServer
ETW provider and applying advanced parsing of the log data.This configuration file does the advanced parsing I wrote about earlier. I look at each configuration block individually, so you understand it.
<Extension _json> (1)
Module xm_json
PrettyPrint TRUE
Unflatten TRUE
</Extension>
<Extension cleanup> (2)
Module xm_rewrite
Keep Hostname, EventID, Channel, Domain, Category, QNAME, dns.answer, Source, Destination, DNSResolvedAddress, dns.query, dns.response.code
Rename dns.answer, DNS_Response_ALL
Rename QNAME, DNS_Query
Rename dns.query, DNS_Query_ALL
Rename Hostname, DNS_Server
Rename DNSResolvedAddress, DNS_Response_IPv4
Rename Source, DNS_Host_Source
Rename Destination, DNS_Host_Destination
Rename dns.response.code DNS_Code
</Extension>
<Input etw> (3)
Module im_etw
Provider Microsoft-Windows-DNSServer
ParsePacketData TRUE
<Exec>
if $EventID NOT IN (256, 257) drop();
parse_json($ParsedPacketData);
to_json();
$DNSResolvedAddress = extract_json("$.dns.answer");
$DNSResolvedAddress =~ /^.*dns\.answer\.data\":\"(\d+\.\d+\.\d+\.\d+)\".*$/;
$DNSResolvedAddress = $1;
cleanup->process();
to_json();
</Exec>
</Input>
1 | The first extension block is responsible for the JSON formatting. It is called with the to_json() procedure from other modules. In addition, it pretty-prints the JSON output, and the Unflatten directive removes the JSON files' nesting. |
2 | The second extension block is responsible for rewriting fields. The Keep directive defines what fields to keep, and then the Rename directive sets the renaming. |
3 | The input block collects logs from the Microsoft-Windows-DNSServer ETW provider, where the ParsePacketData directive enables the further parsing of the nested content from the previous output example. The Exec block within the input block sets the two EventIDs to collect and drops everything else. Then, it parses the resolved address field. |
As you can see below, the output is very different this time. Even though it contains two log messages (the query and the response), it is much shorter than the previous one. It only includes information that is valuable or of interest. In theory, if you were only interested in the resolved address, you could only collect Event ID 257. However, it gives you better context to see the corresponding query.
{
"EventID": 256,
"Channel": "Microsoft-Windows-DNS-Server/Analytical ",
"Category": "LOOK_UP ",
"DNS_Server": "WIN-VO0N2RBUVE4",
"Domain": "NT AUTHORITY",
"DNS_Host_Source": "10.0.1.1",
"DNS_Query": "nasa.gov.",
"DNS_Code": "NOERROR",
"DNS_Query_ALL": [{"dns.query.name":"nasa.gov","dns.query.type":"A","dns.query.class":"IN"}],
"DNS_Response_IPv4": null
}
{
"EventID": 257,
"Channel": "Microsoft-Windows-DNS-Server/Analytical ",
"Category": "LOOK_UP ",
"DNS_Server": "WIN-VO0N2RBUVE4",
"Domain": "NT AUTHORITY",
"DNS_Host_Destination": "10.0.1.1",
"DNS_Query": "nasa.gov.",
"DNS_Code": "NOERROR",
"DNS_Query_ALL": [{"dns.query.name":"nasa.gov","dns.query.type":"A","dns.query.class":"IN"}],
"DNS_Response_ALL": [{"dns.answer.name":"nasa.gov","dns.answer.type":"A","dns.answer.class":"IN","dns.answer.ttl":164,"dns.answer.data":"52.0.14.116"},{"dns.answer.name":"nasa.gov","dns.answer.type":"A","dns.answer.class":"IN","dns.answer.ttl":164,"dns.answer.data":"23.22.39.120"}],
"DNS_Response_IPv4": "23.22.39.120"
}
Closing thoughts
There are a few conclusions we can draw by having read the article. The first is that collecting DNS logs is essential, and the information gathered from them is paramount. Furthermore, you need to do more (well, less) than just collecting all logs; you need to gather only the necessary ones. Filtering and trimming your logs to only collect what you need gives you better visibility of what is essential and reduces costs by having less data to manage. Finally, the importance of the right log collection strategy must be addressed, where you have a clear vision of what you want and how to get it done.