Hello everyone I am trying to figure out the way to drop a domain name DNS resolution before it hits application server. I do not want to do domain to IP mapping and block destination IP (and source IP blocking is also not an option). I can see that a string like this: iptables -A INPUT -p udp -m udp --dport 53 -m string --string "domain" --algo kmp --to 65535 -j DROP this can block "domain" which includes domain.com/domain.net and everything in that pattern. I tried using hexadecimal string for value like domaincom (hexa equivalent) and firewall doesn't pics that at all. The only other option which I found to be working nicely is u32 based string as something suggested on DNS amplification blog post here - http://dnsamplificationattacks.blogspot.in/2013/12/domain-dnsamplificationat... A string like this as suggested on above link works exactly for that domain iptables --insert INPUT -p udp --dport 53 -m u32 --u32 "0x28&0xFFDFDFDF=0x17444e53 && 0x2c&0xDFDFDFDF=0x414d504c && 0x30&0xDFDFDFDF=0x49464943 && 0x34&0xDFDFDFDF=0x4154494f && 0x38&0xDFDFDFDF=0x4e415454 && 0x3c&0xDFDFDFDF=0x41434b53 && 0x40&0xFFDFDFFF=0x02434300" -j DROP -m comment --comment "DROP DNS Q dnsamplificationattacks.cc" but here I am not sure how to create such string out and script them for automation. Can someone suggest a way out for this within IPTables or may be some other open source firewall? Thanks. -- Anurag Bhatia anuragbhatia.com Linkedin <http://in.linkedin.com/in/anuragbhatia21> | Twitter<https://twitter.com/anurag_bhatia> Skype: anuragbhatia.com PGP Key Fingerprint: 3115 677D 2E94 B696 651B 870C C06D D524 245E 58E2
This is going to be tricky to do, as DNS packets don't necessarily contain entire query values or FQDNs as complete strings due to packet label compression (remember, original DNS only has 512 bytes to work with). You can use those u32 module matches to find some known-bad packets if they're sufficiently unique, but it simply lacks enough logic to fully parse DNS queries. Here's an interesting example to visualize what's happening: http://dnsamplificationattacks.blogspot.com/p/iptables-block-list.html One quick thing that would work would be to match a single label (e.g. "google", but not "google.com"), but this will end up blocking any frames with that substring in it (e.g. you want to block "evil.com", but this also blocks "evil.example.com"). If you find yourself needing to parse and block DNS packets based on their content in a more flexible way, I would look into either making an iptables module that does the DNS parsing ( http://inai.de/documents/Netfilter_Modules.pdf), or using a userspace library like with NFQUEUE (e.g. https://pypi.python.org/pypi/NetfilterQueue) or l7-filter (http://l7-filter.sourceforge.net/). Best of luck and happy hacking! Cheers, jof On Sat, Feb 8, 2014 at 12:08 AM, Anurag Bhatia <me@anuragbhatia.com> wrote:
Hello everyone
I am trying to figure out the way to drop a domain name DNS resolution before it hits application server. I do not want to do domain to IP mapping and block destination IP (and source IP blocking is also not an option).
I can see that a string like this:
iptables -A INPUT -p udp -m udp --dport 53 -m string --string "domain" --algo kmp --to 65535 -j DROP
this can block "domain" which includes domain.com/domain.net and everything in that pattern. I tried using hexadecimal string for value like domaincom (hexa equivalent) and firewall doesn't pics that at all.
The only other option which I found to be working nicely is u32 based string as something suggested on DNS amplification blog post here -
http://dnsamplificationattacks.blogspot.in/2013/12/domain-dnsamplificationat...
A string like this as suggested on above link works exactly for that domain
iptables --insert INPUT -p udp --dport 53 -m u32 --u32 "0x28&0xFFDFDFDF=0x17444e53 && 0x2c&0xDFDFDFDF=0x414d504c && 0x30&0xDFDFDFDF=0x49464943 && 0x34&0xDFDFDFDF=0x4154494f && 0x38&0xDFDFDFDF=0x4e415454 && 0x3c&0xDFDFDFDF=0x41434b53 && 0x40&0xFFDFDFFF=0x02434300" -j DROP -m comment --comment "DROP DNS Q dnsamplificationattacks.cc"
but here I am not sure how to create such string out and script them for automation.
Can someone suggest a way out for this within IPTables or may be some other open source firewall?
Thanks.
--
Anurag Bhatia anuragbhatia.com
Linkedin <http://in.linkedin.com/in/anuragbhatia21> | Twitter<https://twitter.com/anurag_bhatia> Skype: anuragbhatia.com
PGP Key Fingerprint: 3115 677D 2E94 B696 651B 870C C06D D524 245E 58E2
On Sat, Feb 8, 2014 at 3:34 AM, Jonathan Lassoff <jof@thejof.com> wrote:
This is going to be tricky to do, as DNS packets don't necessarily contain entire query values or FQDNs as complete strings due to packet label compression (remember, original DNS only has 512 bytes to work with).
Howdy, The DNS query essentially always contains the full string in a sequence. It doesn't *have* to per the protocol but you'll be hard pressed to find a real-world example where it doesn't. The catch is, the dots aren't encoded. The components of the name being queried are separated by a byte indicating the length of the next piece. So, instead of www.google.com the query packet contains www 0x06 google 0x03 com. You can implement this with --hex-string instead of --string but you'll have to convert the entire thing to hex first Regards, Bill Herrin -- William D. Herrin ................ herrin@dirtside.com bill@herrin.us 3005 Crane Dr. ...................... Web: <http://bill.herrin.us/> Falls Church, VA 22042-3004
On 02/08/2014 09:40 AM, William Herrin wrote:
On Sat, Feb 8, 2014 at 3:34 AM, Jonathan Lassoff <jof@thejof.com> wrote:
This is going to be tricky to do, as DNS packets don't necessarily contain entire query values or FQDNs as complete strings due to packet label compression (remember, original DNS only has 512 bytes to work with).
Howdy,
The DNS query essentially always contains the full string in a sequence. It doesn't *have* to per the protocol but you'll be hard pressed to find a real-world example where it doesn't.
The catch is, the dots aren't encoded. The components of the name being queried are separated by a byte indicating the length of the next piece. So, instead of www.google.com the query packet contains www 0x06 google 0x03 com.
For the completeness of the archives, the length of the first token is also encoded and final terminator is 0. 0x03 www 0x06 google 0x03 com 0x00 -DMM
You can implement this with --hex-string instead of --string but you'll have to convert the entire thing to hex first
Regards, Bill Herrin
Thanks everyone for useful responses. I almost used script mentioned by Stephane (http://www.bortzmeyer.org/files/generate-netfilter-u32-dns-rule.py) but I realized that for a rule for "domain.com" it blocks "domain.com" only and their was no easy way out to block subdomains as well. In last few days after my post, I noticed traffic in pattern of sub1.sub2.domain.com where sub1 and sub2 are randomly generated strings. I tried creating .domain.com and other rules in u32 but didn't help for subdomain. Also since there were very high number of subdomains (but limited domains), possibility to generate u32 rule for each sub didn't made sense. I re-visited Hexadecimal string with 03 and 00 for dot was actually able to help. RPZ and some other option I am still exploring. Thanks. On Sat, Feb 8, 2014 at 11:17 PM, David Miller <dmiller@tiggee.com> wrote:
On 02/08/2014 09:40 AM, William Herrin wrote:
On Sat, Feb 8, 2014 at 3:34 AM, Jonathan Lassoff <jof@thejof.com> wrote:
This is going to be tricky to do, as DNS packets don't necessarily contain entire query values or FQDNs as complete strings due to packet label compression (remember, original DNS only has 512 bytes to work with).
Howdy,
The DNS query essentially always contains the full string in a sequence. It doesn't *have* to per the protocol but you'll be hard pressed to find a real-world example where it doesn't.
The catch is, the dots aren't encoded. The components of the name being queried are separated by a byte indicating the length of the next piece. So, instead of www.google.com the query packet contains www 0x06 google 0x03 com.
For the completeness of the archives, the length of the first token is also encoded and final terminator is 0.
0x03 www 0x06 google 0x03 com 0x00
-DMM
You can implement this with --hex-string instead of --string but you'll have to convert the entire thing to hex first
Regards, Bill Herrin
-- Anurag Bhatia anuragbhatia.com Linkedin <http://in.linkedin.com/in/anuragbhatia21> | Twitter<https://twitter.com/anurag_bhatia> Skype: anuragbhatia.com PGP Key Fingerprint: 3115 677D 2E94 B696 651B 870C C06D D524 245E 58E2
I implemented this easily some time ago due to a situation where product development was unable or unwilling to disable open resolvers. i'll post my ruleset then describe it then describe it since it contains multiple functions. Chain INPUT (policy ACCEPT 68M packets, 4377M bytes) pkts bytes target prot opt in out source destination 22M 1423M ACCEPT all -- lo * 0.0.0.0/0 0.0.0.0/0 0 0 REJECT all -- * * 0.0.0.0/0 0.0.0.0/0 recent: CHECK name: blacklist side: source reject-with icmp-admin-prohibited 34M 2463M find_dnsany udp -- * * 0.0.0.0/0 0.0.0.0/0 udp dpt:53 Chain FORWARD (policy ACCEPT 460M packets, 298G bytes) pkts bytes target prot opt in out source destination 0 0 REJECT all -- * * 0.0.0.0/0 0.0.0.0/0 recent: CHECK name: blacklist side: source reject-with icmp-admin-prohibited 0 0 irc tcp -- * eth0 0.0.0.0/0 0.0.0.0/0 multiport dports 6660:6669,6670 1826M 1144G local_ips all -- * * 0.0.0.0/0 0.0.0.0/0 35387 2569K find_dnsany udp -- * * 0.0.0.0/0 0.0.0.0/0 udp dpt:53 Chain OUTPUT (policy ACCEPT 39M packets, 316G bytes) pkts bytes target prot opt in out source destination 0 0 irc tcp -- * eth0 0.0.0.0/0 0.0.0.0/0 multiport dports 6660:6669,6670 22M 1423M ACCEPT all -- * lo 0.0.0.0/0 0.0.0.0/0 310M 1637G local_ips all -- * * 0.0.0.0/0 0.0.0.0/0 13M 1056M CONNMARK udp -- * * 0.0.0.0/0 0.0.0.0/0 udp dpt:53 owner UID match 25 CONNMARK set 0x35 13M 1056M find_dnsany udp -- * * 0.0.0.0/0 0.0.0.0/0 udp dpt:53 Chain find_dnsany (3 references) pkts bytes target prot opt in out source destination 302K 19M limit_dnsany all -- * * 0.0.0.0/0 0.0.0.0/0 u32 "0x0>>0x16&0x3c@0x8>>0xf&0x1=0x0&&0x0>>0x18&0x1=0x1" STRING match "|0000ff0001|" ALGO name bm FROM 36 TO 70 /* match ANY? queries */ Chain irc (2 references) pkts bytes target prot opt in out source destination 0 0 ULOG all -- * * 0.0.0.0/0 0.0.0.0/0 ULOG copy_range 0 nlgroup 30 queue_threshold 1 0 0 LOG all -- * * 0.0.0.0/0 0.0.0.0/0 LOG flags 8 level 4 prefix "[IRC] " 0 0 REJECT all -- * * 0.0.0.0/0 0.0.0.0/0 reject-with icmp-admin-prohibited Chain limit_dnsany (1 references) pkts bytes target prot opt in out source destination 827 53727 ACCEPT all -- * * 1.2.3.4 0.0.0.0/0 limit: avg 20/min burst 60 0 0 limit_venet all -- * * 1.2.3.4 0.0.0.0/0 4297 302K ACCEPT all -- * * 0.0.0.0/0 0.0.0.0/0 CONNMARK match 0x35 limit: avg 10/min burst 30 22798 1475K ACCEPT all -- * * 0.0.0.0/0 0.0.0.0/0 limit: avg 4/min burst 10 7277 468K LOG all -- * * 0.0.0.0/0 0.0.0.0/0 limit: avg 1/min burst 5 LOG flags 0 level 4 prefix "DNSANY: " 279K 18M DROP all -- * * 0.0.0.0/0 0.0.0.0/0 Chain limit_venet (1 references) pkts bytes target prot opt in out source destination 0 0 LOG all -- * * 0.0.0.0/0 0.0.0.0/0 limit: avg 1/min burst 5 LOG flags 0 level 4 prefix "DNSANYint: " 0 0 REJECT all -- * * 0.0.0.0/0 0.0.0.0/0 reject-with icmp-admin-prohibited Chain local_ips (2 references) pkts bytes target prot opt in out source destination 2136M 2782G RETURN all -- * !eth0 0.0.0.0/0 0.0.0.0/0 /* only check outgoing packets */ 0 0 RETURN all -- * * 0.0.0.0/0 0.0.0.0/0 ADDRTYPE match src-type LOCAL /* accept packet generated from any locally bound IP */ 0 0 RETURN all -- * * 0.0.0.0/0 0.0.0.0/0 recent: CHECK name: local_ips side: source 0 0 ULOG all -- * * 0.0.0.0/0 0.0.0.0/0 ULOG copy_range 0 nlgroup 30 queue_threshold 1 0 0 LOG all -- * * 0.0.0.0/0 0.0.0.0/0 limit: avg 1/min burst 5 /* block non-local IPs from exiting */ LOG flags 8 level 4 prefix "SPOOF: " 0 0 REJECT all -- * * 0.0.0.0/0 0.0.0.0/0 reject-with icmp-admin-prohibited First, INPUT, FORWARD, and OUTPUT are very similar. 1) INPUT accepts all /lo/ traffic as does OUTPUT 2) INPUT and FORWARD auto block any IPs admins put into the //proc/net/ipt_recent/blacklist/ (or //proc/net/xt_recent/blacklist/ depending on kernel version) 3) common IRC port traffic is blocked (insane number of bots use these ports) 4) outgoing IPs are matched to interface IPs. this set of rules are used for when the kernel is too old to employ //proc/sys/net/ipv4/conf/all/rp_filter/ mode against forwarded IPs too (prevents spoofing) 5) OUTPUT tags DNS traffic owned by uid 25 (usually for sendmail, postfix, etc, change accordingly) 6) in order to prevent killing our box with syslog when an attack happens, logging is strictly rate limited now on to the DNS specific stuff 1) each of INPUT, FORWARD, and OUTPUT call /find_dnsany/ to identify our suspect traffic 2) the rule in /find_dnsany/ uses a u32 match rule to first identify DNS traffic that is a query, and a string match to identify the ANY flag, further, we try to make this efficient by limiting our match to the byte range of 36 to 70. we've found that 100% of our DNS amplification attacks request zone data that fits within this. it certainly may be a longer zone but is unlikely. change accordingly 3) once DNS ANY queries have been identified, jump to /limit_dnsany/ 4) /limit_dnsany/ is designed to accept packets until a limit is reached. there are three limits employed: 4.1) customers in a VZ or bound to a specific local IP that may have higher than normal rates of legitimate DNS ANY queries 4.2) our local smtp initiated lookups. qmail is horrible in that it employs DNS ANY (broken design, discussion out of scope) 4.3) all other DNS ANY traffic 5) incoming DNS ANY that is rate limited is DROPped on the floor, rate limited outgoing is REJECted so internal customers get a friendly icmp reject message additional notes: 1) outgoing should also be checked against the blacklist, somehow our QA dropped that rule before disting 2) IRC is checked before local traffic to ensure two infected customers aren't communicating with each other this set of rules reliably filters: 1) incoming and outgoing DNS QUERY floods. incoming is rate limited to 4/min per IP source. forwarded and outgoing has higher limits 2) majority of bot traffic on IRC ports 3) spoofed and blacklisted packets -david On 02/08/2014 03:34 AM, Jonathan Lassoff wrote:
This is going to be tricky to do, as DNS packets don't necessarily contain entire query values or FQDNs as complete strings due to packet label compression (remember, original DNS only has 512 bytes to work with).
You can use those u32 module matches to find some known-bad packets if they're sufficiently unique, but it simply lacks enough logic to fully parse DNS queries. Here's an interesting example to visualize what's happening: http://dnsamplificationattacks.blogspot.com/p/iptables-block-list.html
One quick thing that would work would be to match a single label (e.g. "google", but not "google.com"), but this will end up blocking any frames with that substring in it (e.g. you want to block "evil.com", but this also blocks "evil.example.com").
If you find yourself needing to parse and block DNS packets based on their content in a more flexible way, I would look into either making an iptables module that does the DNS parsing ( http://inai.de/documents/Netfilter_Modules.pdf), or using a userspace library like with NFQUEUE (e.g. https://pypi.python.org/pypi/NetfilterQueue) or l7-filter (http://l7-filter.sourceforge.net/).
Best of luck and happy hacking!
Cheers, jof
On Sat, Feb 8, 2014 at 12:08 AM, Anurag Bhatia <me@anuragbhatia.com> wrote:
Hello everyone
I am trying to figure out the way to drop a domain name DNS resolution before it hits application server. I do not want to do domain to IP mapping and block destination IP (and source IP blocking is also not an option).
I can see that a string like this:
iptables -A INPUT -p udp -m udp --dport 53 -m string --string "domain" --algo kmp --to 65535 -j DROP
this can block "domain" which includes domain.com/domain.net and everything in that pattern. I tried using hexadecimal string for value like domaincom (hexa equivalent) and firewall doesn't pics that at all.
The only other option which I found to be working nicely is u32 based string as something suggested on DNS amplification blog post here -
http://dnsamplificationattacks.blogspot.in/2013/12/domain-dnsamplificationat...
A string like this as suggested on above link works exactly for that domain
iptables --insert INPUT -p udp --dport 53 -m u32 --u32 "0x28&0xFFDFDFDF=0x17444e53 && 0x2c&0xDFDFDFDF=0x414d504c && 0x30&0xDFDFDFDF=0x49464943 && 0x34&0xDFDFDFDF=0x4154494f && 0x38&0xDFDFDFDF=0x4e415454 && 0x3c&0xDFDFDFDF=0x41434b53 && 0x40&0xFFDFDFFF=0x02434300" -j DROP -m comment --comment "DROP DNS Q dnsamplificationattacks.cc"
but here I am not sure how to create such string out and script them for automation.
Can someone suggest a way out for this within IPTables or may be some other open source firewall?
Thanks.
--
Anurag Bhatia anuragbhatia.com
Linkedin <http://in.linkedin.com/in/anuragbhatia21> | Twitter<https://twitter.com/anurag_bhatia> Skype: anuragbhatia.com
PGP Key Fingerprint: 3115 677D 2E94 B696 651B 870C C06D D524 245E 58E2
On Sat, Feb 08, 2014 at 12:34:45AM -0800, Jonathan Lassoff <jof@thejof.com> wrote a message of 88 lines which said:
This is going to be tricky to do, as DNS packets don't necessarily contain entire query values or FQDNs as complete strings due to packet label compression
Apprently, the OP wanted to match the *question* in a *query* and these are never compressed (they could, in theory, but are not).
You can use those u32 module matches to find some known-bad packets if they're sufficiently unique, but it simply lacks enough logic to fully parse DNS queries.
u32's language is not Turing-complete but It is sufficient in the case presented here.
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA256 Have you looked at perhaps using DNS RPZ (Response Policy Zones)? https://dnsrpz.info/ - - ferg On 2/8/2014 12:08 AM, Anurag Bhatia wrote:
Hello everyone
I am trying to figure out the way to drop a domain name DNS resolution before it hits application server. I do not want to do domain to IP mapping and block destination IP (and source IP blocking is also not an option).
I can see that a string like this:
iptables -A INPUT -p udp -m udp --dport 53 -m string --string "domain" --algo kmp --to 65535 -j DROP
this can block "domain" which includes domain.com/domain.net and everything in that pattern. I tried using hexadecimal string for value like domaincom (hexa equivalent) and firewall doesn't pics that at all.
The only other option which I found to be working nicely is u32 based string as something suggested on DNS amplification blog post here - http://dnsamplificationattacks.blogspot.in/2013/12/domain-dnsamplificationat...
A string like this as suggested on above link works exactly for that domain
iptables --insert INPUT -p udp --dport 53 -m u32 --u32 "0x28&0xFFDFDFDF=0x17444e53 && 0x2c&0xDFDFDFDF=0x414d504c && 0x30&0xDFDFDFDF=0x49464943 && 0x34&0xDFDFDFDF=0x4154494f && 0x38&0xDFDFDFDF=0x4e415454 && 0x3c&0xDFDFDFDF=0x41434b53 && 0x40&0xFFDFDFFF=0x02434300" -j DROP -m comment --comment "DROP DNS Q dnsamplificationattacks.cc"
but here I am not sure how to create such string out and script them for automation.
Can someone suggest a way out for this within IPTables or may be some other open source firewall?
Thanks.
- -- Paul Ferguson VP Threat Intelligence, IID PGP Public Key ID: 0x54DC85B2 -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.22 (MingW32) Comment: Using GnuPG with Thunderbird - http://www.enigmail.net/ iF4EAREIAAYFAlL2W5YACgkQKJasdVTchbJ+qAD+NP7VDzOK2m416hCvi0Mm3rq+ WA7kTOGgXWQGuz20F/cA/3YOsrrlYIL0plRPRUW1Qex2zZfhG4Z/pO63zA0u8DBE =AfV6 -----END PGP SIGNATURE-----
You could use RPZ but wouldn't something as simple as putting these two entries in a host files meet the mail? Tom On Feb 8, 2014, at 11:30 AM, Paul Ferguson wrote:
Signed PGP part Have you looked at perhaps using DNS RPZ (Response Policy Zones)?
- ferg
On 2/8/2014 12:08 AM, Anurag Bhatia wrote:
Hello everyone
I am trying to figure out the way to drop a domain name DNS resolution before it hits application server. I do not want to do domain to IP mapping and block destination IP (and source IP blocking is also not an option).
I can see that a string like this:
iptables -A INPUT -p udp -m udp --dport 53 -m string --string "domain" --algo kmp --to 65535 -j DROP
this can block "domain" which includes domain.com/domain.net and everything in that pattern. I tried using hexadecimal string for value like domaincom (hexa equivalent) and firewall doesn't pics that at all.
The only other option which I found to be working nicely is u32 based string as something suggested on DNS amplification blog post here - http://dnsamplificationattacks.blogspot.in/2013/12/domain-dnsamplificationat...
A string like this as suggested on above link works exactly for that domain
iptables --insert INPUT -p udp --dport 53 -m u32 --u32 "0x28&0xFFDFDFDF=0x17444e53 && 0x2c&0xDFDFDFDF=0x414d504c && 0x30&0xDFDFDFDF=0x49464943 && 0x34&0xDFDFDFDF=0x4154494f && 0x38&0xDFDFDFDF=0x4e415454 && 0x3c&0xDFDFDFDF=0x41434b53 && 0x40&0xFFDFDFFF=0x02434300" -j DROP -m comment --comment "DROP DNS Q dnsamplificationattacks.cc"
but here I am not sure how to create such string out and script them for automation.
Can someone suggest a way out for this within IPTables or may be some other open source firewall?
Thanks.
-- Paul Ferguson VP Threat Intelligence, IID PGP Public Key ID: 0x54DC85B2
On Sat, Feb 08, 2014 at 01:38:13PM +0530, Anurag Bhatia <me@anuragbhatia.com> wrote a message of 54 lines which said:
but here I am not sure how to create such string out and script them for automation.
Use this program: http://www.bortzmeyer.org/files/generate-netfilter-u32-dns-rule.py
participants (8)
-
Anurag Bhatia
-
David Ford
-
David Miller
-
Jonathan Lassoff
-
Paul Ferguson
-
Stephane Bortzmeyer
-
TR Shaw
-
William Herrin