UPDATE: There’s a new version, with 25% less bugs! Use this instead.
As some of you may know, I wear an Incident Response hat within my organization. As I like to be proactive and actively search for issues rather then just be an IDS alert monkey, I love pages like the Malware Domain List, the ZeuS Tracker, and malwareurl.com. While these are great resources, it is a bit difficult attempting to take the lists and apply them to the environment; most of their usefulness comes from when you have a questionable URL and need to see if someone else has reported it as a bad site. A great service, but not proactive.
While staring at the ZeuS Tracker Domain Block list and trying my usual method of snipe hunting manually entering domains to query the firewalls, a moment of inspiration hit: I don’t care about all the domains, just the domains that people visit. Who knows what domains people visit? The DNS servers! Now it was just a question of trying to coax the information out of the DNS servers. Thankfully, PaulDotCom Security Weekly came to the rescue: They have been talking about getting information out of DNS servers during penetration tests and a simple non-recursive DNS lookup on the local DNS server can tell you if someone queried for the host recently. A couple of quick experiments to verify this fact on my work’s main DNS servers confirmed this fact, and I set to work.
My first attempt was a simple script to take a pre-chewed version of the ZeuS Domain list, feed it through dig and pipe the output through grep. It worked, but I wanted something a touch more automated. Over the next couple of nights on the train, I whipped up a tool to automate the process a little more. The resulting tool is the ZeuS DNS Scraper. It’s a simple script written in Perl and should work straight out of the box with the default modules included in a Perl distribution.
Running the Script
Running the tool is fairly simple, there are only 4 options: –server, specifying which server(s) to query, –file, specifying where to put the downloaded ZeuS Tracker block list (defaulting to /tmp/ztbl.txt) , –download/–nodownload which specifies whether or not the script should attempt to download the block list, and –debug, which specifies the verbosity of the script.
A typical command line would be:
perl zeusdnsscraper.pl --server 192.168.1.2 --server 192.168.1.3
Which would download the block list, and then proceed to query 192.168.1.2 and 192.168.1.3 for each entry in the block list. You can specify as many as many servers as you like, however, the block list often hovers around a thousand entries, so each additional server adds another thousand or so queries.
Alternatively, once the list is downloaded, the script will download the block list only if the local copy is older then 60 minutes, (don’t worry it doesn’t update that frequently). You can also specify that the script doesn’t download the list again with the –nodownload option:
perl zeusdnsscraper.pl --server 192.168.1.2 --server 192.168.1.3 --nodownload
You can also turn on debugging with the debug option, which will display every step in the process:
perl zeusdnsscraper.pl --server 192.168.1.2 --server 192.168.1.3 --debug
Interpreting Results
When the script is run in default mode, a ‘.’ will appear after each query, while in debug mode it will display the result of the query and whether or not it found an entry.
What You Want To See
Completed! NNNN queries made, 0 entries found! Hooray!
In this example, NNNN would be the number of queries sent, remember this increases which each additional server you need to query, and it has found 0 entries, indicating that the DNS servers queried have no cached entries for any of the domains. Congratulations, pat yourself on the back and grab yourself a nice frosty beverage from the refrigerator.
What You Do Not Want To See
NNNN queries made, 4 entries found. Uh Oh. W.X.Y.Z has an entry in it's cache for www.example.net: 10.1.2.3 W.X.Y.Z has an entry in it's cache for www.example.net: 10.1.2.4 W.X.Y.Z has an entry in it's cache for www.example.com: 10.4.5.6 W.X.Y.Z has an entry in it's cache for www.example.org: 10.7.8.9
Well, crap. This time the beverage you need is probably kept in your attrition.org flask. NNNN is the number of queries the script made and the “4″ in this example is number of results found. In this example, “www.example.net” was cached with two separate addresses, while “www.example.com” and “www.example.org” both have one apiece. The W.X.Y.Z in the above example is the DNS server that responded, and the 10.X.X.X addresses are the IP addresses that the DNS server responded with. These IP addresses are what you are interested in.
My DNS Servers Have Cached Entries! Now What?
This is where some good old detective work comes in. The presence of the cached entries on your DNS server only means that one of the clients on your network asked for the entry in question. Normally, it’s time to start plugging IP addresses in your firewall logs to see who’s been visiting them. Then it’s time to start cleaning.
Caveats
Now, obviously, this sends a boat load of queries in a very rapid fashion to DNS servers. Make sure that your DNS server and your connection can handle the load and don’t run it against DNS servers that you do not have permission to do so. Also, some of the DNS entries have small enough TTLs that they may expire quickly, meaning that even if the script comes back clean, there could still be infected hosts.
Thanks
I’d just like to say a big thanks to the folks over at abuse.ch for hosting the ZeuS Tracker. It’s a handy tool and it’s invaluable if you’re running even a moderately sized network.
ZeuS DNS Scraper
Twitter
LinkedIn
Facebook
Flickr
FriendFeed
Will be nice to modify your perl script to work with http://support.clean-mx.de/clean-mx/viruses in addition to zeus tracker
Posted by BK on March 19th, 2010.
Now that was a resource I was unaware of. Bookmarking that bad boy. Problem is their XML file seems to be huge. I’d be worried about releasing a tool to the masses that adds to their load. I need to research it further.
Posted by Ben Jackson on March 19th, 2010.
Yea Ben these guys are tracking an expanded list of malware, but, still it would be a good thing to script out once a week
Posted by Ben Koehl on March 19th, 2010.
Hi Ben
what kind of data do you need form our database, you may adjust xml output to only reviewed ip and url….
.. and please why “bad boy” ????
example:
http://support.clean-mx.de/clean-mx/xmlviruses?format=xml&fields=review,url&response=alive
to dig out active cases…
you may contact me any time by email…
Gerhard
Posted by Gerhard W. Recher on March 19th, 2010.
Sorry Gerhard, lost in translation. “Bad boy” in this context would mean “excellent tool” – Slang. I appreciate the resource.
Thanks for that link. I was unaware you could limit the dataset returned. I’m looking at it now.
Posted by Ben Jackson on March 19th, 2010.
Hi Ben,
currently aprox. 46021 rows will be returned…
you may limit this by appendig a &limit=0,1000 for the first 1000 items (syntax is like mysql sql syntax…)
but if you need qualified data for zeus tracking you may issue a narrowed query…
like: &url=%bin will bring you mostly zeuss related items…
– gerhard
Posted by Gerhard W. Recher on March 19th, 2010.
Interesting, but I guess this might miss malicious domains with low TTL values, specially if fast-flux is used. Did you check that?
Posted by okamalo on March 21st, 2010.
It would work, but, the lower the TTL value is, the higher chance of a false negative. You just need to run it while that TTL hasn’t expired yet. Ever since I’ve started playing with it. I’ve run it multiple times a day to see what happens.
So, no, not perfect, but it’s a step…
Posted by Ben Jackson on March 21st, 2010.
I have just tried the script in a small ISP, the short ttl is dominating the Zeus domains.
I guess this could be evolved into a larger project with more sources of malicious domains and ongoing check of the DNS cache.
Great idea with minimum overheads.
Posted by okamalo on March 22nd, 2010.
Looks like I might need to run some more tests. I knew that TTL would be an issue, but with my initial runs I was seeing stuff around an hour or so.
Thanks for the link and the compliments. I’m glad people are finding it useful!
Posted by Ben Jackson on March 22nd, 2010.
Looking this over, I can see how it could also be useful for incident response. If you know some suspicious domains, those could be added to your list of bad hosts and used as a sniper tool of sorts. The list of bad hosts could be as long or short as you want. This is something I am very interested in trying out.
Posted by Chris on March 23rd, 2010.
@Chris: Agreed. I’m planning a script to do just that. It’s a kludge, but if you make a file with the domains/hostnames you want to check, each name on a separate line you can run:
zeusdnsscrape.pl --nodownload --file=/path/to/fileAnd get that functionality.
Posted by Ben Jackson on March 23rd, 2010.
Thanks to you, I really get an interesting information.
I appreciate you.
Posted by MarlboroGuy on March 30th, 2010.
I don’t know perl much but i had to modify the “use” statement for the LWP module when using OSX:
#use LWP::Simple;
use LWP;
even LWP was installed, i kept on getting:
Can’t locate object method “new” via package “LWP::UserAgent” at ./zeusdnsscrape.pl line 66.
Nice tool!!
Posted by cc on March 31st, 2010.
Interesting, I’ll have to ask my friend who uses Mac OS X if he had the same issue…
Posted by Ben Jackson on March 31st, 2010.