nbs-system / naxsi

NAXSI is an open-source, high performance, low rules maintenance WAF for NGINX
GNU General Public License v3.0
4.8k stars 606 forks source link

default main rules below 1000 give status code 405 #535

Closed Hansh532 closed 11 months ago

Hansh532 commented 3 years ago

Is there someway to give another statuscode like for example 418, my application is already using 405 and I have a custom error page for 405 that I don't want to use when Naxsi blocks sometthing.

wargio commented 3 years ago

actually no. there is only one handler per location where you can define where to redirect when naxsi blocks a request.

Hansh532 commented 3 years ago

Thanks for your quick reply,

Yes, understand but could i change in the config of naxsi 405 to something else for when Naxsi blocks something from the main internal rules. Or be able to set the for example DeniedUrl "/418.html" for internal rules below 1000?

Where can i find the 405 in the code that returns as status code in the Naxsi code: 405 "Not Allowed"?

docker build -f Dockerfile-no-learning-mode -t dmgnx/nginx-naxsi:no-learning-mode . ( see dockerfile below) curl -v http://localhost:41343/\?SQL\=OR\=1\=1\# = default rule (learning mode off) (status code 200) (expected)`

curl -v -XPOST http://localhost:41343/\?SQL\=OR\=1\=1\# = internal rule (16) (learning mode off) (status code 405) (not expected, expected DeniedUrl).`

As far as I can see the internal rules disregard the DeniedUrl while I expected it to respect it.

I reproduced this issue in docker: docker run --rm -it --name naxsi-test -p:41343:80 -v $(pwd)/web:/usr/share/nginx/html dmgnx/nginx-naxsi

=== Begin DockerFile ===
FROM dmgnx/nginx-naxsi

RUN sed -i 's/LearningMode;//g' /etc/nginx/conf.d/default.conf
=== End DockerFile ===
wargio commented 3 years ago

I do not know how you have installed and configured naxsi. Without your configuration i can't give you much help. What i can answer is no, there is no way to say "Hey if < XXX" use this location handler.

Hansh532 commented 3 years ago
user  nginx;
worker_processes  1;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;

events {
    worker_connections  1024;
}

http {
    include       /etc/nginx/mime.types;
    include       /etc/nginx/naxsi_core.rules;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    keepalive_timeout  65;

    #gzip  on;

    include /etc/nginx/conf.d/*.conf;
}

##################################
## INTERNAL RULES IDS:1-999     ##
##################################
#@MainRule "msg:weird request, unable to parse" id:1;
#@MainRule "msg:request too big, stored on disk and not parsed" id:2;
#@MainRule "msg:invalid hex encoding, null bytes" id:10;
#@MainRule "msg:unknown content-type" id:11;
#@MainRule "msg:invalid formatted url" id:12;
#@MainRule "msg:invalid POST format" id:13;
#@MainRule "msg:invalid POST boundary" id:14;
#@MainRule "msg:invalid JSON" id:15;
#@MainRule "msg:empty POST" id:16;
#@MainRule "msg:libinjection_sql" id:17;
#@MainRule "msg:libinjection_xss" id:18;

##################################
## SQL Injections IDs:1000-1099 ##
##################################
MainRule "rx:select|union|update|delete|insert|table|from|ascii|hex|unhex|drop" "msg:sql keywords" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1000;
MainRule "str:\"" "msg:double quote" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8,$XSS:8" id:1001;
MainRule "str:0x" "msg:0x, possible hex encoding" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:2" id:1002;
## Hardcore rules
MainRule "str:/*" "msg:mysql comment (/*)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1003;
MainRule "str:*/" "msg:mysql comment (*/)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1004;
MainRule "str:|" "msg:mysql keyword (|)"  "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1005;
MainRule "str:&&" "msg:mysql keyword (&&)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1006;
## end of hardcore rules
MainRule "str:--" "msg:mysql comment (--)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1007;
MainRule "str:;" "msg:semicolon" "mz:BODY|URL|ARGS" "s:$SQL:4,$XSS:8" id:1008;
MainRule "str:=" "msg:equal sign in var, probable sql/xss" "mz:ARGS|BODY" "s:$SQL:2" id:1009;
MainRule "str:(" "msg:open parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1010;
MainRule "str:)" "msg:close parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1011;
MainRule "str:'" "msg:simple quote" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1013;
MainRule "str:," "msg:comma" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1015;
MainRule "str:#" "msg:mysql comment (#)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1016;
MainRule "str:@@" "msg:double arobase (@@)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1017;

###############################
## OBVIOUS RFI IDs:1100-1199 ##
###############################
MainRule "str:http://" "msg:http:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1100;
MainRule "str:https://" "msg:https:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1101;
MainRule "str:ftp://" "msg:ftp:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1102;
MainRule "str:php://" "msg:php:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1103;
MainRule "str:sftp://" "msg:sftp:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1104;
MainRule "str:zlib://" "msg:zlib:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1105;
MainRule "str:data://" "msg:data:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1106;
MainRule "str:glob://" "msg:glob:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1107;
MainRule "str:phar://" "msg:phar:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1108;
MainRule "str:file://" "msg:file:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1109;
MainRule "str:gopher://" "msg:gopher:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1110;

#######################################
## Directory traversal IDs:1200-1299 ##
#######################################                                          
MainRule "str:.." "msg:double dot" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1200;
MainRule "str:/etc/passwd" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1202;
MainRule "str:c:\\" "msg:obvious windows path" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1203;
MainRule "str:cmd.exe" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1204;
MainRule "str:\\" "msg:backslash" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1205;
#MainRule "str:/" "msg:slash in args" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:2" id:1206;

########################################
## Cross Site Scripting IDs:1300-1399 ##
########################################
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;
MainRule "str:[" "msg:open square backet ([), possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1310;
MainRule "str:]" "msg:close square bracket (]), possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1311;
MainRule "str:~" "msg:tilde (~) character" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1312;
MainRule "str:`"  "msg:grave accent (`)" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1314;
MainRule "rx:%[23]."  "msg:double encoding" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1315;

####################################
## Evading tricks IDs: 1400-1500 ##
####################################
MainRule "str:&#" "msg:utf7/8 encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1400;
MainRule "str:%U" "msg:M$ encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1401;

#############################
## File uploads: 1500-1600 ##
#############################
MainRule "rx:\.ph|\.asp|\.ht" "msg:asp/php file upload" "mz:FILE_EXT" "s:$UPLOAD:8" id:1500;

server {
    listen       80;
    server_name  localhost;

    #charset koi8-r;
    #access_log  /var/log/nginx/log/host.access.log  main;

    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
        SecRulesEnabled;
        DeniedUrl "/50x.html"; 
        CheckRule "$SQL >= 8" BLOCK;
        CheckRule "$RFI >= 8" BLOCK;
        CheckRule "$TRAVERSAL >= 4" BLOCK;
        CheckRule "$EVADE >= 4" BLOCK;
        CheckRule "$XSS >= 8" BLOCK;
    }

    #error_page  404              /404.html;

    # redirect server error pages to the static page /50x.html
    #
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        return 403;
    }

    # proxy the PHP scripts to Apache listening on 127.0.0.1:80
    #
    #location ~ \.php$ {
    #    proxy_pass   http://127.0.0.1;
    #}

    # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
    #
    #location ~ \.php$ {
    #    root           html;
    #    fastcgi_pass   127.0.0.1:9000;
    #    fastcgi_index  index.php;
    #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
    #    include        fastcgi_params;
    #}

    # deny access to .htaccess files, if Apache's document root
    # concurs with nginx's one
    #
    #location ~ /\.ht {
    #    deny  all;
    #}
}
hendrikgroen commented 3 years ago

This is quite interesting. I wrote the following test:

#vi:filetype=perl

use lib 'lib';
use Test::Nginx::Socket;

repeat_each(3);

plan tests => repeat_each(1) * blocks();
no_root_location();
no_long_string();
$ENV{TEST_NGINX_SERVROOT} = server_root();
run_tests();

__DATA__
=== TEST 38: empty post
--- user_files
>>> foobar
eh yo
--- main_config
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
--- http_config
include /tmp/naxsi_ut/naxsi_core.rules;
--- config
location / {
     LearningMode;
     SecRulesEnabled;
     DeniedUrl "/RequestDenied";
     CheckRule "$SQL >= 8" BLOCK;
     CheckRule "$RFI >= 8" BLOCK;
     CheckRule "$TRAVERSAL >= 4" BLOCK;
     CheckRule "$XSS >= 8" BLOCK;
     CheckRule "$TESTSCORE >= 42" BLOCK;
     root $TEST_NGINX_SERVROOT/html/;
       index index.html index.htm;
}
location /RequestDenied {
     return 412;
}
--- request
POST /?testhendrik
--- error_code: 412

ran it:

 make test
if [ !  ] ; then TEST="*.t" ; fi
export PATH="/tmp/nginx//objs/:"/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin ; \
export PERL5LIB="~/perl5/lib/perl5/" ;\
cd .. ; prove -r "t/"
t/00naxsi_base.t ................. 1/1 
#   Failed test 'TEST 38: empty post - status code ok'
#   at /usr/local/share/perl/5.30.0/Test/Nginx/Socket.pm line 935.
#          got: '405'
#     expected: '412'
# Looks like you failed 1 test of 1.
t/00naxsi_base.t ................. Dubious, test returned 1 (wstat 256, 0x100)
Failed 1/1 subtests 

I expected the status code 412 but received the status code 405. The other tests with non-internal rules provide the expected test result (see existing tests).

Is this a bug or maybe something that could be documented?

wargio commented 3 years ago

WTF. Did you build yourself naxsi? what version are you using?

hendrikgroen commented 3 years ago

I just cloned the repo, added the test above and ran it. Latest commit:

commit fd3745ae2ba6f4b067b3ca2c28cfd8b61f59a500 (HEAD -> master, tag: 1.2, origin/master, origin/HEAD)
Author: Giovanni Dante Grazioli <giovannidante.grazioli@nbs-system.com>
Date:   Mon Oct 26 21:01:48 2020 +0100

    Updated to version 1.2
hendrikgroen commented 3 years ago

I'll create a PR

wargio commented 3 years ago

on what system are you? are you sure you do not have some configuration around which already defines this?

hendrikgroen commented 3 years ago

I saw a travis config file, give me a couple of mins. I did this on a fresh VM by the way.

wargio commented 3 years ago
$ TEST=00naxsi_base.t make test
if [ ! 00naxsi_base.t ] ; then TEST="*.t" ; fi
export PATH="/tmp/nginx//objs/:"/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/lib/jvm/default/bin:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl:/home/gdgrazioli/.secu-password/.bin:/home/gdgrazioli/.tools:/home/gdgrazioli/private/gcc-aarch64-elf/bin:/home/gdgrazioli/work/android-studio/platform-tools:/opt/android-sdk/build-tools/28.0.3/ ; \
export PERL5LIB="~/perl5/lib/perl5/" ;\
cd .. ; prove -r "t/00naxsi_base.t"
t/00naxsi_base.t .. ok     
All tests successful.
Files=1, Tests=44,  5 wallclock secs ( 0.04 usr  0.00 sys +  0.78 cusr  0.47 csys =  1.29 CPU)
Result: PASS
wargio commented 3 years ago

wait. that 405 comes from nginx not naxsi @hendrikgroen

hendrikgroen commented 3 years ago

PR: https://github.com/nbs-system/naxsi/pull/536

And yes you're right:

docker run --rm -d -p23421:80 --rm -it nginx

curl -XPOST localhost:23421                      
<html>
<head><title>405 Not Allowed</title></head>
<body>
<center><h1>405 Not Allowed</h1></center>
<hr><center>nginx/1.19.0</center>
</body>
</html>

Thanks for your help!