More on DNS Archeology (with PowerShell)

Published: 2019-10-25. Last Updated: 2019-10-25 15:13:29 UTC
by Rob VandenBrink (Version: 1)
2 comment(s)

I while back I posted a "part 1" story on DNS and DHCP recon ( https://isc.sans.edu/diary/DNS+and+DHCP+Recon+using+Powershell/20995 ) and recent events have given me some more to share on the topic.

There's been a lot of interest in collecting DNS information from client stations lately (specifically with sysmon), but I'm still seeing lots of value in using the DNS logs we've already got centrally positioned in many organisations.   Let's consider a typical windows based company, with DNS implemented on the Domain Controllers.

Let's check the DNS diagnostic / audit setup using PowerShell (this server is close to the defaults):

> Get-DnsServerDiagnostics


SaveLogsToPersistentStorage          : False
Queries                              : False
Answers                              : False
Notifications                        : False
Update                               : False
QuestionTransactions                 : False

UnmatchedResponse                    : False
SendPackets                          : False
ReceivePackets                       : False
TcpPackets                           : False
UdpPackets                           : False
FullPackets                          : False
FilterIPAddressList                  :
EventLogLevel                        : 4
UseSystemEventLog                    : False
EnableLoggingToFile                  : True
EnableLogFileRollover                : False
LogFilePath                          : MaxMBFileSize                        : 500000000
WriteThrough                         : False
EnableLoggingForLocalLookupEvent     : False
EnableLoggingForPluginDllEvent       : False
EnableLoggingForRecursiveLookupEvent : False
EnableLoggingForRemoteServerEvent    : False
EnableLoggingForServerStartStopEvent : False
EnableLoggingForTombstoneEvent       : False
EnableLoggingForZoneDataWriteEvent   : False
EnableLoggingForZoneLoadingEvent     : False

So by default we're not logging DNS requests or responses. To fix this, navigate to DNS / Server Properties, and you'll find most of these settings are available in the GUI.  Note that you can monitor requests and responses separately - this can be valuable depending on what you are looking for, and that DNS in a moderately sized company can generate GB's of logs per day (roughly half of the firewall log size in many "typical" organizations)

Once you have some logs, they're in %systemroot%\system32\dns\dns.log.  To ingest this log in PowerShell (just pulling the dns packet logs):

$alldnsqueries = get-content c:\windows\system\dns\dns.log  | where { $_ -match "PACKET" }

This gets you a list of text strings.  Let's convert this fixed-length (more or less) text fields to CSV, then to a variable list (AKA usable objects)

$alldnsqueries = get-content C:\Windows\System32\dns\dns.log | where { $_ -match "PACKET" }

 

# we'll use a semicolon as a delimeter
$newDelimiter = ";"

# set the field delimeter locations (you may need to adjust these)
$cols = 10,20,54,56,60,66,78,115

$newarray =@()
#define the header row

$newarray += ";date;time;unused1;unused2;proto;direction;clientip;unused3;hostquery;"

$a = 0
$dnscount = $alldnsqueries.count

$alldnsqueries | ForEach-Object {
  $line = $_
  $cols | ForEach-Object {
    $line = $line.Insert($_, $newDelimiter) 
  }

# put the delimeters at the beginning and end of line
  $line = ";" + $line + ";"

  $newarray += $line
  # counter on the 1,000's, just to keep track of progress
  # and to be sure we're not stuck
  if ($a % 1000 -eq 0) { $a of $dnscount }
  ++$a

$queries = $newarray |  ConvertFrom-Csv -Delimiter $newDelimiter | select date,time,proto,direction,clientip,hostquery

Now we have it in a usable format!  This likely took a while - PowerShell will only use a single core (unless you set up jobs etc), and it's not the speediest of text processing tools.  A 500,000 line log file took me a solid 2 hrs to process.

Just for a quick check, let's see if we have any hosts making queries for WPAD (ouch!).  In most shops you'd hope that this would be zero, or at least a low number.
First collect all the queries received to "anything wpad"

$wpadqueries = $queries | where { $_.hostquery -match "wpad" } | where {$_.direction -match "Rcv"}

PS L:\work\dns> $wpadqueries.count
5924

Let's narrow it down to "offending workstations" and get a count:

$wpadstations = $wpadqueries.clientip | group

$wpadstations.count
302

OK, trouble in paradise, that's pretty much the entire station count here!  This environment needs a GPO to say "no proxy here", or else a GPO that explicitly sets the proxy.

Now do a group by A record and requesting station / uniq.  In this situation there are 3 different particpants in a merger (two merged companies and one parent):

Looking at the dns queries themselves, which ones are the "oddballs"?  In most organizations, a one off DNS request is often an indicator of malware activity, or at least an indicator of something that should be investigated.  Picking the first "n" in the list, who is querying for those hosts?.

Let's sort by count, and pick the first 15.  Note that all the single-query counts are "2", because both the send and receive packets are logged.

$uniqqueries = $queries | Group-Object hostquery | Select-Object count,name | sort count 
$uniqqueries | select -f 15

Count Name                                                                             
----- ----                                                                              
    2 (12)wrtwrokhhfgz(7)company(3)com(0)              
    2 EY   (9)1604-ms-7(10)110-896fa7(36)c6350d9f-f5a5-11e9-9598-8cec4b8e1e08(0)     
    2 (11)zbbanxrlced(7)company(3)com(0)                                      
    2 (8)vnwxcxpp(7)company(3)com(0)                                          
    2 (13)icyygacsfacmr(7)company(3)com(0)                                    
    2 (12)tyjkfiuewyus(7)company(3)com(0)                                     
    2 (7)s-jsonp(7)moatads(3)com(0)
    2 (5)e2725(4)dscg(10)akamaiedge(3)net(0)
    2 (6)sbrinc(3)com(0)
    2 (8)fnxugntu(7)company(3)com(0)  
    2 (3)www(4)hape(3)com(0) 
    2 (5)click(4)virt(11)exacttarget(3)com(0)                                       

    2 (14)d1yodfj154g0xj(10)cloudfront(3)net(0)                                      
    2 (5)phone(6)sports(4)tile(5)appex(4)bing(3)com(7)edgekey(3)net(0)               
    2 (6)p33232(12)cedexis-test(3)com(0) 
    2 (15)etsfidhzmqxjvva(7)company(3)com(0)

hmm  - nothing suspicious about those domain names :-(  ... often we'll see ad hosts and malware sites in this list (not that they don't coincide more than we'd like).  Those random-string hostnames with the internal organization's domain name (elided to "company.com") are very disturbing.  None of those domains exist - it looks very much like someone is resolving for random CN's and the host is appending the internal domain.  Either that or it looks like some of the  "fast flux" malware from years gone by.
Digging deeper into this, for example with:

$queries | where { $_.hostquery -match "fnxugntu" }

date      : 10/23/2019
time      : 1:29:23
proto     : UDP
direction : Rcv
clientip  : 10.xxx.xx.11
hostquery : (8)fnxugntu(7)company(3)com(0)

yup - you guessed it, all of those "odd" DNS requests came from the same host!  This one host was making thousands of requests per hour to invalid hostnames, accounting for roughly 10% of the DNS traffic in the organization.  The IR / workstation team is looking at this host next, but if you've seen this pattern please do help fill in the blanks - tell us what you can in our comment form.  So far it does NOT look like another DNS server that might be forwarding requests from infected workstations - it's looking like a server or workstation of some type.

This is a great way to start investigating your DNS activity for free in a Windows environment.  If you find this useful, you might consider scripting and scheduling some of this in your organization.  Or rather than parsing this manually, consider feeding your DNS logs to a SIEM - something you can also do for free (look at SANS 455 and SANS SEC 555 for more info on that)

If you've used methods like these and have found something interesting, or if you've seen the "odd" pattern of requests described in this story, please do share using our comment form (as far as your NDA will let you that is).

========== update ==========

It turns out that the "odd" dns requests were all from a "cloud managed" ethernet switch.  Since this is the only switch of hundreds doing this, we're figuring that it's infected with something.  Since these are all intel / linux machines with a switch bolted to it under the covers, that's not a huge stretch.  So the request still stands - if you've seen requests like this in your environment, please do use our comment form to carry this conversation forward!

 

===============
Rob VandenBrink
rob <at> coherentsecurity.com

2 comment(s)

Comments

A while back, I wrote a PoSh script to convert a DNS Debug log file to CSV for further analysis. But the conversion process took too long.
So I wrote a converter in Python: https://github.com/Sam0rai/Utilities/blob/master/ConvertDNSLogToCSV.py

Instead of hours, it now took minutes (for a > 4 million records log file).
There's a Microsoft blog post for using Logparser to parse DNS server debug logs.

https://blogs.technet.microsoft.com/secadv/2018/01/22/parsing-dns-server-log-to-track-active-clients/

Speed is the primary benefit of using this approach. Logparser is incredibly fast especially when ingesting large files.

There's great information provided in the post making it a must read. The provided command is as follows which may need to be tweaked for your own needs.

LogParser -i:TSV -nskiplines:30 -headerRow:off -iSeparator:space -nSep:1 -fixedSep:off -rtp:-1 "SELECT field9 AS IP, REVERSEDNS(IP) AS Name, count(IP) as QueryCount FROM "C:\Temp\DNS\dns.log" WHERE field11 = 'Q' GROUP BY IP ORDER BY QueryCount DESC"

Diary Archives