Amplification attacks are frustrating, whether you are the target of the flood or you find your system has been taking part in one.
The concept is simple — there are two core items:
- You send a small string to a UDP-based service and you get a large response back.
- You spoof your IP address so that the response goes somewhere else.
By utilizing both items, you can send a very small amount of traffic to a location and have it send a very large amount of traffic to your target. If you find enough services that are “vulnerable”, you can send a comparatively small amount of data and have those services send a lot of data back out to your target in an effort to flood their connection.
Common vectors for this attack are DNS, NTP, SNMP and others. See the below section of a tcpdump, we sent a small packet to a DNS server (as is common) and we got back 163 bytes. Most queries are around 64 bytes, so by sending 64 bytes we got a response of 163 bytes, that’s a response about 2.5 times larger than the request.
20:34:31.960523 IP (tos 0x0, ttl 51, id 10115, offset 0, flags [none], proto UDP (17), length 163) google-public-dns-a.google.com.domain > xxxxxxxxxxxxxxxxx.35760: [udp sum ok] 42237 q: A? google.com. 6/0/1 google.com. [4m59s] A 18.104.22.168, google.com. [4m59s] A 22.214.171.124, google.com. [4m59s] A 126.96.36.199, google.com. [4m59s] A 188.8.131.52, google.com. [4m59s] A 184.108.40.206, google.com. [4m59s] A 220.127.116.11 ar: . OPT UDPsize=512 (135)
That’s a small DNS response, if the right record is found you could easily get a 500% increase in the response compared with the request. Now, let’s be clear – this was a request to a recursive nameserver, but the results are exactly the same if you use an authoritative nameserver.
NTP servers are most prone to attack when they aren’t protected against the monlist command — in most cases they’ll respond with packets about the same size as the request, but with the monlist command they can return a very large response, many times the size of the request.
SNMP is probably one of the highest potential returns with the lowest risk — unless the community string is set to a default like “public” or “rocommunity.”
So we know the problem, what is the solution? Depending on the service there are many ways to tackle the problem. The first solution is to recognize which of your services have what potential to be a vector for attack. Running NTP? DNS? Other UDP-based services? Make sure you know what requests can be made and what the response to those might be. If you’re running NTP, you can disable the monlist command, with SNMP you can keep the community string complex.
There is also a more generic way to handle this, and that’s by using the firewall. On Linux iptables will allow you to limit the number of packets per second using the limit module:
iptables -A INPUT -p udp --dport 53 -m limit --limit 10/s -j ACCEPT iptables -A INPUT -p udp --dport 53 -j DROP
This will allow ten requests per second to the DNS port (UDP 53), anything beyond that will be dropped. This is a set of rules that will need to be tweaked for production on your server!
Another option to complement this would be to look at other iptables modules that will allow to limit per-IP, so maybe you want to allow 100 requests per second overall, but any given IP can only make 10 requests per second.
The third firewall-related solution is a tool such as fail2ban which can read logs from your daemon and block users who you consider to be abusive for a given period. An IP makes more than 3600 requests in an hour? Blocked for an hour. This is a little more dangerous as it means an attacker could use your server to spoof-attack one of the major DNS resolvers like Google,which you then block, and then Google’s public nameservers are unable to resolve any domains on your servers.
As I told someone earlier today, fixing the security holes in your services is important, to be sure. But it shouldn’t be the only solution.