«

»

Sep
19
2008

DDoS-Abwehr mit Apaches mod_security2

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/wrapper

Der 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

  1. defunct says:

    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

  2. Arno says:

    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.

  3. Eduard says:

    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.

  4. Arno says:

    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.

  5. Internetagentur says:

    Wie kann ich mit mod_security für ein bestimmtes Verzeichnis eine einzige IP aussperren? Geht das überhaupt?

  6. Arno says:

    Sicher, zum Beispiel so:

    SecRule REMOTE_ADDR "^133\.3\.3\.7$" phase:1,deny,status:403

    in einem entsprechenden <directory> Abschnitt.

    Einfacher ist das aber vermutlich mit mod_rewrite in einer im Verzeichnis abgelegten .htaccess-Datei:

    RewriteCond %{REMOTE_ADDR} ^1.3.3.7$ [NC]
    RewriteRule . - [F]
  7. Max says:

    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?

  8. Max says:

    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?

  9. Arno says:

    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.

  10. Spacerat says:

    Der link zu wrapper.c ist tot …

  11. Arno says:

    Danke ist behoben (war und ist in der Fußnote allerdings schon verlinkt).

  12. mex says:

    moinsen,

    hast du das ganze mal gegen slowloris getestet?

    mex

  13. Greg Da Mac says:

    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?

  14. Arno says:

    Wenn du wget benutzt, musst du die Datei noch umbenennen,z.B

    mv index.html wrapper.c

  15. MartN says:

    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!

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="">