Kleiner DDOS-Exkurs…

p(abstract). Morgens, 08:00 in Deutschland. *pieeep* *pieeep* Eine SMS kommt rein, „Das Forum ist down!“. Aufgestanden, geguckt, joh, stimmt, und zwar seit ca. 04:55, wenn ich meinem Icinga glauben darf. munin zeigte mir folgendes Bild der Trauer:

h2. Bestandsaufnahme

Was war los? Zuerst mal _htop_ aufgerufen um zu schauen, ob irgendwelche Prozesse Amok liefen. Die auf diesem Server eingesetzten Software ist „aus Gründen“™ relativ alt und hat die blöde Tendenz, die MySQL in ein Deadlock zu prügeln, und dann geht gar nichts mehr.

Wider Erwarten war die Kiste völlig idle. Keine Apache-Prozesse im _htop_, keine MySQL-Prozesse. Hmmm.[1]

host:~$ ps -aef | grep -c apache2
389
host:~$ 

Ok, das entspricht den konfigurierten MaxClients plus ein bisschen Maintenance, ebenfalls mit dem Wort _apache_ im Namen. D.h., maximale Anzahl an Serverprozessen oben, aber kein einziger zieht Last?

host:~$ netstat -an | grep :80  | grep EST
... grosse Liste ESTABLISHED connections snipped...
host:~$ 

Oha! Hmmm, gibt es bestimmte IPs die besonders viele Connections offen halten?

host:~$ netstat -an | perl -lane '/:80/ && /ESTABLISHED/ && $F[4]=~/([^:]+):/ && print $1' | sort | uniq -c
      147 1.2.3.4
      120 2.3.4.5
      ...
host:~$ 

Ok, 147 offene Connects von einer einzelnen Adresse sind definitiv ungewoehnlich. Ein nslookup ergab den Exit eines Tor-Knotens. Die nachfolgende Adresse lieferte gar keinen Namen. Danach noch weitere Tor-Adressen.

_tcpdump -n host 1.2.3.4_ zeigte keinerlei Traffic. Die Verbindungen waren also vollständig idle.

Das ist die einfachste Möglichkeit, einen Webserver lahm zu legen. Einfach eine große Menge an Verbindungen öffnen und keinerlei Daten über diese austauschen. Ein vollständiger Webserver-Prozess ist pro Verbindung blockiert, und der fehlt dann für sinnvolle Anfragen. Eine große Anzahl an Idle-Verbindungen kann jeder Client offen halten, das kostet keinerlei Last oder Bandbreite.

h2. Gegenmaßnahmen

Im ersten Schritt habe ich erstmal die Verbindungen zu diesen Adressen gekappt. Ich habe in der INPUT-Chain des iptables eine eigene sub-Chain zum Blacklisten von einzelnen Hosts.

host:~$ iptables -A blklist -s 1.2.3.4 -j DROP
host:~$ apache2ctl restart
host:~$ 

Der Restart ist notwendig, damit der Apache die bestehenden offenen Verbindungen dicht macht, da sie sonst erst in den Timeout laufen müssten.

Jetzt habe ich das oben stehende _netstat_-Kommando ein paar mal im Sekundenabstand abgesetzt, und es passierte was ich erwartet hatte. Sofort kamen von einer neuen Adresse wieder mehrere zig Verbindungen um den Webserver umgehend wieder zu blockieren.

Jetzt standen 2 Gegenmaßnahmen an.

# Limitieren der gleichzeitigen offenen Verbindungen von einer einzelnen IP-Adresse mittels _iptables_
# Aktivierung von mod-evasive im Apache

Punkt 1 ist am schnellsten umsetzbar:

h3. iptables

host:~$ iptables -N ddos
host:~$ iptables -A ddos -j LOG --log-prefix "DDOS: "
host:~$ iptables -A ddos -j REJECT --reject-with icmp-host-unknown
host:~$ iptables -I INPUT 3 -p tcp --syn --dport 80 -m connlimit --connlimit-above 8 --connlimit-mask 24 -j ddos
host:~$ 

Damit wird

# eine neue Chain _DDOS_ angelegt, die die Adresse als geblockt protokolliert und…
# …die gesamte C-Klasse des Clients mit _Host unreachable_ blockt
# aus der _INPUT_-Chain wird bei mehr als 8 offenen Verbindungen beim nächsten _syn_-State diese Block-Chain _DDOS_ angesprungen und weitere Verbindungen ausgesperrt.

Auf eine „_TARPIT_“:http://de.wikipedia.org/wiki/Teergrube_%28Informationstechnik%29 habe ich hier bewusst verzichtet, um nicht valide User mit getunten Browsern die besonders viele Verbindungen öffnen, oder z.B. die Telekom-Proxies, langfristig auszusperren.

h3. Apache mod-evasive

Mit „_mod-evasive_“:http://www.zdziarski.com/blog/?page_id=442 hatte ich bisher auch noch keine Erfahrungen ausser dem Wissen, dass es existiert. Hierzu vielen Dank an all die 14-Jährigen, die „Howtos“ auf ihren Websites posten, in denen Sie dieselben Infos posten, wie andere 14-Jährige auf deren Homepages sie ihre Anleitungen selber erst ergoogelt haben. Das führt nämlich dazu, dass man 5 Minuten seines Lebens verschwendet, bis man endlich eine Site findet, die die konfigurationsparameter dann auch tatsächlich mal beschreibt, statt wieder und wieder das Default-Set einfach in den Webserver zu ballern, ohne einen Hinweis (und vermutlich auch eine Ahnung), was der ganze Kram überhaupt bedeutet.

Um anderen diese 5min zu ersparen: Eine Parameterbeschreibung findet sich auf „linode.com“:http://library.linode.com/web-servers/apache/mod-evasive#sph_mod-evasive-configuration-options

As a public service, um dem geneigten User weitere 5min verschwendeter Lebenszeit zu ersparen: „mod-evasive Homepage,“:http://www.zdziarski.com/blog/?page_id=442 denn 14-jährige Howto-Schreiber haben es i.d. Regel auch nicht nötig, auf die Original-Quelle zu verlinken.

Tatsächlich, unabhängig von obigem Rant über 14-jährige ist das Default-Set an Konfigurationsparametern einigermaßen sinnvoll, aber das weiss man eben nicht, bevor man es nicht verstanden hat.

_mod-evasive_ ist noch in der Lage, Mails über ausgesperrte Adressen zu versenden, und externe Kommandos aufzurufen, z.B. um Adressen via iptables direkt beim _syn_ zu blocken. Ich habe von beidem abgesehen.

h3. Ergebnis

Die 1. Maßnahme hat bereits ausgereicht, um den Angriff abzuwehren. Die 2. belasse ich dennoch, einfach um mich nicht auf eine einzelne Technologie zu verlassen.

Bei einem groß angelegten Angriff wären diese Maßnahmen sicher nicht ausreichend gewesen. Bei dem halben Dutzend gleichzeitiger Clients (mit wechselnden Adressen) für diese Mini-DDOS haben sie aber bequem ausgereicht und der Server war wieder erreichbar und performte akzeptabel bis gut.

Es gibt natürlich Vermutungen über die Quelle des Angriffs. Der Angriff korrelliert zeitlich mit ausgesprochenen Sperren am Vortag, ein Beweis lässt sich hier aber nicht finden, aufgrund der Anonymisierung durch Tor und andere Dienste.

Fakt ist: Einen mittelgroßen root-Server kann man auch ohne Ausnutzung von Sicherheitslücken schon mit einer normalen DSL-Leitung relativ platt machen. Im Grunde ist es ein Wunder, dass so viele Jahre ohne Angriff vergangen sind. Ob ich diese Maßnahmen bei mir auch deploye, habe ich noch nicht entschieden. Mein Setup sieht durch den vorgeschalteten „_nginx_“:http://nginx.com etwas anders aus und ist nicht ganz so empfindlich gegenüber vielen offenen Idle-Connections. Aber eine Überlegung ist es sicher wert.

h3. nstop

Als Nebenprodukt ist noch das Script _nstop_ heraus gekommen. Ich habe das Script, dass ich während des Angriffs geschrieben habe, noch etwas aufgebohrt, und es tut bereits was es soll, ist aber noch verbesserungswürdig.

nstop ist ein ganz simples _top_ für offene Netzwerkverbindungen. Es sortiert sie nach Anzahl und bietet zusätzlich eine kill-Möglichkeit.

„Download“:http://www.lamertz.net/stuff/nstop

Vielleicht nützt es ja mal jemandem. Patches welcome.

Mon Jun 25 00:09:26 2012

  ID    Count IP                   Hostname
   0        5 176.9.148.6          behemoth.lamertz.net
   1        3 173.194.35.144       muc03s01-in-f16.1e100.net
   2        2 173.194.70.125       fa-in-f125.1e100.net
   3        1 69.171.227.65        channel-hp-12-01-snc7.tfbnw.net
   4        1 95.100.47.144        Cannot resolv
   5        1 141.101.124.178      Cannot resolv
   6        1 173.192.42.178       173.192.42.178-static.reverse.softlayer.com
   7        1 173.194.35.4         mil01s16-in-f4.1e100.net
   8        1 173.194.70.17        fa-in-f17.1e100.net
   9        1 192.168.0.3          Cannot resolv
  10        1 199.30.80.32         www.sfe.sv4.as53922.stumbleupon.net
  11        1 199.47.217.149       sjc-not10.sjc.dropbox.com
  12        1 199.59.148.139       r-199-59-148-139.twttr.com
  13        1 199.59.149.232       r-199-59-149-232.twttr.com
  14        1 209.85.148.100       fra07s07-in-f100.1e100.net
  15        1 209.85.148.102       fra07s07-in-f102.1e100.net
  16        1 209.85.148.138       fra07s07-in-f138.1e100.net
  17        1 213.199.179.147      Cannot resolv

s (5) - new refresh delay; k - kill connections of ID; q - quit

fn1. Alle Code-Snippets hier entsprechen nicht dem Live-Status sondern sind nachträglich aus dem Kopf zusammen editiert. Hostnamen, IP-Adressen und dergleichen sind anonymisiert.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.