Lazy learning

Tags:
No Comments »

So you want to use Naxsi but you’re too lazy to analyze your nginx‘s error log in order to write your own whitelists, and you’re definitely not brave enough to run a learning mode for a week. Relax, they’ve got something for you too. Rendez-vous in the Downloads area of Naxsi’s website and retrieve latest naxsi-ui archive. Within that tarball, you will only need 2 python scripts, nx_intercept.py and nx_extract.py. The first one will read and record all Naxsi matches from the error log, while the second will generate the whitelist.
In order to make those scripts work, you will need python-twisted, which is available for pretty much every decent UNIX-like I’m aware of. Default configuration file, naxsi-ui.conf, will do the job as it is.
Here’s a tiny piece of script which reads all of your log files, pass them to the nx_* scripts and will display all the associated whitelist rules to stdout:

#!/bin/sh

usage()
{
        echo "$0 </path/to/naxsi-ui> </path/to/log-glob>"
        exit 1
}

[ $# -lt 2 ] && usage

nxpath=$1
logpath=$2
tmplog=`mktemp`

zegrep -h NAXSI_FMT ${2}* > ${tmplog}

python ${nxpath}/nx_intercept.py -c naxsi-ui.conf -l ${tmplog}
python ${nxpath}/nx_extract.py -c naxsi-ui.conf -o

rm ${tmplog}

Usage example:

$ ./mkwl . /var/log/nginx/imil.net-error.log

This will read and analyze every log file matching /var/log/nginx/imil.net-error.log*, including gzipped ones.

WordPress 3.5 and Naxsi (update 7, now in production)

Tags: , ,
No Comments »

Update: This setup is now in production, you are actually reading this blog through a Naxsi protected WordPress !
Update 2: This setup is also in production on GCU-Squad‘s Website.

I’m slowly preparing iMil.net migration to a new server. Yeah, it’s a bit confusing to be the CTO of a hosting company and having my personnal website elsewhere, but you know, time and stuff… anyway, it’s coming.

While preparing the migration, I decided to get rid of Apache’s modsecurity and to put naxsi, the WAF plugin for nginx in front of the website. I’ve been working on good rules for WordPress, as this software sets some strange variables that can confuse any WAF; so here’s my naxsi.rules, operational for WordPress 3.5, enjoy.

SecRulesEnabled;
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,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 "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: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";

### 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";
# 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";
# 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";

NB: note that these are rules I am enhancing on my new server as I write, I’ll update this post whenever new invalid blocking is found.

Ça va pas être possible avec vos baskets

Tags: , , ,
No Comments »

Dans ma boîte, l’équipe sécurité a publié voila quelques mois de cela un module pour nginx: un firewall applicatif du nom de naxsi.

Ce module, sous licence GPLv2, je viens de le publier dans pkgsrc current sous la forme d’une option de www/nginx. Je me propose de vous montrer ici comment sécuriser simplement votre serveur web / proxy inverse nginx grâce à naxsi.

Premièrement, si comme moi (et comme il se doit) vous utilisez une branche stable de pkgsrc, mettez simplement à jour www/nginx comme ceci:

$ cd /usr/pkgsrc/www/nginx
# cvs up -rHEAD -dP

Puis spécifiez à pkgsrc que vous souhaitez activer l’option naxsi pour le paquet nginx:

$ grep nginx /etc/mk.conf
PKG_OPTIONS.nginx+=     naxsi

Ceci fait, reconstruisez le paquet comme d’habitude:

$ cd /usr/pkgsrc/www/nginx
$ sudo make update clean

Dans la configuration de nginx, incluez les règles par défaut de naxsi de cette façon:

http {
    include       /usr/pkg/etc/nginx/mime.types;
    include       /usr/pkg/etc/nginx/naxsi_core.rules; # < --- ici
    default_type  application/octet-stream;

Comme vous pourrez le constater, le fichier /usr/pkg/etc/nginx/naxsi_core.rules contient un set de règles déjà très efficaces contre bon nombre d'attaques connues.
Reste alors à activer le filtrage sur une location et choisir quels types d'attaques vous souhaitez bloquer; par exemple:

        location / {
            SecRulesEnabled;
            CheckRule "$SQL >= 8" BLOCK;
            CheckRule "$RFI >= 8" BLOCK;
            CheckRule "$TRAVERSAL >= 4" BLOCK;
            CheckRule "$EVADE >= 4" BLOCK;
            CheckRule "$XSS >= 8" BLOCK;

Un redémarrage de nginx plus loin, on essayera par exemple d’accéder à une adresse louche:

$ wget -O- http://coruscant/?../../etc/passwd
--2012-04-22 10:21:01--  http://coruscant/?../../etc/passwd
Resolving coruscant... 192.168.1.2
Connecting to coruscant|192.168.1.2|:80... connected.
HTTP request sent, awaiting response... No data received.

Et de constater dans /var/log/nginx/error.log:

2012/04/22 10:21:01 [error] 25353#0: *15 NAXSI_FMT: ip=192.168.1.1&server=192.168.1.2&uri=/&total_processed=1&total_blocked=1&zone0=ARGS&id0=1200&var_name0=, client: 192.168.1.1, server: localhost, request: "GET /?../../etc/passwd HTTP/1.0", host: "192.168.1.2"

Moi j’trouve ça assez classe tout de même.

Toutes les infos relatives à la configuration de naxsi sont disponibles sur le Wiki de ce dernier.

WP Theme & Icons based on GlossyBlue by N.Design Studio
Banner from www.trynthlas.com
Entries RSS Comments RSS Log in
Performance Optimization WordPress Plugins by W3 EDGE