Tag Archive: naxsi

Naxsi Whitelist erstellen (deprecated: nx_util)

Naxsi (Logo von MARE system)

Ich möchte in diesem Beitrag gerne erklären, wie die Whitelist für Naxsi um eigene Einträge erweitert werden kann.
Dieser Beitrag betrifft Naxsi in der Version >=0.44.
Debian Squeeze und Debian Wheezy benutzen Version 0.45, sind also kompatibel.
Ich empfehle für Debian trotzallem die Pakete aus dem dotdeb.org Repository, mehr dazu hier.
Getestet wurde dieser Beitrag auf einem Debian 7.3 System mit „nginx-naxsi“ aus dem Debian-Repository.

Voraussetzung

  • Nginx mit Naxsi Modul >=0.44. Eine Anleitung habe ich hier ebenfalls online gestellt. Diese betrifft Debian und Derivate.
  • Git (Debian und Derivate: „apt-get install git“)

Installation

Zuerst Naxsi aus dem Github in das Heimverzeichnis holen. Gebraucht wird am Ende nur das „nx_util„, jedoch ist es so einfacher:

cd
git clone https://github.com/nbs-system/naxsi.git

Nun in das „nx_util“ Verzeichnis wechseln und von dort die Installation anstoßen:

cd naxsi/nxapi
chmod +x setup.py
./setup.py build
./setup.py install

Altbekannt von Installationen aus dem Quellcode, sind die entsprechenden Dateien nun in „/usr/local“ zu finden.

Vorbereitung

Konfiguration

Der Pfad zur Konfigurationsdatei lautet „/usr/local/etc/nx_util.conf„. Der Inhalt ist aktuell (Stand 12/2013) folgender:

[nx_util]
data_dir=/usr/local/nx_datas
database_dir=
naxsi_core_rules=/etc/nginx/naxsi_core.rules

Diese ist tatsächlich so unspektakulär wie sie ausschaut. Wichtig ist lediglich der Pfad zur Datei „naxsi_core.rules„. Der bereits gesetzte Pfad stimmt mit einer Standardinstallation unter Debian überein. Sollte er geändert worden sein oder die Datei einen anderen Namen haben etc., kann der Pfad nun angepasst werden.
database_dir=“ bedeutet, dass „nx_util“ die Datenbank mit den erstellten Regeln (dazu später mehr) im aktuellen Verzeichnis, in welchem es ausgeführt wird, erstellt.

LearningMode

Da ich davon ausgehe, dass Naxsi bereits konfiguriert wurde, ist bereits eine Datei für die Regeln einer Site vorhanden, etwa definiert in der Location. Hiermit sind natürlich nicht die „naxsi_core.rules“ gemeint. Etwa:

location / {
                include    /etc/nginx/naxsi.rules;
		...
}

In der Kopfzeile der Datei (in diesem Fall „/etc/nginx/naxsi.rules„) befindet sich der Parameter „LearningMode„. Dieser wird nun auskommentiert:
Aus

#LearningMode

wird

LearningMode

Anschließend den Nginx Dienst neuladen:

service nginx reload

Hinweis: Das Einschalten des „LearningMode“ ist nicht zwingend notwendig. Wichtig sind am Ende nur die Fehler/Filter in der „error.log„. Jedoch ist es zu empfehlen, da auf diese Art Naxsi Daten sammeln kann und die Site trotzdem benutzbar bleibt. Der Parameter „DeniedUrl“ greift in diesem Fall nicht.
Der Pfad für „error_log“ der Site sollte natürlich schon bekannt sein. In einer Standardinstallation und wenn nicht explizit im „server“-Block der Site geändert, lautet „/var/log/nginx/error.log„.

Daten sammeln

An diesem Punkt heißt es: Daten sammeln. Am einfachsten geht das, indem so ziemlich alles angeklickt und ausprobiert wird, was klickbar ist. Sicherlich kann man dies auch automatisieren, allerdings wäre ich damit vorsichtig, um nicht einen Lösch-Trigger auszulösen. ;) Vermutlich sollte der LearningMode ein paar Stunden oder Tage aktiv bleiben, wobei das stark von der Benutzung abhängt.

Optional: Alte Daten verwenden

War Naxsi bereits im Einsatz und hat Kopfzerbrechen bereit, indem es fälschlich gefilter hat, können auch diese (alten) Daten verwendet werden.
Aber Vorsicht! Die vorhandenen Logs filtert ihr vorher am Besten nach eurer eigenen IP, um nicht REELLE Angriffe auf die Whitelist zu setzen! Etwa so:

cat /var/log/nginx/error.log | grep -i naxsi_fmt | grep EIGENE.IP.ADRE.SSE

Da die älteren Logdateien der Standardinstallation wohl schon archiviert wurden, verwendet an diesem Punkt „zcat“ anstatt „cat„. Am Ende könnte etwa stehen:

cat /var/log/nginx/error.log | grep -i naxsi_fmt | grep EIGENE.IP.ADRE.SSE >> ~/naxsi_fmt_errors
cat /var/log/nginx/error.log.1 | grep -i naxsi_fmt | grep EIGENE.IP.ADRE.SSE >> ~/naxsi_fmt_errors
zcat /var/log/nginx/error.log.*.gz | grep -i naxsi_fmt | grep EIGENE.IP.ADRE.SSE >> ~/naxsi_fmt_errors

Die Daten lägen nun zusammengefasst in der Datei „~/naxsi_fmt_erors„.

Naxsi Whitelist erstellen

Nun zum eigentlichen Erstellen der Regeln auf Basis der gesammelten Filter.
nx_util“ arbeitet mit Datenbanken. Diese Datenbanken können für spätere Ausführungen immer wieder verwendet werden, um den Inhalt zu erweitern – wenn erwünscht.

In diesem Beispiel würde eine Datenbank mit dem Namen „site_rules_db_name“ im aktuellen Verzeichnis gespeichert werden. Vorausgesetzt „database_dir“ wurde in der Konfiguration nicht definiert:

nx_util.py -d site_rules_db_name -l /pfad/zu/nginx/error.log

Um diese später um neue Einträge zu erweitern, wird der Parameter „-i“ verwendet:

nx_util.py -i -d site_rules_db_name -l /pfad/zu/nginx/error.log

Wird keine Datenbank definiert, erstellt das Util eine Datenbank mit dem Namen „naxsi_sig„:

nx_util.py -l /pfad/zu/nginx/error.log

Zum Auslesen der Datenbank, wird das Util mit dem Parameter „-o DB_NAME“ ausgeführt:

nx_util.py -o site_rules_db_name

Die Befehle lassen sich außerdem kombinieren. So würde eine Regel-Datenbank mit eigenem Namen auf Basis einer definierten Log erstellt und ausgegeben werden:

nx_util.py -d site_name_db -l /pfad/zu/nginx/error.log -o

Auch kann die Ausgabe in eine HTML-Datei erfolgen, hierzu wird von „-H output.html“ Gebrauch gemacht. Zum Beispiel:

nx_util.py -d site_name_db -l /pfad/zu/nginx/error.log -H output.html

Generell empfehle ich einen Blick in die „man page“ des Utils.

Eine Beispielausgabe:

12/31/2013 08:26:16 Deleting old database :site_name_db
12/31/2013 08:26:16 Unable to delete old database :site_name_db
12/31/2013 08:26:17 List of files :[‚/var/log/nginx/error.log‘]
########### Optimized Rules Suggestion ##################
# total_count:1 (11.11%), peer_count:1 (100.0%) | double quote
BasicRule wl:1001 „mz:$URL:/tag/mariadb/|$BODY_VAR:customized“;
# total_count:1 (11.11%), peer_count:1 (100.0%) | double quote
BasicRule wl:1001 „mz:$URL:/tag/bash/|$BODY_VAR:customized“;
# total_count:1 (11.11%), peer_count:1 (100.0%) | double quote
BasicRule wl:1001 „mz:$URL:/category/linux-allgemein/|$BODY_VAR:customized“;

Alle Zeilen mit „BasicRule“ beginnend sind relevant für die Whitelist. Selbstverständlich kann auch „grep“ zum Filtern benutzt werden:

nx_util.py -d site_name_db -l /pfad/zu/nginx/error.log -o | grep BasicRule

Wie erwähnt ist in diesem Beitrag die Datei „/etc/nginx/naxsi.rules“ hierfür zuständig. Am Ende dieser Datei werden die erstellten Regeln eingefügt und Nginx neu geladen:

service nginx reload

Zum Abschluss sollten die Regeln noch auf Ihre Funktion hin getestet werden. Entsprechende Whitelist-Einträge sollten nun nicht mehr als gefiltert in der jeweiligen „error.log“ auftauchen.

Kleiner Tipp: IDs kombinieren

Oft betrifft ein Filter gleich mehrere Regeln, zum Beispiel kann die Ausgabe des Utils Folgende sein:

BasicRule wl:1303 „mz:$URL:/|$ARGS_VAR:a“;
BasicRule wl:1302 „mz:$URL:/|$ARGS_VAR:a“;

Anmerkung: Das bedeutet – nach einem Blick in die „naxsi_core.rules“-Datei – dass folgende Regeln gelten:

MainRule "str:<" "msg:html open tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1302;
MainRule "str:>" "msg:html close tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1303;

Kombiniert wird hieraus für die Whitelist:

BasicRule wl:1302,1303 „mz:$URL:/|$ARGS_VAR:a“;

Viel Erfolg!

WordPress mit naxsi

Naxsi (Logo von MARE system)

Changelog

  • 24.10.2014 – Merged latest rules from github
  • 29.12.2013 – Merged latest rules from github
  • 29.12.2013 – Fix for „wp-admin/customize.php“

Ich habe mittlerweile einen Beitrag zum erstellen eigener Whitelists erstellt.

Naxsi ist die Web Application Firewall von nginx. Sie läuft als Modul mit nginx und ist im Gegensatz zu anderen Lösungen sehr einfach zu konfigurieren. Ebenfalls gibt es einen „Lernmodus“, um Regeln quasi anzulernen. Hierzu erschien im Admin Magazin ein schöner Artikel, der sich auch zu kaufen lohnt.

Mehr oder weniger durch Zufall bin ich auf ein fertiges Regelwerk gestoßen, um WordPress mit naxsi abzusichern. Die Quelle befindet sich hier.

Voraussetzung ist natürlich, dass nginx bereits installiert ist. Das Meta-Paket „nginx“ beinhaltet naxsi nicht, jedoch kann nginx mit dem Modul naxsi einfach nachinstalliert werden, die Konfigurationen bleiben erhalten und werden nicht überschrieben:

sudo apt-get install nginx-naxsi

In der Datei /etc/nginx/nginx.conf befand sich bereits ein Parameter für naxsi, der nun aktivieret werden kann:

/etc/nginx/nginx.conf:

http {
...
        include /etc/nginx/naxsi_core.rules;
...
}

Naxsi sieht vor, für jede Location im Server-Abschnitt ein eigenes Regelwerk zu verwenden. Dazu wird nun die jeweilige Site Konfiguration geöffnet, die WordPress beinhaltet. Zusätzlich wird eine neue Location angelegt, in die der Besucher im Falle weitergeleitet wird.

/etc/nginx/sites-available/default (Beispiel!):

server {
...
        location / {
                include    /etc/nginx/naxsi_wp.rules;
                ...
        }

        location /Denied {
                return 500;
        }
...
}

In diesem Fall befinden sich die Regeln – logischerweise – in der Datei „/etc/nginx/naxsi_wp.rules“.
Die Datei herunterladen:

wget http://debinux.de/wp-content/uploads/naxsi.rules -O /etc/nginx/naxsi_wp.rules

Zur Übersicht/Vollständigkeit hier der Code:

# Sample rules file for default vhost.
 
#LearningMode;
SecRulesEnabled;
#SecRulesDisabled;
DeniedUrl "/Denied";
 
## check rules
CheckRule "$SQL >= 8" BLOCK;
CheckRule "$RFI >= 8" BLOCK;
CheckRule "$TRAVERSAL >= 4" BLOCK;
CheckRule "$EVADE >= 4" BLOCK;
CheckRule "$XSS >= 8" BLOCK;
 
# WordPress naxsi rules

### HEADERS
BasicRule wl:1000,1001,1005,1007,1010,1011,1013,1100,1200,1308,1309,1310,1311,1315 "mz:$HEADERS_VAR:cookie";
# xmlrpc
BasicRule wl:1402 "mz:$HEADERS_VAR:content-type";

### simple BODY (POST)
# comments
BasicRule wl:1000,1010,1011,1013,1015,1200,1310,1311 "mz:$BODY_VAR:post_title";
BasicRule wl:1000 "mz:$BODY_VAR:original_publish";
BasicRule wl:1000 "mz:$BODY_VAR:save";
BasicRule wl:1008,1010,1011,1013,1015 "mz:$BODY_VAR:sk2_my_js_payload";
BasicRule wl:1001,1009,1005,1016,1100,1310 "mz:$BODY_VAR:url";
BasicRule wl:1009,1100 "mz:$BODY_VAR:referredby";
BasicRule wl:1009,1100 "mz:$BODY_VAR:_wp_original_http_referer";
BasicRule wl:1000,1001,1005,1008,1007,1009,1010,1011,1013,1015,1016,1100,1200,1302,1303,1310,1311,1315,1400 "mz:$BODY_VAR:comment";
BasicRule wl:1100 "mz:$BODY_VAR:redirect_to";
BasicRule wl:1000,1009,1315 "mz:$BODY_VAR:_wp_http_referer";
BasicRule wl:1000 "mz:$BODY_VAR:action";
BasicRule wl:1001,1013 "mz:$BODY_VAR:blogname";
BasicRule wl:1015,1013 "mz:$BODY_VAR:blogdescription";
BasicRule wl:1015 "mz:$BODY_VAR:date_format_custom";
BasicRule wl:1015 "mz:$BODY_VAR:date_format";
BasicRule wl:1015 "mz:$BODY_VAR:tax_input%5bpost_tag%5d";
BasicRule wl:1015 "mz:$BODY_VAR:tax_input[post_tag]";
BasicRule wl:1100 "mz:$BODY_VAR:siteurl";
BasicRule wl:1100 "mz:$BODY_VAR:home";
BasicRule wl:1000,1015 "mz:$BODY_VAR:submit";
# news content matches pretty much everything
BasicRule wl:0 "mz:$BODY_VAR:content";
BasicRule wl:1000 "mz:$BODY_VAR:delete_option";
BasicRule wl:1000 "mz:$BODY_VAR:prowl-msg-message";
BasicRule wl:1100 "mz:$BODY_VAR:_url";
BasicRule wl:1001,1009 "mz:$BODY_VAR:c2c_text_replace%5btext_to_replace%5d";
BasicRule wl:1200 "mz:$BODY_VAR:ppn_post_note";
BasicRule wl:1100 "mz:$BODY_VAR:author";
BasicRule wl:1001,1015 "mz:$BODY_VAR:excerpt";
BasicRule wl:1015 "mz:$BODY_VAR:catslist";
BasicRule wl:1005,1008,1009,1010,1011,1015,1315 "mz:$BODY_VAR:cookie";
BasicRule wl:1101 "mz:$BODY_VAR:googleplus";
BasicRule wl:1007 "mz:$BODY_VAR:name";
BasicRule wl:1007 "mz:$BODY_VAR:action";
BasicRule wl:1100 "mz:$BODY_VAR:attachment%5burl%5d";
BasicRule wl:1100 "mz:$BODY_VAR:attachment_url";
BasicRule wl:1001,1009,1100,1302,1303,1310,1311 "mz:$BODY_VAR:html";
BasicRule wl:1015 "mz:$BODY_VAR:title";
BasicRule wl:1001,1009,1015 "mz:$BODY_VAR:recaptcha_challenge_field";
BasicRule wl:1011 "mz:$BODY_VAR:pwd";
BasicRule wl:1000 "mz:$BODY_VAR:excerpt";

### BODY|NAME
BasicRule wl:1000 "mz:$BODY_VAR:delete_option|NAME";
BasicRule wl:1000 "mz:$BODY_VAR:from|NAME";

### Simple ARGS (GET)
# WP login screen
BasicRule wl:1100 "mz:$ARGS_VAR:redirect_to";
BasicRule wl:1000,1009 "mz:$ARGS_VAR:_wp_http_referer";
BasicRule wl:1000 "mz:$ARGS_VAR:wp_http_referer";
BasicRule wl:1000 "mz:$ARGS_VAR:action";
BasicRule wl:1000 "mz:$ARGS_VAR:action2";
# load and load[] GET variable
BasicRule wl:1000,1015 "mz:$ARGS_VAR:load";
BasicRule wl:1000,1015 "mz:$ARGS_VAR:load[]";
BasicRule wl:1015 "mz:$ARGS_VAR:q";
BasicRule wl:1000,1015 "mz:$ARGS_VAR:load%5b%5d";

### URL
BasicRule wl:1000 "mz:URL|$URL:/wp-admin/update-core.php";
BasicRule wl:1000 "mz:URL|$URL:/wp-admin/update.php";
# URL|BODY
BasicRule wl:1009,1100 "mz:$URL:/wp-admin/post.php|$BODY_VAR:_wp_http_referer";
BasicRule wl:1016 "mz:$URL:/wp-admin/post.php|$BODY_VAR:metakeyselect";
BasicRule wl:11 "mz:$URL:/xmlrpc.php|BODY";
BasicRule wl:11 "mz:$URL:/wp-cron.php|BODY";
BasicRule wl:2 "mz:$URL:/wp-admin/async-upload.php|BODY";
# URL|BODY|NAME
BasicRule wl:1100 "mz:$URL:/wp-admin/post.php|$BODY_VAR:_wp_original_http_referer|NAME";
BasicRule wl:1000 "mz:$URL:/wp-admin/post.php|$BODY_VAR:metakeyselect|NAME";
BasicRule wl:1000 "mz:$URL:/wp-admin/user-edit.php|$BODY_VAR:from|NAME";
BasicRule wl:1100 "mz:$URL:/wp-admin/admin-ajax.php|$BODY_VAR:attachment%5burl%5d|NAME";
BasicRule wl:1100 "mz:$URL:/wp-admin/post.php|$BODY_VAR:attachment_url|NAME";
BasicRule wl:1000 "mz:$URL:/wp-admin/plugins.php|$BODY_VAR:verify-delete|NAME";
BasicRule wl:1310,1311 "mz:$URL:/wp-admin/post.php|$BODY_VAR:post_category[]|NAME";
BasicRule wl:1311 "mz:$URL:/wp-admin/post.php|$BODY_VAR:post_category|NAME";
BasicRule wl:1310,1311 "mz:$URL:/wp-admin/post.php|$BODY_VAR:tax_input[post_tag]|NAME";
BasicRule wl:1310,1311 "mz:$URL:/wp-admin/post.php|$BODY_VAR:newtag[post_tag]|NAME";
BasicRule wl:1310,1311 "mz:$URL:/wp-admin/users.php|$BODY_VAR:users[]|NAME";
# URL|ARGS|NAME
BasicRule wl:1310,1311 "mz:$URL:/wp-admin/load-scripts.php|$ARGS_VAR:load[]|NAME";
BasicRule wl:1000 "mz:$URL:/wp-admin/users.php|$ARGS_VAR:delete_count|NAME";
BasicRule wl:1000 "mz:$URL:/wp-admin/users.php|$ARGS_VAR:update|NAME";

# plain WP site
BasicRule wl:1000 "mz:URL|$URL:/wp-admin/update-core.php";
BasicRule wl:1000 "mz:URL|$URL:/wp-admin/update.php";
# URL|BODY
BasicRule wl:1009,1100 "mz:$URL:/wp-admin/post.php|$BODY_VAR:_wp_http_referer";
BasicRule wl:1016 "mz:$URL:/wp-admin/post.php|$BODY_VAR:metakeyselect";
BasicRule wl:11 "mz:$URL:/xmlrpc.php|BODY";
BasicRule wl:11 "mz:$URL:/wp-cron.php|BODY";
# URL|BODY|NAME
BasicRule wl:1100 "mz:$URL:/wp-admin/post.php|$BODY_VAR:_wp_original_http_referer|NAME";
BasicRule wl:1000 "mz:$URL:/wp-admin/post.php|$BODY_VAR:metakeyselect|NAME";
BasicRule wl:1000 "mz:$URL:/wp-admin/user-edit.php|$BODY_VAR:from|NAME";
BasicRule wl:1100 "mz:$URL:/wp-admin/admin-ajax.php|$BODY_VAR:attachment%5burl%5d|NAME";
BasicRule wl:1310,1311 "mz:$URL:/wp-admin/admin-ajax.php|$BODY_VAR:data[wp-auth-check]|NAME";
BasicRule wl:1310,1311 "mz:$URL:/wp-admin/update-core.php|$BODY_VAR:checked[]|NAME";

# URL|ARGS|NAME
BasicRule wl:1310,1311 "mz:$URL:/wp-admin/load-scripts.php|$ARGS_VAR:load[]|NAME";
BasicRule wl:1000 "mz:$URL:/wp-admin/users.php|$ARGS_VAR:delete_count|NAME";
BasicRule wl:1000 "mz:$URL:/wp-admin/users.php|$ARGS_VAR:update|NAME";

# WP 3.6/8 Fix
BasicRule wl:1100 "mz:$URL:/|$ARGS_VAR:statify_referrer";
BasicRule wl:1001,1015,1009,1311,1310,1101,1016 "mz:$URL:/|$BODY_VAR:customized";
### Plugins
#WP Minify
BasicRule wl:1015 "mz:$URL:/wp-content/plugins/bwp-minify/min/|$ARGS_VAR:f";

Via „naxsi_wp.rules“ wird /Denied als Umleitung für geblockte Anfragen definiert. „return 500“ würde ergo auf die Fehlerseite 500 verweisen. Selbstverständlich sind diese Pfade und Parameter nach Belieben einzustellen. Wer witzig ist, darf auch 418 zurückgeben. ;)

Zur Aktivierung den Dienst neustarten:

service nginx restart

Achtung: Bei WordPress Updates kann es passieren, dass die Regeln geändert werden müssen! Ich habe die obigen Regeln erfolgreich auf WordPress 3.8 getestet. Sollte etwas nicht mehr funktionieren, empfehle ich den Lernmodus („LearningMode“) zu aktivieren und das Wiki zu naxsi zu studieren. Ein Blick in die Funktionsweise schadet generell nicht und sollte in jedem Fall schon geschehen sein.

Hat naxsi etwas gefiltert, so wird dies in der Fehler-Logdatei (via „ErrorLog“) des jeweiligen Servers festgehalten.
Getestet werden kann die Konfiguration und Wirksamkeit, indem „?a=<>“ an eine WordPress URL angehängt wird, z.B. www.domain.tld/?a=<>
Der Fehler wird protokolliert:

NAXSI_FMT: ip=X.X.X.X&server=www.domain.tld&uri=/&learning=0&total_processed=8&total_blocked=2&zone0=ARGS&id0=1302&var_name0=a, client: X.X.X.X, server: debinux.de, request: „GET /?a=%3C%3E HTTP/1.1“, host: „www.domain.tld“


Das naxsi-Logo ist Eigentum von MARE system.