[Iptables DNS Rules]

Drop all the bad domains


lately I have mainly been reporting on domains that are being used to perform DNS amplification Attacks.

From now on I will also include IPtables rules to block request for these domains and when needed the used record type. 

The rules can be found on my GitHub repository. 


The rules will use the u32 module to match traffic, as this is really fast. 
With these rules I try not to block legitimate traffic. 

If you have tips on how to better distribute or manage these rules let me know. 



A rule explained:

Here I will describe the following rule to drop nearly all isc.org IN ANY noise from my Open Resolver.

iptables --insert INPUT -p udp --dport 53 -m u32 --u32 "0x1c=0x1d420100 && 0x28=0x03697363 && 0x2c=0x036f7267 && 0x30=0x0000ff00" -j DROP -m comment --comment "DROP DNS Q ANY isc.org dns.id 0x1d42"

This iptables command will add (--insert) a new rule in to the 'INPUT' chain of the firewall. All traffic goes through this chain that is destined from that computer.
Match only UDP and only traffic with a destination port of 53.

From here on I start to use the module (-m) u32 and try to find traffic that matches those hex strings. More on that later.  If this matches I drop it so it does not reach the DNS service.

All my rules will include a comment to describe what it does.. as the u32 makes that a bit cryptic. 

The hex part:

To fully understand the signature it is important to have a look at a DNS query packet in HEX. Here is the hex representation of the packet I based my rule on:

11:53:05.434215 IP 184.22.223.129.23722 > 1.2.3.4.53: 7490+ [1au] ANY? isc.org. (36)
0x0000:  4500 0040 bf0d 0000 7411 FFFF   b816 DF81  E..@....t..f....
0x0010:  0102 0304 5caa 0035 002c 0000  1d42 0100  ....\..5.,...B..
0x0020:  0001 0000 0000 0001 0369 7363 036F 7267  .........isc.org
0x0030:  0000 FF00 0100 0029 1000 0000  8000 0000  .......)........

Green is the source IP, 
Orange is the destination IP(Fake). 
Red is the checksum(Fake)
DarkBlue is the DNS Transaction ID
Purple is the DNS Flags
DarkPurple is the Query Name
LightBlue are a few NULL Bytes and the Query Type and the Class (IN).


The best way to see what, means what is by using wireshark, you can click on hex values and see the representing values. 

In the case of ISC.ORG I noticed a lot of the Amplification attacks are made with static source ports, but they change a lot and at any given time I see about 2 or 3 active. 
But all of them are currently sharing the same DNS ID. So the first part of our signature will be to match this:

0x1c=0x1d420100

The 0x1 in 0x1c means it is on the line indicated by 0x0010 and it will be the c-st value, it is hex.. so that would be 13th value. From there on I want to match in hex the following 4 bytes (the max you can do at the time with u32:

0x1d420100

As you can see I don't just match the DNS ID but also the DNS Flags. This is party because it is easier to match 4 bytes at the time and not use 'Masks' (to ignore parts of the 4 bytes) and also to make sure it is actually a query. 

With the && you can specify multiple matches, this does not keep track of it last match position, matching start again at 0x0.

Second match is for the first 4 bytes of the domain name. This is 0369 7363 This means what follows is a 3 byte domain label: I S C. This is followed by another match from position 0x2c:

0x2c=0x036f7267

0x03 that means the same thing.. O R G (036F 7267). 

The last match starts at the first byte of the 3rd line. This matches the record type and the first half of the Query Class 'IN'. 

"0x1c=0x1d420100 && 0x28=0x03697363 && 0x2c=0x036f7267 && 0x30=0x0000ff00"




20 comments:

  1. Have you a script to automate it? tnx

    ReplyDelete
  2. I normally do it manually or with a little python for loop that does the '&' for me. I have come across a script that generated these rules automatically...

    ReplyDelete
  3. Any alternatives for u32? Centos 5.x (yes I know, is dated) doesn't have any support for u32 and thus far I've found no way to compile it

    ReplyDelete
    Replies
    1. Wouldn't know sorry! The other method of blocking domain that is a lot harder on the CPU is using STRING matching.

      Delete
  4. Do you have alternative? i'm using FreeBSD :) thanks

    ReplyDelete
    Replies
    1. Nope sorry.. I've investigated u32 and nothing else. Let me know if you find a way using PF

      Delete
    2. Isn't it your lucky day today:
      https://github.com/smurfmonitor/dns-iptables-rules/blob/master/domain-blacklist-string.txt

      Delete
  5. 19:50:09.324117 IP (tos 0x0, ttl 142, id 2382, offset 0, flags [none], proto UDP (17), length 32)
    117.236.73.7.57396 > XXX.XXX.XXX.XXX.7844: [no cksum] UDP, length 0
    0x0000: 9c8e 9908 c704 000b fcd5 9640 0800 4500 ...........@..E.
    0x0010: 0020 094e 0000 8e11 c598 75ec 4907 5511 ...N......u.I.U.
    0x0020: 49e2 e034 1ea4 0008 0000 0000 0000 0000 I..4............
    0x0030: 0000 0000 0000 0000 0000 0000 ............
    19:50:09.324229 IP (tos 0x0, ttl 149, id 61676, offset 0, flags [none], proto UDP (17), length 32)
    1.29.191.29.60672 > XXX.XXX.XXX.XXX.62468: [no cksum] UDP, length 0
    0x0000: 9c8e 9908 c704 000b fcd5 9640 0800 4500 ...........@..E.
    0x0010: 0020 f0ec 0000 9511 d5b2 011d bf1d 5511 ..............U.
    0x0020: 49e2 ed00 f404 0008 0000 0000 0000 0000 I...............
    0x0030: 0000 0000 0000 0000 0000 0000 ............
    19:50:09.324425 IP (tos 0x0, ttl 238, id 47359, offset 0, flags [none], proto UDP (17), length 32)
    222.50.29.87.33584 > XXX.XXX.XXX.XXX.39500: [no cksum] UDP, length 0
    0x0000: 9c8e 9908 c704 000b fcd5 9640 0800 4500 ...........@..E.
    0x0010: 0020 b8ff 0000 ee11 7950 de32 1d57 5511 ........yP.2.WU.
    0x0020: 49e2 8330 9a4c 0008 0000 0000 0000 0000 I..0.L..........
    0x0030: 0000 0000 0000 0000 0000 0000 ............
    19:50:09.324471 IP (tos 0x0, ttl 119, id 25422, offset 0, flags [none], proto UDP (17), length 32)
    172.214.143.9.29442 > XXX.XXX.XXX.XXX.27116: [no cksum] UDP, length 0
    0x0000: 9c8e 9908 c704 000b fcd5 9640 0800 4500 ...........@..E.
    0x0010: 0020 634e 0000 7711 05ac acd6 8f09 5511 ..cN..w.......U.
    0x0020: 49e2 7302 69ec 0008 0000 0000 0000 0000 I.s.i...........
    0x0030: 0000 0000 0000 0000 0000 0000 ............

    ReplyDelete
  6. Hi,

    How can I block "." type ANY queries ? I can see them used on my DNS for DDOS ...

    ReplyDelete
  7. It looks like the packets changed in the last few week, the packets I just reviewed started at HEX position )x32, so the string version, all failed since they start at 0x40. If you want I can send the packets to you.

    ReplyDelete
  8. Easiest way:
    iptables -A INPUT -p udp -m string --hex-string "|03697363036f726700|" --algo bm --to 65535 -j DROP

    ReplyDelete
    Replies
    1. It is the easy way, but it probably has a bigger performance impact.

      Delete
    2. You can minimize the impact of the string search by narrowing the search range. In your case, you know exactly how long the string is, and where it starts (offset 40), so:

      iptables -A INPUT -p udp -m string --hex-string "|03697363036f726700|" --algo bm --from 40 --to 49 -j DROP

      Delete
  9. iptables --insert INPUT -p udp --dport 53 -m string --from 40 --to 54 --algo bm --hex-string '|09486363466f72756d73026e6c00|' -j DROP -m comment --comment "DROP DNS Q HccForums.nl"

    ReplyDelete
  10. If you want a script to automate the production of such rules... http://www.bortzmeyer.org/files/generate-netfilter-u32-dns-rule.py

    ReplyDelete
    Replies
    1. I am using a adapted version of this script. Its very useful!

      Delete
    2. Stephane,
      When I run your script I get the following output:

      "0>>22&0x3C@20&0xFFDFDFDF=0x04524950&&0>>22&0x3C@24&0xDFFFDFDF=0x45034e45&&0>>22&0x3C@28&0xDFFFFFFF=0x540000FF"

      Are the characters getting munged on output? ie Is "0>>22" valid data or is pything dumping out garbage because I don't have the right Python version or a module installed? Can you please how the output of the example in your script pls?
      "% python generate-netfilter-u32-rule.py --qname ripe.net --qtype ANY"

      Thanks

      Delete
  11. One thing I have done to further enhance the efficiency of these rules is to create a separate chain for evaluations:

    iptables -N FILTER
    iptables --append FILTER -j RETURN -m comment --comment "Default action: Return"

    Then, for each of the rules I create two: one for the general input chain and one that does more extensive evaluations. Using this method, you can mix and match U32 and STRING matching based on a very short match parameter at the input chain, then further, more aggressive evaluations. For instance, lately there has been a couple of amplification attacks based on TXT records observed on my DNS servers for "admin.brucechao.com" (and "admin.blueorangecare.com"), So I first look for the "TXT" record type at hex offset 0x34, and if found, send it to the FILTER chain for further evaluation:

    iptables --insert INPUT -p udp --dport 53 -m u32 --u32 "0x34=0x000f0001" -j FILTER -m comment --comment "20140312: TXT? admin.brucechao.com"'

    Then add the filter rule:
    iptables --insert FILTER -p udp --dport 53 -m string --hex-string "|05|admin|09|brucechao|03|com|00|" --icase --algo bm --from 40 --to 62 -j DROP -m comment --comment "20140312: TXT admin.brucechao.com"'

    One other tip -- I've started date tagging my filters in the comment section. Lately with the barrage of resource exhaustion attacks, I find it necessary to delete these filters after a while since the domains used only last for a few hours. After adding several hundred such rules, IPTABLES will choke on the extra overhead of rule matching, so it's worth culling these rules after a day or two.

    ReplyDelete
  12. Hey Guys,

    would you please help me to create correct IPTables policy for following case? :

    192.168.1.13.domain > 99.253.113.44.36891: [udp sum ok] 43028| q: ANY? ping.zong.co.ua. 0/0/1 ar: . OPT UDPsize=4096 (44)
    0x0000: 4500 0048 e2db 0000 4011 00eb c0a8 010d
    0x0010: 63fd 712c 0035 901b 0034 f814 a814 8380
    0x0020: 0001 0000 0000 0001 0470 696e 6704 7a6f
    0x0030: 6e67 0263 6f02 7561 0000 ff00 0100 0029
    0x0040: 1000 0000 0000 0000


    I have tried something like:

    iptables --insert INPUT -p udp --dport 53 -m u32 --u32 "0x1c=0x75328380 && 0x28=0x0470696e && 0x2c=0x67047a6f && 0x30=0x6e670263" -j DROP -m comment --comment "Drops"

    but apparently it doesn't work..

    My System is Fedora 20 (iptables 1.4.x)

    thank you in advance

    ReplyDelete
  13. Any tool that delivers the iptables or if possible share the script that generates the domain-blacklist.txt

    Thanks

    ReplyDelete