As I posted last week, intrusion detection systems (IDS) are catching me doing Internet-scale surveys. This gives me a chance to try and evade them. Of course, once I evade them, I have to document my tricks, so they can catch me again -- because I'm not actually trying to go undetected. I'm just having fun.
My latest trick involves DNS and the "version.bind" detection. You can query the version information of a DNS server by sending a "chaos txt version.bind" query to it instead of a normal query like www.google.com. The Snort signature that detects this looked like the following:
But DNS is tricky. Since you typically look up multiple related names, DNS supports compression. In place of one of the "labels" of a name, you can instead include an offset to somewhere else in the packet where the label is located.
Back in 2000 at DefCon 8, I described this, and demonstrated how in place of the above string I could put the "version" label in one place in the packet, and then the "bind" label in another place, and then use the compression pointers to link them back together. This evades the above signature.
Snort fixed this by updating their signatures to look like the following:
In other words, instead of looking for "version.bind" as one string, it looks for the individual labels "version" and "bind", no matter where I move them around in the packet. And that's the way the signature has been for the past 13 years.
But here's the thing, the name "version.bind" doesn't consist of 2 labels but 3. At the end of every name is the "empty label". That's the 00 at the end of the "bind" string you see above. I can "compress" that label by pointing it to somewhere else in the packet, making that trailing byte non-zero.
So I did just that, and checked in the code in my scanner. You can see the change in the following diff:
As you see, I changed the end label of \x00 to \xc0\x08. The number 8 is the offset from the start of the header, which points to one of those zeroes encoded on line 71.
I tested it out with a recent version of Snort, v18.104.22.168 (the version that current comes with Kali Linux), and indeed the evasion works. Snort catches me running the typical dig command:
dig chaos txt version.bind @22.214.171.124
But it doesn't catch the new masscan code doing:
masscan 126.96.36.199 -pU:53 --banners
Both scans produce the same result for that server, which is either "9.8.5-rpz2+rl.156.01-P2" or "NSD 3.2.7". Either due to load balancing or anycasting, I'm getting two different results for the same query from my comcast home account. (BTW, this IP address is one of the .org primary name servers, a0.org.afilias-nst.info).
There are three fixes to Snort for this evasion. The first is simply to remove the trailing zero from the existing signature:
The second fix is to add specific signature that identifies my scanner (calling it something like "Bob's friendly DNS scan"):
Thirdly, you could write a DNS preprocessor in Snort, then write a signature that looks like the following:
dns.type:txt dns.class:chaos dns.name:version.bind.
This preprocessor signature should catch all my evasion shenanigans, and also tighten up the signature against false positives (so things like "version.bind.example.com" don't trigger). The DNS code in masscan is actually a good start for for a Snort preprocessor, robustly parsing DNS labels (if not, if you can crash masscan with bad DNS input, then according to the terms of the vuln bounty, you'll get $100).
BTW, the abuse complaints I get from scanning the Internet don't come from Snort itself, or any other commercial IDS, but from the independent "Emerging Threats" rules. I wonder why that is. All IDS should detect my pre-evasion packet, but only those people running Emerging Threats rules complained about it. Maybe those who invest more in commercial products are more likely to tune out the noise, like minor scans from the Internet?