Im März berichtete ich ja schon, wie ich einen DDoS mit Lighttpd und dessen mod_magnet abwehrte, nun nutzte ich die Gelegenheit die selbe Vorgangsweise für Apache und das Modul mod_security2 umzusetzen.
Wie beim letzten großen DDoS, kam auch hier wieder mein Wrapper für den Paketfilter iptables zum Einsatz. Zum Download gibts diesen nun auch direkt bei mir.
Ursächlich ist es immer das selbe: ein Neider hat inhaltlich nichts gegen eine die Webseite aufzuwenden, so behilft man sich eben anderer Methoden. Zum Beispiel eines Botnetzes. Oder SYN-Flood. Oder eben beides zugleich, wenn man das als Aggressor für sinnvoll hält (nein, ist es nicht – hinderte meinen neuen Freund aber nicht). Dem SYN-Flood widmete ich mich bereits ausführlich, so will ich das heute mit herkömmlichen DDoS und herkömmlichen Webserver – Apache – nachholen.
DDoS mit Botnetzen
Gelegentlich ist eine billige flächendeckende Versorgung von Breitbandanschlüssen unter Privatkunden eine Qual. Zum Beispiel, wenn man am anderen Ende der Leitung sitzt und für Webserver verantwortlich ist. Wenn es nach mir ginge, kann man gerne wieder zu den 16,6K-Modemanschlüssen zurück kehren, solange ich selbstverständlich davon ausgenommen bin.
Mitunter am meisten Arbeit, natürlich bar jeder regulären Arbeitszeit, machst nämlich du und du und du. Vielleicht auch du. Das Problem ist nämlich so trivial wie effizient: Unter den vielen unnützen Helferlein und Programmen, die du auf deinem Windows installiert hast, ist eines dabei, das mehr als den vordergründigen Zweck erfüllt. Neben dem dämlichen Spaßprogramm enthält es nämlich auch Schadroutinen, mit denen sich dein Rechner mitsamt Internetanschluss missbrauchen lässt und so einen beliebigen Webserver unter Beschuss nehmen kann. Wird das von genügend Anschlüssen parallel ausgeführt stehen die Chancen gut, den Zweck zu erfüllen, eine Webseite für reguläre Besucher lahm zu legen.
Hat man genügend entführte Desktop-Rechner im Repertoire, kann man diese instruieren einen Angriff auf eine Webseite zu starten. Dieses Botnetz nimmt dann auch schnell seine Arbeit auf und erzielt damit sehr gute Ergebnisse. Auch die allergrößten Webseiten haben kaum eine Chance, ist der Angriff stark genug. Auch meine Lösung ist keinesfalls ein Allheilmittel, kann aber je nach Angriffsart und -stärke sehr gute Ergebnisse erzielen. Die Herausforderung besteht für den Administrator der angegriffenen Seite die guten von den bösen Anfragen zu unterscheiden. Ziel ist es, für die einen erreichbar zu sein, für die anderen hingegen nicht um die Erreichbarkeit der Seite für reguläre Besucher zu gewährleisten.
Die meisten stumpf ausgeführten Angriffe unterliegen einem Muster, denn sie werden schließlich flächendeckend von den selben Drohnen im Botnetz durchgeführt. Diese haben dann alle ein oder mehrere gemeinsame Merkmale, anhand derer sich diese von regulären Besuchern unterscheiden. Diese haben nämlich in der Regel eine große Entropie, so finden sich in den Logs der Webserver viele verschiedene Browser, viele verschiedene aufgerufene Seiten und vielerlei Auswüchse in verwendeten und gesendeten Headern. Im konkreten Fall, war das Muster trivial, ein Blick in das Fehler-Log des Webservers liefert darüber bereits Auskunft. So scrollte folgende Log-Meldung 4873302 mal durchs Log:
[Thu Sep 18 17:56:07 2008] [error] [client 195.252.79.115] Invalid URI in request GET 86715 HTTP/1.0 [Thu Sep 18 17:56:07 2008] [error] [client 119.154.43.129] Invalid URI in request GET 394887 HTTP/1.0 [Thu Sep 18 17:56:07 2008] [error] [client 41.221.19.224] Invalid URI in request GET 577414 HTTP/1.0 [Thu Sep 18 17:56:07 2008] [error] [client 213.100.149.160] Invalid URI in request GET 429128 HTTP/1.0 [Thu Sep 18 17:56:07 2008] [error] [client 80.78.74.193] Invalid URI in request GET 856214 HTTP/1.0 [Thu Sep 18 17:56:07 2008] [error] [client 41.231.3.254] Invalid URI in request GET 109903 HTTP/1.0 [Thu Sep 18 17:56:07 2008] [error] [client 83.9.214.72] Invalid URI in request GET 149462 HTTP/1.0 [Thu Sep 18 17:56:07 2008] [error] [client 85.232.206.168] Invalid URI in request GET 396126 HTTP/1.0
Einer der ersten Schritte ist es, einen Sniffer anzuwerfen und die Pakete zu analysieren. Ich bevorzuge dafür den grafischen Analysator wireshark (früher “Ethereal”). Da es auf einem Server sinnigerweise keine grafische Oberfläche gibt, ist es einfacher ein paar Pakete zu speichern und diese lokal auf dem Desktop zu analysieren:
root@hostname# tshark -w dump Running as user "root" and group "root". This could be dangerous. Capturing on eth0 154839
Ein paar Tausend mitgesniffte Pakete (wie im Screenshot ersichtlich) machen den Ablauf ersichtlich.
Klientanfrage:
GET 360088 HTTP/1.0 Accept: */*, text/html
Antwort des Browsers:
HTTP/1.1 400 Bad Request Content-Length: 226 Connection: close Content-Type: text/html; charset=iso-8859-1
Die Drohnen machen sich also nicht einmal die Mühe eine gültige HTTP-Anfrage an den Server zu stellen, sondern fragen eine sinnlose Zahlenkombination mit dem heute ungewöhnlichen HTTP 1.0 Protokoll an. Das erfüllt natürlich genauso den Zweck, ist aber für einen geübteren Administrator einfach zu erkennen. Es geht also darum, Klienten die HTTP 1.0 benutzen und dabei eine zufällige und sinnlose sechsstellige Zahlenkombination an den Webserver zu schicken abzufangen und zu sperren.
mod_security
Anders als mod_magnet für Lighttpd, was eine eher kreativere Umsetzung benötigt, ist mod_security genau zu diesem Zweck geschaffen und nennt sich selbst “Web Application Firewall”. So unterstützt es von Haus aus Profiling und Filterung von verdächtigen Aktivitäten, die dabei helfen können einer Reihe von sicherheitsrelevanten Problemen wie Code Injection, XSS und eben auch DDoS zu begegnen. Dazu gibt es bereits eine ganze Reihe von fertigen mitgelieferten Profilen und Filtern, die verwendet werden können und die Sicherheit des Webservers pauschal zu erhöhen. Einem konkreten Angriff kann man aber auch gezielt begegnen und konkrete Abwehrmaßnahmen ergreifen.
Im vorliegenden Fall ist das Ziel eben jene oben beschriebenen Muster zu filtern, die Verbindung unmittelbar abzubrechen und die IP auf Ebene des Paketfilters des Betriebssystemes zu sperren. Zunächst aber zur Installation von mod_security2 unter Debian/Ubuntu. Für Debian gibt es inoffizielle Pakete unter dieser Adresse, auf beiden Distributionen ist es aber auch keine Schwierigkeit, sich das Modul selbst zu bauen:
root@hostname# apt-get install apache2-prefork-dev libxml2 libxml2-dev
[..]
Setting up apache2-prefork-dev (2.2.8-1ubuntu0.3) ...
Processing triggers for libc6 ...
ldconfig deferred processing now taking place
root@hostname# a2enmod unique_id
Module unique_id installed; run /etc/init.d/apache2 force-reload to enable.
root@hostname#:~# wget 'http://www.modsecurity.org/download/modsecurity-apache_2.5.6.tar.gz'
--15:11:54-- http://www.modsecurity.org/download/modsecurity-apache_2.5.6.tar.gz
=> `modsecurity-apache_2.5.6.tar.gz'
[..]
15:11:55 (2.79 MB/s) - `modsecurity-apache_2.5.6.tar.gz' saved [1079094/1079094]
root@hostname#:~# tar zxf modsecurity-apache_2.5.6.tar.gz
root@hostname#:~# cd modsecurity-apache_2.5.6
root@hostname#:~/modsecurity-apache_2.5.6# cd apache2/
root@hostname#:~/modsecurity-apache_2.5.6/apache2# ./configure
[..]
root@hostname#:~/modsecurity-apache_2.5.6/apache2# make
[..]
root@hostname#:~/modsecurity-apache_2.5.6/apache2# make install
[..]
cp .libs/mod_security2.so /usr/lib/apache2/modules/mod_security2.so
[..]Mein eigenentwickelter Wrapper ist auch schmerzlos, sofern eine gängige Entwicklungsumgebung vorhanden ist (siehe auch Download-Link am Textende):
root@hostname# wget 'http://daemonkeeper.net/download/2/'
--15:18:59-- http://daemonkeeper.net/download/2/
=> `wrapper.c'
[..]
15:18:59 (8.30 MB/s) - `wrapper.c' saved [13917/13917]
root@hostname:~# gcc -o wrapper wrapper.c
root@hostname:~# mv wrapper /etc/apache2/ && chown root:root /etc/apache2/wrapper && chmod 7555 /etc/apache2/wrapperDer wesentlich anspruchsvollere Teil ist es aber, dieses Modul zu konfigurieren. Mit dem Laden vom Modul allein ist es nämlich nicht getan, die Konfiguration benötigt mehr Zeit. Neben allgemein gültigen Direktiven die sich mit Logging und Aktivierung von Features befassen, spielt es eine zentrale Rolle einen passenden Filter zu finden, der den Anforderungen genügt. Für meinen Zweck tut dies folgende Konfiguration:
root@hostname:~# cat /etc/apache2/mods-enabled/mod_security.load LoadModule security2_module /usr/lib/apache2/modules/mod_security2.so # Basic configuration options SecRuleEngine On SecRequestBodyAccess On SecResponseBodyAccess Off SecUploadKeepFiles Off # Debug log SecDebugLog /var/log/apache2/modsec_debug.log SecDebugLogLevel 1 SecAuditEngine RelevantOnly SecAuditLogParts ABCFHZ SecAuditLogRelevantStatus ^(?:5|4\d[^4]) SecAuditLog /var/log/apache2/audit.log SecRuleEngine On SecRule REQUEST_LINE "get\s*\d+\s*http/1\.0$" "phase:1,t:lowercase,msg:'DDos match',drop,auditlog,exec:'/etc/apache2/wrapper'"
Eine zentrale Rolle nimmt dabei die Direktive “SecRule” ein, die eine neue Regel für mod_security definiert. Diese besteht aus drei Teilen, eine HTTP-Status-Variable die untersucht werden soll, das Muster wonach gesucht werden soll, sowie Optionen und Aktionen die bei zutreffen ergriffen werden sollen. Wichtige Optionen im Zusammenhang zu DDoS sind mitunter folgende:
- auditlog – Den Vorfall im auditlog vermerken
- deny – Den Zugriff verweigern (HTTP 403 Statuscode)
- drop – Die Verbindung unmittelbar abbrechen (TCP-FIN)
- exec – Ein Script und/oder Programm ausführen
- deprecatevar – Eine Variable dekrementieren, das ist zum Beispiel nützlich um lediglich eine gewisse Anzahl an Zugriffen zu erlauben
- redirect – Eine Verbindung umleiten, um einen Angreifer zum Beispiel in einen Honey- oder Tarpit umzuleiten
Eine vollständige Liste gibt es natürlich in der zugehörigen Dokumentation zu mod_security2. Die verfügbaren Variablen richten sich nach dem CGI-Standard (REMOTE_ADDR, REQUEST_URI, QUERY_STRING und so weiter) und sind ebenso dokumentiert. Immerhin schon ein Fortschritt zum üblichen Lighttpd-Modul …
Das Log, das aus Performancegründen deaktiviert werden sollte, sobald alle Muster zufriedenstellend funktionieren, gibt Aufschluss über etwaige Erfolge:
[18/Sep/2008:20:06:50 +0200] [hostname/sid#7fec20][rid#ae58a8][480100]\ [1] Access denied with connection close (phase 1). Pattern match "get\s*\d+\s*http/1\.0$" at REQUEST_LINE. \ [file "/etc/apache2/mods-enabled/mod_security.load"] [line "21"] [msg "DDos match"]
Ebenso im Syslog durch den von mir programmierten Wrapper:
Sep 18 23:25:55 lupus mod_security[9983]: Blacklisting IP 88.71.97.231 Sep 18 23:26:03 lupus mod_security[9998]: Blacklisting IP 84.10.25.116 Sep 18 23:26:03 lupus mod_security[10002]: Got positive match from IP 84.10.25.116
Der Wrapper selbst ist ein kleines Hilfsprogramm zu iptables, der die betreffende IP automatisch blockt, die Konfiguration ist selbsterklärend, im Quellcode finden sich zwei Systemkommandos die zum sperren bzw. entsperren der IP verwendet werden, zum Beispiel wie folgt:
/* DROP/REJECT rule in your netfilter. * %s is replaced by the clients IP address */ #define CMD "/sbin/iptables -A INPUT -s %s -j posmatch" /* Removess a client from your netfilters chain * %s is replaced by the clients IP address */ #define UNCMD "/sbin/iptables -D INPUT -s %s -j posmatch"
Dies verweist eine zu bannende IP in eine zuvor erstellte Queue, die die Verbindung zum Server einfach verwirft. Das bedarf einer zuvor erstellen Filterregel, etwa der folgenden:
/sbin/iptables -N posmatch /sbin/iptables -A posmatch -j DROP
wrapper.c (13.6 KiB, 1,782 hits)
















15 comments
No ping yet
defunct says:
November 28, 2008 at 10:58 (UTC 1)
Die Idee ist Toll alles supi aber seit einbindung der ddos abwehr mit mod_security2 und dem wrapper bekomme ich leider häßliche defuncts :(
www-data 13934 0.0 0.0 0 0 ? Z Nov27 0:00 [sh]
www-data 27322 0.0 0.0 0 0 ? Z Nov27 0:00 [sh]
www-data 6397 0.0 0.0 0 0 ? Z Nov27 0:00 [sh]
www-data 25617 0.0 0.0 0 0 ? Z 08:04 0:00 [sh]
Grüße
Robert
Arno says:
November 28, 2008 at 12:17 (UTC 1)
Wie sieht denn deine “SecRule” aus? Da der Prozess www-data gehört, kann der, richtig konfiguriert, nicht vom wrapper stammen. Es muss daher der Apache sein, der hier Prozesse spawnt.
Bleibt herauszufinden wo und wieso.
Eduard says:
December 16, 2008 at 18:03 (UTC 1)
Hi,
danke für das Howto.
Eine Frage habe ich aber noch:
Ich habe es jetzt so wie oben beschrieben durchgeführt, also die /etc/apache2/mods-enabled/mod_security.load inklusive Inhalt erstellt.
Bedarf es nicht noch nach einer “.conf” oder kann die .load sozusagen auch direkt als Config genutzt werden ?
Funktioniert deine Lösung genau wie oben beschrieben, oder müsste ich noch Konfigurationen erstellen ?
Danke dir.
Arno says:
December 17, 2008 at 12:50 (UTC 1)
Du kannst entweder die Konfiguration direkt in die .load-Datei packen oder in eine zugehörige .conf-Datei. Wie du willst, hier macht das keinen Unterschied.
Funktionieren tut dies schon genau so, aber nur für mein spezifisches Problem. Allgemein wirst du nicht darum herum kommen eine für dich gültige SecRule zu bauen, da man natürlich nicht allgemein jeden bösartigen Angriff filtern kann.
Internetagentur says:
February 23, 2009 at 22:46 (UTC 1)
Wie kann ich mit mod_security für ein bestimmtes Verzeichnis eine einzige IP aussperren? Geht das überhaupt?
Arno says:
February 24, 2009 at 00:26 (UTC 1)
Sicher, zum Beispiel so:
in einem entsprechenden <directory> Abschnitt.
Einfacher ist das aber vermutlich mit mod_rewrite in einer im Verzeichnis abgelegten .htaccess-Datei:
Max says:
March 18, 2009 at 02:24 (UTC 1)
Erstmal vielen Dank für die ausführliche Anleitung! Bei mir klappt das alles ganz gut, bis auf das weiterleiten der “Bad IPs” an die iptables-Kette… sowohl in den Logfiles als auch unter /etc/apache2/wrapper -s werden mir die IPs die es zu droppen gilt angezeigt, alleine die iptables machen keinen mucks. Hab das alles nach deiner Anleitung gemacht, also Kette “posmatch” und den wrapper unverändert gelassen. Haste evtl. ‘nen Tip für mich wo’s haken könnte?
Max says:
March 18, 2009 at 10:41 (UTC 1)
Hab das Problem gefunden:
Exec: Execution failed while reading output: /etc/apache2/wrapper (End of file found)
und dann:
Failed to execute: /etc/apache2/wrapper
die recht für den wrapper sind gesetzt wie in deinem Beispiel:
chown root:root /etc/apache2/wrapper && chmod 7555 /etc/apache2/wrapper
was tun?
Arno says:
March 18, 2009 at 20:02 (UTC 1)
Das deutet darauf hin, dass es ein Problem damit gibt, eine IP zu sperren. Lies im Syslog nach, dort sollten weitere Details und Fehlermeldungen stehen.
Spacerat says:
June 2, 2009 at 11:08 (UTC 1)
Der link zu wrapper.c ist tot …
Arno says:
June 2, 2009 at 21:23 (UTC 1)
Danke ist behoben (war und ist in der Fußnote allerdings schon verlinkt).
mex says:
December 13, 2009 at 11:45 (UTC 1)
moinsen,
hast du das ganze mal gegen slowloris getestet?
mex
Greg Da Mac says:
December 14, 2009 at 21:15 (UTC 1)
Bei mir kommt beim get des Wrappers das:
http://daemonkeeper.net/download/2/
=> `index.html’
Entsprechend kann danach per “gcc -o wrapper wrapper.c” der Wrapper nicht gefunden werden. :(
Was tun?
Arno says:
December 22, 2009 at 13:51 (UTC 1)
Wenn du wget benutzt, musst du die Datei noch umbenennen,z.B
MartN says:
December 30, 2009 at 15:59 (UTC 1)
Wurde mir von OVH.de empfohlen und es es funktioniert super.
Zwar ist der Seitenaufbau nicht so schnell wie sonst, aber das kann man bei einem randalierenden Botnetz auch nicht erwarten :)
Immerhin ist das surfen möglich!