1

I have a customer that is sending a lot of queries for a particular domain on my DNS server. I want block that queries and I found the hex string that is possible to be implemented. That is the string:

iptables -I INPUT 1 -p udp --dport 53 --match string --algo kmp --hex-string '|77 70 61 64 2e 64 6f 6d 61 69 6e 2e 6e 61 6d 65|' -j DROP

-

0     0 DROP       udp  --  *      *       0.0.0.0/0            0.0.0.0/0            udp dpt:53 STRING match  "wpad.domain.name" ALGO name kmp TO 65535

But seems that does not work, because when I query that name against that server, I can see the reply and the counters remain at zero

root@banana:~# dig @5.172.120.59 wpad.domain.name

; <<>> DiG 9.8.4-rpz2+rl005.12-P1 <<>> @5.172.120.59 wpad.domain.name
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: SERVFAIL, id: 29891
;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 0

;; QUESTION SECTION:
;wpad.domain.name.              IN      A

;; Query time: 136 msec
;; SERVER: 5.172.120.59#53(5.172.120.59)
;; WHEN: Thu Nov 26 22:55:30 2015
;; MSG SIZE  rcvd: 34

Can you spot some error?

Thanks

3

don't do this

... at least not for this particular DNS request. Why not? Well, first, you're not really making it easier for yourself, your CPU, and you're making it much worse for the client. Nearly every browser on every platform when given a DHCP address uses 'automatic proxy detection'. This auto detection works by looking for a DNS entry for a hostname wpad.subdomain.subdomain.currentdomain.xx proceding down to wpad.currentdomain.xx. Second, the browser requests the OS to get the DNS, and the OS (subject to various TCP / UDP parameters) will keep trying the request to the DNS server until it gets an answer. When you block the input, you prevent your DNS server from giving any answer at all, so the client tries again and again, and again. And by blocking this, you're not somehow "training" the client to stop sending the requests. If it doesn't get an answer, it simply stops trying to use a proxy and uses the direct connection. Meanwhile the user is just waiting there for the browser to try the direct connection. She might be waiting there for 15 seconds before the browser becomes responsive. This mechanism is now built into all Android and iOS devices, so every iPad, AppleTV, Samsung smartphone is taking a long time to connect to the internet. So basically, you're bring a dick.

How to do it (optimally, correctly)

But let's say you're going to block a DNS query based on the QNAME. The QNAME comprises a set of "labels". While we see the labels in dotted notation, in the query packet, each label is prefixed by its length in bytes, all of which are terminated by a NUL. 1

1The OP figured this part out already, but made it sound like it had something to do with the hex-string.

The hex-string, as I confirmed by looking at the source to iptables 1.4.9, since no manual I could find adequately describes its behavior, is of the (quasi BNF) form

HEXSTRING := SUBSTRING [ SUBSTRING ... ]

where each SUBSTRING is one of

'\' CHAR
'|' HEXDIGIT HEXDIGIT [ SPACE ] '|'
CHAR

where CHAR is pretty much anything the input can handler, and HEXDIGIT is what isxdigit() says it is, with the combination converted via sscanf(s,"%x",buf). The escaping character ensures that one can bypass "hex mode" and match the literal string "|". Otherwise, it does nothing special. The trailing | is not strictly necessary if the present SUBSTRING is the last in HEX-STRING.

Next, optimizing the search: the Query section starts no earlier than the 13th byte of the DNS request, which means starting from byte 40 ( 20 (IP) + 8 (UDP) + 13 - 1 ). If you can test that the packet is a Query, which you should, then you don't need to worry about a limit to search for -- just search to the end of the packet. To test if it's a query, test the high 5 bits of of byte 31 in the packet. You might also want to take into account the slight difference the IP header, which in rare conditions is 24+ bytes instead of 20, making the byte to test 35 (or 39).

Optimize further by using the bm (Boyer-Moore) algorithm. There's nothing wrong with the kmp, but in general, Boyer-Moore is faster and uses fewer resources.

Tying it all together, we should have something like:

-A INPUT -s 5.172.121.170/32 -p udp -m udp --dport 53 \
-m u32 --u32 "28 & 0xF8 = 0" \
-m string --algo bm --from 40 \
--hex-string "|04|wpad|08|mydomain|03|isa|04|dick" 

The --u32 part says "grab 4 bytes starting from the 28th (0-indexed) byte of the packet... that is, bytes 28, 29, 30, 31, and mask the 32-bit value with 0x000000F8 ... that is only look at the high five bits of byte 31 ... and if it's 0, match." The --from part, as mentioned, starts the string-search in the "question" section of the packet. This should avoid false positives.

Tested on RHEL 6.5 iptables 1.4.7.

To be pedantic, make another rule which tests the IP header field byte 0 for the header length and and matches the DNS query starting at byte 32.

0

Found by myself. the hex string was incorrect. That is the right one.

Also, in easier format --hex-string "|04|wpad|06|domanin|04|name|" where the numeric values mean the length of the word

-A INPUT -s 5.172.121.170/32 -p udp -m udp --dport 53 -m string \
--hex-string "|047770616406646f6d61696e046e616d65|" --algo kmp \
--to 65535 -m comment --comment "wpad.domain.name" -j DROP
  • Alright. My point is though, by not responding to wpad.domain.xx, you're not making it any easier on anyone. The browsing client is not going to stop sending those requests just because your server stops responding to them. And the only reason your DNS server is responding to them is because you have a DHCP server which says to use your DNS server. When you block the packets, the client doesn't get the response, so it tries again. And again. And again. Either reject them, or simply let the DNS server respond as usual. It's the same bandwidth, and about the same CPU power. – Otheus Nov 27 '15 at 18:20
  • But thanks for posting the follow-up. – Otheus Nov 27 '15 at 18:21
  • 1
    Also, use the bm algorithm instead of the kmp one. The Boyer-Moore algorithm is the boss of text search algorithms, though kmp is definitely better than 'brute force'. – Otheus Nov 27 '15 at 18:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.