16

I have iptables blocking all UDP traffic at the moment, however I want to allow only certain DNS queries to get through.

Let's use google.com as an example.

I am trying to use string matching to find the domain name in the request, and allow it. This is what I came up with.

iptables -A OUTPUT -o eth0 -p udp --sport 53 -m string --string "google.com" --algo bm -j ACCEPT

I have also tried --dport 53 instead of --sport. No dice.

If anyone knows how this can be done or see's where I went wrong your help is appreciated!

Thanks, Jarred

derekerdmann
  • 17,696
  • 11
  • 76
  • 110
Jarred Kenny
  • 33
  • 1
  • 1
  • 7

2 Answers2

27

I know this is a bit late, but since you haven't closed the question...

If you look at the contents of the DNS request packet in wireshark or similar you will find that the dot character is not used. Each part of the domain name is a counted string, so the actual bytes of the request for google.com will be:

06 67 6f 6f 67 6c 65 03 63 6f 6d

The first byte (06) is the length of google, followed by the 6 ASCII characters, then a count byte (03) for the length of com followed by... you get the idea.

To match this in iptables, use the following:

iptables -A OUTPUT -o eth0 -p udp --port 53 -m string --hex-string "|06|google|03|com" -algo bm -j ACCEPT

The --hex-string parameter parses the provided string looking for hex values delimited by pairs of vertical bars. Anything outside of the vertical bars is interpreted as ASCII text.

If you list the OUTPUT table after adding the entry you'll find something along the lines of:

ACCEPT     udp  --  anywhere             anywhere             udp dpt:domain STRING match  "|06676f6f676c6503636f6d|" ALGO name bm TO 65535

You can tune the rule slightly - and speed it up - by restricting the search range using the --from and --to parameters.

Corey
  • 15,524
  • 2
  • 35
  • 68
  • Awesome man, You saved a lot of time. Though It took time to verify this. – Kishor Pawar Aug 30 '16 at 12:57
  • why algo type is bm (Boyer-Moore) ? – Gaurav Kansal Sep 30 '17 at 14:20
  • 1
    @GauravKansal From memory it was the one used in all the examples I could find at the time I was doing this. The other option is Knuth-Morris-Pratt, which isn't necessarily faster than Boyer-Moore in this context, and may have slightly higher overheads. I can't confirm this as I haven't done any testing. – Corey Oct 03 '17 at 00:19
  • 1
    This answer is great and I adopted it happily! However, it should be pointed out, that technically it is possible to craft DNS queries with multiple questions, allowing someone to resolve arbitrary hosts as long as the allowed host is also in the questions section of the DNS query. Support for DNS queries with multiple questions is rare, though. – schlimmchen Nov 07 '18 at 13:50
  • @schlimmchen True, but I think the actual bytes would be present somewhere in the request packet, so worst case is it rejects the entire query if any of the requested DNS entries match the pattern. I've never really seen multi-DNS in action so I can't figure how much impact it might have. – Corey Nov 08 '18 at 00:23
  • 2
    @Corey If the rule would *reject* requests for a certain domain, yes. But the original task (and mine) was to *only allow* certain hosts. If you then craft a multi-DNS query with at least one allowed host in the questions section, you will be able to query any host by adding it to the questions section. – schlimmchen Nov 08 '18 at 13:43
  • @schlimmchen You're right. So it would need to be paired with some rejection of multi-part queries if that could be managed. I'd need to study the packets. I'm just happy that 5 years on the answer is still *mostly* correct :) – Corey Nov 08 '18 at 22:52
  • @Corey My solution was to severely limit the length of packets matching the ACCEPT rule, such that multi-question queries, due to their additional length, would be dropped even if they included the allowed hostname. – schlimmchen Nov 09 '18 at 09:24
4

I found that is not reliable with strings with dots.

This will work:

iptables -A OUTPUT -o eth0 -p udp --port 53 -m string --string google --algo bm -j ACCEPT
Saverio Proto
  • 1,085
  • 9
  • 20