naxsi

Note: This process is intended to run on a fully up-to-date Debian Stretch, that follows installation best practices

Build dependencies

Get naxsi

You can download naxsi from the releases page here Get the latest release source code and the corresponding signature file :

usr@008885ac189c:~$ export NAXSI_VER=1.3
usr@008885ac189c:~$ wget https://github.com/wargio/naxsi/releases/download/$NAXSI_VER/naxsi-$NAXSI_VER-src-with-deps.tar.gz -O naxsi-$NAXSI_VER-src-with-deps.tar.gz

Get Nginx

Next, you need to get the source code for the nginx version you are running (in this example, we are using the nginx from the debian stretch repository) :

usr@008885ac189c:~$ export NGINX_VER=X.YY.Z
usr@008885ac189c:~$ wget https://nginx.org/download/nginx-$NGINX_VER.tar.gz
usr@008885ac189c:~$ wget https://nginx.org/download/nginx-$NGINX_VER.tar.gz.asc
usr@008885ac189c:~$ gpg --recv-key 520A9993A1C052F8
usr@008885ac189c:~$ gpg --verify nginx-$NGINX_VER.tar.gz.asc
usr@008885ac189c:~$ rm nginx-$NGINX_VER.tar.gz.asc 

If the signature fails, check that both files have been correctly downloaded and are not corrupted.

Build naxsi as a dynamic extension

First, extract the source code :

usr@008885ac189c:~$ mkdir -p naxsi-$NAXSI_VER
usr@008885ac189c:~$ tar -C naxsi-$NAXSI_VER -xzf naxsi-$NAXSI_VER-src-with-deps.tar.gz
usr@008885ac189c:~$ tar vxf nginx-$NGINX_VER.tar.gz 
usr@008885ac189c:~$ cd nginx-$NGINX_VER  
usr@008885ac189c:~/nginx-X.YY.Z$ ./configure --add-dynamic-module=../naxsi-$NAXSI_VER/naxsi_src/
usr@008885ac189c:~/nginx-X.YY.Z$ make modules

The resulting ngx_http_naxsi_module.so will be located in the objs directory. Copy it to your nginx server, in /etc/nginx/modules/. Also copy naxsi_core.rules to /etc/nginx/.

Build naxsi as a dynamic extension for nginx from your distribution package (i.e Ubuntu)

You need to build the module with the same flags as your NGINX server package was compiled for example via apt-get source nginx

Execute this to list them:

usr@008885ac189c:~$ nginx -V 

Then pass them to ./configure.

usr@008885ac189c:~/nginx-X.YY.Z$ ./configure <FLAGS FROM ABOVE COMMAND> --add-dynamic-module=../naxsi-$NAXSI_VER/naxsi_src/
usr@008885ac189c:~/nginx-X.YY.Z$ make modules

This will ensure module will be compatible with your nginx and will load properly.

Basic setup

Main configuration

You need to tell nginx to load the naxsi module and naxsi core rules (if you don’t load them, naxsi will block every requests) :

usr@008885ac189c:~$ cat /etc/nginx/nginx.conf
...
load_module /etc/nginx/modules/ngx_http_naxsi_module.so; # load naxsi

http {
    include /etc/nginx/naxsi_core.rules; # load naxsi core rules
    ...
}
...

Naxsi works on a per-location basis, meaning you can only enable it inside a location :

server {
...

    location / { # naxsi is enabled, and in learning mode

        SecRulesEnabled; #enable naxsi
        LearningMode; #enable learning mode
        LibInjectionSql; #enable libinjection support for SQLI
        LibInjectionXss; #enable libinjection support for XSS

        DeniedUrl "/RequestDenied"; #the location where naxsi will redirect the request when it is blocked
        CheckRule "$SQL >= 8" BLOCK; #the action to take when the $SQL score is superior or equal to 8
        CheckRule "$RFI >= 8" BLOCK;
        CheckRule "$TRAVERSAL >= 5" BLOCK;
        CheckRule "$UPLOAD >= 5" BLOCK;
        CheckRule "$XSS >= 8" BLOCK;


        proxy_pass http://127.0.0.1;
        ....
    }

    location /admin { # naxsi is disabled 

        SecRulesDisabled; #optional, naxsi is disabled by default
        
        allow 1.2.3.4;
        deny all;
        proxy_pass http://127.0.0.1;
        ....
    }

    location /vuln_page.php { # naxsi is enabled, and is *not* in learning mode

        SecRulesEnabled;
        proxy_pass http://127.0.0.1;
    }
    
    location /RequestDenied {
        internal;
        return 403;
    }
...

}

Whitelist

As naxsi uses a whitelist approach, a lot of false positives may be generated, potentially dropping legitimate requests. To prevent this, whitelists must be written (either manually or with nx-tool). For example, if you have an e-commerce website that sells furniture, people will be likely to search for something like table. Unfortunately, table is also a SQL keyword, which will trigger naxsi. To prevent this, you can write a whitelist telling naxsi to allow the table keyword in the search form (assuming the search form in on /search) :

server {
    
    location / {
        SecRulesEnabled; #enable naxsi
        LearningMode; #enable learning mode
        LibInjectionSql; #enable libinjection support for SQLI
        LibInjectionXss; #enable libinjection support for XSS

        DeniedUrl "/RequestDenied"; #the location where naxsi will redirect the request when it is blocked
        CheckRule "$SQL >= 8" BLOCK; #the action to take when the $SQL score is superior or equal to 8
        CheckRule "$RFI >= 8" BLOCK;
        CheckRule "$TRAVERSAL >= 5" BLOCK;
        CheckRule "$UPLOAD >= 5" BLOCK;
        CheckRule "$XSS >= 8" BLOCK;

        BasicRule wl:1000 "mz:$URL:/search|$ARGS_VAR:q";
        proxy_pass http://127.0.0.1;
        ....
        
    }    
    
}

See here and here for more informations about whitelists.

Blacklist

You can also create blacklist with naxsi, allowing you to drop requests even when in learning mode. Let’s imagine a PHP script (/vuln_page.php) vulnerable to an unquoted SQLI in the id parameter, a typical exploitation would look like this :

GET /vuln_page.php?id=1'+or+1=1/*

You can use naxsi to virtually patch this vulnerabilty by adding a blacklist denying every requests on /vuln_page.php where the id parameter contains anything other than a number (in this example, the blacklist will only apply to the / location, you can use MainRule and put it outside in the http block to make it global):

server {
    
    location / {
        SecRulesEnabled; #enable naxsi
        LearningMode; #enable learning mode
        LibInjectionSql; #enable libinjection support for SQLI
        LibInjectionXss; #enable libinjection support for XSS

        DeniedUrl "/RequestDenied"; #the location where naxsi will redirect the request when it is blocked
        CheckRule "$SQL >= 8" BLOCK; #the action to take when the $SQL score is superior or equal to 8
        CheckRule "$RFI >= 8" BLOCK;
        CheckRule "$TRAVERSAL >= 5" BLOCK;
        CheckRule "$UPLOAD >= 5" BLOCK;
        CheckRule "$XSS >= 8" BLOCK;

        BasicRule id:4242 "mz:$URL:/vuln_page.php|$ARGS_VAR:id" "rx:[^\d]+" "s:DROP" "msg:blacklist for SQLI in /vuln_page.php";
        proxy_pass http://127.0.0.1;
        ....
        
    }    
    
}