Initial Commit
This commit is contained in:
1
naxsi-0.55.3/.gitattributes
vendored
Normal file
1
naxsi-0.55.3/.gitattributes
vendored
Normal file
@@ -0,0 +1 @@
|
||||
t/* linguist-vendored
|
||||
45
naxsi-0.55.3/.travis.yml
Normal file
45
naxsi-0.55.3/.travis.yml
Normal file
@@ -0,0 +1,45 @@
|
||||
language: C
|
||||
|
||||
os:
|
||||
- linux
|
||||
|
||||
cache:
|
||||
- apt
|
||||
- pip
|
||||
|
||||
env:
|
||||
global:
|
||||
- VER_NGINX=1.9.11
|
||||
- COV=1
|
||||
|
||||
compiler:
|
||||
- clang
|
||||
- gcc
|
||||
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- lcov
|
||||
- cpanminus
|
||||
|
||||
install:
|
||||
- gem install coveralls-lcov
|
||||
- cd ./naxsi_src
|
||||
- if [ "$CC" == "clang" ]; then COV=0; fi
|
||||
- make
|
||||
- cpanm -v --notest Test::Nginx
|
||||
|
||||
before_script:
|
||||
- lcov --directory "../nginx-${VER_NGINX}" --zerocounters
|
||||
|
||||
script: make test
|
||||
|
||||
after_failure:
|
||||
- cat ../t/servroot/logs/error.log
|
||||
- cat /tmp/ngx_error.log
|
||||
- cat /tmp/ngx_access.log
|
||||
|
||||
after_success:
|
||||
- lcov --list naxsi.info
|
||||
- coveralls-lcov --repo-token ${COVERALLS_TOKEN} naxsi.info
|
||||
|
||||
108
naxsi-0.55.3/Makefile
Normal file
108
naxsi-0.55.3/Makefile
Normal file
@@ -0,0 +1,108 @@
|
||||
CORE_VERS := $(shell grep NAXSI_VERSION naxsi.h | cut -d '"' -f 2)
|
||||
MOD_PATH := $(shell pwd)
|
||||
TMP_DIR := /tmp/nginx/
|
||||
|
||||
# Keys for coverity
|
||||
CAN :=
|
||||
CAK :=
|
||||
|
||||
#Set to 1 if you want coverage report
|
||||
COV ?= 1
|
||||
|
||||
#Allows to force for specific UT only
|
||||
#TEST := "29*.t"
|
||||
NGINX_VERS := "1.9.11"
|
||||
|
||||
NGINX_OPTIONS="--error-log-path=/tmp/naxsi_ut/error.log"
|
||||
NGINX_OPTIONS+="--conf-path=/tmp/naxsi_ut/nginx.conf"
|
||||
NGINX_OPTIONS+="--http-client-body-temp-path=/tmp/naxsi_ut/body/"
|
||||
NGINX_OPTIONS+="--http-fastcgi-temp-path=/tmp/naxsi_ut/fastcgi/"
|
||||
NGINX_OPTIONS+="--http-log-path=/tmp/naxsi_ut/access.log"
|
||||
NGINX_OPTIONS+="--http-proxy-temp-path=/tmp/naxsi_ut/proxy/"
|
||||
NGINX_OPTIONS+="--lock-path=/tmpnginx.lock"
|
||||
NGINX_OPTIONS+="--pid-path=/tmp/naxsi_ut/nginx.pid"
|
||||
NGINX_OPTIONS+="--modules-path=/tmp/naxsi_ut/modules/"
|
||||
NGINX_OPTIONS+="--with-http_ssl_module"
|
||||
NGINX_OPTIONS+="--without-mail_pop3_module"
|
||||
NGINX_OPTIONS+="--without-mail_smtp_module"
|
||||
NGINX_OPTIONS+="--without-mail_imap_module"
|
||||
NGINX_OPTIONS+="--without-http_uwsgi_module"
|
||||
NGINX_OPTIONS+="--without-http_scgi_module"
|
||||
NGINX_OPTIONS+="--add-dynamic-module=$(MOD_PATH)"
|
||||
NGINX_OPTIONS+="--with-ipv6"
|
||||
NGINX_OPTIONS+="--prefix=/tmp"
|
||||
NGINX_OPTIONS+="--with-debug"
|
||||
|
||||
CFLAGS:="-Wall -Wextra"
|
||||
|
||||
all: nginx_download configure build install deploy
|
||||
|
||||
re: clean all test
|
||||
|
||||
clean:
|
||||
rm -f "nginx-"$(NGINX_VERS)".tar.gz"
|
||||
rm -f "nginx-"$(NGINX_VERS)".tar.gz.asc"
|
||||
rm -rf /tmp/naxsi_ut/
|
||||
rm -rf $(TMP_DIR)/
|
||||
|
||||
nginx_download:
|
||||
wget --no-clobber "http://nginx.org/download/nginx-"$(NGINX_VERS)".tar.gz" || exit 1
|
||||
wget --no-clobber "http://nginx.org/download/nginx-"$(NGINX_VERS)".tar.gz.asc" || exit 1
|
||||
gpg --keyserver pgp.key-server.io --recv-keys 0x251a28de2685aed4 0x520A9993A1C052F8
|
||||
gpg --verify "nginx-"$(NGINX_VERS)".tar.gz.asc" "nginx-"$(NGINX_VERS)".tar.gz" || exit 1
|
||||
mkdir -p $(TMP_DIR)/
|
||||
tar -C $(TMP_DIR)/ -xzf nginx-$(NGINX_VERS).tar.gz --strip-components=1
|
||||
|
||||
configure:
|
||||
ifeq ($(COV),1)
|
||||
cd $(TMP_DIR)/ && ./configure --with-cc-opt="--coverage -g3 -gstabs" --with-ld-opt="-lgcov" $(NGINX_OPTIONS)
|
||||
else
|
||||
cd $(TMP_DIR)/ && ./configure --with-cc-opt="-g3 -ggdb" $(NGINX_OPTIONS)
|
||||
endif
|
||||
|
||||
build:
|
||||
cd $(TMP_DIR)/ && make
|
||||
if [ -d "/tmp/naxsi_ut" ] ; then cp $(TMP_DIR)/objs/ngx_http_naxsi_module.so /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so ; fi
|
||||
|
||||
install:
|
||||
cd $(TMP_DIR)/ && make install
|
||||
|
||||
deploy:
|
||||
@cp ./nginx.conf.example /tmp/naxsi_ut/nginx.conf
|
||||
@cp ../naxsi_config/naxsi_core.rules /tmp/naxsi_ut/naxsi_core.rules
|
||||
|
||||
|
||||
# RUN UNIT TESTS
|
||||
test:
|
||||
ifeq ($(COV),1)
|
||||
lcov --directory $(TMP_DIR) --zerocounters
|
||||
endif
|
||||
|
||||
if [ ! $(TEST) ] ; then TEST="*.t" ; fi
|
||||
|
||||
export PATH="$(TMP_DIR)/objs/:"$(PATH) ; \
|
||||
export PERL5LIB="~/perl5/lib/perl5/:/home/travis/perl5/lib/perl5/" ; \
|
||||
cd .. ; prove -r "t/$(TEST)"
|
||||
|
||||
ifeq ($(COV),1)
|
||||
lcov --directory $(TMP_DIR)/objs/addon/naxsi_src/ --capture --output-file naxsi.info --base-directory $(TMP_DIR)
|
||||
genhtml -s -o /tmp/naxsicov.html naxsi.info
|
||||
endif
|
||||
|
||||
#Build for coverity and submit build !
|
||||
coverity:
|
||||
@CAK=$(shell cat ../../coverity.key | cut -d ':' -f2) ; \
|
||||
CAN=$(shell cat ../../coverity.key | cut -d ':' -f1) ; \
|
||||
echo "Coverity token/login : $$CAK and $$CAN"; \
|
||||
wget -nc https://scan.coverity.com/download/linux-64 --post-data "token=$$CAK&project=nbs-system%2Fnaxsi" -O /tmp/coverity.tgz ; \
|
||||
mkdir /tmp/cov && cd /tmp/cov && cat ../coverity.tgz | tar --strip-components=1 -xvzf - ; \
|
||||
cd $(TMP_DIR) ; \
|
||||
./configure $(NGINX_OPTIONS) && \
|
||||
/tmp/cov/bin/cov-build --dir cov-int make && \
|
||||
tar cvzf coverity-res-naxsi.tgz cov-int/ ; \
|
||||
curl --form token="$$CAK" \
|
||||
--form email="$$CAN" \
|
||||
--form file=@$(TMP_DIR)/coverity-res-naxsi.tgz \
|
||||
--form version="$(CORE_VERS)" \
|
||||
--form description="Automatically submitted" \
|
||||
https://scan.coverity.com/builds?project=nbs-system%2Fnaxsi
|
||||
52
naxsi-0.55.3/README.md
Normal file
52
naxsi-0.55.3/README.md
Normal file
@@ -0,0 +1,52 @@
|
||||
<img alt="naxsi logo" src="https://www.nbs-system.com/wp-content/uploads/nbs-logo-naxsi1.png" align="center"/>
|
||||
|
||||
[](https://scan.coverity.com/projects/1883)
|
||||
[](https://travis-ci.org/nbs-system/naxsi)
|
||||
[](https://coveralls.io/github/nbs-system/naxsi?branch=master)
|
||||
[](http://codecov.io/github/nbs-system/naxsi?branch=master)
|
||||
|
||||
### We need your help
|
||||
|
||||
[Please fill in this little feedback survey](https://docs.google.com/spreadsheet/viewform?formkey=dG9UWDFuTEhiWWt4UF9fZEtwWFVJUlE6MQ), 2 minutes of your time, great help for us !
|
||||
|
||||
|
||||
## What is Naxsi?
|
||||
|
||||
NAXSI means [Nginx]( http://nginx.org/ ) Anti [XSS]( https://www.owasp.org/index.php/Cross-site_Scripting_%28XSS%29 ) & [SQL Injection]( https://www.owasp.org/index.php/SQL_injection ).
|
||||
|
||||
Technically, it is a third party nginx module, available as a package for
|
||||
many UNIX-like platforms. This module, by default, reads a small subset of
|
||||
[simple (and readable) rules]( https://github.com/nbs-system/naxsi/blob/master/naxsi_config/naxsi_core.rules )
|
||||
containing 99% of known patterns involved in
|
||||
website vulnerabilities. For example, `<`, `|` or `drop` are not supposed
|
||||
to be part of a URI.
|
||||
|
||||
Being very simple, those patterns may match legitimate queries, it is
|
||||
the Naxsi's administrator duty to add specific rules that will whitelist
|
||||
legitimate behaviours. The administrator can either add whitelists manually
|
||||
by analyzing nginx's error log, or (recommended) start the project with an
|
||||
intensive auto-learning phase that will automatically generate whitelisting
|
||||
rules regarding a website's behaviour.
|
||||
|
||||
In short, Naxsi behaves like a DROP-by-default firewall, the only task
|
||||
is to add required ACCEPT rules for the target website to work properly.
|
||||
|
||||
## Why is it different?
|
||||
|
||||
Contrary to most Web Application Firewalls, Naxsi doesn't rely on a
|
||||
signature base like an antivirus, and thus cannot be circumvented by an
|
||||
"unknown" attack pattern. Another main difference between Naxsi and other
|
||||
WAFs, Naxsi filters only GET and POST requests,
|
||||
is [Free software]( https://www.gnu.org/licenses/gpl.html ) (as in freedom)
|
||||
and free (as in free beer) to use.
|
||||
|
||||
## What does it run on?
|
||||
Naxsi is compatible with any nginx version, although it currently doesn't play well with the new HTTPv2 protocol added in recent nginx versions. See [issue #227]( https://github.com/nbs-system/naxsi/issues/227 ) for more details.
|
||||
|
||||
It depends on `libpcre` for its regexp support, and is reported to work great on NetBSD, FreeBSD, OpenBSD, Debian, Ubuntu and CentOS.
|
||||
|
||||
### Getting started
|
||||
|
||||
- The [documentation](https://github.com/nbs-system/naxsi/wiki)
|
||||
- Some [rules]( https://github.com/nbs-system/naxsi-rules ) for mainstream software
|
||||
- The [nxapi/nxtool]( https://github.com/nbs-system/naxsi/tree/master/nxapi ) to generate rules
|
||||
12
naxsi-0.55.3/config
Normal file
12
naxsi-0.55.3/config
Normal file
@@ -0,0 +1,12 @@
|
||||
ngx_addon_name=ngx_http_naxsi_module
|
||||
|
||||
if test -n "$ngx_module_link"; then
|
||||
ngx_module_type=HTTP
|
||||
ngx_module_name=ngx_http_naxsi_module
|
||||
ngx_module_srcs="$ngx_addon_dir/naxsi_runtime.c $ngx_addon_dir/naxsi_config.c $ngx_addon_dir/naxsi_utils.c $ngx_addon_dir/naxsi_skeleton.c $ngx_addon_dir/naxsi_json.c $ngx_addon_dir/naxsi_raw.c $ngx_addon_dir/ext/libinjection/libinjection_sqli.c $ngx_addon_dir/ext/libinjection/libinjection_xss.c $ngx_addon_dir/ext/libinjection/libinjection_html5.c"
|
||||
. auto/module
|
||||
else
|
||||
HTTP_MODULES="$HTTP_MODULES ngx_http_naxsi_module"
|
||||
NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/naxsi_runtime.c $ngx_addon_dir/naxsi_config.c $ngx_addon_dir/naxsi_utils.c $ngx_addon_dir/naxsi_skeleton.c $ngx_addon_dir/naxsi_json.c $ngx_addon_dir/naxsi_raw.c $ngx_addon_dir/ext/libinjection/libinjection_sqli.c $ngx_addon_dir/ext/libinjection/libinjection_xss.c $ngx_addon_dir/ext/libinjection/libinjection_html5.c"
|
||||
NGX_ADDON_DEPS="$NGX_ADDON_DEPS $ngx_addon_dir/naxsi.h"
|
||||
fi
|
||||
30
naxsi-0.55.3/ext/libinjection/example1.c
Normal file
30
naxsi-0.55.3/ext/libinjection/example1.c
Normal file
@@ -0,0 +1,30 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "libinjection.h"
|
||||
|
||||
int main(int argc, const char* argv[])
|
||||
{
|
||||
char fingerprint[8];
|
||||
const char* input;
|
||||
size_t slen;
|
||||
int issqli;
|
||||
|
||||
if (argc < 2) {
|
||||
fprintf(stderr, "Usage: %s inputstring\n", argv[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
input = argv[1];
|
||||
slen = strlen(input);
|
||||
|
||||
|
||||
issqli = libinjection_sqli(input, slen, fingerprint);
|
||||
if (issqli) {
|
||||
printf("sqli with fingerprint of '%s'\n", fingerprint);
|
||||
} else {
|
||||
printf("not sqli\n");
|
||||
}
|
||||
|
||||
|
||||
return issqli;
|
||||
}
|
||||
79
naxsi-0.55.3/ext/libinjection/fptool.c
Normal file
79
naxsi-0.55.3/ext/libinjection/fptool.c
Normal file
@@ -0,0 +1,79 @@
|
||||
/**
|
||||
* Copyright 2012, 2013 Nick Galbreath
|
||||
* nickg@client9.com
|
||||
* BSD License -- see COPYING.txt for details
|
||||
*
|
||||
* This is for testing against files in ../data/ *.txt
|
||||
* Reads from stdin or a list of files, and emits if a line
|
||||
* is a SQLi attack or not, and does basic statistics
|
||||
*
|
||||
*/
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
#include "libinjection.h"
|
||||
#include "libinjection_sqli.h"
|
||||
|
||||
int main(int argc, const char* argv[])
|
||||
{
|
||||
size_t slen;
|
||||
int ok;
|
||||
int single = 0;
|
||||
int offset = 1;
|
||||
|
||||
sfilter sf;
|
||||
if (argc < 2) {
|
||||
fprintf(stderr, "need more args\n");
|
||||
return 1;
|
||||
}
|
||||
while (1) {
|
||||
if (strcmp(argv[offset], "-0") == 0) {
|
||||
single = 1;
|
||||
offset += 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
slen = strlen(argv[offset]);
|
||||
|
||||
if (slen == 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* "plain" context.. test string "as-is"
|
||||
*/
|
||||
libinjection_sqli_init(&sf, argv[offset], slen, 0);
|
||||
|
||||
if (single) {
|
||||
libinjection_sqli_fingerprint(&sf, FLAG_QUOTE_NONE | FLAG_SQL_ANSI);
|
||||
ok = libinjection_sqli_check_fingerprint(&sf);
|
||||
fprintf(stdout, "%s\n", sf.fingerprint);
|
||||
return 0;
|
||||
}
|
||||
|
||||
libinjection_sqli_fingerprint(&sf, FLAG_QUOTE_NONE | FLAG_SQL_ANSI);
|
||||
ok = libinjection_sqli_check_fingerprint(&sf);
|
||||
fprintf(stdout, "plain-asni\t%s\t%s\n", sf.fingerprint, ok ? "true": "false");
|
||||
|
||||
libinjection_sqli_fingerprint(&sf, FLAG_QUOTE_NONE | FLAG_SQL_MYSQL);
|
||||
ok = libinjection_sqli_check_fingerprint(&sf);
|
||||
fprintf(stdout, "plain-mysql\t%s\t%s\n", sf.fingerprint, ok ? "true": "false");
|
||||
|
||||
libinjection_sqli_fingerprint(&sf, FLAG_QUOTE_SINGLE | FLAG_SQL_ANSI);
|
||||
ok = libinjection_sqli_check_fingerprint(&sf);
|
||||
fprintf(stdout, "single-ansi\t%s\t%s\n", sf.fingerprint, ok ? "true": "false");
|
||||
|
||||
libinjection_sqli_fingerprint(&sf, FLAG_QUOTE_SINGLE | FLAG_SQL_MYSQL);
|
||||
ok = libinjection_sqli_check_fingerprint(&sf);
|
||||
fprintf(stdout, "single-mysql\t%s\t%s\n", sf.fingerprint, ok ? "true": "false");
|
||||
|
||||
libinjection_sqli_fingerprint(&sf, FLAG_QUOTE_DOUBLE | FLAG_SQL_MYSQL);
|
||||
ok = libinjection_sqli_check_fingerprint(&sf);
|
||||
fprintf(stdout, "double-mysql\t%s\t%s\n", sf.fingerprint, ok ? "true": "false");
|
||||
|
||||
return 0;
|
||||
}
|
||||
168
naxsi-0.55.3/ext/libinjection/html5_cli.c
Normal file
168
naxsi-0.55.3/ext/libinjection/html5_cli.c
Normal file
@@ -0,0 +1,168 @@
|
||||
/**
|
||||
* Copyright 2012, 2013, 2014 Nick Galbreath
|
||||
* nickg@client9.com
|
||||
* BSD License -- see COPYING.txt for details
|
||||
*
|
||||
* This is for testing against files in ../data/ *.txt
|
||||
* Reads from stdin or a list of files, and emits if a line
|
||||
* is a SQLi attack or not, and does basic statistics
|
||||
*
|
||||
*/
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "libinjection_html5.h"
|
||||
#include "libinjection_xss.h"
|
||||
#include "libinjection.h"
|
||||
|
||||
int urlcharmap(char ch);
|
||||
size_t modp_url_decode(char* dest, const char* s, size_t len);
|
||||
const char* h5_type_to_string(enum html5_type x);
|
||||
void print_html5_token(h5_state_t* hs);
|
||||
|
||||
int urlcharmap(char ch) {
|
||||
switch (ch) {
|
||||
case '0': return 0;
|
||||
case '1': return 1;
|
||||
case '2': return 2;
|
||||
case '3': return 3;
|
||||
case '4': return 4;
|
||||
case '5': return 5;
|
||||
case '6': return 6;
|
||||
case '7': return 7;
|
||||
case '8': return 8;
|
||||
case '9': return 9;
|
||||
case 'a': case 'A': return 10;
|
||||
case 'b': case 'B': return 11;
|
||||
case 'c': case 'C': return 12;
|
||||
case 'd': case 'D': return 13;
|
||||
case 'e': case 'E': return 14;
|
||||
case 'f': case 'F': return 15;
|
||||
default:
|
||||
return 256;
|
||||
}
|
||||
}
|
||||
|
||||
size_t modp_url_decode(char* dest, const char* s, size_t len)
|
||||
{
|
||||
const char* deststart = dest;
|
||||
|
||||
size_t i = 0;
|
||||
int d = 0;
|
||||
while (i < len) {
|
||||
switch (s[i]) {
|
||||
case '+':
|
||||
*dest++ = ' ';
|
||||
i += 1;
|
||||
break;
|
||||
case '%':
|
||||
if (i+2 < len) {
|
||||
d = (urlcharmap(s[i+1]) << 4) | urlcharmap(s[i+2]);
|
||||
if ( d < 256) {
|
||||
*dest = (char) d;
|
||||
dest++;
|
||||
i += 3; /* loop will increment one time */
|
||||
} else {
|
||||
*dest++ = '%';
|
||||
i += 1;
|
||||
}
|
||||
} else {
|
||||
*dest++ = '%';
|
||||
i += 1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
*dest++ = s[i];
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
*dest = '\0';
|
||||
return (size_t)(dest - deststart); /* compute "strlen" of dest */
|
||||
}
|
||||
|
||||
const char* h5_type_to_string(enum html5_type x)
|
||||
{
|
||||
switch (x) {
|
||||
case DATA_TEXT: return "DATA_TEXT";
|
||||
case TAG_NAME_OPEN: return "TAG_NAME_OPEN";
|
||||
case TAG_NAME_CLOSE: return "TAG_NAME_CLOSE";
|
||||
case TAG_NAME_SELFCLOSE: return "TAG_NAME_SELFCLOSE";
|
||||
case TAG_DATA: return "TAG_DATA";
|
||||
case TAG_CLOSE: return "TAG_CLOSE";
|
||||
case ATTR_NAME: return "ATTR_NAME";
|
||||
case ATTR_VALUE: return "ATTR_VALUE";
|
||||
case TAG_COMMENT: return "TAG_COMMENT";
|
||||
case DOCTYPE: return "DOCTYPE";
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
void print_html5_token(h5_state_t* hs)
|
||||
{
|
||||
char* tmp = (char*) malloc(hs->token_len + 1);
|
||||
memcpy(tmp, hs->token_start, hs->token_len);
|
||||
/* TODO.. encode to be printable */
|
||||
tmp[hs->token_len] = '\0';
|
||||
|
||||
printf("%s,%d,%s\n",
|
||||
h5_type_to_string(hs->token_type),
|
||||
(int) hs->token_len,
|
||||
tmp);
|
||||
|
||||
free(tmp);
|
||||
}
|
||||
|
||||
int main(int argc, const char* argv[])
|
||||
{
|
||||
size_t slen;
|
||||
h5_state_t hs;
|
||||
char* copy;
|
||||
int offset = 1;
|
||||
int flag = 0;
|
||||
int urldecode = 0;
|
||||
|
||||
if (argc < 2) {
|
||||
fprintf(stderr, "need more args\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
while (offset < argc) {
|
||||
if (strcmp(argv[offset], "-u") == 0) {
|
||||
offset += 1;
|
||||
urldecode = 1;
|
||||
|
||||
} else if (strcmp(argv[offset], "-f") == 0) {
|
||||
offset += 1;
|
||||
flag = atoi(argv[offset]);
|
||||
offset += 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* ATTENTION: argv is a C-string, null terminated. We copy this
|
||||
* to it's own location, WITHOUT null byte. This way, valgrind
|
||||
* can see if we run past the buffer.
|
||||
*/
|
||||
|
||||
slen = strlen(argv[offset]);
|
||||
copy = (char* ) malloc(slen);
|
||||
memcpy(copy, argv[offset], slen);
|
||||
if (urldecode) {
|
||||
slen = modp_url_decode(copy, copy, slen);
|
||||
}
|
||||
|
||||
libinjection_h5_init(&hs, copy, slen, (enum html5_flags) flag);
|
||||
while (libinjection_h5_next(&hs)) {
|
||||
print_html5_token(&hs);
|
||||
}
|
||||
|
||||
if (libinjection_is_xss(copy, slen, flag)) {
|
||||
printf("is injection!\n");
|
||||
}
|
||||
free(copy);
|
||||
return 0;
|
||||
}
|
||||
65
naxsi-0.55.3/ext/libinjection/libinjection.h
Normal file
65
naxsi-0.55.3/ext/libinjection/libinjection.h
Normal file
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* Copyright 2012, 2013 Nick Galbreath
|
||||
* nickg@client9.com
|
||||
* BSD License -- see COPYING.txt for details
|
||||
*
|
||||
* https://libinjection.client9.com/
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _LIBINJECTION_H
|
||||
#define _LIBINJECTION_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
# define LIBINJECTION_BEGIN_DECLS extern "C" {
|
||||
# define LIBINJECTION_END_DECLS }
|
||||
#else
|
||||
# define LIBINJECTION_BEGIN_DECLS
|
||||
# define LIBINJECTION_END_DECLS
|
||||
#endif
|
||||
|
||||
LIBINJECTION_BEGIN_DECLS
|
||||
|
||||
/*
|
||||
* Pull in size_t
|
||||
*/
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
* Version info.
|
||||
*
|
||||
* This is moved into a function to allow SWIG and other auto-generated
|
||||
* binding to not be modified during minor release changes. We change
|
||||
* change the version number in the c source file, and not regenerated
|
||||
* the binding
|
||||
*
|
||||
* See python's normalized version
|
||||
* http://www.python.org/dev/peps/pep-0386/#normalizedversion
|
||||
*/
|
||||
const char* libinjection_version(void);
|
||||
|
||||
/**
|
||||
* Simple API for SQLi detection - returns a SQLi fingerprint or NULL
|
||||
* is benign input
|
||||
*
|
||||
* \param[in] s input string, may contain nulls, does not need to be null-terminated
|
||||
* \param[in] slen input string length
|
||||
* \param[out] fingerprint buffer of 8+ characters. c-string,
|
||||
* \return 1 if SQLi, 0 if benign. fingerprint will be set or set to empty string.
|
||||
*/
|
||||
int libinjection_sqli(const char* s, size_t slen, char fingerprint[]);
|
||||
|
||||
/** ALPHA version of xss detector.
|
||||
*
|
||||
* NOT DONE.
|
||||
*
|
||||
* \param[in] s input string, may contain nulls, does not need to be null-terminated
|
||||
* \param[in] slen input string length
|
||||
* \return 1 if XSS found, 0 if benign
|
||||
*
|
||||
*/
|
||||
int libinjection_xss(const char* s, size_t slen);
|
||||
|
||||
LIBINJECTION_END_DECLS
|
||||
|
||||
#endif /* _LIBINJECTION_H */
|
||||
847
naxsi-0.55.3/ext/libinjection/libinjection_html5.c
Normal file
847
naxsi-0.55.3/ext/libinjection/libinjection_html5.c
Normal file
@@ -0,0 +1,847 @@
|
||||
#include "libinjection_html5.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#ifdef DEBUG
|
||||
#include <stdio.h>
|
||||
#define TRACE() printf("%s:%d\n", __FUNCTION__, __LINE__)
|
||||
#else
|
||||
#define TRACE()
|
||||
#endif
|
||||
|
||||
|
||||
#define CHAR_EOF -1
|
||||
#define CHAR_NULL 0
|
||||
#define CHAR_BANG 33
|
||||
#define CHAR_DOUBLE 34
|
||||
#define CHAR_PERCENT 37
|
||||
#define CHAR_SINGLE 39
|
||||
#define CHAR_DASH 45
|
||||
#define CHAR_SLASH 47
|
||||
#define CHAR_LT 60
|
||||
#define CHAR_EQUALS 61
|
||||
#define CHAR_GT 62
|
||||
#define CHAR_QUESTION 63
|
||||
#define CHAR_RIGHTB 93
|
||||
#define CHAR_TICK 96
|
||||
|
||||
/* prototypes */
|
||||
|
||||
static int h5_skip_white(h5_state_t* hs);
|
||||
static int h5_is_white(char c);
|
||||
static int h5_state_eof(h5_state_t* hs);
|
||||
static int h5_state_data(h5_state_t* hs);
|
||||
static int h5_state_tag_open(h5_state_t* hs);
|
||||
static int h5_state_tag_name(h5_state_t* hs);
|
||||
static int h5_state_tag_name_close(h5_state_t* hs);
|
||||
static int h5_state_end_tag_open(h5_state_t* hs);
|
||||
static int h5_state_self_closing_start_tag(h5_state_t* hs);
|
||||
static int h5_state_attribute_name(h5_state_t* hs);
|
||||
static int h5_state_after_attribute_name(h5_state_t* hs);
|
||||
static int h5_state_before_attribute_name(h5_state_t* hs);
|
||||
static int h5_state_before_attribute_value(h5_state_t* hs);
|
||||
static int h5_state_attribute_value_double_quote(h5_state_t* hs);
|
||||
static int h5_state_attribute_value_single_quote(h5_state_t* hs);
|
||||
static int h5_state_attribute_value_back_quote(h5_state_t* hs);
|
||||
static int h5_state_attribute_value_no_quote(h5_state_t* hs);
|
||||
static int h5_state_after_attribute_value_quoted_state(h5_state_t* hs);
|
||||
static int h5_state_comment(h5_state_t* hs);
|
||||
static int h5_state_cdata(h5_state_t* hs);
|
||||
|
||||
|
||||
/* 12.2.4.44 */
|
||||
static int h5_state_bogus_comment(h5_state_t* hs);
|
||||
static int h5_state_bogus_comment2(h5_state_t* hs);
|
||||
|
||||
/* 12.2.4.45 */
|
||||
static int h5_state_markup_declaration_open(h5_state_t* hs);
|
||||
|
||||
/* 8.2.4.52 */
|
||||
static int h5_state_doctype(h5_state_t* hs);
|
||||
|
||||
/**
|
||||
* public function
|
||||
*/
|
||||
void libinjection_h5_init(h5_state_t* hs, const char* s, size_t len, enum html5_flags flags)
|
||||
{
|
||||
memset(hs, 0, sizeof(h5_state_t));
|
||||
hs->s = s;
|
||||
hs->len = len;
|
||||
|
||||
switch (flags) {
|
||||
case DATA_STATE:
|
||||
hs->state = h5_state_data;
|
||||
break;
|
||||
case VALUE_NO_QUOTE:
|
||||
hs->state = h5_state_before_attribute_name;
|
||||
break;
|
||||
case VALUE_SINGLE_QUOTE:
|
||||
hs->state = h5_state_attribute_value_single_quote;
|
||||
break;
|
||||
case VALUE_DOUBLE_QUOTE:
|
||||
hs->state = h5_state_attribute_value_double_quote;
|
||||
break;
|
||||
case VALUE_BACK_QUOTE:
|
||||
hs->state = h5_state_attribute_value_back_quote;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* public function
|
||||
*/
|
||||
int libinjection_h5_next(h5_state_t* hs)
|
||||
{
|
||||
assert(hs->state != NULL);
|
||||
return (*hs->state)(hs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Everything below here is private
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
static int h5_is_white(char ch)
|
||||
{
|
||||
/*
|
||||
* \t = htab = 0x09
|
||||
* \n = newline = 0x0A
|
||||
* \v = vtab = 0x0B
|
||||
* \f = form feed = 0x0C
|
||||
* \r = cr = 0x0D
|
||||
*/
|
||||
return strchr(" \t\n\v\f\r", ch) != NULL;
|
||||
}
|
||||
|
||||
static int h5_skip_white(h5_state_t* hs)
|
||||
{
|
||||
char ch;
|
||||
while (hs->pos < hs->len) {
|
||||
ch = hs->s[hs->pos];
|
||||
switch (ch) {
|
||||
case 0x00: /* IE only */
|
||||
case 0x20:
|
||||
case 0x09:
|
||||
case 0x0A:
|
||||
case 0x0B: /* IE only */
|
||||
case 0x0C:
|
||||
case 0x0D: /* IE only */
|
||||
hs->pos += 1;
|
||||
break;
|
||||
default:
|
||||
return ch;
|
||||
}
|
||||
}
|
||||
return CHAR_EOF;
|
||||
}
|
||||
|
||||
static int h5_state_eof(h5_state_t* hs)
|
||||
{
|
||||
/* eliminate unused function argument warning */
|
||||
(void)hs;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int h5_state_data(h5_state_t* hs)
|
||||
{
|
||||
const char* idx;
|
||||
|
||||
TRACE();
|
||||
assert(hs->len >= hs->pos);
|
||||
idx = (const char*) memchr(hs->s + hs->pos, CHAR_LT, hs->len - hs->pos);
|
||||
if (idx == NULL) {
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = hs->len - hs->pos;
|
||||
hs->token_type = DATA_TEXT;
|
||||
hs->state = h5_state_eof;
|
||||
if (hs->token_len == 0) {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_type = DATA_TEXT;
|
||||
hs->token_len = (size_t)(idx - hs->s) - hs->pos;
|
||||
hs->pos = (size_t)(idx - hs->s) + 1;
|
||||
hs->state = h5_state_tag_open;
|
||||
if (hs->token_len == 0) {
|
||||
return h5_state_tag_open(hs);
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 12 2.4.8
|
||||
*/
|
||||
static int h5_state_tag_open(h5_state_t* hs)
|
||||
{
|
||||
char ch;
|
||||
|
||||
TRACE();
|
||||
ch = hs->s[hs->pos];
|
||||
if (ch == CHAR_BANG) {
|
||||
hs->pos += 1;
|
||||
return h5_state_markup_declaration_open(hs);
|
||||
} else if (ch == CHAR_SLASH) {
|
||||
hs->pos += 1;
|
||||
hs->is_close = 1;
|
||||
return h5_state_end_tag_open(hs);
|
||||
} else if (ch == CHAR_QUESTION) {
|
||||
hs->pos += 1;
|
||||
return h5_state_bogus_comment(hs);
|
||||
} else if (ch == CHAR_PERCENT) {
|
||||
/* this is not in spec.. alternative comment format used
|
||||
by IE <= 9 and Safari < 4.0.3 */
|
||||
hs->pos += 1;
|
||||
return h5_state_bogus_comment2(hs);
|
||||
} else if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) {
|
||||
return h5_state_tag_name(hs);
|
||||
} else if (ch == CHAR_NULL) {
|
||||
/* IE-ism NULL characters are ignored */
|
||||
return h5_state_tag_name(hs);
|
||||
} else {
|
||||
/* user input mistake in configuring state */
|
||||
if (hs->pos == 0) {
|
||||
return h5_state_data(hs);
|
||||
}
|
||||
hs->token_start = hs->s + hs->pos - 1;
|
||||
hs->token_len = 1;
|
||||
hs->token_type = DATA_TEXT;
|
||||
hs->state = h5_state_data;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 12.2.4.9
|
||||
*/
|
||||
static int h5_state_end_tag_open(h5_state_t* hs)
|
||||
{
|
||||
char ch;
|
||||
|
||||
TRACE();
|
||||
|
||||
if (hs->pos >= hs->len) {
|
||||
return 0;
|
||||
}
|
||||
ch = hs->s[hs->pos];
|
||||
if (ch == CHAR_GT) {
|
||||
return h5_state_data(hs);
|
||||
} else if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) {
|
||||
return h5_state_tag_name(hs);
|
||||
}
|
||||
|
||||
hs->is_close = 0;
|
||||
return h5_state_bogus_comment(hs);
|
||||
}
|
||||
/*
|
||||
*
|
||||
*/
|
||||
static int h5_state_tag_name_close(h5_state_t* hs)
|
||||
{
|
||||
TRACE();
|
||||
hs->is_close = 0;
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = 1;
|
||||
hs->token_type = TAG_NAME_CLOSE;
|
||||
hs->pos += 1;
|
||||
if (hs->pos < hs->len) {
|
||||
hs->state = h5_state_data;
|
||||
} else {
|
||||
hs->state = h5_state_eof;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 12.2.4.10
|
||||
*/
|
||||
static int h5_state_tag_name(h5_state_t* hs)
|
||||
{
|
||||
char ch;
|
||||
size_t pos;
|
||||
|
||||
TRACE();
|
||||
pos = hs->pos;
|
||||
while (pos < hs->len) {
|
||||
ch = hs->s[pos];
|
||||
if (ch == 0) {
|
||||
/* special non-standard case */
|
||||
/* allow nulls in tag name */
|
||||
/* some old browsers apparently allow and ignore them */
|
||||
pos += 1;
|
||||
} else if (h5_is_white(ch)) {
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = pos - hs->pos;
|
||||
hs->token_type = TAG_NAME_OPEN;
|
||||
hs->pos = pos + 1;
|
||||
hs->state = h5_state_before_attribute_name;
|
||||
return 1;
|
||||
} else if (ch == CHAR_SLASH) {
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = pos - hs->pos;
|
||||
hs->token_type = TAG_NAME_OPEN;
|
||||
hs->pos = pos + 1;
|
||||
hs->state = h5_state_self_closing_start_tag;
|
||||
return 1;
|
||||
} else if (ch == CHAR_GT) {
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = pos - hs->pos;
|
||||
if (hs->is_close) {
|
||||
hs->pos = pos + 1;
|
||||
hs->is_close = 0;
|
||||
hs->token_type = TAG_CLOSE;
|
||||
hs->state = h5_state_data;
|
||||
} else {
|
||||
hs->pos = pos;
|
||||
hs->token_type = TAG_NAME_OPEN;
|
||||
hs->state = h5_state_tag_name_close;
|
||||
}
|
||||
return 1;
|
||||
} else {
|
||||
pos += 1;
|
||||
}
|
||||
}
|
||||
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = hs->len - hs->pos;
|
||||
hs->token_type = TAG_NAME_OPEN;
|
||||
hs->state = h5_state_eof;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 12.2.4.34
|
||||
*/
|
||||
static int h5_state_before_attribute_name(h5_state_t* hs)
|
||||
{
|
||||
int ch;
|
||||
|
||||
TRACE();
|
||||
ch = h5_skip_white(hs);
|
||||
switch (ch) {
|
||||
case CHAR_EOF: {
|
||||
return 0;
|
||||
}
|
||||
case CHAR_SLASH: {
|
||||
hs->pos += 1;
|
||||
return h5_state_self_closing_start_tag(hs);
|
||||
}
|
||||
case CHAR_GT: {
|
||||
hs->state = h5_state_data;
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = 1;
|
||||
hs->token_type = TAG_NAME_CLOSE;
|
||||
hs->pos += 1;
|
||||
return 1;
|
||||
}
|
||||
default: {
|
||||
return h5_state_attribute_name(hs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int h5_state_attribute_name(h5_state_t* hs)
|
||||
{
|
||||
char ch;
|
||||
size_t pos;
|
||||
|
||||
TRACE();
|
||||
pos = hs->pos + 1;
|
||||
while (pos < hs->len) {
|
||||
ch = hs->s[pos];
|
||||
if (h5_is_white(ch)) {
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = pos - hs->pos;
|
||||
hs->token_type = ATTR_NAME;
|
||||
hs->state = h5_state_after_attribute_name;
|
||||
hs->pos = pos + 1;
|
||||
return 1;
|
||||
} else if (ch == CHAR_SLASH) {
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = pos - hs->pos;
|
||||
hs->token_type = ATTR_NAME;
|
||||
hs->state = h5_state_self_closing_start_tag;
|
||||
hs->pos = pos + 1;
|
||||
return 1;
|
||||
} else if (ch == CHAR_EQUALS) {
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = pos - hs->pos;
|
||||
hs->token_type = ATTR_NAME;
|
||||
hs->state = h5_state_before_attribute_value;
|
||||
hs->pos = pos + 1;
|
||||
return 1;
|
||||
} else if (ch == CHAR_GT) {
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = pos - hs->pos;
|
||||
hs->token_type = ATTR_NAME;
|
||||
hs->state = h5_state_tag_name_close;
|
||||
hs->pos = pos;
|
||||
return 1;
|
||||
} else {
|
||||
pos += 1;
|
||||
}
|
||||
}
|
||||
/* EOF */
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = hs->len - hs->pos;
|
||||
hs->token_type = ATTR_NAME;
|
||||
hs->state = h5_state_eof;
|
||||
hs->pos = hs->len;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 12.2.4.36
|
||||
*/
|
||||
static int h5_state_after_attribute_name(h5_state_t* hs)
|
||||
{
|
||||
int c;
|
||||
|
||||
TRACE();
|
||||
c = h5_skip_white(hs);
|
||||
switch (c) {
|
||||
case CHAR_EOF: {
|
||||
return 0;
|
||||
}
|
||||
case CHAR_SLASH: {
|
||||
hs->pos += 1;
|
||||
return h5_state_self_closing_start_tag(hs);
|
||||
}
|
||||
case CHAR_EQUALS: {
|
||||
hs->pos += 1;
|
||||
return h5_state_before_attribute_value(hs);
|
||||
}
|
||||
case CHAR_GT: {
|
||||
return h5_state_tag_name_close(hs);
|
||||
}
|
||||
default: {
|
||||
return h5_state_attribute_name(hs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 12.2.4.37
|
||||
*/
|
||||
static int h5_state_before_attribute_value(h5_state_t* hs)
|
||||
{
|
||||
int c;
|
||||
TRACE();
|
||||
|
||||
c = h5_skip_white(hs);
|
||||
|
||||
if (c == CHAR_EOF) {
|
||||
hs->state = h5_state_eof;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (c == CHAR_DOUBLE) {
|
||||
return h5_state_attribute_value_double_quote(hs);
|
||||
} else if (c == CHAR_SINGLE) {
|
||||
return h5_state_attribute_value_single_quote(hs);
|
||||
} else if (c == CHAR_TICK) {
|
||||
/* NON STANDARD IE */
|
||||
return h5_state_attribute_value_back_quote(hs);
|
||||
} else {
|
||||
return h5_state_attribute_value_no_quote(hs);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int h5_state_attribute_value_quote(h5_state_t* hs, char qchar)
|
||||
{
|
||||
const char* idx;
|
||||
|
||||
TRACE();
|
||||
|
||||
/* skip initial quote in normal case.
|
||||
* dont do this is pos == 0 since it means we have started
|
||||
* in a non-data state. given an input of '><foo
|
||||
* we want to make 0-length attribute name
|
||||
*/
|
||||
if (hs->pos > 0) {
|
||||
hs->pos += 1;
|
||||
}
|
||||
|
||||
|
||||
idx = (const char*) memchr(hs->s + hs->pos, qchar, hs->len - hs->pos);
|
||||
if (idx == NULL) {
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = hs->len - hs->pos;
|
||||
hs->token_type = ATTR_VALUE;
|
||||
hs->state = h5_state_eof;
|
||||
} else {
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = (size_t)(idx - hs->s) - hs->pos;
|
||||
hs->token_type = ATTR_VALUE;
|
||||
hs->state = h5_state_after_attribute_value_quoted_state;
|
||||
hs->pos += hs->token_len + 1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static
|
||||
int h5_state_attribute_value_double_quote(h5_state_t* hs)
|
||||
{
|
||||
TRACE();
|
||||
return h5_state_attribute_value_quote(hs, CHAR_DOUBLE);
|
||||
}
|
||||
|
||||
static
|
||||
int h5_state_attribute_value_single_quote(h5_state_t* hs)
|
||||
{
|
||||
TRACE();
|
||||
return h5_state_attribute_value_quote(hs, CHAR_SINGLE);
|
||||
}
|
||||
|
||||
static
|
||||
int h5_state_attribute_value_back_quote(h5_state_t* hs)
|
||||
{
|
||||
TRACE();
|
||||
return h5_state_attribute_value_quote(hs, CHAR_TICK);
|
||||
}
|
||||
|
||||
static int h5_state_attribute_value_no_quote(h5_state_t* hs)
|
||||
{
|
||||
char ch;
|
||||
size_t pos;
|
||||
|
||||
TRACE();
|
||||
pos = hs->pos;
|
||||
while (pos < hs->len) {
|
||||
ch = hs->s[pos];
|
||||
if (h5_is_white(ch)) {
|
||||
hs->token_type = ATTR_VALUE;
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = pos - hs->pos;
|
||||
hs->pos = pos + 1;
|
||||
hs->state = h5_state_before_attribute_name;
|
||||
return 1;
|
||||
} else if (ch == CHAR_GT) {
|
||||
hs->token_type = ATTR_VALUE;
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = pos - hs->pos;
|
||||
hs->pos = pos;
|
||||
hs->state = h5_state_tag_name_close;
|
||||
return 1;
|
||||
}
|
||||
pos += 1;
|
||||
}
|
||||
TRACE();
|
||||
/* EOF */
|
||||
hs->state = h5_state_eof;
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = hs->len - hs->pos;
|
||||
hs->token_type = ATTR_VALUE;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 12.2.4.41
|
||||
*/
|
||||
static int h5_state_after_attribute_value_quoted_state(h5_state_t* hs)
|
||||
{
|
||||
char ch;
|
||||
|
||||
TRACE();
|
||||
if (hs->pos >= hs->len) {
|
||||
return 0;
|
||||
}
|
||||
ch = hs->s[hs->pos];
|
||||
if (h5_is_white(ch)) {
|
||||
hs->pos += 1;
|
||||
return h5_state_before_attribute_name(hs);
|
||||
} else if (ch == CHAR_SLASH) {
|
||||
hs->pos += 1;
|
||||
return h5_state_self_closing_start_tag(hs);
|
||||
} else if (ch == CHAR_GT) {
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = 1;
|
||||
hs->token_type = TAG_NAME_CLOSE;
|
||||
hs->pos += 1;
|
||||
hs->state = h5_state_data;
|
||||
return 1;
|
||||
} else {
|
||||
return h5_state_before_attribute_name(hs);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 12.2.4.43
|
||||
*/
|
||||
static int h5_state_self_closing_start_tag(h5_state_t* hs)
|
||||
{
|
||||
char ch;
|
||||
|
||||
TRACE();
|
||||
if (hs->pos >= hs->len) {
|
||||
return 0;
|
||||
}
|
||||
ch = hs->s[hs->pos];
|
||||
if (ch == CHAR_GT) {
|
||||
assert(hs->pos > 0);
|
||||
hs->token_start = hs->s + hs->pos -1;
|
||||
hs->token_len = 2;
|
||||
hs->token_type = TAG_NAME_SELFCLOSE;
|
||||
hs->state = h5_state_data;
|
||||
hs->pos += 1;
|
||||
return 1;
|
||||
} else {
|
||||
return h5_state_before_attribute_name(hs);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 12.2.4.44
|
||||
*/
|
||||
static int h5_state_bogus_comment(h5_state_t* hs)
|
||||
{
|
||||
const char* idx;
|
||||
|
||||
TRACE();
|
||||
idx = (const char*) memchr(hs->s + hs->pos, CHAR_GT, hs->len - hs->pos);
|
||||
if (idx == NULL) {
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = hs->len - hs->pos;
|
||||
hs->pos = hs->len;
|
||||
hs->state = h5_state_eof;
|
||||
} else {
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = (size_t)(idx - hs->s) - hs->pos;
|
||||
hs->pos = (size_t)(idx - hs->s) + 1;
|
||||
hs->state = h5_state_data;
|
||||
}
|
||||
|
||||
hs->token_type = TAG_COMMENT;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 12.2.4.44 ALT
|
||||
*/
|
||||
static int h5_state_bogus_comment2(h5_state_t* hs)
|
||||
{
|
||||
const char* idx;
|
||||
size_t pos;
|
||||
|
||||
TRACE();
|
||||
pos = hs->pos;
|
||||
while (1) {
|
||||
idx = (const char*) memchr(hs->s + pos, CHAR_PERCENT, hs->len - pos);
|
||||
if (idx == NULL || (idx + 1 >= hs->s + hs->len)) {
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = hs->len - hs->pos;
|
||||
hs->pos = hs->len;
|
||||
hs->token_type = TAG_COMMENT;
|
||||
hs->state = h5_state_eof;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (*(idx +1) != CHAR_GT) {
|
||||
pos = (size_t)(idx - hs->s) + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* ends in %> */
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = (size_t)(idx - hs->s) - hs->pos;
|
||||
hs->pos = (size_t)(idx - hs->s) + 2;
|
||||
hs->state = h5_state_data;
|
||||
hs->token_type = TAG_COMMENT;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 8.2.4.45
|
||||
*/
|
||||
static int h5_state_markup_declaration_open(h5_state_t* hs)
|
||||
{
|
||||
size_t remaining;
|
||||
|
||||
TRACE();
|
||||
remaining = hs->len - hs->pos;
|
||||
if (remaining >= 7 &&
|
||||
/* case insensitive */
|
||||
(hs->s[hs->pos + 0] == 'D' || hs->s[hs->pos + 0] == 'd') &&
|
||||
(hs->s[hs->pos + 1] == 'O' || hs->s[hs->pos + 1] == 'o') &&
|
||||
(hs->s[hs->pos + 2] == 'C' || hs->s[hs->pos + 2] == 'c') &&
|
||||
(hs->s[hs->pos + 3] == 'T' || hs->s[hs->pos + 3] == 't') &&
|
||||
(hs->s[hs->pos + 4] == 'Y' || hs->s[hs->pos + 4] == 'y') &&
|
||||
(hs->s[hs->pos + 5] == 'P' || hs->s[hs->pos + 5] == 'p') &&
|
||||
(hs->s[hs->pos + 6] == 'E' || hs->s[hs->pos + 6] == 'e')
|
||||
) {
|
||||
return h5_state_doctype(hs);
|
||||
} else if (remaining >= 7 &&
|
||||
/* upper case required */
|
||||
hs->s[hs->pos + 0] == '[' &&
|
||||
hs->s[hs->pos + 1] == 'C' &&
|
||||
hs->s[hs->pos + 2] == 'D' &&
|
||||
hs->s[hs->pos + 3] == 'A' &&
|
||||
hs->s[hs->pos + 4] == 'T' &&
|
||||
hs->s[hs->pos + 5] == 'A' &&
|
||||
hs->s[hs->pos + 6] == '['
|
||||
) {
|
||||
hs->pos += 7;
|
||||
return h5_state_cdata(hs);
|
||||
} else if (remaining >= 2 &&
|
||||
hs->s[hs->pos + 0] == '-' &&
|
||||
hs->s[hs->pos + 1] == '-') {
|
||||
hs->pos += 2;
|
||||
return h5_state_comment(hs);
|
||||
}
|
||||
|
||||
return h5_state_bogus_comment(hs);
|
||||
}
|
||||
|
||||
/**
|
||||
* 12.2.4.48
|
||||
* 12.2.4.49
|
||||
* 12.2.4.50
|
||||
* 12.2.4.51
|
||||
* state machine spec is confusing since it can only look
|
||||
* at one character at a time but simply it's comments end by:
|
||||
* 1) EOF
|
||||
* 2) ending in -->
|
||||
* 3) ending in -!>
|
||||
*/
|
||||
static int h5_state_comment(h5_state_t* hs)
|
||||
{
|
||||
char ch;
|
||||
const char* idx;
|
||||
size_t pos;
|
||||
size_t offset;
|
||||
const char* end = hs->s + hs->len;
|
||||
|
||||
TRACE();
|
||||
pos = hs->pos;
|
||||
while (1) {
|
||||
|
||||
idx = (const char*) memchr(hs->s + pos, CHAR_DASH, hs->len - pos);
|
||||
|
||||
/* did not find anything or has less than 3 chars left */
|
||||
if (idx == NULL || idx > hs->s + hs->len - 3) {
|
||||
hs->state = h5_state_eof;
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = hs->len - hs->pos;
|
||||
hs->token_type = TAG_COMMENT;
|
||||
return 1;
|
||||
}
|
||||
offset = 1;
|
||||
|
||||
/* skip all nulls */
|
||||
while (idx + offset < end && *(idx + offset) == 0) {
|
||||
offset += 1;
|
||||
}
|
||||
if (idx + offset == end) {
|
||||
hs->state = h5_state_eof;
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = hs->len - hs->pos;
|
||||
hs->token_type = TAG_COMMENT;
|
||||
return 1;
|
||||
}
|
||||
|
||||
ch = *(idx + offset);
|
||||
if (ch != CHAR_DASH && ch != CHAR_BANG) {
|
||||
pos = (size_t)(idx - hs->s) + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* need to test */
|
||||
#if 0
|
||||
/* skip all nulls */
|
||||
while (idx + offset < end && *(idx + offset) == 0) {
|
||||
offset += 1;
|
||||
}
|
||||
if (idx + offset == end) {
|
||||
hs->state = h5_state_eof;
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = hs->len - hs->pos;
|
||||
hs->token_type = TAG_COMMENT;
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
offset += 1;
|
||||
if (idx + offset == end) {
|
||||
hs->state = h5_state_eof;
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = hs->len - hs->pos;
|
||||
hs->token_type = TAG_COMMENT;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
ch = *(idx + offset);
|
||||
if (ch != CHAR_GT) {
|
||||
pos = (size_t)(idx - hs->s) + 1;
|
||||
continue;
|
||||
}
|
||||
offset += 1;
|
||||
|
||||
/* ends in --> or -!> */
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = (size_t)(idx - hs->s) - hs->pos;
|
||||
hs->pos = (size_t)(idx + offset - hs->s);
|
||||
hs->state = h5_state_data;
|
||||
hs->token_type = TAG_COMMENT;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
static int h5_state_cdata(h5_state_t* hs)
|
||||
{
|
||||
const char* idx;
|
||||
size_t pos;
|
||||
|
||||
TRACE();
|
||||
pos = hs->pos;
|
||||
while (1) {
|
||||
idx = (const char*) memchr(hs->s + pos, CHAR_RIGHTB, hs->len - pos);
|
||||
|
||||
/* did not find anything or has less than 3 chars left */
|
||||
if (idx == NULL || idx > hs->s + hs->len - 3) {
|
||||
hs->state = h5_state_eof;
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = hs->len - hs->pos;
|
||||
hs->token_type = DATA_TEXT;
|
||||
return 1;
|
||||
} else if ( *(idx+1) == CHAR_RIGHTB && *(idx+2) == CHAR_GT) {
|
||||
hs->state = h5_state_data;
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = (size_t)(idx - hs->s) - hs->pos;
|
||||
hs->pos = (size_t)(idx - hs->s) + 3;
|
||||
hs->token_type = DATA_TEXT;
|
||||
return 1;
|
||||
} else {
|
||||
pos = (size_t)(idx - hs->s) + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 8.2.4.52
|
||||
* http://www.w3.org/html/wg/drafts/html/master/syntax.html#doctype-state
|
||||
*/
|
||||
static int h5_state_doctype(h5_state_t* hs)
|
||||
{
|
||||
const char* idx;
|
||||
|
||||
TRACE();
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_type = DOCTYPE;
|
||||
|
||||
idx = (const char*) memchr(hs->s + hs->pos, CHAR_GT, hs->len - hs->pos);
|
||||
if (idx == NULL) {
|
||||
hs->state = h5_state_eof;
|
||||
hs->token_len = hs->len - hs->pos;
|
||||
} else {
|
||||
hs->state = h5_state_data;
|
||||
hs->token_len = (size_t)(idx - hs->s) - hs->pos;
|
||||
hs->pos = (size_t)(idx - hs->s) + 1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
54
naxsi-0.55.3/ext/libinjection/libinjection_html5.h
Normal file
54
naxsi-0.55.3/ext/libinjection/libinjection_html5.h
Normal file
@@ -0,0 +1,54 @@
|
||||
#ifndef LIBINJECTION_HTML5
|
||||
#define LIBINJECTION_HTML5
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* pull in size_t */
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
enum html5_type {
|
||||
DATA_TEXT
|
||||
, TAG_NAME_OPEN
|
||||
, TAG_NAME_CLOSE
|
||||
, TAG_NAME_SELFCLOSE
|
||||
, TAG_DATA
|
||||
, TAG_CLOSE
|
||||
, ATTR_NAME
|
||||
, ATTR_VALUE
|
||||
, TAG_COMMENT
|
||||
, DOCTYPE
|
||||
};
|
||||
|
||||
enum html5_flags {
|
||||
DATA_STATE
|
||||
, VALUE_NO_QUOTE
|
||||
, VALUE_SINGLE_QUOTE
|
||||
, VALUE_DOUBLE_QUOTE
|
||||
, VALUE_BACK_QUOTE
|
||||
};
|
||||
|
||||
struct h5_state;
|
||||
typedef int (*ptr_html5_state)(struct h5_state*);
|
||||
|
||||
typedef struct h5_state {
|
||||
const char* s;
|
||||
size_t len;
|
||||
size_t pos;
|
||||
int is_close;
|
||||
ptr_html5_state state;
|
||||
const char* token_start;
|
||||
size_t token_len;
|
||||
enum html5_type token_type;
|
||||
} h5_state_t;
|
||||
|
||||
|
||||
void libinjection_h5_init(h5_state_t* hs, const char* s, size_t len, enum html5_flags);
|
||||
int libinjection_h5_next(h5_state_t* hs);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
2298
naxsi-0.55.3/ext/libinjection/libinjection_sqli.c
Normal file
2298
naxsi-0.55.3/ext/libinjection/libinjection_sqli.c
Normal file
File diff suppressed because it is too large
Load Diff
295
naxsi-0.55.3/ext/libinjection/libinjection_sqli.h
Normal file
295
naxsi-0.55.3/ext/libinjection/libinjection_sqli.h
Normal file
@@ -0,0 +1,295 @@
|
||||
/**
|
||||
* Copyright 2012, 2013 Nick Galbreath
|
||||
* nickg@client9.com
|
||||
* BSD License -- see COPYING.txt for details
|
||||
*
|
||||
* https://libinjection.client9.com/
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _LIBINJECTION_SQLI_H
|
||||
#define _LIBINJECTION_SQLI_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Pull in size_t
|
||||
*/
|
||||
#include <string.h>
|
||||
|
||||
enum sqli_flags {
|
||||
FLAG_NONE = 0
|
||||
, FLAG_QUOTE_NONE = 1 /* 1 << 0 */
|
||||
, FLAG_QUOTE_SINGLE = 2 /* 1 << 1 */
|
||||
, FLAG_QUOTE_DOUBLE = 4 /* 1 << 2 */
|
||||
|
||||
, FLAG_SQL_ANSI = 8 /* 1 << 3 */
|
||||
, FLAG_SQL_MYSQL = 16 /* 1 << 4 */
|
||||
};
|
||||
|
||||
enum lookup_type {
|
||||
LOOKUP_WORD = 1
|
||||
, LOOKUP_TYPE = 2
|
||||
, LOOKUP_OPERATOR = 3
|
||||
, LOOKUP_FINGERPRINT = 4
|
||||
};
|
||||
|
||||
struct libinjection_sqli_token {
|
||||
#ifdef SWIG
|
||||
%immutable;
|
||||
#endif
|
||||
char type;
|
||||
char str_open;
|
||||
char str_close;
|
||||
|
||||
/*
|
||||
* position and length of token
|
||||
* in original string
|
||||
*/
|
||||
size_t pos;
|
||||
size_t len;
|
||||
|
||||
/* count:
|
||||
* in type 'v', used for number of opening '@'
|
||||
* but maybe unsed in other contexts
|
||||
*/
|
||||
int count;
|
||||
|
||||
char val[32];
|
||||
};
|
||||
|
||||
typedef struct libinjection_sqli_token stoken_t;
|
||||
|
||||
/**
|
||||
* Pointer to function, takes cstr input,
|
||||
* returns '\0' for no match, else a char
|
||||
*/
|
||||
struct libinjection_sqli_state;
|
||||
typedef char (*ptr_lookup_fn)(struct libinjection_sqli_state*, int lookuptype, const char* word, size_t len);
|
||||
|
||||
struct libinjection_sqli_state {
|
||||
#ifdef SWIG
|
||||
%immutable;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* input, does not need to be null terminated.
|
||||
* it is also not modified.
|
||||
*/
|
||||
const char *s;
|
||||
|
||||
/*
|
||||
* input length
|
||||
*/
|
||||
size_t slen;
|
||||
|
||||
/*
|
||||
* How to lookup a word or fingerprint
|
||||
*/
|
||||
ptr_lookup_fn lookup;
|
||||
void* userdata;
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
int flags;
|
||||
|
||||
/*
|
||||
* pos is index in string we are at when tokenizing
|
||||
*/
|
||||
size_t pos;
|
||||
|
||||
#ifndef SWIG
|
||||
/* for SWIG.. don't use this.. use functional API instead */
|
||||
|
||||
/* MAX TOKENS + 1 since we use one extra token
|
||||
* to determine the type of the previous token
|
||||
*/
|
||||
struct libinjection_sqli_token tokenvec[8];
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Pointer to token position in tokenvec, above
|
||||
*/
|
||||
struct libinjection_sqli_token *current;
|
||||
|
||||
/*
|
||||
* fingerprint pattern c-string
|
||||
* +1 for ending null
|
||||
* Mimimum of 8 bytes to add gcc's -fstack-protector to work
|
||||
*/
|
||||
char fingerprint[8];
|
||||
|
||||
/*
|
||||
* Line number of code that said decided if the input was SQLi or
|
||||
* not. Most of the time it's line that said "it's not a matching
|
||||
* fingerprint" but there is other logic that sometimes approves
|
||||
* an input. This is only useful for debugging.
|
||||
*
|
||||
*/
|
||||
int reason;
|
||||
|
||||
/* Number of ddw (dash-dash-white) comments
|
||||
* These comments are in the form of
|
||||
* '--[whitespace]' or '--[EOF]'
|
||||
*
|
||||
* All databases treat this as a comment.
|
||||
*/
|
||||
int stats_comment_ddw;
|
||||
|
||||
/* Number of ddx (dash-dash-[notwhite]) comments
|
||||
*
|
||||
* ANSI SQL treats these are comments, MySQL treats this as
|
||||
* two unary operators '-' '-'
|
||||
*
|
||||
* If you are parsing result returns FALSE and
|
||||
* stats_comment_dd > 0, you should reparse with
|
||||
* COMMENT_MYSQL
|
||||
*
|
||||
*/
|
||||
int stats_comment_ddx;
|
||||
|
||||
/*
|
||||
* c-style comments found /x .. x/
|
||||
*/
|
||||
int stats_comment_c;
|
||||
|
||||
/* '#' operators or mysql EOL comments found
|
||||
*
|
||||
*/
|
||||
int stats_comment_hash;
|
||||
|
||||
/*
|
||||
* number of tokens folded away
|
||||
*/
|
||||
int stats_folds;
|
||||
|
||||
/*
|
||||
* total tokens processed
|
||||
*/
|
||||
int stats_tokens;
|
||||
|
||||
};
|
||||
|
||||
typedef struct libinjection_sqli_state sfilter;
|
||||
|
||||
struct libinjection_sqli_token* libinjection_sqli_get_token(
|
||||
struct libinjection_sqli_state* sqlistate, int i);
|
||||
|
||||
/*
|
||||
* Version info.
|
||||
*
|
||||
* This is moved into a function to allow SWIG and other auto-generated
|
||||
* binding to not be modified during minor release changes. We change
|
||||
* change the version number in the c source file, and not regenerated
|
||||
* the binding
|
||||
*
|
||||
* See python's normalized version
|
||||
* http://www.python.org/dev/peps/pep-0386/#normalizedversion
|
||||
*/
|
||||
const char* libinjection_version(void);
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void libinjection_sqli_init(struct libinjection_sqli_state* sql_state,
|
||||
const char* s, size_t slen,
|
||||
int flags);
|
||||
|
||||
/**
|
||||
* Main API: tests for SQLi in three possible contexts, no quotes,
|
||||
* single quote and double quote
|
||||
*
|
||||
* \param sql_state core data structure
|
||||
*
|
||||
* \return 1 (true) if SQLi, 0 (false) if benign
|
||||
*/
|
||||
int libinjection_is_sqli(struct libinjection_sqli_state* sql_state);
|
||||
|
||||
/* FOR H@CKERS ONLY
|
||||
*
|
||||
*/
|
||||
void libinjection_sqli_callback(struct libinjection_sqli_state* sql_state,
|
||||
ptr_lookup_fn fn,
|
||||
void* userdata);
|
||||
|
||||
|
||||
/*
|
||||
* Resets state, but keeps initial string and callbacks
|
||||
*/
|
||||
void libinjection_sqli_reset(struct libinjection_sqli_state* sql_state,
|
||||
int flags);
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* This detects SQLi in a single context, mostly useful for custom
|
||||
* logic and debugging.
|
||||
*
|
||||
* \param sql_state Main data structure
|
||||
* \param flags flags to adjust parsing
|
||||
*
|
||||
* \returns a pointer to sfilter.fingerprint as convenience
|
||||
* do not free!
|
||||
*
|
||||
*/
|
||||
const char* libinjection_sqli_fingerprint(struct libinjection_sqli_state* sql_state,
|
||||
int flags);
|
||||
|
||||
/**
|
||||
* The default "word" to token-type or fingerprint function. This
|
||||
* uses a ASCII case-insensitive binary tree.
|
||||
*/
|
||||
char libinjection_sqli_lookup_word(struct libinjection_sqli_state* sql_state,
|
||||
int lookup_type,
|
||||
const char* s,
|
||||
size_t slen);
|
||||
|
||||
/* Streaming tokenization interface.
|
||||
*
|
||||
* sql_state->current is updated with the current token.
|
||||
*
|
||||
* \returns 1, has a token, keep going, or 0 no tokens
|
||||
*
|
||||
*/
|
||||
int libinjection_sqli_tokenize(struct libinjection_sqli_state * sql_state);
|
||||
|
||||
/**
|
||||
* parses and folds input, up to 5 tokens
|
||||
*
|
||||
*/
|
||||
int libinjection_sqli_fold(struct libinjection_sqli_state * sql_state);
|
||||
|
||||
/** The built-in default function to match fingerprints
|
||||
* and do false negative/positive analysis. This calls the following
|
||||
* two functions. With this, you over-ride one part or the other.
|
||||
*
|
||||
* return libinjection_sqli_blacklist(sql_state) &&
|
||||
* libinject_sqli_not_whitelist(sql_state);
|
||||
*
|
||||
* \param sql_state should be filled out after libinjection_sqli_fingerprint is called
|
||||
*/
|
||||
int libinjection_sqli_check_fingerprint(struct libinjection_sqli_state * sql_state);
|
||||
|
||||
/* Given a pattern determine if it's a SQLi pattern.
|
||||
*
|
||||
* \return TRUE if sqli, false otherwise
|
||||
*/
|
||||
int libinjection_sqli_blacklist(struct libinjection_sqli_state* sql_state);
|
||||
|
||||
/* Given a positive match for a pattern (i.e. pattern is SQLi), this function
|
||||
* does additional analysis to reduce false positives.
|
||||
*
|
||||
* \return TRUE if sqli, false otherwise
|
||||
*/
|
||||
int libinjection_sqli_not_whitelist(struct libinjection_sqli_state * sql_state);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _LIBINJECTION_SQLI_H */
|
||||
9349
naxsi-0.55.3/ext/libinjection/libinjection_sqli_data.h
Normal file
9349
naxsi-0.55.3/ext/libinjection/libinjection_sqli_data.h
Normal file
File diff suppressed because it is too large
Load Diff
531
naxsi-0.55.3/ext/libinjection/libinjection_xss.c
Normal file
531
naxsi-0.55.3/ext/libinjection/libinjection_xss.c
Normal file
@@ -0,0 +1,531 @@
|
||||
|
||||
#include "libinjection.h"
|
||||
#include "libinjection_xss.h"
|
||||
#include "libinjection_html5.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
|
||||
typedef enum attribute {
|
||||
TYPE_NONE
|
||||
, TYPE_BLACK /* ban always */
|
||||
, TYPE_ATTR_URL /* attribute value takes a URL-like object */
|
||||
, TYPE_STYLE
|
||||
, TYPE_ATTR_INDIRECT /* attribute *name* is given in *value* */
|
||||
} attribute_t;
|
||||
|
||||
|
||||
static attribute_t is_black_attr(const char* s, size_t len);
|
||||
static int is_black_tag(const char* s, size_t len);
|
||||
static int is_black_url(const char* s, size_t len);
|
||||
static int cstrcasecmp_with_null(const char *a, const char *b, size_t n);
|
||||
static int html_decode_char_at(const char* src, size_t len, size_t* consumed);
|
||||
static int htmlencode_startswith(const char* prefix, const char *src, size_t n);
|
||||
|
||||
|
||||
typedef struct stringtype {
|
||||
const char* name;
|
||||
attribute_t atype;
|
||||
} stringtype_t;
|
||||
|
||||
|
||||
static const int gsHexDecodeMap[256] = {
|
||||
256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
|
||||
256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
|
||||
256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
|
||||
256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 256, 256,
|
||||
256, 256, 256, 256, 256, 10, 11, 12, 13, 14, 15, 256,
|
||||
256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
|
||||
256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
|
||||
256, 10, 11, 12, 13, 14, 15, 256, 256, 256, 256, 256,
|
||||
256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
|
||||
256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
|
||||
256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
|
||||
256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
|
||||
256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
|
||||
256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
|
||||
256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
|
||||
256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
|
||||
256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
|
||||
256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
|
||||
256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
|
||||
256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
|
||||
256, 256, 256, 256
|
||||
};
|
||||
|
||||
static int html_decode_char_at(const char* src, size_t len, size_t* consumed)
|
||||
{
|
||||
int val = 0;
|
||||
size_t i;
|
||||
int ch;
|
||||
|
||||
if (len == 0 || src == NULL) {
|
||||
*consumed = 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
*consumed = 1;
|
||||
if (*src != '&' || len < 2) {
|
||||
return (unsigned char)(*src);
|
||||
}
|
||||
|
||||
|
||||
if (*(src+1) != '#') {
|
||||
/* normally this would be for named entities
|
||||
* but for this case we don't actually care
|
||||
*/
|
||||
return '&';
|
||||
}
|
||||
|
||||
if (*(src+2) == 'x' || *(src+2) == 'X') {
|
||||
ch = (unsigned char) (*(src+3));
|
||||
ch = gsHexDecodeMap[ch];
|
||||
if (ch == 256) {
|
||||
/* degenerate case '&#[?]' */
|
||||
return '&';
|
||||
}
|
||||
val = ch;
|
||||
i = 4;
|
||||
while (i < len) {
|
||||
ch = (unsigned char) src[i];
|
||||
if (ch == ';') {
|
||||
*consumed = i + 1;
|
||||
return val;
|
||||
}
|
||||
ch = gsHexDecodeMap[ch];
|
||||
if (ch == 256) {
|
||||
*consumed = i;
|
||||
return val;
|
||||
}
|
||||
val = (val * 16) + ch;
|
||||
if (val > 0x1000FF) {
|
||||
return '&';
|
||||
}
|
||||
++i;
|
||||
}
|
||||
*consumed = i;
|
||||
return val;
|
||||
} else {
|
||||
i = 2;
|
||||
ch = (unsigned char) src[i];
|
||||
if (ch < '0' || ch > '9') {
|
||||
return '&';
|
||||
}
|
||||
val = ch - '0';
|
||||
i += 1;
|
||||
while (i < len) {
|
||||
ch = (unsigned char) src[i];
|
||||
if (ch == ';') {
|
||||
*consumed = i + 1;
|
||||
return val;
|
||||
}
|
||||
if (ch < '0' || ch > '9') {
|
||||
*consumed = i;
|
||||
return val;
|
||||
}
|
||||
val = (val * 10) + (ch - '0');
|
||||
if (val > 0x1000FF) {
|
||||
return '&';
|
||||
}
|
||||
++i;
|
||||
}
|
||||
*consumed = i;
|
||||
return val;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* view-source:
|
||||
* data:
|
||||
* javascript:
|
||||
*/
|
||||
static stringtype_t BLACKATTR[] = {
|
||||
{ "ACTION", TYPE_ATTR_URL } /* form */
|
||||
, { "ATTRIBUTENAME", TYPE_ATTR_INDIRECT } /* SVG allow indirection of attribute names */
|
||||
, { "BY", TYPE_ATTR_URL } /* SVG */
|
||||
, { "BACKGROUND", TYPE_ATTR_URL } /* IE6, O11 */
|
||||
, { "DATAFORMATAS", TYPE_BLACK } /* IE */
|
||||
, { "DATASRC", TYPE_BLACK } /* IE */
|
||||
, { "DYNSRC", TYPE_ATTR_URL } /* Obsolete img attribute */
|
||||
, { "FILTER", TYPE_STYLE } /* Opera, SVG inline style */
|
||||
, { "FORMACTION", TYPE_ATTR_URL } /* HTML5 */
|
||||
, { "FOLDER", TYPE_ATTR_URL } /* Only on A tags, IE-only */
|
||||
, { "FROM", TYPE_ATTR_URL } /* SVG */
|
||||
, { "HANDLER", TYPE_ATTR_URL } /* SVG Tiny, Opera */
|
||||
, { "HREF", TYPE_ATTR_URL }
|
||||
, { "LOWSRC", TYPE_ATTR_URL } /* Obsolete img attribute */
|
||||
, { "POSTER", TYPE_ATTR_URL } /* Opera 10,11 */
|
||||
, { "SRC", TYPE_ATTR_URL }
|
||||
, { "STYLE", TYPE_STYLE }
|
||||
, { "TO", TYPE_ATTR_URL } /* SVG */
|
||||
, { "VALUES", TYPE_ATTR_URL } /* SVG */
|
||||
, { "XLINK:HREF", TYPE_ATTR_URL }
|
||||
, { NULL, TYPE_NONE }
|
||||
};
|
||||
|
||||
/* xmlns */
|
||||
/* xml-stylesheet > <eval>, <if expr=> */
|
||||
|
||||
/*
|
||||
static const char* BLACKATTR[] = {
|
||||
"ATTRIBUTENAME",
|
||||
"BACKGROUND",
|
||||
"DATAFORMATAS",
|
||||
"HREF",
|
||||
"SCROLL",
|
||||
"SRC",
|
||||
"STYLE",
|
||||
"SRCDOC",
|
||||
NULL
|
||||
};
|
||||
*/
|
||||
|
||||
static const char* BLACKTAG[] = {
|
||||
"APPLET"
|
||||
/* , "AUDIO" */
|
||||
, "BASE"
|
||||
, "COMMENT" /* IE http://html5sec.org/#38 */
|
||||
, "EMBED"
|
||||
/* , "FORM" */
|
||||
, "FRAME"
|
||||
, "FRAMESET"
|
||||
, "HANDLER" /* Opera SVG, effectively a script tag */
|
||||
, "IFRAME"
|
||||
, "IMPORT"
|
||||
, "ISINDEX"
|
||||
, "LINK"
|
||||
, "LISTENER"
|
||||
/* , "MARQUEE" */
|
||||
, "META"
|
||||
, "NOSCRIPT"
|
||||
, "OBJECT"
|
||||
, "SCRIPT"
|
||||
, "STYLE"
|
||||
/* , "VIDEO" */
|
||||
, "VMLFRAME"
|
||||
, "XML"
|
||||
, "XSS"
|
||||
, NULL
|
||||
};
|
||||
|
||||
|
||||
static int cstrcasecmp_with_null(const char *a, const char *b, size_t n)
|
||||
{
|
||||
char ca;
|
||||
char cb;
|
||||
/* printf("Comparing to %s %.*s\n", a, (int)n, b); */
|
||||
while (n-- > 0) {
|
||||
cb = *b++;
|
||||
if (cb == '\0') continue;
|
||||
|
||||
ca = *a++;
|
||||
|
||||
if (cb >= 'a' && cb <= 'z') {
|
||||
cb -= 0x20;
|
||||
}
|
||||
/* printf("Comparing %c vs %c with %d left\n", ca, cb, (int)n); */
|
||||
if (ca != cb) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (*a == 0) {
|
||||
/* printf(" MATCH \n"); */
|
||||
return 0;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Does an HTML encoded binary string (const char*, lenght) start with
|
||||
* a all uppercase c-string (null terminated), case insenstive!
|
||||
*
|
||||
* also ignore any embedded nulls in the HTML string!
|
||||
*
|
||||
* return 1 if match / starts with
|
||||
* return 0 if not
|
||||
*/
|
||||
static int htmlencode_startswith(const char *a, const char *b, size_t n)
|
||||
{
|
||||
size_t consumed;
|
||||
int cb;
|
||||
int first = 1;
|
||||
/* printf("Comparing %s with %.*s\n", a,(int)n,b); */
|
||||
while (n > 0) {
|
||||
if (*a == 0) {
|
||||
/* printf("Match EOL!\n"); */
|
||||
return 1;
|
||||
}
|
||||
cb = html_decode_char_at(b, n, &consumed);
|
||||
b += consumed;
|
||||
n -= consumed;
|
||||
|
||||
if (first && cb <= 32) {
|
||||
/* ignore all leading whitespace and control characters */
|
||||
continue;
|
||||
}
|
||||
first = 0;
|
||||
|
||||
if (cb == 0) {
|
||||
/* always ignore null characters in user input */
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cb == 10) {
|
||||
/* always ignore vtab characters in user input */
|
||||
/* who allows this?? */
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cb >= 'a' && cb <= 'z') {
|
||||
/* upcase */
|
||||
cb -= 0x20;
|
||||
}
|
||||
|
||||
if (*a != (char) cb) {
|
||||
/* printf(" %c != %c\n", *a, cb); */
|
||||
/* mismatch */
|
||||
return 0;
|
||||
}
|
||||
a++;
|
||||
}
|
||||
|
||||
return (*a == 0) ? 1 : 0;
|
||||
}
|
||||
|
||||
static int is_black_tag(const char* s, size_t len)
|
||||
{
|
||||
const char** black;
|
||||
|
||||
if (len < 3) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
black = BLACKTAG;
|
||||
while (*black != NULL) {
|
||||
if (cstrcasecmp_with_null(*black, s, len) == 0) {
|
||||
/* printf("Got black tag %s\n", *black); */
|
||||
return 1;
|
||||
}
|
||||
black += 1;
|
||||
}
|
||||
|
||||
/* anything SVG related */
|
||||
if ((s[0] == 's' || s[0] == 'S') &&
|
||||
(s[1] == 'v' || s[1] == 'V') &&
|
||||
(s[2] == 'g' || s[2] == 'G')) {
|
||||
/* printf("Got SVG tag \n"); */
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Anything XSL(t) related */
|
||||
if ((s[0] == 'x' || s[0] == 'X') &&
|
||||
(s[1] == 's' || s[1] == 'S') &&
|
||||
(s[2] == 'l' || s[2] == 'L')) {
|
||||
/* printf("Got XSL tag\n"); */
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static attribute_t is_black_attr(const char* s, size_t len)
|
||||
{
|
||||
stringtype_t* black;
|
||||
|
||||
if (len < 2) {
|
||||
return TYPE_NONE;
|
||||
}
|
||||
|
||||
/* javascript on.* */
|
||||
if ((s[0] == 'o' || s[0] == 'O') && (s[1] == 'n' || s[1] == 'N')) {
|
||||
/* printf("Got javascript on- attribute name\n"); */
|
||||
return TYPE_BLACK;
|
||||
}
|
||||
|
||||
|
||||
if (len >= 5) {
|
||||
/* XMLNS can be used to create arbitrary tags */
|
||||
if (cstrcasecmp_with_null("XMLNS", s, 5) == 0 || cstrcasecmp_with_null("XLINK", s, 5) == 0) {
|
||||
/* printf("Got XMLNS and XLINK tags\n"); */
|
||||
return TYPE_BLACK;
|
||||
}
|
||||
}
|
||||
|
||||
black = BLACKATTR;
|
||||
while (black->name != NULL) {
|
||||
if (cstrcasecmp_with_null(black->name, s, len) == 0) {
|
||||
/* printf("Got banned attribute name %s\n", black->name); */
|
||||
return black->atype;
|
||||
}
|
||||
black += 1;
|
||||
}
|
||||
|
||||
return TYPE_NONE;
|
||||
}
|
||||
|
||||
static int is_black_url(const char* s, size_t len)
|
||||
{
|
||||
|
||||
static const char* data_url = "DATA";
|
||||
static const char* viewsource_url = "VIEW-SOURCE";
|
||||
|
||||
/* obsolete but interesting signal */
|
||||
static const char* vbscript_url = "VBSCRIPT";
|
||||
|
||||
/* covers JAVA, JAVASCRIPT, + colon */
|
||||
static const char* javascript_url = "JAVA";
|
||||
|
||||
/* skip whitespace */
|
||||
while (len > 0 && (*s <= 32 || *s >= 127)) {
|
||||
/*
|
||||
* HEY: this is a signed character.
|
||||
* We are intentionally skipping high-bit characters too
|
||||
* since they are not ascii, and Opera sometimes uses UTF8 whitespace.
|
||||
*
|
||||
* Also in EUC-JP some of the high bytes are just ignored.
|
||||
*/
|
||||
++s;
|
||||
--len;
|
||||
}
|
||||
|
||||
if (htmlencode_startswith(data_url, s, len)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (htmlencode_startswith(viewsource_url, s, len)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (htmlencode_startswith(javascript_url, s, len)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (htmlencode_startswith(vbscript_url, s, len)) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int libinjection_is_xss(const char* s, size_t len, int flags)
|
||||
{
|
||||
h5_state_t h5;
|
||||
attribute_t attr = TYPE_NONE;
|
||||
|
||||
libinjection_h5_init(&h5, s, len, (enum html5_flags) flags);
|
||||
while (libinjection_h5_next(&h5)) {
|
||||
if (h5.token_type != ATTR_VALUE) {
|
||||
attr = TYPE_NONE;
|
||||
}
|
||||
|
||||
if (h5.token_type == DOCTYPE) {
|
||||
return 1;
|
||||
} else if (h5.token_type == TAG_NAME_OPEN) {
|
||||
if (is_black_tag(h5.token_start, h5.token_len)) {
|
||||
return 1;
|
||||
}
|
||||
} else if (h5.token_type == ATTR_NAME) {
|
||||
attr = is_black_attr(h5.token_start, h5.token_len);
|
||||
} else if (h5.token_type == ATTR_VALUE) {
|
||||
/*
|
||||
* IE6,7,8 parsing works a bit differently so
|
||||
* a whole <script> or other black tag might be hiding
|
||||
* inside an attribute value under HTML5 parsing
|
||||
* See http://html5sec.org/#102
|
||||
* to avoid doing a full reparse of the value, just
|
||||
* look for "<". This probably need adjusting to
|
||||
* handle escaped characters
|
||||
*/
|
||||
/*
|
||||
if (memchr(h5.token_start, '<', h5.token_len) != NULL) {
|
||||
return 1;
|
||||
}
|
||||
*/
|
||||
|
||||
switch (attr) {
|
||||
case TYPE_NONE:
|
||||
break;
|
||||
case TYPE_BLACK:
|
||||
return 1;
|
||||
case TYPE_ATTR_URL:
|
||||
if (is_black_url(h5.token_start, h5.token_len)) {
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
case TYPE_STYLE:
|
||||
return 1;
|
||||
case TYPE_ATTR_INDIRECT:
|
||||
/* an attribute name is specified in a _value_ */
|
||||
if (is_black_attr(h5.token_start, h5.token_len)) {
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
/*
|
||||
default:
|
||||
assert(0);
|
||||
*/
|
||||
}
|
||||
attr = TYPE_NONE;
|
||||
} else if (h5.token_type == TAG_COMMENT) {
|
||||
/* IE uses a "`" as a tag ending char */
|
||||
if (memchr(h5.token_start, '`', h5.token_len) != NULL) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* IE conditional comment */
|
||||
if (h5.token_len > 3) {
|
||||
if (h5.token_start[0] == '[' &&
|
||||
(h5.token_start[1] == 'i' || h5.token_start[1] == 'I') &&
|
||||
(h5.token_start[2] == 'f' || h5.token_start[2] == 'F')) {
|
||||
return 1;
|
||||
}
|
||||
if ((h5.token_start[0] == 'x' || h5.token_start[1] == 'X') &&
|
||||
(h5.token_start[1] == 'm' || h5.token_start[1] == 'M') &&
|
||||
(h5.token_start[2] == 'l' || h5.token_start[2] == 'L')) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (h5.token_len > 5) {
|
||||
/* IE <?import pseudo-tag */
|
||||
if (cstrcasecmp_with_null("IMPORT", h5.token_start, 6) == 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* XML Entity definition */
|
||||
if (cstrcasecmp_with_null("ENTITY", h5.token_start, 6) == 0) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* wrapper
|
||||
*/
|
||||
int libinjection_xss(const char* s, size_t len)
|
||||
{
|
||||
if (libinjection_is_xss(s, len, DATA_STATE)) {
|
||||
return 1;
|
||||
}
|
||||
if (libinjection_is_xss(s, len, VALUE_NO_QUOTE)) {
|
||||
return 1;
|
||||
}
|
||||
if (libinjection_is_xss(s, len, VALUE_SINGLE_QUOTE)) {
|
||||
return 1;
|
||||
}
|
||||
if (libinjection_is_xss(s, len, VALUE_DOUBLE_QUOTE)) {
|
||||
return 1;
|
||||
}
|
||||
if (libinjection_is_xss(s, len, VALUE_BACK_QUOTE)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
21
naxsi-0.55.3/ext/libinjection/libinjection_xss.h
Normal file
21
naxsi-0.55.3/ext/libinjection/libinjection_xss.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#ifndef LIBINJECTION_XSS
|
||||
#define LIBINJECTION_XSS
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* HEY THIS ISN'T DONE
|
||||
*/
|
||||
|
||||
/* pull in size_t */
|
||||
|
||||
#include <string.h>
|
||||
|
||||
int libinjection_is_xss(const char* s, size_t len, int flags);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
314
naxsi-0.55.3/ext/libinjection/reader.c
Normal file
314
naxsi-0.55.3/ext/libinjection/reader.c
Normal file
@@ -0,0 +1,314 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "libinjection.h"
|
||||
#include "libinjection_sqli.h"
|
||||
#include "libinjection_xss.h"
|
||||
|
||||
#ifndef TRUE
|
||||
#define TRUE 1
|
||||
#endif
|
||||
#ifndef FALSE
|
||||
#define FALSE 0
|
||||
#endif
|
||||
|
||||
static int g_test_ok = 0;
|
||||
static int g_test_fail = 0;
|
||||
|
||||
typedef enum {
|
||||
MODE_SQLI,
|
||||
MODE_XSS
|
||||
} detect_mode_t;
|
||||
|
||||
static void usage(const char* argv[]);
|
||||
size_t modp_rtrim(char* str, size_t len);
|
||||
void modp_toprint(char* str, size_t len);
|
||||
void test_positive(FILE * fd, const char *fname, detect_mode_t mode,
|
||||
int flag_invert, int flag_true, int flag_quiet);
|
||||
|
||||
int urlcharmap(char ch);
|
||||
size_t modp_url_decode(char* dest, const char* s, size_t len);
|
||||
|
||||
int urlcharmap(char ch) {
|
||||
switch (ch) {
|
||||
case '0': return 0;
|
||||
case '1': return 1;
|
||||
case '2': return 2;
|
||||
case '3': return 3;
|
||||
case '4': return 4;
|
||||
case '5': return 5;
|
||||
case '6': return 6;
|
||||
case '7': return 7;
|
||||
case '8': return 8;
|
||||
case '9': return 9;
|
||||
case 'a': case 'A': return 10;
|
||||
case 'b': case 'B': return 11;
|
||||
case 'c': case 'C': return 12;
|
||||
case 'd': case 'D': return 13;
|
||||
case 'e': case 'E': return 14;
|
||||
case 'f': case 'F': return 15;
|
||||
default:
|
||||
return 256;
|
||||
}
|
||||
}
|
||||
|
||||
size_t modp_url_decode(char* dest, const char* s, size_t len)
|
||||
{
|
||||
const char* deststart = dest;
|
||||
|
||||
size_t i = 0;
|
||||
int d = 0;
|
||||
while (i < len) {
|
||||
switch (s[i]) {
|
||||
case '+':
|
||||
*dest++ = ' ';
|
||||
i += 1;
|
||||
break;
|
||||
case '%':
|
||||
if (i+2 < len) {
|
||||
d = (urlcharmap(s[i+1]) << 4) | urlcharmap(s[i+2]);
|
||||
if ( d < 256) {
|
||||
*dest = (char) d;
|
||||
dest++;
|
||||
i += 3; /* loop will increment one time */
|
||||
} else {
|
||||
*dest++ = '%';
|
||||
i += 1;
|
||||
}
|
||||
} else {
|
||||
*dest++ = '%';
|
||||
i += 1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
*dest++ = s[i];
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
*dest = '\0';
|
||||
return (size_t)(dest - deststart); /* compute "strlen" of dest */
|
||||
}
|
||||
|
||||
void modp_toprint(char* str, size_t len)
|
||||
{
|
||||
size_t i;
|
||||
for (i = 0; i < len; ++i) {
|
||||
if (str[i] < 32 || str[i] > 126) {
|
||||
str[i] = '?';
|
||||
}
|
||||
}
|
||||
}
|
||||
size_t modp_rtrim(char* str, size_t len)
|
||||
{
|
||||
while (len) {
|
||||
char c = str[len -1];
|
||||
if (c == ' ' || c == '\n' || c == '\t' || c == '\r') {
|
||||
str[len -1] = '\0';
|
||||
len -= 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
void test_positive(FILE * fd, const char *fname, detect_mode_t mode,
|
||||
int flag_invert, int flag_true, int flag_quiet)
|
||||
{
|
||||
char linebuf[8192];
|
||||
int issqli;
|
||||
int linenum = 0;
|
||||
size_t len;
|
||||
sfilter sf;
|
||||
|
||||
while (fgets(linebuf, sizeof(linebuf), fd)) {
|
||||
linenum += 1;
|
||||
len = modp_rtrim(linebuf, strlen(linebuf));
|
||||
if (len == 0) {
|
||||
continue;
|
||||
}
|
||||
if (linebuf[0] == '#') {
|
||||
continue;
|
||||
}
|
||||
|
||||
len = modp_url_decode(linebuf, linebuf, len);
|
||||
issqli = 0;
|
||||
switch (mode) {
|
||||
case MODE_SQLI: {
|
||||
libinjection_sqli_init(&sf, linebuf, len, 0);
|
||||
issqli = libinjection_is_sqli(&sf);
|
||||
break;
|
||||
}
|
||||
case MODE_XSS: {
|
||||
issqli = libinjection_xss(linebuf, len);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
|
||||
if (issqli) {
|
||||
g_test_ok += 1;
|
||||
} else {
|
||||
g_test_fail += 1;
|
||||
}
|
||||
|
||||
if (!flag_quiet) {
|
||||
if ((issqli && flag_true && ! flag_invert) ||
|
||||
(!issqli && flag_true && flag_invert) ||
|
||||
!flag_true) {
|
||||
|
||||
modp_toprint(linebuf, len);
|
||||
|
||||
switch (mode) {
|
||||
case MODE_SQLI: {
|
||||
/*
|
||||
* if we didn't find a SQLi and fingerprint from
|
||||
* sqlstats is is 'sns' or 'snsns' then redo using
|
||||
* plain context
|
||||
*/
|
||||
if (!issqli && (strcmp(sf.fingerprint, "sns") == 0 ||
|
||||
strcmp(sf.fingerprint, "snsns") == 0)) {
|
||||
libinjection_sqli_fingerprint(&sf, 0);
|
||||
}
|
||||
|
||||
fprintf(stdout, "%s\t%d\t%s\t%s\t%s\n",
|
||||
fname, linenum,
|
||||
(issqli ? "True" : "False"), sf.fingerprint, linebuf);
|
||||
break;
|
||||
}
|
||||
case MODE_XSS: {
|
||||
fprintf(stdout, "%s\t%d\t%s\t%s\n",
|
||||
fname, linenum,
|
||||
(issqli ? "True" : "False"), linebuf);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void usage(const char* argv[])
|
||||
{
|
||||
fprintf(stdout, "usage: %s [flags] [files...]\n", argv[0]);
|
||||
fprintf(stdout, "%s\n", "");
|
||||
fprintf(stdout, "%s\n", "-q --quiet : quiet mode");
|
||||
fprintf(stdout, "%s\n", "-m --max-fails : number of failed cases need to fail entire test");
|
||||
fprintf(stdout, "%s\n", "-s INTEGER : repeat each test N time "
|
||||
"(for performance testing)");
|
||||
fprintf(stdout, "%s\n", "-t : only print positive matches");
|
||||
fprintf(stdout, "%s\n", "-x --mode-xss : test input for XSS");
|
||||
fprintf(stdout, "%s\n", "-i --invert : invert test logic "
|
||||
"(input is tested for being safe)");
|
||||
|
||||
fprintf(stdout, "%s\n", "");
|
||||
fprintf(stdout, "%s\n", "-? -h -help --help : this page");
|
||||
fprintf(stdout, "%s\n", "");
|
||||
}
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
/*
|
||||
* invert output, by
|
||||
*/
|
||||
int flag_invert = FALSE;
|
||||
|
||||
/*
|
||||
* don't print anything.. useful for
|
||||
* performance monitors, gprof.
|
||||
*/
|
||||
int flag_quiet = FALSE;
|
||||
|
||||
/*
|
||||
* only print postive results
|
||||
* with invert, only print negative results
|
||||
*/
|
||||
int flag_true = FALSE;
|
||||
detect_mode_t mode = MODE_SQLI;
|
||||
|
||||
int flag_slow = 1;
|
||||
int count = 0;
|
||||
int max = -1;
|
||||
|
||||
int i, j;
|
||||
int offset = 1;
|
||||
|
||||
while (offset < argc) {
|
||||
if (strcmp(argv[offset], "-?") == 0 ||
|
||||
strcmp(argv[offset], "-h") == 0 ||
|
||||
strcmp(argv[offset], "-help") == 0 ||
|
||||
strcmp(argv[offset], "--help") == 0) {
|
||||
usage(argv);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (strcmp(argv[offset], "-i") == 0) {
|
||||
offset += 1;
|
||||
flag_invert = TRUE;
|
||||
} else if (strcmp(argv[offset], "-q") == 0 ||
|
||||
strcmp(argv[offset], "--quiet") == 0) {
|
||||
offset += 1;
|
||||
flag_quiet = TRUE;
|
||||
} else if (strcmp(argv[offset], "-t") == 0) {
|
||||
offset += 1;
|
||||
flag_true = TRUE;
|
||||
} else if (strcmp(argv[offset], "-s") == 0) {
|
||||
offset += 1;
|
||||
flag_slow = 100;
|
||||
} else if (strcmp(argv[offset], "-m") == 0 ||
|
||||
strcmp(argv[offset], "--max-fails") == 0) {
|
||||
offset += 1;
|
||||
max = atoi(argv[offset]);
|
||||
offset += 1;
|
||||
} else if (strcmp(argv[offset], "-x") == 0 ||
|
||||
strcmp(argv[offset], "--mode-xss") == 0) {
|
||||
mode = MODE_XSS;
|
||||
offset += 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (offset == argc) {
|
||||
test_positive(stdin, "stdin", mode, flag_invert, flag_true, flag_quiet);
|
||||
} else {
|
||||
for (j = 0; j < flag_slow; ++j) {
|
||||
for (i = offset; i < argc; ++i) {
|
||||
FILE* fd = fopen(argv[i], "r");
|
||||
if (fd) {
|
||||
test_positive(fd, argv[i], mode, flag_invert, flag_true, flag_quiet);
|
||||
fclose(fd);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!flag_quiet) {
|
||||
fprintf(stdout, "%s", "\n");
|
||||
fprintf(stdout, "SQLI : %d\n", g_test_ok);
|
||||
fprintf(stdout, "SAFE : %d\n", g_test_fail);
|
||||
fprintf(stdout, "TOTAL : %d\n", g_test_ok + g_test_fail);
|
||||
}
|
||||
|
||||
if (max == -1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
count = g_test_ok;
|
||||
if (flag_invert) {
|
||||
count = g_test_fail;
|
||||
}
|
||||
|
||||
if (count > max) {
|
||||
printf("\nThreshold is %d, got %d, failing.\n", max, count);
|
||||
return 1;
|
||||
} else {
|
||||
printf("\nThreshold is %d, got %d, passing.\n", max, count);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
144
naxsi-0.55.3/ext/libinjection/sqli_cli.c
Normal file
144
naxsi-0.55.3/ext/libinjection/sqli_cli.c
Normal file
@@ -0,0 +1,144 @@
|
||||
/**
|
||||
* Copyright 2012, 2013 Nick Galbreath
|
||||
* nickg@client9.com
|
||||
* BSD License -- see COPYING.txt for details
|
||||
*
|
||||
* This is for testing against files in ../data/ *.txt
|
||||
* Reads from stdin or a list of files, and emits if a line
|
||||
* is a SQLi attack or not, and does basic statistics
|
||||
*
|
||||
*/
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "libinjection.h"
|
||||
#include "libinjection_sqli.h"
|
||||
|
||||
void print_string(stoken_t* t);
|
||||
void print_var(stoken_t* t);
|
||||
void print_token(stoken_t *t);
|
||||
|
||||
void print_string(stoken_t* t)
|
||||
{
|
||||
/* print opening quote */
|
||||
if (t->str_open != '\0') {
|
||||
printf("%c", t->str_open);
|
||||
}
|
||||
|
||||
/* print content */
|
||||
printf("%s", t->val);
|
||||
|
||||
/* print closing quote */
|
||||
if (t->str_close != '\0') {
|
||||
printf("%c", t->str_close);
|
||||
}
|
||||
}
|
||||
|
||||
void print_var(stoken_t* t)
|
||||
{
|
||||
if (t->count >= 1) {
|
||||
printf("%c", '@');
|
||||
}
|
||||
if (t->count == 2) {
|
||||
printf("%c", '@');
|
||||
}
|
||||
print_string(t);
|
||||
}
|
||||
|
||||
void print_token(stoken_t *t) {
|
||||
printf("%c ", t->type);
|
||||
switch (t->type) {
|
||||
case 's':
|
||||
print_string(t);
|
||||
break;
|
||||
case 'v':
|
||||
print_var(t);
|
||||
break;
|
||||
default:
|
||||
printf("%s", t->val);
|
||||
}
|
||||
printf("%s", "\n");
|
||||
}
|
||||
|
||||
int main(int argc, const char* argv[])
|
||||
{
|
||||
size_t slen;
|
||||
char* copy;
|
||||
|
||||
int flags = 0;
|
||||
int fold = 0;
|
||||
int detect = 0;
|
||||
|
||||
int i;
|
||||
int count;
|
||||
int offset = 1;
|
||||
int issqli;
|
||||
|
||||
sfilter sf;
|
||||
|
||||
if (argc < 2) {
|
||||
fprintf(stderr, "need more args\n");
|
||||
return 1;
|
||||
}
|
||||
while (1) {
|
||||
if (strcmp(argv[offset], "-m") == 0) {
|
||||
flags |= FLAG_SQL_MYSQL;
|
||||
offset += 1;
|
||||
}
|
||||
else if (strcmp(argv[offset], "-f") == 0 || strcmp(argv[offset], "--fold") == 0) {
|
||||
fold = 1;
|
||||
offset += 1;
|
||||
} else if (strcmp(argv[offset], "-d") == 0 || strcmp(argv[offset], "--detect") == 0) {
|
||||
detect = 1;
|
||||
offset += 1;
|
||||
} else if (strcmp(argv[offset], "-ca") == 0) {
|
||||
flags |= FLAG_SQL_ANSI;
|
||||
offset += 1;
|
||||
} else if (strcmp(argv[offset], "-cm") == 0) {
|
||||
flags |= FLAG_SQL_MYSQL;
|
||||
offset += 1;
|
||||
} else if (strcmp(argv[offset], "-q0") == 0) {
|
||||
flags |= FLAG_QUOTE_NONE;
|
||||
offset += 1;
|
||||
} else if (strcmp(argv[offset], "-q1") == 0) {
|
||||
flags |= FLAG_QUOTE_SINGLE;
|
||||
offset += 1;
|
||||
} else if (strcmp(argv[offset], "-q2") == 0) {
|
||||
flags |= FLAG_QUOTE_DOUBLE;
|
||||
offset += 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* ATTENTION: argv is a C-string, null terminated. We copy this
|
||||
* to it's own location, WITHOUT null byte. This way, valgrind
|
||||
* can see if we run past the buffer.
|
||||
*/
|
||||
|
||||
slen = strlen(argv[offset]);
|
||||
copy = (char* ) malloc(slen);
|
||||
memcpy(copy, argv[offset], slen);
|
||||
libinjection_sqli_init(&sf, copy, slen, flags);
|
||||
|
||||
if (detect == 1) {
|
||||
issqli = libinjection_is_sqli(&sf);
|
||||
if (issqli) {
|
||||
printf("%s\n", sf.fingerprint);
|
||||
}
|
||||
} else if (fold == 1) {
|
||||
count = libinjection_sqli_fold(&sf);
|
||||
for (i = 0; i < count; ++i) {
|
||||
print_token(&(sf.tokenvec[i]));
|
||||
}
|
||||
} else {
|
||||
while (libinjection_sqli_tokenize(&sf)) {
|
||||
print_token(sf.current);
|
||||
}
|
||||
}
|
||||
|
||||
free(copy);
|
||||
|
||||
return 0;
|
||||
}
|
||||
68
naxsi-0.55.3/ext/libinjection/test_speed_sqli.c
Normal file
68
naxsi-0.55.3/ext/libinjection/test_speed_sqli.c
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* A not very good test for performance. This is mostly useful in
|
||||
* testing performance -regressions-
|
||||
*
|
||||
*/
|
||||
#include <time.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "libinjection.h"
|
||||
#include "libinjection_sqli.h"
|
||||
int testIsSQL(void);
|
||||
|
||||
int testIsSQL(void)
|
||||
{
|
||||
const char* const s[] = {
|
||||
"123 LIKE -1234.5678E+2;",
|
||||
"APPLE 19.123 'FOO' \"BAR\"",
|
||||
"/* BAR */ UNION ALL SELECT (2,3,4)",
|
||||
"1 || COS(+0X04) --FOOBAR",
|
||||
"dog apple @cat banana bar",
|
||||
"dog apple cat \"banana \'bar",
|
||||
"102 TABLE CLOTH",
|
||||
"(1001-'1') union select 1,2,3,4 from credit_cards",
|
||||
NULL
|
||||
};
|
||||
const int imax = 1000000;
|
||||
int i, j;
|
||||
size_t slen;
|
||||
sfilter sf;
|
||||
clock_t t0,t1;
|
||||
double total;
|
||||
int tps;
|
||||
|
||||
t0 = clock();
|
||||
for (i = imax, j=0; i != 0; --i, ++j) {
|
||||
if (s[j] == NULL) {
|
||||
j = 0;
|
||||
}
|
||||
|
||||
slen = strlen(s[j]);
|
||||
libinjection_sqli_init(&sf, s[j], slen, FLAG_QUOTE_NONE | FLAG_SQL_ANSI);
|
||||
libinjection_is_sqli(&sf);
|
||||
}
|
||||
|
||||
t1 = clock();
|
||||
total = (double) (t1 - t0) / (double) CLOCKS_PER_SEC;
|
||||
tps = (int)((double) imax / total);
|
||||
return tps;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
const int mintps = 450000;
|
||||
int tps = testIsSQL();
|
||||
|
||||
printf("\nTPS : %d\n\n", tps);
|
||||
|
||||
if (tps < mintps) {
|
||||
printf("FAIL: %d < %d\n", tps, mintps);
|
||||
/* FAIL */
|
||||
return 1;
|
||||
} else {
|
||||
printf("OK: %d > %d\n", tps, mintps);
|
||||
/* OK */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
84
naxsi-0.55.3/ext/libinjection/test_speed_xss.c
Normal file
84
naxsi-0.55.3/ext/libinjection/test_speed_xss.c
Normal file
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* A not very good test for performance. This is mostly useful in
|
||||
* testing performance -regressions-
|
||||
*
|
||||
*/
|
||||
#include <time.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "libinjection.h"
|
||||
int testIsSQL(void);
|
||||
|
||||
int testIsSQL(void)
|
||||
{
|
||||
const char* const s[] = {
|
||||
"<script>alert(1);</script>",
|
||||
"><script>alert(1);</script>"
|
||||
"x ><script>alert(1);</script>",
|
||||
"' ><script>alert(1);</script>",
|
||||
"\"><script>alert(1);</script>",
|
||||
"red;</style><script>alert(1);</script>",
|
||||
"red;}</style><script>alert(1);</script>",
|
||||
"red;\"/><script>alert(1);</script>",
|
||||
"');}</style><script>alert(1);</script>",
|
||||
"onerror=alert(1)>",
|
||||
"x onerror=alert(1);>",
|
||||
"x' onerror=alert(1);>",
|
||||
"x\" onerror=alert(1);>",
|
||||
"<a href=\"javascript:alert(1)\">",
|
||||
"<a href='javascript:alert(1)'>",
|
||||
"<a href=javascript:alert(1)>",
|
||||
"<a href = javascript:alert(1); >",
|
||||
"<a href=\" javascript:alert(1);\" >",
|
||||
"<a href=\"JAVASCRIPT:alert(1);\" >",
|
||||
"123 LIKE -1234.5678E+2;",
|
||||
"APPLE 19.123 'FOO' \"BAR\"",
|
||||
"/* BAR */ UNION ALL SELECT (2,3,4)",
|
||||
"1 || COS(+0X04) --FOOBAR",
|
||||
"dog apple @cat banana bar",
|
||||
"dog apple cat \"banana \'bar",
|
||||
"102 TABLE CLOTH",
|
||||
"(1001-'1') union select 1,2,3,4 from credit_cards",
|
||||
NULL
|
||||
};
|
||||
const int imax = 1000000;
|
||||
int i, j;
|
||||
size_t slen;
|
||||
clock_t t0,t1;
|
||||
double total;
|
||||
int tps;
|
||||
|
||||
t0 = clock();
|
||||
for (i = imax, j=0; i != 0; --i, ++j) {
|
||||
if (s[j] == NULL) {
|
||||
j = 0;
|
||||
}
|
||||
|
||||
slen = strlen(s[j]);
|
||||
libinjection_xss(s[j], slen);
|
||||
}
|
||||
|
||||
t1 = clock();
|
||||
total = (double) (t1 - t0) / (double) CLOCKS_PER_SEC;
|
||||
tps = (int)((double) imax / total);
|
||||
return tps;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
const int mintps = 500000;
|
||||
int tps = testIsSQL();
|
||||
|
||||
printf("\nTPS : %d\n\n", tps);
|
||||
|
||||
if (tps < 500000) {
|
||||
printf("FAIL: %d < %d\n", tps, mintps);
|
||||
/* FAIL */
|
||||
return 1;
|
||||
} else {
|
||||
printf("OK: %d > %d\n", tps, mintps);
|
||||
/* OK */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
312
naxsi-0.55.3/ext/libinjection/testdriver.c
Normal file
312
naxsi-0.55.3/ext/libinjection/testdriver.c
Normal file
@@ -0,0 +1,312 @@
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <glob.h>
|
||||
#include "libinjection.h"
|
||||
#include "libinjection_sqli.h"
|
||||
#include "libinjection_html5.h"
|
||||
#include "libinjection_xss.h"
|
||||
|
||||
static char g_test[8096];
|
||||
static char g_input[8096];
|
||||
static char g_expected[8096];
|
||||
|
||||
size_t modp_rtrim(char* str, size_t len);
|
||||
size_t print_string(char* buf, size_t len, stoken_t* t);
|
||||
size_t print_var(char* buf, size_t len, stoken_t* t);
|
||||
size_t print_token(char* buf, size_t len, stoken_t *t);
|
||||
int read_file(const char* fname, int flags, int testtype);
|
||||
const char* h5_type_to_string(enum html5_type x);
|
||||
size_t print_html5_token(char* buf, size_t len, h5_state_t* hs);
|
||||
|
||||
size_t modp_rtrim(char* str, size_t len)
|
||||
{
|
||||
while (len) {
|
||||
char c = str[len -1];
|
||||
if (c == ' ' || c == '\n' || c == '\t' || c == '\r') {
|
||||
str[len -1] = '\0';
|
||||
len -= 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
size_t print_string(char* buf, size_t len, stoken_t* t)
|
||||
{
|
||||
int slen = 0;
|
||||
|
||||
/* print opening quote */
|
||||
if (t->str_open != '\0') {
|
||||
slen = sprintf(buf + len, "%c", t->str_open);
|
||||
assert(slen >= 0);
|
||||
len += (size_t) slen;
|
||||
}
|
||||
|
||||
/* print content */
|
||||
slen = sprintf(buf + len, "%s", t->val);
|
||||
assert(slen >= 0);
|
||||
len += (size_t) slen;
|
||||
|
||||
/* print closing quote */
|
||||
if (t->str_close != '\0') {
|
||||
slen = sprintf(buf + len, "%c", t->str_close);
|
||||
assert(slen >= 0);
|
||||
len += (size_t) slen;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
size_t print_var(char* buf, size_t len, stoken_t* t)
|
||||
{
|
||||
int slen = 0;
|
||||
if (t->count >= 1) {
|
||||
slen = sprintf(buf + len, "%c", '@');
|
||||
assert(slen >= 0);
|
||||
len += (size_t) slen;
|
||||
}
|
||||
if (t->count == 2) {
|
||||
slen = sprintf(buf + len, "%c", '@');
|
||||
assert(slen >= 0);
|
||||
len += (size_t) slen;
|
||||
}
|
||||
return print_string(buf, len, t);
|
||||
}
|
||||
|
||||
const char* h5_type_to_string(enum html5_type x)
|
||||
{
|
||||
switch (x) {
|
||||
case DATA_TEXT: return "DATA_TEXT";
|
||||
case TAG_NAME_OPEN: return "TAG_NAME_OPEN";
|
||||
case TAG_NAME_CLOSE: return "TAG_NAME_CLOSE";
|
||||
case TAG_NAME_SELFCLOSE: return "TAG_NAME_SELFCLOSE";
|
||||
case TAG_DATA: return "TAG_DATA";
|
||||
case TAG_CLOSE: return "TAG_CLOSE";
|
||||
case ATTR_NAME: return "ATTR_NAME";
|
||||
case ATTR_VALUE: return "ATTR_VALUE";
|
||||
case TAG_COMMENT: return "TAG_COMMENT";
|
||||
case DOCTYPE: return "DOCTYPE";
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
size_t print_html5_token(char* buf, size_t len, h5_state_t* hs)
|
||||
{
|
||||
int slen;
|
||||
char* tmp = (char*) malloc(hs->token_len + 1);
|
||||
memcpy(tmp, hs->token_start, hs->token_len);
|
||||
/* TODO.. encode to be printable */
|
||||
tmp[hs->token_len] = '\0';
|
||||
|
||||
slen = sprintf(buf + len, "%s,%d,%s\n",
|
||||
h5_type_to_string(hs->token_type),
|
||||
(int) hs->token_len,
|
||||
tmp);
|
||||
len += (size_t) slen;
|
||||
free(tmp);
|
||||
return len;
|
||||
}
|
||||
|
||||
size_t print_token(char* buf, size_t len, stoken_t *t)
|
||||
{
|
||||
int slen;
|
||||
|
||||
slen = sprintf(buf + len, "%c ", t->type);
|
||||
assert(slen >= 0);
|
||||
len += (size_t) slen;
|
||||
switch (t->type) {
|
||||
case 's':
|
||||
len = print_string(buf, len, t);
|
||||
break;
|
||||
case 'v':
|
||||
len = print_var(buf, len, t);
|
||||
break;
|
||||
default:
|
||||
slen = sprintf(buf + len, "%s", t->val);
|
||||
assert(slen >= 0);
|
||||
len += (size_t) slen;
|
||||
}
|
||||
slen = sprintf(buf + len, "%c", '\n');
|
||||
assert(slen >= 0);
|
||||
len += (size_t) slen;
|
||||
return len;
|
||||
}
|
||||
|
||||
int read_file(const char* fname, int flags, int testtype)
|
||||
{
|
||||
int count = 0;
|
||||
FILE *fp = NULL;
|
||||
char linebuf[8192];
|
||||
char g_actual[8192];
|
||||
char* bufptr = NULL;
|
||||
size_t slen;
|
||||
char* copy;
|
||||
sfilter sf;
|
||||
int ok = 1;
|
||||
int num_tokens;
|
||||
int issqli;
|
||||
int i;
|
||||
|
||||
g_test[0] = '\0';
|
||||
g_input[0] = '\0';
|
||||
g_expected[0] = '\0';
|
||||
|
||||
fp = fopen(fname, "r");
|
||||
while(fgets(linebuf, sizeof(linebuf), fp) != NULL) {
|
||||
if (count == 0 && strcmp(linebuf, "--TEST--\n") == 0) {
|
||||
bufptr = g_test;
|
||||
count = 1;
|
||||
} else if (count == 1 && strcmp(linebuf, "--INPUT--\n") == 0) {
|
||||
bufptr = g_input;
|
||||
count = 2;
|
||||
} else if (count == 2 && strcmp(linebuf, "--EXPECTED--\n") == 0) {
|
||||
bufptr = g_expected;
|
||||
count = 3;
|
||||
} else {
|
||||
assert(bufptr != NULL);
|
||||
strcat(bufptr, linebuf);
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
if (count != 3) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
g_expected[modp_rtrim(g_expected, strlen(g_expected))] = '\0';
|
||||
g_input[modp_rtrim(g_input, strlen(g_input))] = '\0';
|
||||
|
||||
|
||||
slen = strlen(g_input);
|
||||
copy = (char* ) malloc(slen);
|
||||
memcpy(copy, g_input, slen);
|
||||
|
||||
g_actual[0] = '\0';
|
||||
if (testtype == 0) {
|
||||
/*
|
||||
* print sqli tokenization only
|
||||
*/
|
||||
libinjection_sqli_init(&sf, copy, slen, flags);
|
||||
libinjection_sqli_callback(&sf, NULL, NULL);
|
||||
slen =0;
|
||||
while (libinjection_sqli_tokenize(&sf) == 1) {
|
||||
slen = print_token(g_actual, slen, sf.current);
|
||||
}
|
||||
} else if (testtype == 1) {
|
||||
/*
|
||||
* testing tokenization + folding
|
||||
*/
|
||||
libinjection_sqli_init(&sf, copy, slen, flags);
|
||||
libinjection_sqli_callback(&sf, NULL, NULL);
|
||||
slen =0;
|
||||
num_tokens = libinjection_sqli_fold(&sf);
|
||||
for (i = 0; i < num_tokens; ++i) {
|
||||
slen = print_token(g_actual, slen, libinjection_sqli_get_token(&sf, i));
|
||||
}
|
||||
} else if (testtype == 2) {
|
||||
/**
|
||||
* test sqli detection
|
||||
*/
|
||||
char buf[100];
|
||||
issqli = libinjection_sqli(copy, slen, buf);
|
||||
if (issqli) {
|
||||
sprintf(g_actual, "%s", buf);
|
||||
}
|
||||
} else if (testtype == 3) {
|
||||
/*
|
||||
* test html5 tokenization only
|
||||
*/
|
||||
|
||||
h5_state_t hs;
|
||||
libinjection_h5_init(&hs, copy, slen, DATA_STATE);
|
||||
slen = 0;
|
||||
while (libinjection_h5_next(&hs)) {
|
||||
slen = print_html5_token(g_actual, slen, &hs);
|
||||
}
|
||||
} else if (testtype == 4) {
|
||||
/*
|
||||
* test XSS detection
|
||||
*/
|
||||
sprintf(g_actual, "%d", libinjection_xss(copy, slen));
|
||||
} else {
|
||||
fprintf(stderr, "Got stange testtype value of %d\n", testtype);
|
||||
assert(0);
|
||||
}
|
||||
|
||||
g_actual[modp_rtrim(g_actual, strlen(g_actual))] = '\0';
|
||||
|
||||
if (strcmp(g_expected, g_actual) != 0) {
|
||||
printf("INPUT: \n%s\n==\n", g_input);
|
||||
printf("EXPECTED: \n%s\n==\n", g_expected);
|
||||
printf("GOT: \n%s\n==\n", g_actual);
|
||||
ok = 0;
|
||||
}
|
||||
|
||||
free(copy);
|
||||
return ok;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
int offset = 1;
|
||||
int i;
|
||||
int ok;
|
||||
int count = 0;
|
||||
int count_fail = 0;
|
||||
int flags = 0;
|
||||
int testtype = 0;
|
||||
int quiet = 0;
|
||||
|
||||
const char* fname;
|
||||
while (1) {
|
||||
if (strcmp(argv[offset], "-q") == 0 || strcmp(argv[offset], "--quiet") == 0) {
|
||||
quiet = 1;
|
||||
offset += 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
printf("%s\n", libinjection_version());
|
||||
|
||||
for (i = offset; i < argc; ++i) {
|
||||
fname = argv[i];
|
||||
count += 1;
|
||||
if (strstr(fname, "test-tokens-")) {
|
||||
flags = FLAG_QUOTE_NONE | FLAG_SQL_ANSI;
|
||||
testtype = 0;
|
||||
} else if (strstr(fname, "test-folding-")) {
|
||||
flags = FLAG_QUOTE_NONE | FLAG_SQL_ANSI;
|
||||
testtype = 1;
|
||||
} else if (strstr(fname, "test-sqli-")) {
|
||||
flags = FLAG_NONE;
|
||||
testtype = 2;
|
||||
} else if (strstr(fname, "test-html5-")) {
|
||||
flags = FLAG_NONE;
|
||||
testtype = 3;
|
||||
} else if (strstr(fname, "test-xss-")) {
|
||||
flags = FLAG_NONE;
|
||||
testtype = 4;
|
||||
} else {
|
||||
fprintf(stderr, "Unknown test type: %s, failing\n", fname);
|
||||
count_fail += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
ok = read_file(fname, flags, testtype);
|
||||
if (ok) {
|
||||
if (! quiet) {
|
||||
fprintf(stderr, "%s: ok\n", fname);
|
||||
}
|
||||
} else {
|
||||
count_fail += 1;
|
||||
if (! quiet) {
|
||||
fprintf(stderr, "%s: fail\n", fname);
|
||||
}
|
||||
}
|
||||
}
|
||||
return count > 0 && count_fail > 0;
|
||||
}
|
||||
617
naxsi-0.55.3/naxsi.h
Normal file
617
naxsi-0.55.3/naxsi.h
Normal file
@@ -0,0 +1,617 @@
|
||||
/*
|
||||
* NAXSI, a web application firewall for NGINX
|
||||
* Copyright (C) 2016, Thibault 'bui' Koechlin
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give
|
||||
* permission to link the code of portions of this program with the
|
||||
* OpenSSL library under certain conditions as described in each
|
||||
* individual source file, and distribute linked combinations
|
||||
* including the two.
|
||||
* You must obey the GNU General Public License in all respects
|
||||
* for all of the code used other than OpenSSL. If you modify
|
||||
* file(s) with this exception, you may extend this exception to your
|
||||
* version of the file(s), but you are not obligated to do so. If you
|
||||
* do not wish to do so, delete this exception statement from your
|
||||
* version. If you delete this exception statement from all source
|
||||
* files in the program, then also delete it here.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __FOO_H__
|
||||
#define __FOO_H__
|
||||
|
||||
#define NAXSI_VERSION "0.55.3"
|
||||
|
||||
#include <nginx.h>
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
#include <ngx_event.h>
|
||||
#include <ngx_md5.h>
|
||||
#include <ngx_http_core_module.h>
|
||||
#include <pcre.h>
|
||||
#include <ctype.h>
|
||||
#include "ext/libinjection/libinjection_sqli.h"
|
||||
#include "ext/libinjection/libinjection_xss.h"
|
||||
|
||||
|
||||
extern ngx_module_t ngx_http_naxsi_module;
|
||||
|
||||
/*
|
||||
** as the #ifdef #endif for debug are getting really messy ...
|
||||
** Bellow are all the possibles debug defines. To enable associated feature
|
||||
** debug, just set it to 1. Do not comment the actual define except if you
|
||||
** know all the associated debug calls are deleted.
|
||||
** The idea is that the compiler will optimize out the do { if (0) ... } while (0);
|
||||
*/
|
||||
|
||||
#define _naxsi_rawbody 0
|
||||
#define _debug_basestr_ruleset 0
|
||||
#define _debug_custom_score 0
|
||||
#define _debug_body_parse 0
|
||||
#define _debug_cfg_parse_one_rule 0
|
||||
#define _debug_zone 0
|
||||
#define _debug_extensive_log 0
|
||||
#define _debug_loc_conf 0
|
||||
#define _debug_main_conf 0
|
||||
#define _debug_mechanics 0
|
||||
#define _debug_json 0
|
||||
#define _debug_modifier 0
|
||||
#define _debug_payload_handler 0
|
||||
#define _debug_post_heavy 0
|
||||
#define _debug_rawbody 0
|
||||
#define _debug_readconf 0
|
||||
#define _debug_rx 0
|
||||
#define _debug_score 0
|
||||
#define _debug_spliturl_ruleset 0
|
||||
#define _debug_whitelist_compat 0
|
||||
#define _debug_whitelist 0
|
||||
#define _debug_whitelist_heavy 0
|
||||
#define _debug_whitelist_light 0
|
||||
#define wl_debug_rx 0
|
||||
|
||||
#ifndef __NAXSI_DEBUG
|
||||
#define __NAXSI_DEBUG
|
||||
#define NX_DEBUG(FEATURE, DEF, LOG, ST, ...) do { if (FEATURE) ngx_log_debug(DEF, LOG, ST, __VA_ARGS__); } while (0)
|
||||
#endif
|
||||
|
||||
#ifndef __NAXSI_LOG_DEBUG
|
||||
#define __NAXSI_LOG_DEBUG
|
||||
#define NX_LOG_DEBUG(FEATURE, DEF, LOG, ST, ...) do { if (FEATURE) ngx_conf_log_error(DEF, LOG, ST, __VA_ARGS__); } while (0)
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** Here is globally how the structures are organized :
|
||||
**
|
||||
** [[ngx_http_dummy_main_conf_t]] is the main structure for the module.
|
||||
** it contains the core rules and a set of ngx_http_dummy_loc_conf_t,
|
||||
** for each NGINX location.
|
||||
** ---
|
||||
** [[ngx_http_dummy_loc_conf_t]] is the main structure for any NGINX
|
||||
** locations, that is - a web site. It contains both pointers to the core
|
||||
** rules, as well as whitelists, scores, denied_url and all flags. all
|
||||
** the data of a nginx location is held into the loc_conf_t struct.
|
||||
** The sets of rules are actually containted into [[ngx_http_rule_t]] structs.
|
||||
** ---
|
||||
** [[ngx_http_rule_t]] structs are used to hold any info about a rule, as well
|
||||
** as whitelists. (whitelists is just a 'kind' of rule).
|
||||
**
|
||||
*/
|
||||
|
||||
/*
|
||||
** basic rule can have 4 (so far) kind of matching mechanisms
|
||||
** RX
|
||||
** STR
|
||||
** LIBINJ_XSS
|
||||
** LIBINJ_SQL
|
||||
*/
|
||||
enum DETECT_MECHANISM {
|
||||
NONE = -1,
|
||||
RX,
|
||||
STR,
|
||||
LIBINJ_XSS,
|
||||
LIBINJ_SQL
|
||||
};
|
||||
|
||||
enum MATCH_TYPE {
|
||||
URI_ONLY=0,
|
||||
NAME_ONLY,
|
||||
MIXED
|
||||
};
|
||||
|
||||
enum DUMMY_MATCH_ZONE {
|
||||
HEADERS=0,
|
||||
URL,
|
||||
ARGS,
|
||||
BODY,
|
||||
RAW_BODY,
|
||||
FILE_EXT,
|
||||
UNKNOWN
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
** struct used to store a specific match zone
|
||||
** in conf : MATCH_ZONE:[GET_VAR|HEADER|POST_VAR]:VAR_NAME:
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
/* match in [name] var of body */
|
||||
ngx_flag_t body_var:1;
|
||||
/* match in [name] var of headers */
|
||||
ngx_flag_t headers_var:1;
|
||||
/* match in [name] var of args */
|
||||
ngx_flag_t args_var:1;
|
||||
/* match on URL [name] */
|
||||
ngx_flag_t specific_url:1;
|
||||
ngx_str_t target;
|
||||
/* to be used for regexed match zones */
|
||||
ngx_regex_compile_t *target_rx;
|
||||
ngx_uint_t hash;
|
||||
|
||||
} ngx_http_custom_rule_location_t;
|
||||
|
||||
|
||||
/*
|
||||
** WhiteList Rules Definition :
|
||||
** A whitelist contains :
|
||||
** - an URI
|
||||
**
|
||||
** - one or several sets containing :
|
||||
** - an variable name ('foo') associated with a zone ($GET_VAR:foo)
|
||||
** - one or several rules id to whitelist
|
||||
*/
|
||||
|
||||
typedef struct
|
||||
{
|
||||
/* match in full body (POST DATA) */
|
||||
ngx_flag_t body:1;
|
||||
/* match in [name] var of body */
|
||||
ngx_flag_t body_var:1;
|
||||
/* match in all headers */
|
||||
ngx_flag_t headers:1;
|
||||
/* match in [name] var of headers */
|
||||
ngx_flag_t headers_var:1;
|
||||
/* match in URI */
|
||||
ngx_flag_t url:1;
|
||||
/* match in args (bla.php?<ARGS>) */
|
||||
ngx_flag_t args:1;
|
||||
/* match in [name] var of args */
|
||||
ngx_flag_t args_var:1;
|
||||
/* match on a global flag : weird_request, big_body etc. */
|
||||
ngx_flag_t flags:1;
|
||||
/* match on file upload extension */
|
||||
ngx_flag_t file_ext:1;
|
||||
/* set if defined "custom" match zone (GET_VAR/POST_VAR/...) */
|
||||
ngx_array_t *ids;
|
||||
ngx_str_t *name;
|
||||
} ngx_http_whitelist_location_t;
|
||||
|
||||
|
||||
/*
|
||||
** this struct is used to aggregate all whitelist
|
||||
** that point to the same URI or the same VARNAME
|
||||
** all the "subrules" will then be stored in the "whitelist_locations"
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
/*ngx_http_whitelist_location_t **/
|
||||
ngx_array_t *whitelist_locations;
|
||||
/* zone to wich the WL applies */
|
||||
enum DUMMY_MATCH_ZONE zone;
|
||||
/* if the "name" is only an url, specify it */
|
||||
int uri_only:1;
|
||||
/* does the rule targets the name
|
||||
instead of the content ?*/
|
||||
int target_name;
|
||||
|
||||
ngx_str_t *name;
|
||||
ngx_int_t hash;
|
||||
ngx_array_t *ids;
|
||||
} ngx_http_whitelist_rule_t;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* basic rule */
|
||||
typedef struct
|
||||
{
|
||||
ngx_str_t *str; // string
|
||||
ngx_regex_compile_t *rx; // or regex
|
||||
/*
|
||||
** basic rule can have 4 (so far) kind of matching mechanisms :
|
||||
** RX, STR, LIBINJ_XSS, LIBINJ_SQL
|
||||
*/
|
||||
enum DETECT_MECHANISM match_type;
|
||||
/* is the match zone a regex or a string (hashtable) */
|
||||
ngx_int_t rx_mz;
|
||||
/* ~~~~~ match zones ~~~~~~ */
|
||||
ngx_int_t zone;
|
||||
/* match in full body (POST DATA) */
|
||||
ngx_flag_t body:1;
|
||||
ngx_flag_t raw_body:1;
|
||||
ngx_flag_t body_var:1;
|
||||
/* match in all headers */
|
||||
ngx_flag_t headers:1;
|
||||
ngx_flag_t headers_var:1;
|
||||
/* match in URI */
|
||||
ngx_flag_t url:1;
|
||||
/* match in args (bla.php?<ARGS>) */
|
||||
ngx_flag_t args:1;
|
||||
ngx_flag_t args_var:1;
|
||||
/* match on flags (weird_uri, big_body etc. */
|
||||
ngx_flag_t flags:1;
|
||||
/* match on file upload extension */
|
||||
ngx_flag_t file_ext:1;
|
||||
/* set if defined "custom" match zone (GET_VAR/POST_VAR/...) */
|
||||
ngx_flag_t custom_location:1;
|
||||
ngx_int_t custom_location_only;
|
||||
/* does the rule targets variable name instead ? */
|
||||
ngx_int_t target_name;
|
||||
|
||||
/* custom location match zones list (GET_VAR/POST_VAR ...) */
|
||||
ngx_array_t *custom_locations;
|
||||
/* ~~~~~~~ specific flags ~~~~~~~~~ */
|
||||
ngx_flag_t negative:1;
|
||||
} ngx_http_basic_rule_t;
|
||||
|
||||
|
||||
|
||||
/* define for RULE TYPE in rule_t */
|
||||
#define BR 1
|
||||
|
||||
/* flags used for 'custom match rules', like $XSS > 7 */
|
||||
#define SUP 1
|
||||
#define SUP_OR_EQUAL 2
|
||||
#define INF 3
|
||||
#define INF_OR_EQUAL 4
|
||||
|
||||
/*
|
||||
** This struct is used to store custom scores at runtime.
|
||||
** ie : $XSS = 7
|
||||
** tag is the $XSS and sc_score is 7
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
ngx_str_t *sc_tag;
|
||||
ngx_int_t sc_score;
|
||||
ngx_flag_t block:1;
|
||||
ngx_flag_t allow:1;
|
||||
ngx_flag_t drop:1;
|
||||
ngx_flag_t log:1;
|
||||
} ngx_http_special_score_t;
|
||||
|
||||
/*
|
||||
** This one is very related to the previous one,
|
||||
** it's used to store a score rule comparison.
|
||||
** ie : $XSS > 7
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
ngx_str_t sc_tag;
|
||||
ngx_int_t sc_score;
|
||||
ngx_int_t cmp;
|
||||
ngx_flag_t block:1;
|
||||
ngx_flag_t allow:1;
|
||||
ngx_flag_t drop:1;
|
||||
ngx_flag_t log:1;
|
||||
} ngx_http_check_rule_t;
|
||||
|
||||
/* TOP level rule structure */
|
||||
typedef struct
|
||||
{
|
||||
/* type of the rule */
|
||||
ngx_int_t type;
|
||||
/* simply put a flag if it's a wlr,
|
||||
wl_id array will be used to store the whitelisted IDs */
|
||||
ngx_flag_t whitelist:1;
|
||||
ngx_array_t *wlid_array;
|
||||
/* "common" data for all rules */
|
||||
ngx_int_t rule_id;
|
||||
ngx_str_t *log_msg; // a specific log message
|
||||
ngx_int_t score; //also handles DENY and ALLOW
|
||||
|
||||
/* List of scores increased on rule match. */
|
||||
ngx_array_t *sscores;
|
||||
ngx_flag_t sc_block:1; //
|
||||
ngx_flag_t sc_allow:1; //
|
||||
// end of specific score tag stuff
|
||||
ngx_flag_t block:1;
|
||||
ngx_flag_t allow:1;
|
||||
ngx_flag_t drop:1;
|
||||
ngx_flag_t log:1;
|
||||
/* pointers on specific rule stuff */
|
||||
ngx_http_basic_rule_t *br;
|
||||
} ngx_http_rule_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
ngx_array_t *get_rules; /*ngx_http_rule_t*/
|
||||
ngx_array_t *body_rules;
|
||||
ngx_array_t *header_rules;
|
||||
ngx_array_t *generic_rules;
|
||||
ngx_array_t *raw_body_rules;
|
||||
|
||||
ngx_array_t *locations; /*ngx_http_dummy_loc_conf_t*/
|
||||
ngx_log_t *log;
|
||||
|
||||
} ngx_http_dummy_main_conf_t;
|
||||
|
||||
|
||||
/* TOP level configuration structure */
|
||||
typedef struct
|
||||
{
|
||||
/*
|
||||
** basicrule / mainrules, sorted by target zone
|
||||
*/
|
||||
ngx_array_t *get_rules;
|
||||
ngx_array_t *body_rules;
|
||||
ngx_array_t *raw_body_rules;
|
||||
ngx_array_t *header_rules;
|
||||
ngx_array_t *generic_rules;
|
||||
ngx_array_t *check_rules;
|
||||
/* raw array of whitelisted rules */
|
||||
ngx_array_t *whitelist_rules;
|
||||
/* raw array of transformed whitelists */
|
||||
ngx_array_t *tmp_wlr;
|
||||
/* raw array of regex-mz whitelists */
|
||||
ngx_array_t *rxmz_wlr;
|
||||
/* hash table of whitelisted URL rules */
|
||||
ngx_hash_t *wlr_url_hash;
|
||||
/* hash table of whitelisted ARGS rules */
|
||||
ngx_hash_t *wlr_args_hash;
|
||||
/* hash table of whitelisted BODY rules */
|
||||
ngx_hash_t *wlr_body_hash;
|
||||
/* hash table of whitelisted HEADERS rules */
|
||||
ngx_hash_t *wlr_headers_hash;
|
||||
/* rules that are globally disabled in one location */
|
||||
ngx_array_t *disabled_rules;
|
||||
/* counters for both processed requests and
|
||||
blocked requests, used in naxsi_fmt */
|
||||
size_t request_processed;
|
||||
size_t request_blocked;
|
||||
ngx_int_t error;
|
||||
ngx_array_t *persistant_data;
|
||||
ngx_flag_t extensive:1;
|
||||
ngx_flag_t learning:1;
|
||||
ngx_flag_t enabled:1;
|
||||
ngx_flag_t force_disabled:1;
|
||||
ngx_flag_t pushed:1;
|
||||
ngx_flag_t libinjection_sql_enabled:1;
|
||||
ngx_flag_t libinjection_xss_enabled:1;
|
||||
ngx_str_t *denied_url;
|
||||
/* precomputed hash for dynamic variable lookup,
|
||||
variable themselves are boolean */
|
||||
ngx_uint_t flag_enable_h;
|
||||
ngx_uint_t flag_learning_h;
|
||||
ngx_uint_t flag_post_action_h;
|
||||
ngx_uint_t flag_extensive_log_h;
|
||||
/* precomputed hash for
|
||||
libinjection dynamic flags */
|
||||
ngx_uint_t flag_libinjection_xss_h;
|
||||
ngx_uint_t flag_libinjection_sql_h;
|
||||
|
||||
} ngx_http_dummy_loc_conf_t;
|
||||
|
||||
|
||||
/*
|
||||
** used to store sets of matched rules during runtime
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
/* matched in [name] var of body */
|
||||
ngx_flag_t body_var:1;
|
||||
/* matched in [name] var of headers */
|
||||
ngx_flag_t headers_var:1;
|
||||
/* matched in [name] var of args */
|
||||
ngx_flag_t args_var:1;
|
||||
/* matched on URL */
|
||||
ngx_flag_t url:1;
|
||||
/* matched in filename [name] of args*/
|
||||
ngx_flag_t file_ext:1;
|
||||
/* matched within the 'NAME' */
|
||||
ngx_flag_t target_name:1;
|
||||
|
||||
ngx_str_t *name;
|
||||
ngx_http_rule_t *rule;
|
||||
} ngx_http_matched_rule_t;
|
||||
|
||||
/*
|
||||
** Context structure
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
ngx_array_t *special_scores;
|
||||
ngx_int_t score;
|
||||
/* blocking flags */
|
||||
ngx_flag_t log:1;
|
||||
ngx_flag_t block:1;
|
||||
ngx_flag_t allow:1;
|
||||
ngx_flag_t drop:1;
|
||||
/* state */
|
||||
ngx_flag_t wait_for_body:1;
|
||||
ngx_flag_t ready:1;
|
||||
ngx_flag_t over:1;
|
||||
/* matched rules */
|
||||
ngx_array_t *matched;
|
||||
/* runtime flags (modifiers) */
|
||||
ngx_flag_t learning:1;
|
||||
ngx_flag_t enabled:1;
|
||||
ngx_flag_t post_action:1;
|
||||
ngx_flag_t extensive_log:1;
|
||||
/* did libinjection sql/xss matched ? */
|
||||
ngx_flag_t libinjection_sql:1;
|
||||
ngx_flag_t libinjection_xss:1;
|
||||
} ngx_http_request_ctx_t;
|
||||
|
||||
/*
|
||||
** this structure is used only for json parsing.
|
||||
*/
|
||||
typedef struct ngx_http_nx_json_s {
|
||||
ngx_str_t json;
|
||||
u_char *src;
|
||||
ngx_int_t off, len;
|
||||
u_char c;
|
||||
int depth;
|
||||
ngx_http_request_t *r;
|
||||
ngx_http_request_ctx_t *ctx;
|
||||
ngx_str_t ckey;
|
||||
ngx_http_dummy_main_conf_t *main_cf;
|
||||
ngx_http_dummy_loc_conf_t *loc_cf;
|
||||
} ngx_json_t;
|
||||
|
||||
|
||||
|
||||
#define TOP_DENIED_URL_T "DeniedUrl"
|
||||
#define TOP_LEARNING_FLAG_T "LearningMode"
|
||||
#define TOP_ENABLED_FLAG_T "SecRulesEnabled"
|
||||
#define TOP_DISABLED_FLAG_T "SecRulesDisabled"
|
||||
#define TOP_CHECK_RULE_T "CheckRule"
|
||||
#define TOP_BASIC_RULE_T "BasicRule"
|
||||
#define TOP_MAIN_BASIC_RULE_T "MainRule"
|
||||
#define TOP_LIBINJECTION_SQL_T "LibInjectionSql"
|
||||
#define TOP_LIBINJECTION_XSS_T "LibInjectionXss"
|
||||
|
||||
/* nginx-style names */
|
||||
#define TOP_DENIED_URL_N "denied_url"
|
||||
#define TOP_LEARNING_FLAG_N "learning_mode"
|
||||
#define TOP_ENABLED_FLAG_N "rules_enabled"
|
||||
#define TOP_DISABLED_FLAG_N "rules_disabled"
|
||||
#define TOP_CHECK_RULE_N "check_rule"
|
||||
#define TOP_BASIC_RULE_N "basic_rule"
|
||||
#define TOP_MAIN_BASIC_RULE_N "main_rule"
|
||||
#define TOP_LIBINJECTION_SQL_N "libinjection_sql"
|
||||
#define TOP_LIBINJECTION_XSS_N "libinjection_xss"
|
||||
|
||||
|
||||
/*possible 'tokens' in rule */
|
||||
#define ID_T "id:"
|
||||
#define SCORE_T "s:"
|
||||
#define MSG_T "msg:"
|
||||
#define RX_T "rx:"
|
||||
#define STR_T "str:"
|
||||
#define MATCH_ZONE_T "mz:"
|
||||
#define WHITELIST_T "wl:"
|
||||
#define LIBINJ_XSS_T "d:libinj_xss"
|
||||
#define LIBINJ_SQL_T "d:libinj_sql"
|
||||
#define NEGATIVE_T "negative"
|
||||
|
||||
/*
|
||||
** name of hardcoded variables to
|
||||
** change behavior of naxsi at runtime
|
||||
*/
|
||||
#define RT_EXTENSIVE_LOG "naxsi_extensive_log"
|
||||
#define RT_ENABLE "naxsi_flag_enable"
|
||||
#define RT_LEARNING "naxsi_flag_learning"
|
||||
#define RT_POST_ACTION "naxsi_flag_post_action"
|
||||
#define RT_LIBINJECTION_SQL "naxsi_flag_libinjection_sql"
|
||||
#define RT_LIBINJECTION_XSS "naxsi_flag_libinjection_xss"
|
||||
|
||||
|
||||
/*
|
||||
** To avoid getting DoS'ed, define max depth
|
||||
** for JSON parser, as it is recursive
|
||||
*/
|
||||
#define JSON_MAX_DEPTH 10
|
||||
|
||||
|
||||
void *ngx_http_dummy_cfg_parse_one_rule(ngx_conf_t *cf,
|
||||
ngx_str_t *value,
|
||||
ngx_http_rule_t *rule,
|
||||
ngx_int_t nb_elem);
|
||||
char *strfaststr(unsigned char *haystack, unsigned int hl,
|
||||
unsigned char *needle, unsigned int nl);
|
||||
char *strnchr(const char *s, int c, int len);
|
||||
ngx_int_t ngx_http_dummy_create_hashtables_n(ngx_http_dummy_loc_conf_t *dlc,
|
||||
ngx_conf_t *cf);
|
||||
void ngx_http_dummy_data_parse(ngx_http_request_ctx_t *ctx,
|
||||
ngx_http_request_t *r);
|
||||
ngx_int_t ngx_http_output_forbidden_page(ngx_http_request_ctx_t *ctx,
|
||||
ngx_http_request_t *r);
|
||||
int nx_check_ids(ngx_int_t match_id, ngx_array_t *wl_ids);
|
||||
int naxsi_unescape(ngx_str_t *str);
|
||||
|
||||
void ngx_http_dummy_json_parse(ngx_http_request_ctx_t *ctx,
|
||||
ngx_http_request_t *r,
|
||||
u_char *src,
|
||||
u_int len);
|
||||
|
||||
void ngx_http_libinjection(ngx_pool_t *pool,
|
||||
ngx_str_t *name,
|
||||
ngx_str_t *value,
|
||||
ngx_http_request_ctx_t *ctx,
|
||||
ngx_http_request_t *req,
|
||||
enum DUMMY_MATCH_ZONE zone);
|
||||
/*
|
||||
** JSON parsing prototypes.
|
||||
*/
|
||||
ngx_int_t ngx_http_nx_json_forward(ngx_json_t *js) ;
|
||||
ngx_int_t ngx_http_nx_json_seek(ngx_json_t *js, unsigned char seek);
|
||||
ngx_int_t ngx_http_nx_json_quoted(ngx_json_t *js, ngx_str_t *ve);
|
||||
ngx_int_t ngx_http_nx_json_array(ngx_json_t *js);
|
||||
ngx_int_t ngx_http_nx_json_val(ngx_json_t *js);
|
||||
ngx_int_t ngx_http_nx_json_obj(ngx_json_t *js);
|
||||
|
||||
|
||||
/*
|
||||
** naxsi_runtime
|
||||
**
|
||||
*/
|
||||
|
||||
void ngx_http_dummy_update_current_ctx_status(ngx_http_request_ctx_t *ctx,
|
||||
ngx_http_dummy_loc_conf_t *cf,
|
||||
ngx_http_request_t *r);
|
||||
int ngx_http_process_basic_rule_buffer(ngx_str_t *str, ngx_http_rule_t *rl,
|
||||
ngx_int_t *match);
|
||||
void ngx_http_dummy_payload_handler(ngx_http_request_t *r);
|
||||
int ngx_http_basestr_ruleset_n(ngx_pool_t *pool,
|
||||
ngx_str_t *name,
|
||||
ngx_str_t *value,
|
||||
ngx_array_t *rules,
|
||||
ngx_http_request_t *req,
|
||||
ngx_http_request_ctx_t *ctx,
|
||||
enum DUMMY_MATCH_ZONE zone);
|
||||
void ngx_http_dummy_body_parse(ngx_http_request_ctx_t *ctx,
|
||||
ngx_http_request_t *r,
|
||||
ngx_http_dummy_loc_conf_t *cf,
|
||||
ngx_http_dummy_main_conf_t *main_cf);
|
||||
void naxsi_log_offending(ngx_str_t *name, ngx_str_t *val, ngx_http_request_t *req,
|
||||
ngx_http_rule_t *rule, enum DUMMY_MATCH_ZONE zone, ngx_int_t target_name);
|
||||
|
||||
int ngx_http_apply_rulematch_v_n(ngx_http_rule_t *r, ngx_http_request_ctx_t *ctx,
|
||||
ngx_http_request_t *req, ngx_str_t *name,
|
||||
ngx_str_t *value, enum DUMMY_MATCH_ZONE zone,
|
||||
ngx_int_t nb_match, ngx_int_t target_name);
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** externs for internal rules that requires it.
|
||||
*/
|
||||
extern ngx_http_rule_t *nx_int__libinject_sql;
|
||||
extern ngx_http_rule_t *nx_int__libinject_xss;
|
||||
|
||||
/*libinjection_xss wrapper not exported by libinject_xss.h.*/
|
||||
int libinjection_xss(const char* s, size_t len);
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
593
naxsi-0.55.3/naxsi_config.c
Normal file
593
naxsi-0.55.3/naxsi_config.c
Normal file
@@ -0,0 +1,593 @@
|
||||
/*
|
||||
* NAXSI, a web application firewall for NGINX
|
||||
* Copyright (C) 2016, Thibault 'bui' Koechlin
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give
|
||||
* permission to link the code of portions of this program with the
|
||||
* OpenSSL library under certain conditions as described in each
|
||||
* individual source file, and distribute linked combinations
|
||||
* including the two.
|
||||
* You must obey the GNU General Public License in all respects
|
||||
* for all of the code used other than OpenSSL. If you modify
|
||||
* file(s) with this exception, you may extend this exception to your
|
||||
* version of the file(s), but you are not obligated to do so. If you
|
||||
* do not wish to do so, delete this exception statement from your
|
||||
* version. If you delete this exception statement from all source
|
||||
* files in the program, then also delete it here.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "naxsi.h"
|
||||
/*
|
||||
** TOP LEVEL configuration parsing code
|
||||
*/
|
||||
/*
|
||||
** code to parse FLAGS and OPTIONS on each line.
|
||||
*/
|
||||
void *dummy_id(ngx_conf_t *r, ngx_str_t *tmp, ngx_http_rule_t *rule);
|
||||
void *dummy_score(ngx_conf_t *r, ngx_str_t *tmp, ngx_http_rule_t *rule);
|
||||
void *dummy_msg(ngx_conf_t *r, ngx_str_t *tmp, ngx_http_rule_t *rule);
|
||||
void *dummy_rx(ngx_conf_t *r, ngx_str_t *tmp, ngx_http_rule_t *rule);
|
||||
void *dummy_zone(ngx_conf_t *r, ngx_str_t *tmp, ngx_http_rule_t *rule);
|
||||
void *dummy_str(ngx_conf_t *r, ngx_str_t *tmp, ngx_http_rule_t *rule);
|
||||
void *dummy_negative(ngx_conf_t *r, ngx_str_t *tmp, ngx_http_rule_t *rule);
|
||||
void *dummy_libinj_xss(ngx_conf_t *r, ngx_str_t *tmp, ngx_http_rule_t *rule);
|
||||
void *dummy_libinj_sql(ngx_conf_t *r, ngx_str_t *tmp, ngx_http_rule_t *rule);
|
||||
void *dummy_whitelist(ngx_conf_t *r, ngx_str_t *tmp, ngx_http_rule_t *rule);
|
||||
/*
|
||||
** Structures related to the configuration parser
|
||||
*/
|
||||
typedef struct {
|
||||
char *prefix;
|
||||
void *(*pars)(ngx_conf_t *, ngx_str_t *, ngx_http_rule_t *);
|
||||
} ngx_http_dummy_parser_t;
|
||||
|
||||
|
||||
|
||||
static ngx_http_dummy_parser_t rule_parser[] = {
|
||||
{ID_T, dummy_id},
|
||||
{SCORE_T, dummy_score},
|
||||
{MSG_T, dummy_msg},
|
||||
{RX_T, dummy_rx},
|
||||
{STR_T, dummy_str},
|
||||
{LIBINJ_XSS_T, dummy_libinj_xss},
|
||||
{LIBINJ_SQL_T, dummy_libinj_sql},
|
||||
{MATCH_ZONE_T, dummy_zone},
|
||||
{NEGATIVE_T, dummy_negative},
|
||||
{WHITELIST_T, dummy_whitelist},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
|
||||
|
||||
void *
|
||||
dummy_negative(ngx_conf_t *r, ngx_str_t *tmp, ngx_http_rule_t *rule)
|
||||
{
|
||||
rule->br->negative = 1;
|
||||
return (NGX_CONF_OK);
|
||||
}
|
||||
|
||||
void *
|
||||
dummy_libinj_xss(ngx_conf_t *r, ngx_str_t *tmp, ngx_http_rule_t *rule)
|
||||
{
|
||||
rule->br->match_type = LIBINJ_XSS;
|
||||
return (NGX_CONF_OK);
|
||||
}
|
||||
|
||||
void *
|
||||
dummy_libinj_sql(ngx_conf_t *r, ngx_str_t *tmp, ngx_http_rule_t *rule)
|
||||
{
|
||||
rule->br->match_type = LIBINJ_SQL;
|
||||
return (NGX_CONF_OK);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void *
|
||||
dummy_score(ngx_conf_t *r, ngx_str_t *tmp, ngx_http_rule_t *rule)
|
||||
{
|
||||
int score, len;
|
||||
char *tmp_ptr, *tmp_end;
|
||||
ngx_http_special_score_t *sc;
|
||||
|
||||
rule->score = 0;
|
||||
rule->block = 0;
|
||||
rule->allow = 0;
|
||||
rule->drop = 0;
|
||||
tmp_ptr = (char *) (tmp->data + strlen(SCORE_T));
|
||||
NX_LOG_DEBUG(_debug_score, NGX_LOG_EMERG, r, 0,
|
||||
"XX-(debug) dummy score (%V)",
|
||||
tmp);
|
||||
/*allocate scores array*/
|
||||
if (!rule->sscores) {
|
||||
rule->sscores = ngx_array_create(r->pool, 1, sizeof(ngx_http_special_score_t));
|
||||
}
|
||||
|
||||
while (*tmp_ptr) {
|
||||
if (tmp_ptr[0] == '$') {
|
||||
NX_LOG_DEBUG(_debug_score, NGX_LOG_EMERG, r, 0,
|
||||
"XX-(debug) special scoring rule (%s)",
|
||||
tmp_ptr);
|
||||
tmp_end = strchr(tmp_ptr, ':');
|
||||
if (!tmp_end)
|
||||
return (NGX_CONF_ERROR);
|
||||
len = tmp_end - tmp_ptr;
|
||||
if (len <= 0)
|
||||
return (NGX_CONF_ERROR);
|
||||
sc = ngx_array_push(rule->sscores);
|
||||
if (!sc)
|
||||
return (NGX_CONF_ERROR);
|
||||
sc->sc_tag = ngx_pcalloc(r->pool, sizeof(ngx_str_t));
|
||||
if (!sc->sc_tag)
|
||||
return (NGX_CONF_ERROR);
|
||||
sc->sc_tag->data = ngx_pcalloc(r->pool, len+1);
|
||||
if (!sc->sc_tag->data)
|
||||
return (NGX_CONF_ERROR);
|
||||
//memset(rule->sc_tag->data, 0, len+1);
|
||||
memcpy(sc->sc_tag->data, tmp_ptr, len);
|
||||
sc->sc_tag->len = len;
|
||||
sc->sc_score = atoi(tmp_end+1);
|
||||
NX_LOG_DEBUG(_debug_score, NGX_LOG_EMERG, r, 0,
|
||||
"XX-(debug) special scoring (%V) => (%d)",
|
||||
sc->sc_tag, sc->sc_score);
|
||||
|
||||
/* move to end of score. */
|
||||
while ( /*don't overflow*/((unsigned int)((unsigned char *)tmp_ptr - tmp->data)) < tmp->len &&
|
||||
/*and seek for next score */ *tmp_ptr != ',')
|
||||
++tmp_ptr;
|
||||
}
|
||||
else if (tmp_ptr[0] == ',')
|
||||
++tmp_ptr;
|
||||
else if (!strcasecmp(tmp_ptr, "BLOCK")) {
|
||||
rule->block = 1;
|
||||
tmp_ptr += 5;
|
||||
}
|
||||
else if (!strcasecmp(tmp_ptr, "DROP")) {
|
||||
rule->drop = 1;
|
||||
tmp_ptr += 4;
|
||||
}
|
||||
else if (!strcasecmp(tmp_ptr, "ALLOW")) {
|
||||
rule->allow = 1;
|
||||
tmp_ptr += 5;
|
||||
}
|
||||
else if (!strcasecmp(tmp_ptr, "LOG")) {
|
||||
rule->log = 1;
|
||||
tmp_ptr += 3;
|
||||
}
|
||||
|
||||
//or maybe you just want to assign a score
|
||||
else if ( (tmp_ptr[0] >= '0' && tmp_ptr[0] <= '9') || tmp_ptr[0] == '-') {
|
||||
score = atoi((const char *)tmp->data+2);
|
||||
rule->score = score;
|
||||
break;
|
||||
}
|
||||
else
|
||||
return (NGX_CONF_ERROR);
|
||||
}
|
||||
#if defined(_debug_score) && _debug_score != 0
|
||||
unsigned int z;
|
||||
ngx_http_special_score_t *scr;
|
||||
scr = rule->sscores->elts;
|
||||
if (rule->sscores) {
|
||||
for (z = 0; z < rule->sscores->nelts; z++) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, r, 0,
|
||||
"XX-score n°%d special scoring (%V) => (%d)",
|
||||
z, scr[z].sc_tag, scr[z].sc_score);
|
||||
|
||||
}
|
||||
}
|
||||
else
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, r, 0,
|
||||
"XX-no custom scores for this rule.");
|
||||
#endif
|
||||
return (NGX_CONF_OK);
|
||||
}
|
||||
|
||||
void *
|
||||
dummy_zone(ngx_conf_t *r, ngx_str_t *tmp, ngx_http_rule_t *rule)
|
||||
{
|
||||
int tmp_len, has_zone=0;
|
||||
ngx_http_custom_rule_location_t *custom_rule;
|
||||
char *tmp_ptr, *tmp_end;
|
||||
|
||||
|
||||
if (!rule->br)
|
||||
return (NGX_CONF_ERROR);
|
||||
|
||||
tmp_ptr = (char *) tmp->data+strlen(MATCH_ZONE_T);
|
||||
while (*tmp_ptr) {
|
||||
|
||||
if (tmp_ptr[0] == '|')
|
||||
tmp_ptr++;
|
||||
/* match global zones */
|
||||
if (!strncmp(tmp_ptr, "RAW_BODY", strlen("RAW_BODY"))) {
|
||||
rule->br->raw_body = 1;
|
||||
tmp_ptr += strlen("RAW_BODY");
|
||||
has_zone = 1;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
if (!strncmp(tmp_ptr, "BODY", strlen("BODY"))) {
|
||||
rule->br->body = 1;
|
||||
tmp_ptr += strlen("BODY");
|
||||
has_zone = 1;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
if (!strncmp(tmp_ptr, "HEADERS", strlen("HEADERS"))) {
|
||||
rule->br->headers = 1;
|
||||
tmp_ptr += strlen("HEADERS");
|
||||
has_zone = 1;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
if (!strncmp(tmp_ptr, "URL", strlen("URL"))) {
|
||||
rule->br->url = 1;
|
||||
tmp_ptr += strlen("URL");
|
||||
has_zone = 1;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
if (!strncmp(tmp_ptr, "ARGS", strlen("ARGS"))) {
|
||||
rule->br->args = 1;
|
||||
tmp_ptr += strlen("ARGS");
|
||||
has_zone = 1;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
/* match against variable name*/
|
||||
if (!strncmp(tmp_ptr, "NAME", strlen("NAME"))) {
|
||||
rule->br->target_name = 1;
|
||||
tmp_ptr += strlen("NAME");
|
||||
has_zone = 1;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
/* for file_ext, just push'em in the body rules.
|
||||
when multipart parsing comes in, it'll tag the zone as
|
||||
FILE_EXT as the rule will be pushed in body rules it'll be
|
||||
checked !*/
|
||||
if (!strncmp(tmp_ptr, "FILE_EXT", strlen("FILE_EXT"))) {
|
||||
rule->br->file_ext = 1;
|
||||
rule->br->body = 1;
|
||||
tmp_ptr += strlen("FILE_EXT");
|
||||
has_zone = 1;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
/* custom match zones */
|
||||
#define MZ_GET_VAR_T "$ARGS_VAR:"
|
||||
#define MZ_HEADER_VAR_T "$HEADERS_VAR:"
|
||||
#define MZ_POST_VAR_T "$BODY_VAR:"
|
||||
#define MZ_SPECIFIC_URL_T "$URL:"
|
||||
//probably a custom zone
|
||||
if (tmp_ptr[0] == '$') {
|
||||
// tag as a custom_location rule.
|
||||
rule->br->custom_location = 1;
|
||||
if (!rule->br->custom_locations) {
|
||||
rule->br->custom_locations = ngx_array_create(r->pool, 1,
|
||||
sizeof(ngx_http_custom_rule_location_t));
|
||||
if (!rule->br->custom_locations)
|
||||
return (NGX_CONF_ERROR);
|
||||
}
|
||||
custom_rule = ngx_array_push(rule->br->custom_locations);
|
||||
if (!custom_rule)
|
||||
return (NGX_CONF_ERROR);
|
||||
memset(custom_rule, 0, sizeof(ngx_http_custom_rule_location_t));
|
||||
if (!strncmp(tmp_ptr, MZ_GET_VAR_T, strlen(MZ_GET_VAR_T))) {
|
||||
has_zone = 1;
|
||||
custom_rule->args_var = 1;
|
||||
rule->br->args_var = 1;
|
||||
tmp_ptr += strlen(MZ_GET_VAR_T);
|
||||
}
|
||||
else if (!strncmp(tmp_ptr, MZ_POST_VAR_T,
|
||||
strlen(MZ_POST_VAR_T))) {
|
||||
has_zone = 1;
|
||||
custom_rule->body_var = 1;
|
||||
rule->br->body_var = 1;
|
||||
tmp_ptr += strlen(MZ_POST_VAR_T);
|
||||
}
|
||||
else if (!strncmp(tmp_ptr, MZ_HEADER_VAR_T,
|
||||
strlen(MZ_HEADER_VAR_T))) {
|
||||
has_zone = 1;
|
||||
custom_rule->headers_var = 1;
|
||||
rule->br->headers_var = 1;
|
||||
tmp_ptr += strlen(MZ_HEADER_VAR_T);
|
||||
}
|
||||
else if (!strncmp(tmp_ptr, MZ_SPECIFIC_URL_T,
|
||||
strlen(MZ_SPECIFIC_URL_T))) {
|
||||
custom_rule->specific_url = 1;
|
||||
tmp_ptr += strlen(MZ_SPECIFIC_URL_T);
|
||||
}
|
||||
else
|
||||
/* add support for regex-style match zones.
|
||||
** this whole function should be rewritten as it's getting
|
||||
** messy as hell
|
||||
*/
|
||||
#define MZ_GET_VAR_X "$ARGS_VAR_X:"
|
||||
#define MZ_HEADER_VAR_X "$HEADERS_VAR_X:"
|
||||
#define MZ_POST_VAR_X "$BODY_VAR_X:"
|
||||
#define MZ_SPECIFIC_URL_X "$URL_X:"
|
||||
/*
|
||||
** if the rule is a negative rule (has an ID, not a WL field)
|
||||
** we need to pre-compile the regex for runtime.
|
||||
** Don't do it for whitelists, as its done in a separate manner.
|
||||
*/
|
||||
if (!strncmp(tmp_ptr, MZ_GET_VAR_X, strlen(MZ_GET_VAR_X))) {
|
||||
has_zone = 1;
|
||||
custom_rule->args_var = 1;
|
||||
rule->br->args_var = 1;
|
||||
rule->br->rx_mz = 1;
|
||||
tmp_ptr += strlen(MZ_GET_VAR_X);
|
||||
}
|
||||
else if (!strncmp(tmp_ptr, MZ_POST_VAR_X,
|
||||
strlen(MZ_POST_VAR_X))) {
|
||||
has_zone = 1;
|
||||
rule->br->rx_mz = 1;
|
||||
custom_rule->body_var = 1;
|
||||
rule->br->body_var = 1;
|
||||
tmp_ptr += strlen(MZ_POST_VAR_X);
|
||||
}
|
||||
else if (!strncmp(tmp_ptr, MZ_HEADER_VAR_X,
|
||||
strlen(MZ_HEADER_VAR_X))) {
|
||||
has_zone = 1;
|
||||
custom_rule->headers_var = 1;
|
||||
rule->br->headers_var = 1;
|
||||
rule->br->rx_mz = 1;
|
||||
tmp_ptr += strlen(MZ_HEADER_VAR_X);
|
||||
}
|
||||
else if (!strncmp(tmp_ptr, MZ_SPECIFIC_URL_X,
|
||||
strlen(MZ_SPECIFIC_URL_X))) {
|
||||
custom_rule->specific_url = 1;
|
||||
rule->br->rx_mz = 1;
|
||||
tmp_ptr += strlen(MZ_SPECIFIC_URL_X);
|
||||
}
|
||||
else
|
||||
return (NGX_CONF_ERROR);
|
||||
|
||||
/* else
|
||||
return (NGX_CONF_ERROR);*/
|
||||
tmp_end = strchr((const char *) tmp_ptr, '|');
|
||||
if (!tmp_end)
|
||||
tmp_end = tmp_ptr + strlen(tmp_ptr);
|
||||
tmp_len = tmp_end - tmp_ptr;
|
||||
if (tmp_len <= 0)
|
||||
return (NGX_CONF_ERROR);
|
||||
custom_rule->target.data = ngx_pcalloc(r->pool, tmp_len+1);
|
||||
if (!custom_rule->target.data)
|
||||
return (NGX_CONF_ERROR);
|
||||
custom_rule->target.len = tmp_len;
|
||||
memcpy(custom_rule->target.data, tmp_ptr, tmp_len);
|
||||
/*
|
||||
** pre-compile regex !
|
||||
*/
|
||||
if (rule->br->rx_mz == 1) {
|
||||
|
||||
custom_rule->target_rx = ngx_pcalloc(r->pool, sizeof(ngx_regex_compile_t));
|
||||
if (!custom_rule->target_rx)
|
||||
return (NGX_CONF_ERROR);
|
||||
custom_rule->target_rx->options = PCRE_CASELESS|PCRE_MULTILINE;
|
||||
custom_rule->target_rx->pattern = custom_rule->target;
|
||||
custom_rule->target_rx->pool = r->pool;
|
||||
custom_rule->target_rx->err.len = 0;
|
||||
custom_rule->target_rx->err.data = NULL;
|
||||
|
||||
if (ngx_regex_compile(custom_rule->target_rx) != NGX_OK) {
|
||||
NX_LOG_DEBUG(_debug_rx, NGX_LOG_EMERG, r, 0, "XX-FAILED RX:%V",
|
||||
custom_rule->target);
|
||||
return (NGX_CONF_ERROR);
|
||||
}
|
||||
}
|
||||
custom_rule->hash = ngx_hash_key_lc(custom_rule->target.data,
|
||||
custom_rule->target.len);
|
||||
|
||||
NX_LOG_DEBUG(_debug_zone, NGX_LOG_EMERG, r, 0, "XX- ZONE:[%V]",
|
||||
&(custom_rule->target));
|
||||
tmp_ptr += tmp_len;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
return (NGX_CONF_ERROR);
|
||||
}
|
||||
/*
|
||||
** ensure the match-zone actually returns a zone :)
|
||||
*/
|
||||
if (has_zone == 0) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, r, 0,
|
||||
"matchzone doesn't target an actual zone.");
|
||||
return (NGX_CONF_ERROR);
|
||||
}
|
||||
|
||||
return (NGX_CONF_OK);
|
||||
}
|
||||
|
||||
void *
|
||||
dummy_id(ngx_conf_t *r, ngx_str_t *tmp, ngx_http_rule_t *rule)
|
||||
{
|
||||
rule->rule_id = atoi((const char *) tmp->data+strlen(ID_T));
|
||||
return (NGX_CONF_OK);
|
||||
}
|
||||
|
||||
void *
|
||||
dummy_str(ngx_conf_t *r, ngx_str_t *tmp, ngx_http_rule_t *rule)
|
||||
{
|
||||
ngx_str_t *str;
|
||||
uint i;
|
||||
|
||||
if (!rule->br)
|
||||
return (NGX_CONF_ERROR);
|
||||
rule->br->match_type = STR;
|
||||
str = ngx_pcalloc(r->pool, sizeof(ngx_str_t));
|
||||
if (!str)
|
||||
return (NGX_CONF_ERROR);
|
||||
str->data = tmp->data + strlen(STR_T);
|
||||
str->len = tmp->len - strlen(STR_T);
|
||||
for (i = 0; i < str->len; i++)
|
||||
str->data[i] = tolower(str->data[i]);
|
||||
rule->br->str = str;
|
||||
return (NGX_CONF_OK);
|
||||
}
|
||||
|
||||
void *
|
||||
dummy_msg(ngx_conf_t *r, ngx_str_t *tmp, ngx_http_rule_t *rule)
|
||||
{
|
||||
ngx_str_t *str;
|
||||
|
||||
if (!rule->br)
|
||||
return (NGX_CONF_ERROR);
|
||||
str = ngx_pcalloc(r->pool, sizeof(ngx_str_t));
|
||||
if (!str)
|
||||
return (NGX_CONF_ERROR);
|
||||
str->data = tmp->data + strlen(STR_T);
|
||||
str->len = tmp->len - strlen(STR_T);
|
||||
rule->log_msg = str;
|
||||
return (NGX_CONF_OK);
|
||||
}
|
||||
|
||||
void *
|
||||
dummy_whitelist(ngx_conf_t *r, ngx_str_t *tmp, ngx_http_rule_t *rule)
|
||||
{
|
||||
|
||||
ngx_array_t *wl_ar;
|
||||
unsigned int i, ct;
|
||||
ngx_int_t *id;
|
||||
ngx_str_t str;
|
||||
|
||||
str.data = tmp->data + strlen(WHITELIST_T);
|
||||
str.len = tmp->len - strlen(WHITELIST_T);
|
||||
for (ct = 1, i = 0; i < str.len; i++)
|
||||
if (str.data[i] == ',')
|
||||
ct++;
|
||||
wl_ar = ngx_array_create(r->pool, ct, sizeof(ngx_int_t));
|
||||
if (!wl_ar)
|
||||
return (NGX_CONF_ERROR);
|
||||
NX_LOG_DEBUG(_debug_whitelist, NGX_LOG_EMERG, r, 0, "XX- allocated %d elems for WL", ct);
|
||||
for (i = 0; i < str.len; i++) {
|
||||
if (i == 0 || str.data[i-1] == ',') {
|
||||
id = (ngx_int_t *) ngx_array_push(wl_ar);
|
||||
if (!id)
|
||||
return (NGX_CONF_ERROR);
|
||||
*id = (ngx_int_t) atoi((const char *)str.data+i);
|
||||
}
|
||||
}
|
||||
rule->wlid_array = wl_ar;
|
||||
return (NGX_CONF_OK);
|
||||
}
|
||||
|
||||
void *
|
||||
dummy_rx(ngx_conf_t *r, ngx_str_t *tmp, ngx_http_rule_t *rule)
|
||||
{
|
||||
ngx_regex_compile_t *rgc;
|
||||
ngx_str_t ha;
|
||||
|
||||
|
||||
if (!rule->br)
|
||||
return (NGX_CONF_ERROR);
|
||||
rule->br->match_type = RX;
|
||||
//just prepare a string to hold the directive without 'rx:'
|
||||
ha.data = tmp->data+strlen(RX_T);
|
||||
ha.len = tmp->len-strlen(RX_T);
|
||||
rgc = ngx_pcalloc(r->pool, sizeof(ngx_regex_compile_t));
|
||||
if (!rgc)
|
||||
return (NGX_CONF_ERROR);
|
||||
rgc->options = PCRE_CASELESS|PCRE_MULTILINE;
|
||||
rgc->pattern = ha;
|
||||
rgc->pool = r->pool;
|
||||
rgc->err.len = 0;
|
||||
rgc->err.data = NULL;
|
||||
|
||||
if (ngx_regex_compile(rgc) != NGX_OK) {
|
||||
NX_LOG_DEBUG(_debug_rx, NGX_LOG_EMERG, r, 0, "XX-FAILED RX:%V",
|
||||
tmp);
|
||||
return (NGX_CONF_ERROR);
|
||||
}
|
||||
rule->br->rx = rgc;
|
||||
NX_LOG_DEBUG(_debug_rx, NGX_LOG_EMERG, r, 0, "XX- RX:[%V]",
|
||||
&(rule->br->rx->pattern));
|
||||
return (NGX_CONF_OK);
|
||||
}
|
||||
|
||||
/* Parse one rule line */
|
||||
/*
|
||||
** in : nb elem, value array, rule to fill
|
||||
** does : creates a rule struct from configuration line
|
||||
** For each element name matching a tag
|
||||
** (cf. rule_parser), then call the associated func.
|
||||
*/
|
||||
void *
|
||||
ngx_http_dummy_cfg_parse_one_rule(ngx_conf_t *cf,
|
||||
ngx_str_t *value,
|
||||
ngx_http_rule_t *current_rule,
|
||||
ngx_int_t nb_elem)
|
||||
{
|
||||
int i, z;
|
||||
void *ret;
|
||||
int valid;
|
||||
|
||||
if (!value || !value[0].data)
|
||||
return NGX_CONF_ERROR;
|
||||
/*
|
||||
** parse basic rule
|
||||
*/
|
||||
if (!ngx_strcmp(value[0].data, TOP_CHECK_RULE_T) ||
|
||||
!ngx_strcmp(value[0].data, TOP_CHECK_RULE_N) ||
|
||||
!ngx_strcmp(value[0].data, TOP_BASIC_RULE_T) ||
|
||||
!ngx_strcmp(value[0].data, TOP_BASIC_RULE_N) ||
|
||||
!ngx_strcmp(value[0].data, TOP_MAIN_BASIC_RULE_T) ||
|
||||
!ngx_strcmp(value[0].data, TOP_MAIN_BASIC_RULE_N)) {
|
||||
NX_LOG_DEBUG(_debug_cfg_parse_one_rule, NGX_LOG_EMERG, cf, 0, "naxsi-basic rule %V", &(value[1]));
|
||||
current_rule->type = BR;
|
||||
current_rule->br = ngx_pcalloc(cf->pool, sizeof(ngx_http_basic_rule_t));
|
||||
if (!current_rule->br)
|
||||
return (NGX_CONF_ERROR);
|
||||
}
|
||||
else {
|
||||
NX_LOG_DEBUG(_debug_cfg_parse_one_rule, NGX_LOG_EMERG, cf, 0,
|
||||
"Unknown start keyword in rule %V", &(value[1]));
|
||||
return (NGX_CONF_ERROR);
|
||||
}
|
||||
|
||||
// check each word of config line against each rule
|
||||
for(i = 1; i < nb_elem && value[i].len > 0; i++) {
|
||||
valid = 0;
|
||||
for (z = 0; rule_parser[z].pars; z++) {
|
||||
if (!ngx_strncmp(value[i].data,
|
||||
rule_parser[z].prefix,
|
||||
strlen(rule_parser[z].prefix))) {
|
||||
ret = rule_parser[z].pars(cf, &(value[i]),
|
||||
current_rule);
|
||||
if (ret != NGX_CONF_OK) {
|
||||
NX_LOG_DEBUG(_debug_cfg_parse_one_rule, NGX_LOG_EMERG, cf, 0,
|
||||
"XX-FAILED PARSING '%s'",
|
||||
value[i].data);
|
||||
return (ret);
|
||||
}
|
||||
valid = 1;
|
||||
}
|
||||
}
|
||||
if (!valid)
|
||||
return (NGX_CONF_ERROR);
|
||||
}
|
||||
/* validate the structure, and fill empty fields.*/
|
||||
if (!current_rule->log_msg)
|
||||
{
|
||||
current_rule->log_msg = ngx_pcalloc(cf->pool, sizeof(ngx_str_t));
|
||||
current_rule->log_msg->data = NULL;
|
||||
current_rule->log_msg->len = 0;
|
||||
}
|
||||
return (NGX_CONF_OK);
|
||||
}
|
||||
|
||||
|
||||
83
naxsi-0.55.3/naxsi_config/naxsi_core.rules
Normal file
83
naxsi-0.55.3/naxsi_config/naxsi_core.rules
Normal file
@@ -0,0 +1,83 @@
|
||||
##################################
|
||||
## 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:%[2|3]." "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;
|
||||
347
naxsi-0.55.3/naxsi_json.c
Normal file
347
naxsi-0.55.3/naxsi_json.c
Normal file
@@ -0,0 +1,347 @@
|
||||
/*
|
||||
* NAXSI, a web application firewall for NGINX
|
||||
* Copyright (C) 2016, Thibault 'bui' Koechlin
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give
|
||||
* permission to link the code of portions of this program with the
|
||||
* OpenSSL library under certain conditions as described in each
|
||||
* individual source file, and distribute linked combinations
|
||||
* including the two.
|
||||
* You must obey the GNU General Public License in all respects
|
||||
* for all of the code used other than OpenSSL. If you modify
|
||||
* file(s) with this exception, you may extend this exception to your
|
||||
* version of the file(s), but you are not obligated to do so. If you
|
||||
* do not wish to do so, delete this exception statement from your
|
||||
* version. If you delete this exception statement from all source
|
||||
* files in the program, then also delete it here.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "naxsi.h"
|
||||
|
||||
//#define _debug_json 1
|
||||
|
||||
ngx_http_rule_t nx_int__invalid_json = {/*type*/ 0, /*whitelist flag*/ 0,
|
||||
/*wl_id ptr*/ NULL, /*rule_id*/ 15,
|
||||
/*log_msg*/ NULL, /*score*/ 0,
|
||||
/*sscores*/ NULL,
|
||||
/*sc_block*/ 1, /*sc_allow*/ 0,
|
||||
/*block*/ 1, /*allow*/ 0, /*drop*/ 0, /*log*/ 0,
|
||||
/*br ptrs*/ NULL};
|
||||
|
||||
|
||||
ngx_int_t
|
||||
ngx_http_nx_json_forward(ngx_json_t *js)
|
||||
{
|
||||
while ((*(js->src+js->off) == ' ' ||
|
||||
*(js->src+js->off) == '\t' ||
|
||||
*(js->src+js->off) == '\n' ||
|
||||
*(js->src+js->off) == '\r') && js->off < js->len) {
|
||||
js->off++;
|
||||
}
|
||||
js->c = *(js->src + js->off);
|
||||
return (NGX_OK);
|
||||
}
|
||||
|
||||
/*
|
||||
** used to fast forward in json POSTS,
|
||||
** we skip whitespaces/tab/CR/LF
|
||||
*/
|
||||
ngx_int_t
|
||||
ngx_http_nx_json_seek(ngx_json_t *js, unsigned char seek)
|
||||
{
|
||||
|
||||
ngx_http_nx_json_forward(js);
|
||||
if (js->c != seek)
|
||||
return (NGX_ERROR);
|
||||
return (NGX_OK);
|
||||
}
|
||||
|
||||
/*
|
||||
** extract a quoted strings,
|
||||
** JSON spec only supports double-quoted strings,
|
||||
** so do we.
|
||||
*/
|
||||
ngx_int_t
|
||||
ngx_http_nx_json_quoted(ngx_json_t *js, ngx_str_t *ve)
|
||||
{
|
||||
u_char *vn_start, *vn_end;
|
||||
|
||||
vn_start = vn_end = NULL;
|
||||
|
||||
if (*(js->src+js->off) != '"')
|
||||
return (NGX_ERROR);
|
||||
js->off++;
|
||||
vn_start = js->src+js->off;
|
||||
/* extract varname inbetween "..."*/
|
||||
while (js->off < js->len) {
|
||||
/* skip next character if backslashed */
|
||||
if (*(js->src+js->off) == '\\') {
|
||||
js->off += 2;
|
||||
if (js->off >= js->len) break;
|
||||
}
|
||||
if (*(js->src+js->off) == '"') {
|
||||
vn_end = js->src+js->off;
|
||||
js->off++;
|
||||
break;
|
||||
}
|
||||
js->off++;
|
||||
}
|
||||
if (!vn_start || !vn_end)
|
||||
return (NGX_ERROR);
|
||||
if (!*vn_start || !*vn_end)
|
||||
return (NGX_ERROR);
|
||||
ve->data = vn_start;
|
||||
ve->len = vn_end - vn_start;
|
||||
return (NGX_OK);
|
||||
}
|
||||
|
||||
/*
|
||||
** an array is values separated by ','
|
||||
*/
|
||||
ngx_int_t
|
||||
ngx_http_nx_json_array(ngx_json_t *js) {
|
||||
ngx_int_t rc;
|
||||
|
||||
js->c = *(js->src + js->off);
|
||||
if (js->c != '[' || js->depth > JSON_MAX_DEPTH)
|
||||
return (NGX_ERROR);
|
||||
js->off++;
|
||||
do {
|
||||
rc = ngx_http_nx_json_val(js);
|
||||
/* if we cannot extract the value,
|
||||
we may have reached array end. */
|
||||
if (rc != NGX_OK)
|
||||
break;
|
||||
ngx_http_nx_json_forward(js);
|
||||
if (js->c == ',') {
|
||||
js->off++;
|
||||
ngx_http_nx_json_forward(js);
|
||||
} else break;
|
||||
} while (rc == NGX_OK);
|
||||
if (js->c != ']')
|
||||
return (NGX_ERROR);
|
||||
return (NGX_OK);
|
||||
}
|
||||
|
||||
|
||||
|
||||
ngx_int_t
|
||||
ngx_http_nx_json_val(ngx_json_t *js) {
|
||||
ngx_str_t val;
|
||||
ngx_int_t ret;
|
||||
ngx_str_t empty = ngx_string("");
|
||||
|
||||
val.data = NULL;
|
||||
val.len = 0;
|
||||
|
||||
ngx_http_nx_json_forward(js);
|
||||
if (js->c == '"') {
|
||||
ret = ngx_http_nx_json_quoted(js, &val);
|
||||
if (ret == NGX_OK)
|
||||
{
|
||||
/* parse extracted values. */
|
||||
if (js->loc_cf->body_rules)
|
||||
ngx_http_basestr_ruleset_n(js->r->pool, &js->ckey, &val,
|
||||
js->loc_cf->body_rules, js->r, js->ctx,
|
||||
BODY);
|
||||
if (js->main_cf->body_rules)
|
||||
ngx_http_basestr_ruleset_n(js->r->pool, &js->ckey, &val,
|
||||
js->main_cf->body_rules, js->r, js->ctx,
|
||||
BODY);
|
||||
NX_DEBUG(_debug_json, NGX_LOG_DEBUG_HTTP, js->r->connection->log, 0, "JSON '%V' : '%V'",
|
||||
&(js->ckey), &(val));
|
||||
}
|
||||
return (ret);
|
||||
}
|
||||
if ((js->c >= '0' && js->c <= '9') || js->c == '-') {
|
||||
val.data = js->src+js->off;
|
||||
while ( ((*(js->src+js->off) >= '0' && *(js->src+js->off) <= '9') ||
|
||||
*(js->src+js->off) == '.' || *(js->src+js->off) == '-') && js->off < js->len) {
|
||||
val.len++;
|
||||
js->off++;
|
||||
}
|
||||
/* parse extracted values. */
|
||||
if (js->loc_cf->body_rules)
|
||||
ngx_http_basestr_ruleset_n(js->r->pool, &js->ckey, &val,
|
||||
js->loc_cf->body_rules, js->r, js->ctx,
|
||||
BODY);
|
||||
if (js->main_cf->body_rules)
|
||||
ngx_http_basestr_ruleset_n(js->r->pool, &js->ckey, &val,
|
||||
js->main_cf->body_rules, js->r, js->ctx,
|
||||
BODY);
|
||||
NX_DEBUG(_debug_json, NGX_LOG_DEBUG_HTTP, js->r->connection->log, 0, "JSON '%V' : '%V'",
|
||||
&(js->ckey), &(val));
|
||||
return (NGX_OK);
|
||||
}
|
||||
if (!strncasecmp((const char *) (js->src + js->off), (const char *) "true", 4) ||
|
||||
!strncasecmp((const char *) (js->src + js->off), (const char *) "false", 5) ||
|
||||
!strncasecmp((const char *) (js->src + js->off), (const char *) "null", 4)) {
|
||||
js->c = *(js->src + js->off);
|
||||
/* we don't check static values, do we ?! */
|
||||
val.data = js->src+js->off;
|
||||
if (js->c == 'F' || js->c == 'f') {
|
||||
js->off += 5;
|
||||
val.len = 5;
|
||||
}
|
||||
else {
|
||||
js->off += 4;
|
||||
val.len = 4;
|
||||
}
|
||||
/* parse extracted values. */
|
||||
if (js->loc_cf->body_rules)
|
||||
ngx_http_basestr_ruleset_n(js->r->pool, &js->ckey, &val,
|
||||
js->loc_cf->body_rules, js->r, js->ctx,
|
||||
BODY);
|
||||
if (js->main_cf->body_rules)
|
||||
ngx_http_basestr_ruleset_n(js->r->pool, &js->ckey, &val,
|
||||
js->main_cf->body_rules, js->r, js->ctx,
|
||||
BODY);
|
||||
NX_DEBUG(_debug_json, NGX_LOG_DEBUG_HTTP, js->r->connection->log, 0, "JSON '%V' : '%V'",
|
||||
&(js->ckey), &(val));
|
||||
return (NGX_OK);
|
||||
}
|
||||
|
||||
if (js->c == '[') {
|
||||
ret = ngx_http_nx_json_array(js);
|
||||
if (js->c != ']')
|
||||
return (NGX_ERROR);
|
||||
js->off++;
|
||||
return (ret);
|
||||
}
|
||||
if (js->c == '{') {
|
||||
/*
|
||||
** if sub-struct, parse key without value :
|
||||
** "foobar" : { "bar" : [1,2,3]} => "foobar" parsed alone.
|
||||
** this is to avoid "foobar" left unparsed, as we won't have
|
||||
** key/value here with "foobar" as a key.
|
||||
*/
|
||||
if (js->loc_cf->body_rules)
|
||||
ngx_http_basestr_ruleset_n(js->r->pool, &js->ckey, &empty,
|
||||
js->loc_cf->body_rules, js->r, js->ctx,
|
||||
BODY);
|
||||
if (js->main_cf->body_rules)
|
||||
ngx_http_basestr_ruleset_n(js->r->pool, &js->ckey, &empty,
|
||||
js->main_cf->body_rules, js->r, js->ctx,
|
||||
BODY);
|
||||
ret = ngx_http_nx_json_obj(js);
|
||||
ngx_http_nx_json_forward(js);
|
||||
if (js->c != '}')
|
||||
return (NGX_ERROR);
|
||||
js->off++;
|
||||
return (ret);
|
||||
}
|
||||
return (NGX_ERROR);
|
||||
}
|
||||
|
||||
|
||||
ngx_int_t
|
||||
ngx_http_nx_json_obj(ngx_json_t *js)
|
||||
{
|
||||
js->c = *(js->src + js->off);
|
||||
|
||||
if (js->c != '{' || js->depth > JSON_MAX_DEPTH)
|
||||
return (NGX_ERROR);
|
||||
js->off++;
|
||||
|
||||
do {
|
||||
ngx_http_nx_json_forward(js);
|
||||
/* check subs (arrays, objects) */
|
||||
switch (js->c) {
|
||||
case '[': /* array */
|
||||
js->depth++;
|
||||
ngx_http_nx_json_array(js);
|
||||
if (ngx_http_nx_json_seek(js, ']'))
|
||||
return (NGX_ERROR);
|
||||
js->off++;
|
||||
js->depth--;
|
||||
break;
|
||||
case '{': /* sub-object */
|
||||
js->depth++;
|
||||
ngx_http_nx_json_obj(js);
|
||||
if (js->c != '}')
|
||||
return (NGX_ERROR);
|
||||
js->off++;
|
||||
js->depth--;
|
||||
break;
|
||||
case '"': /* key : value, extract and parse. */
|
||||
if (ngx_http_nx_json_quoted(js, &(js->ckey)) != NGX_OK)
|
||||
return (NGX_ERROR);
|
||||
if (ngx_http_nx_json_seek(js, ':'))
|
||||
return (NGX_ERROR);
|
||||
js->off++;
|
||||
ngx_http_nx_json_forward(js);
|
||||
if (ngx_http_nx_json_val(js) != NGX_OK)
|
||||
return (NGX_ERROR);
|
||||
}
|
||||
ngx_http_nx_json_forward(js);
|
||||
/* another element ? */
|
||||
if (js->c == ',') {
|
||||
js->off++;
|
||||
ngx_http_nx_json_forward(js);
|
||||
continue;
|
||||
|
||||
} else if (js->c == '}') {
|
||||
js->depth--;
|
||||
/* or maybe we just finished parsing this object */
|
||||
return (NGX_OK);
|
||||
} else {
|
||||
/* nothing we expected, die. */
|
||||
return (NGX_ERROR);
|
||||
}
|
||||
} while (js->off < js->len);
|
||||
|
||||
return (NGX_ERROR);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
** Parse a JSON request
|
||||
*/
|
||||
void
|
||||
ngx_http_dummy_json_parse(ngx_http_request_ctx_t *ctx,
|
||||
ngx_http_request_t *r,
|
||||
u_char *src,
|
||||
u_int len)
|
||||
{
|
||||
ngx_json_t *js;
|
||||
|
||||
|
||||
js = ngx_pcalloc(r->pool, sizeof(ngx_json_t));
|
||||
if (!js) return ;
|
||||
js->json.data = js->src = src;
|
||||
js->json.len = js->len = len;
|
||||
js->r = r;
|
||||
js->ctx = ctx;
|
||||
js->loc_cf = ngx_http_get_module_loc_conf(r, ngx_http_naxsi_module);
|
||||
js->main_cf = ngx_http_get_module_main_conf(r, ngx_http_naxsi_module);
|
||||
|
||||
if (ngx_http_nx_json_seek(js, '{')) {
|
||||
ngx_http_apply_rulematch_v_n(&nx_int__invalid_json, ctx, r, NULL, NULL, BODY, 1, 0);
|
||||
return ;
|
||||
}
|
||||
if (ngx_http_nx_json_obj(js) != NGX_OK) {
|
||||
ngx_http_apply_rulematch_v_n(&nx_int__invalid_json, ctx, r, NULL, NULL, BODY, 1, 0);
|
||||
NX_DEBUG(_debug_json, NGX_LOG_DEBUG_HTTP, js->r->connection->log, 0, "nx_json_obj returned error, apply invalid_json.");
|
||||
|
||||
}
|
||||
/* we are now on closing bracket, check for garbage. */
|
||||
js->off++;
|
||||
ngx_http_nx_json_forward(js);
|
||||
if (js->off != js->len)
|
||||
ngx_http_apply_rulematch_v_n(&nx_int__invalid_json, ctx, r, NULL, NULL, BODY, 1, 0);
|
||||
return ;
|
||||
}
|
||||
|
||||
70
naxsi-0.55.3/naxsi_raw.c
Normal file
70
naxsi-0.55.3/naxsi_raw.c
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* NAXSI, a web application firewall for NGINX
|
||||
* Copyright (C) 2016, Thibault 'bui' Koechlin
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give
|
||||
* permission to link the code of portions of this program with the
|
||||
* OpenSSL library under certain conditions as described in each
|
||||
* individual source file, and distribute linked combinations
|
||||
* including the two.
|
||||
* You must obey the GNU General Public License in all respects
|
||||
* for all of the code used other than OpenSSL. If you modify
|
||||
* file(s) with this exception, you may extend this exception to your
|
||||
* version of the file(s), but you are not obligated to do so. If you
|
||||
* do not wish to do so, delete this exception statement from your
|
||||
* version. If you delete this exception statement from all source
|
||||
* files in the program, then also delete it here.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "naxsi.h"
|
||||
|
||||
|
||||
void
|
||||
ngx_http_dummy_rawbody_parse(ngx_http_request_ctx_t *ctx,
|
||||
ngx_http_request_t *r,
|
||||
u_char *src,
|
||||
u_int len)
|
||||
{
|
||||
ngx_http_dummy_loc_conf_t *cf;
|
||||
ngx_str_t body;
|
||||
ngx_http_dummy_main_conf_t *main_cf;
|
||||
ngx_str_t empty = ngx_string("");
|
||||
|
||||
NX_DEBUG(_debug_rawbody, NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "XX-RAWBODY CALLED len:%d",len);
|
||||
if (len <= 0 || !src)
|
||||
return;
|
||||
cf = ngx_http_get_module_loc_conf(r, ngx_http_naxsi_module);
|
||||
main_cf = ngx_http_get_module_main_conf(r, ngx_http_naxsi_module);
|
||||
|
||||
|
||||
body.data = src;
|
||||
body.len = len;
|
||||
|
||||
naxsi_unescape(&body);
|
||||
|
||||
/* here we got val name + val content !*/
|
||||
if (cf->raw_body_rules) {
|
||||
NX_DEBUG(_debug_rawbody, NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "XX-(local) RAW BODY RULES");
|
||||
ngx_http_basestr_ruleset_n(r->pool, &empty, &body,
|
||||
cf->raw_body_rules, r, ctx, BODY);
|
||||
}
|
||||
|
||||
if (main_cf->raw_body_rules) {
|
||||
NX_DEBUG(_debug_rawbody, NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "XX-(global) RAW BODY RULES");
|
||||
ngx_http_basestr_ruleset_n(r->pool, &empty, &body,
|
||||
main_cf->raw_body_rules, r, ctx, BODY);
|
||||
}
|
||||
}
|
||||
2273
naxsi-0.55.3/naxsi_runtime.c
Normal file
2273
naxsi-0.55.3/naxsi_runtime.c
Normal file
File diff suppressed because it is too large
Load Diff
1161
naxsi-0.55.3/naxsi_skeleton.c
Normal file
1161
naxsi-0.55.3/naxsi_skeleton.c
Normal file
File diff suppressed because it is too large
Load Diff
108
naxsi-0.55.3/naxsi_src/Makefile
Normal file
108
naxsi-0.55.3/naxsi_src/Makefile
Normal file
@@ -0,0 +1,108 @@
|
||||
CORE_VERS := $(shell grep NAXSI_VERSION naxsi.h | cut -d '"' -f 2)
|
||||
MOD_PATH := $(shell pwd)
|
||||
TMP_DIR := /tmp/nginx/
|
||||
|
||||
# Keys for coverity
|
||||
CAN :=
|
||||
CAK :=
|
||||
|
||||
#Set to 1 if you want coverage report
|
||||
COV ?= 1
|
||||
|
||||
#Allows to force for specific UT only
|
||||
#TEST := "29*.t"
|
||||
NGINX_VERS := "1.9.11"
|
||||
|
||||
NGINX_OPTIONS="--error-log-path=/tmp/naxsi_ut/error.log"
|
||||
NGINX_OPTIONS+="--conf-path=/tmp/naxsi_ut/nginx.conf"
|
||||
NGINX_OPTIONS+="--http-client-body-temp-path=/tmp/naxsi_ut/body/"
|
||||
NGINX_OPTIONS+="--http-fastcgi-temp-path=/tmp/naxsi_ut/fastcgi/"
|
||||
NGINX_OPTIONS+="--http-log-path=/tmp/naxsi_ut/access.log"
|
||||
NGINX_OPTIONS+="--http-proxy-temp-path=/tmp/naxsi_ut/proxy/"
|
||||
NGINX_OPTIONS+="--lock-path=/tmpnginx.lock"
|
||||
NGINX_OPTIONS+="--pid-path=/tmp/naxsi_ut/nginx.pid"
|
||||
NGINX_OPTIONS+="--modules-path=/tmp/naxsi_ut/modules/"
|
||||
NGINX_OPTIONS+="--with-http_ssl_module"
|
||||
NGINX_OPTIONS+="--without-mail_pop3_module"
|
||||
NGINX_OPTIONS+="--without-mail_smtp_module"
|
||||
NGINX_OPTIONS+="--without-mail_imap_module"
|
||||
NGINX_OPTIONS+="--without-http_uwsgi_module"
|
||||
NGINX_OPTIONS+="--without-http_scgi_module"
|
||||
NGINX_OPTIONS+="--add-dynamic-module=$(MOD_PATH)"
|
||||
NGINX_OPTIONS+="--with-ipv6"
|
||||
NGINX_OPTIONS+="--prefix=/tmp"
|
||||
NGINX_OPTIONS+="--with-debug"
|
||||
|
||||
CFLAGS:="-Wall -Wextra"
|
||||
|
||||
all: nginx_download configure build install deploy
|
||||
|
||||
re: clean all test
|
||||
|
||||
clean:
|
||||
rm -f "nginx-"$(NGINX_VERS)".tar.gz"
|
||||
rm -f "nginx-"$(NGINX_VERS)".tar.gz.asc"
|
||||
rm -rf /tmp/naxsi_ut/
|
||||
rm -rf $(TMP_DIR)/
|
||||
|
||||
nginx_download:
|
||||
wget --no-clobber "http://nginx.org/download/nginx-"$(NGINX_VERS)".tar.gz" || exit 1
|
||||
wget --no-clobber "http://nginx.org/download/nginx-"$(NGINX_VERS)".tar.gz.asc" || exit 1
|
||||
gpg --keyserver pgp.key-server.io --recv-keys 0x251a28de2685aed4 0x520A9993A1C052F8
|
||||
gpg --verify "nginx-"$(NGINX_VERS)".tar.gz.asc" "nginx-"$(NGINX_VERS)".tar.gz" || exit 1
|
||||
mkdir -p $(TMP_DIR)/
|
||||
tar -C $(TMP_DIR)/ -xzf nginx-$(NGINX_VERS).tar.gz --strip-components=1
|
||||
|
||||
configure:
|
||||
ifeq ($(COV),1)
|
||||
cd $(TMP_DIR)/ && ./configure --with-cc-opt="--coverage -g3 -gstabs" --with-ld-opt="-lgcov" $(NGINX_OPTIONS)
|
||||
else
|
||||
cd $(TMP_DIR)/ && ./configure --with-cc-opt="-g3 -ggdb" $(NGINX_OPTIONS)
|
||||
endif
|
||||
|
||||
build:
|
||||
cd $(TMP_DIR)/ && make
|
||||
if [ -d "/tmp/naxsi_ut" ] ; then cp $(TMP_DIR)/objs/ngx_http_naxsi_module.so /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so ; fi
|
||||
|
||||
install:
|
||||
cd $(TMP_DIR)/ && make install
|
||||
|
||||
deploy:
|
||||
@cp ./nginx.conf.example /tmp/naxsi_ut/nginx.conf
|
||||
@cp ../naxsi_config/naxsi_core.rules /tmp/naxsi_ut/naxsi_core.rules
|
||||
|
||||
|
||||
# RUN UNIT TESTS
|
||||
test:
|
||||
ifeq ($(COV),1)
|
||||
lcov --directory $(TMP_DIR) --zerocounters
|
||||
endif
|
||||
|
||||
if [ ! $(TEST) ] ; then TEST="*.t" ; fi
|
||||
|
||||
export PATH="$(TMP_DIR)/objs/:"$(PATH) ; \
|
||||
export PERL5LIB="~/perl5/lib/perl5/:/home/travis/perl5/lib/perl5/" ; \
|
||||
cd .. ; prove -r "t/$(TEST)"
|
||||
|
||||
ifeq ($(COV),1)
|
||||
lcov --directory $(TMP_DIR)/objs/addon/naxsi_src/ --capture --output-file naxsi.info --base-directory $(TMP_DIR)
|
||||
genhtml -s -o /tmp/naxsicov.html naxsi.info
|
||||
endif
|
||||
|
||||
#Build for coverity and submit build !
|
||||
coverity:
|
||||
@CAK=$(shell cat ../../coverity.key | cut -d ':' -f2) ; \
|
||||
CAN=$(shell cat ../../coverity.key | cut -d ':' -f1) ; \
|
||||
echo "Coverity token/login : $$CAK and $$CAN"; \
|
||||
wget -nc https://scan.coverity.com/download/linux-64 --post-data "token=$$CAK&project=nbs-system%2Fnaxsi" -O /tmp/coverity.tgz ; \
|
||||
mkdir /tmp/cov && cd /tmp/cov && cat ../coverity.tgz | tar --strip-components=1 -xvzf - ; \
|
||||
cd $(TMP_DIR) ; \
|
||||
./configure $(NGINX_OPTIONS) && \
|
||||
/tmp/cov/bin/cov-build --dir cov-int make && \
|
||||
tar cvzf coverity-res-naxsi.tgz cov-int/ ; \
|
||||
curl --form token="$$CAK" \
|
||||
--form email="$$CAN" \
|
||||
--form file=@$(TMP_DIR)/coverity-res-naxsi.tgz \
|
||||
--form version="$(CORE_VERS)" \
|
||||
--form description="Automatically submitted" \
|
||||
https://scan.coverity.com/builds?project=nbs-system%2Fnaxsi
|
||||
12
naxsi-0.55.3/naxsi_src/config
Normal file
12
naxsi-0.55.3/naxsi_src/config
Normal file
@@ -0,0 +1,12 @@
|
||||
ngx_addon_name=ngx_http_naxsi_module
|
||||
|
||||
if test -n "$ngx_module_link"; then
|
||||
ngx_module_type=HTTP
|
||||
ngx_module_name=ngx_http_naxsi_module
|
||||
ngx_module_srcs="$ngx_addon_dir/naxsi_runtime.c $ngx_addon_dir/naxsi_config.c $ngx_addon_dir/naxsi_utils.c $ngx_addon_dir/naxsi_skeleton.c $ngx_addon_dir/naxsi_json.c $ngx_addon_dir/naxsi_raw.c $ngx_addon_dir/ext/libinjection/libinjection_sqli.c $ngx_addon_dir/ext/libinjection/libinjection_xss.c $ngx_addon_dir/ext/libinjection/libinjection_html5.c"
|
||||
. auto/module
|
||||
else
|
||||
HTTP_MODULES="$HTTP_MODULES ngx_http_naxsi_module"
|
||||
NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/naxsi_runtime.c $ngx_addon_dir/naxsi_config.c $ngx_addon_dir/naxsi_utils.c $ngx_addon_dir/naxsi_skeleton.c $ngx_addon_dir/naxsi_json.c $ngx_addon_dir/naxsi_raw.c $ngx_addon_dir/ext/libinjection/libinjection_sqli.c $ngx_addon_dir/ext/libinjection/libinjection_xss.c $ngx_addon_dir/ext/libinjection/libinjection_html5.c"
|
||||
NGX_ADDON_DEPS="$NGX_ADDON_DEPS $ngx_addon_dir/naxsi.h"
|
||||
fi
|
||||
30
naxsi-0.55.3/naxsi_src/ext/libinjection/example1.c
Normal file
30
naxsi-0.55.3/naxsi_src/ext/libinjection/example1.c
Normal file
@@ -0,0 +1,30 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "libinjection.h"
|
||||
|
||||
int main(int argc, const char* argv[])
|
||||
{
|
||||
char fingerprint[8];
|
||||
const char* input;
|
||||
size_t slen;
|
||||
int issqli;
|
||||
|
||||
if (argc < 2) {
|
||||
fprintf(stderr, "Usage: %s inputstring\n", argv[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
input = argv[1];
|
||||
slen = strlen(input);
|
||||
|
||||
|
||||
issqli = libinjection_sqli(input, slen, fingerprint);
|
||||
if (issqli) {
|
||||
printf("sqli with fingerprint of '%s'\n", fingerprint);
|
||||
} else {
|
||||
printf("not sqli\n");
|
||||
}
|
||||
|
||||
|
||||
return issqli;
|
||||
}
|
||||
79
naxsi-0.55.3/naxsi_src/ext/libinjection/fptool.c
Normal file
79
naxsi-0.55.3/naxsi_src/ext/libinjection/fptool.c
Normal file
@@ -0,0 +1,79 @@
|
||||
/**
|
||||
* Copyright 2012, 2013 Nick Galbreath
|
||||
* nickg@client9.com
|
||||
* BSD License -- see COPYING.txt for details
|
||||
*
|
||||
* This is for testing against files in ../data/ *.txt
|
||||
* Reads from stdin or a list of files, and emits if a line
|
||||
* is a SQLi attack or not, and does basic statistics
|
||||
*
|
||||
*/
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
#include "libinjection.h"
|
||||
#include "libinjection_sqli.h"
|
||||
|
||||
int main(int argc, const char* argv[])
|
||||
{
|
||||
size_t slen;
|
||||
int ok;
|
||||
int single = 0;
|
||||
int offset = 1;
|
||||
|
||||
sfilter sf;
|
||||
if (argc < 2) {
|
||||
fprintf(stderr, "need more args\n");
|
||||
return 1;
|
||||
}
|
||||
while (1) {
|
||||
if (strcmp(argv[offset], "-0") == 0) {
|
||||
single = 1;
|
||||
offset += 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
slen = strlen(argv[offset]);
|
||||
|
||||
if (slen == 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* "plain" context.. test string "as-is"
|
||||
*/
|
||||
libinjection_sqli_init(&sf, argv[offset], slen, 0);
|
||||
|
||||
if (single) {
|
||||
libinjection_sqli_fingerprint(&sf, FLAG_QUOTE_NONE | FLAG_SQL_ANSI);
|
||||
ok = libinjection_sqli_check_fingerprint(&sf);
|
||||
fprintf(stdout, "%s\n", sf.fingerprint);
|
||||
return 0;
|
||||
}
|
||||
|
||||
libinjection_sqli_fingerprint(&sf, FLAG_QUOTE_NONE | FLAG_SQL_ANSI);
|
||||
ok = libinjection_sqli_check_fingerprint(&sf);
|
||||
fprintf(stdout, "plain-asni\t%s\t%s\n", sf.fingerprint, ok ? "true": "false");
|
||||
|
||||
libinjection_sqli_fingerprint(&sf, FLAG_QUOTE_NONE | FLAG_SQL_MYSQL);
|
||||
ok = libinjection_sqli_check_fingerprint(&sf);
|
||||
fprintf(stdout, "plain-mysql\t%s\t%s\n", sf.fingerprint, ok ? "true": "false");
|
||||
|
||||
libinjection_sqli_fingerprint(&sf, FLAG_QUOTE_SINGLE | FLAG_SQL_ANSI);
|
||||
ok = libinjection_sqli_check_fingerprint(&sf);
|
||||
fprintf(stdout, "single-ansi\t%s\t%s\n", sf.fingerprint, ok ? "true": "false");
|
||||
|
||||
libinjection_sqli_fingerprint(&sf, FLAG_QUOTE_SINGLE | FLAG_SQL_MYSQL);
|
||||
ok = libinjection_sqli_check_fingerprint(&sf);
|
||||
fprintf(stdout, "single-mysql\t%s\t%s\n", sf.fingerprint, ok ? "true": "false");
|
||||
|
||||
libinjection_sqli_fingerprint(&sf, FLAG_QUOTE_DOUBLE | FLAG_SQL_MYSQL);
|
||||
ok = libinjection_sqli_check_fingerprint(&sf);
|
||||
fprintf(stdout, "double-mysql\t%s\t%s\n", sf.fingerprint, ok ? "true": "false");
|
||||
|
||||
return 0;
|
||||
}
|
||||
168
naxsi-0.55.3/naxsi_src/ext/libinjection/html5_cli.c
Normal file
168
naxsi-0.55.3/naxsi_src/ext/libinjection/html5_cli.c
Normal file
@@ -0,0 +1,168 @@
|
||||
/**
|
||||
* Copyright 2012, 2013, 2014 Nick Galbreath
|
||||
* nickg@client9.com
|
||||
* BSD License -- see COPYING.txt for details
|
||||
*
|
||||
* This is for testing against files in ../data/ *.txt
|
||||
* Reads from stdin or a list of files, and emits if a line
|
||||
* is a SQLi attack or not, and does basic statistics
|
||||
*
|
||||
*/
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "libinjection_html5.h"
|
||||
#include "libinjection_xss.h"
|
||||
#include "libinjection.h"
|
||||
|
||||
int urlcharmap(char ch);
|
||||
size_t modp_url_decode(char* dest, const char* s, size_t len);
|
||||
const char* h5_type_to_string(enum html5_type x);
|
||||
void print_html5_token(h5_state_t* hs);
|
||||
|
||||
int urlcharmap(char ch) {
|
||||
switch (ch) {
|
||||
case '0': return 0;
|
||||
case '1': return 1;
|
||||
case '2': return 2;
|
||||
case '3': return 3;
|
||||
case '4': return 4;
|
||||
case '5': return 5;
|
||||
case '6': return 6;
|
||||
case '7': return 7;
|
||||
case '8': return 8;
|
||||
case '9': return 9;
|
||||
case 'a': case 'A': return 10;
|
||||
case 'b': case 'B': return 11;
|
||||
case 'c': case 'C': return 12;
|
||||
case 'd': case 'D': return 13;
|
||||
case 'e': case 'E': return 14;
|
||||
case 'f': case 'F': return 15;
|
||||
default:
|
||||
return 256;
|
||||
}
|
||||
}
|
||||
|
||||
size_t modp_url_decode(char* dest, const char* s, size_t len)
|
||||
{
|
||||
const char* deststart = dest;
|
||||
|
||||
size_t i = 0;
|
||||
int d = 0;
|
||||
while (i < len) {
|
||||
switch (s[i]) {
|
||||
case '+':
|
||||
*dest++ = ' ';
|
||||
i += 1;
|
||||
break;
|
||||
case '%':
|
||||
if (i+2 < len) {
|
||||
d = (urlcharmap(s[i+1]) << 4) | urlcharmap(s[i+2]);
|
||||
if ( d < 256) {
|
||||
*dest = (char) d;
|
||||
dest++;
|
||||
i += 3; /* loop will increment one time */
|
||||
} else {
|
||||
*dest++ = '%';
|
||||
i += 1;
|
||||
}
|
||||
} else {
|
||||
*dest++ = '%';
|
||||
i += 1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
*dest++ = s[i];
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
*dest = '\0';
|
||||
return (size_t)(dest - deststart); /* compute "strlen" of dest */
|
||||
}
|
||||
|
||||
const char* h5_type_to_string(enum html5_type x)
|
||||
{
|
||||
switch (x) {
|
||||
case DATA_TEXT: return "DATA_TEXT";
|
||||
case TAG_NAME_OPEN: return "TAG_NAME_OPEN";
|
||||
case TAG_NAME_CLOSE: return "TAG_NAME_CLOSE";
|
||||
case TAG_NAME_SELFCLOSE: return "TAG_NAME_SELFCLOSE";
|
||||
case TAG_DATA: return "TAG_DATA";
|
||||
case TAG_CLOSE: return "TAG_CLOSE";
|
||||
case ATTR_NAME: return "ATTR_NAME";
|
||||
case ATTR_VALUE: return "ATTR_VALUE";
|
||||
case TAG_COMMENT: return "TAG_COMMENT";
|
||||
case DOCTYPE: return "DOCTYPE";
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
void print_html5_token(h5_state_t* hs)
|
||||
{
|
||||
char* tmp = (char*) malloc(hs->token_len + 1);
|
||||
memcpy(tmp, hs->token_start, hs->token_len);
|
||||
/* TODO.. encode to be printable */
|
||||
tmp[hs->token_len] = '\0';
|
||||
|
||||
printf("%s,%d,%s\n",
|
||||
h5_type_to_string(hs->token_type),
|
||||
(int) hs->token_len,
|
||||
tmp);
|
||||
|
||||
free(tmp);
|
||||
}
|
||||
|
||||
int main(int argc, const char* argv[])
|
||||
{
|
||||
size_t slen;
|
||||
h5_state_t hs;
|
||||
char* copy;
|
||||
int offset = 1;
|
||||
int flag = 0;
|
||||
int urldecode = 0;
|
||||
|
||||
if (argc < 2) {
|
||||
fprintf(stderr, "need more args\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
while (offset < argc) {
|
||||
if (strcmp(argv[offset], "-u") == 0) {
|
||||
offset += 1;
|
||||
urldecode = 1;
|
||||
|
||||
} else if (strcmp(argv[offset], "-f") == 0) {
|
||||
offset += 1;
|
||||
flag = atoi(argv[offset]);
|
||||
offset += 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* ATTENTION: argv is a C-string, null terminated. We copy this
|
||||
* to it's own location, WITHOUT null byte. This way, valgrind
|
||||
* can see if we run past the buffer.
|
||||
*/
|
||||
|
||||
slen = strlen(argv[offset]);
|
||||
copy = (char* ) malloc(slen);
|
||||
memcpy(copy, argv[offset], slen);
|
||||
if (urldecode) {
|
||||
slen = modp_url_decode(copy, copy, slen);
|
||||
}
|
||||
|
||||
libinjection_h5_init(&hs, copy, slen, (enum html5_flags) flag);
|
||||
while (libinjection_h5_next(&hs)) {
|
||||
print_html5_token(&hs);
|
||||
}
|
||||
|
||||
if (libinjection_is_xss(copy, slen, flag)) {
|
||||
printf("is injection!\n");
|
||||
}
|
||||
free(copy);
|
||||
return 0;
|
||||
}
|
||||
65
naxsi-0.55.3/naxsi_src/ext/libinjection/libinjection.h
Normal file
65
naxsi-0.55.3/naxsi_src/ext/libinjection/libinjection.h
Normal file
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* Copyright 2012, 2013 Nick Galbreath
|
||||
* nickg@client9.com
|
||||
* BSD License -- see COPYING.txt for details
|
||||
*
|
||||
* https://libinjection.client9.com/
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _LIBINJECTION_H
|
||||
#define _LIBINJECTION_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
# define LIBINJECTION_BEGIN_DECLS extern "C" {
|
||||
# define LIBINJECTION_END_DECLS }
|
||||
#else
|
||||
# define LIBINJECTION_BEGIN_DECLS
|
||||
# define LIBINJECTION_END_DECLS
|
||||
#endif
|
||||
|
||||
LIBINJECTION_BEGIN_DECLS
|
||||
|
||||
/*
|
||||
* Pull in size_t
|
||||
*/
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
* Version info.
|
||||
*
|
||||
* This is moved into a function to allow SWIG and other auto-generated
|
||||
* binding to not be modified during minor release changes. We change
|
||||
* change the version number in the c source file, and not regenerated
|
||||
* the binding
|
||||
*
|
||||
* See python's normalized version
|
||||
* http://www.python.org/dev/peps/pep-0386/#normalizedversion
|
||||
*/
|
||||
const char* libinjection_version(void);
|
||||
|
||||
/**
|
||||
* Simple API for SQLi detection - returns a SQLi fingerprint or NULL
|
||||
* is benign input
|
||||
*
|
||||
* \param[in] s input string, may contain nulls, does not need to be null-terminated
|
||||
* \param[in] slen input string length
|
||||
* \param[out] fingerprint buffer of 8+ characters. c-string,
|
||||
* \return 1 if SQLi, 0 if benign. fingerprint will be set or set to empty string.
|
||||
*/
|
||||
int libinjection_sqli(const char* s, size_t slen, char fingerprint[]);
|
||||
|
||||
/** ALPHA version of xss detector.
|
||||
*
|
||||
* NOT DONE.
|
||||
*
|
||||
* \param[in] s input string, may contain nulls, does not need to be null-terminated
|
||||
* \param[in] slen input string length
|
||||
* \return 1 if XSS found, 0 if benign
|
||||
*
|
||||
*/
|
||||
int libinjection_xss(const char* s, size_t slen);
|
||||
|
||||
LIBINJECTION_END_DECLS
|
||||
|
||||
#endif /* _LIBINJECTION_H */
|
||||
847
naxsi-0.55.3/naxsi_src/ext/libinjection/libinjection_html5.c
Normal file
847
naxsi-0.55.3/naxsi_src/ext/libinjection/libinjection_html5.c
Normal file
@@ -0,0 +1,847 @@
|
||||
#include "libinjection_html5.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#ifdef DEBUG
|
||||
#include <stdio.h>
|
||||
#define TRACE() printf("%s:%d\n", __FUNCTION__, __LINE__)
|
||||
#else
|
||||
#define TRACE()
|
||||
#endif
|
||||
|
||||
|
||||
#define CHAR_EOF -1
|
||||
#define CHAR_NULL 0
|
||||
#define CHAR_BANG 33
|
||||
#define CHAR_DOUBLE 34
|
||||
#define CHAR_PERCENT 37
|
||||
#define CHAR_SINGLE 39
|
||||
#define CHAR_DASH 45
|
||||
#define CHAR_SLASH 47
|
||||
#define CHAR_LT 60
|
||||
#define CHAR_EQUALS 61
|
||||
#define CHAR_GT 62
|
||||
#define CHAR_QUESTION 63
|
||||
#define CHAR_RIGHTB 93
|
||||
#define CHAR_TICK 96
|
||||
|
||||
/* prototypes */
|
||||
|
||||
static int h5_skip_white(h5_state_t* hs);
|
||||
static int h5_is_white(char c);
|
||||
static int h5_state_eof(h5_state_t* hs);
|
||||
static int h5_state_data(h5_state_t* hs);
|
||||
static int h5_state_tag_open(h5_state_t* hs);
|
||||
static int h5_state_tag_name(h5_state_t* hs);
|
||||
static int h5_state_tag_name_close(h5_state_t* hs);
|
||||
static int h5_state_end_tag_open(h5_state_t* hs);
|
||||
static int h5_state_self_closing_start_tag(h5_state_t* hs);
|
||||
static int h5_state_attribute_name(h5_state_t* hs);
|
||||
static int h5_state_after_attribute_name(h5_state_t* hs);
|
||||
static int h5_state_before_attribute_name(h5_state_t* hs);
|
||||
static int h5_state_before_attribute_value(h5_state_t* hs);
|
||||
static int h5_state_attribute_value_double_quote(h5_state_t* hs);
|
||||
static int h5_state_attribute_value_single_quote(h5_state_t* hs);
|
||||
static int h5_state_attribute_value_back_quote(h5_state_t* hs);
|
||||
static int h5_state_attribute_value_no_quote(h5_state_t* hs);
|
||||
static int h5_state_after_attribute_value_quoted_state(h5_state_t* hs);
|
||||
static int h5_state_comment(h5_state_t* hs);
|
||||
static int h5_state_cdata(h5_state_t* hs);
|
||||
|
||||
|
||||
/* 12.2.4.44 */
|
||||
static int h5_state_bogus_comment(h5_state_t* hs);
|
||||
static int h5_state_bogus_comment2(h5_state_t* hs);
|
||||
|
||||
/* 12.2.4.45 */
|
||||
static int h5_state_markup_declaration_open(h5_state_t* hs);
|
||||
|
||||
/* 8.2.4.52 */
|
||||
static int h5_state_doctype(h5_state_t* hs);
|
||||
|
||||
/**
|
||||
* public function
|
||||
*/
|
||||
void libinjection_h5_init(h5_state_t* hs, const char* s, size_t len, enum html5_flags flags)
|
||||
{
|
||||
memset(hs, 0, sizeof(h5_state_t));
|
||||
hs->s = s;
|
||||
hs->len = len;
|
||||
|
||||
switch (flags) {
|
||||
case DATA_STATE:
|
||||
hs->state = h5_state_data;
|
||||
break;
|
||||
case VALUE_NO_QUOTE:
|
||||
hs->state = h5_state_before_attribute_name;
|
||||
break;
|
||||
case VALUE_SINGLE_QUOTE:
|
||||
hs->state = h5_state_attribute_value_single_quote;
|
||||
break;
|
||||
case VALUE_DOUBLE_QUOTE:
|
||||
hs->state = h5_state_attribute_value_double_quote;
|
||||
break;
|
||||
case VALUE_BACK_QUOTE:
|
||||
hs->state = h5_state_attribute_value_back_quote;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* public function
|
||||
*/
|
||||
int libinjection_h5_next(h5_state_t* hs)
|
||||
{
|
||||
assert(hs->state != NULL);
|
||||
return (*hs->state)(hs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Everything below here is private
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
static int h5_is_white(char ch)
|
||||
{
|
||||
/*
|
||||
* \t = htab = 0x09
|
||||
* \n = newline = 0x0A
|
||||
* \v = vtab = 0x0B
|
||||
* \f = form feed = 0x0C
|
||||
* \r = cr = 0x0D
|
||||
*/
|
||||
return strchr(" \t\n\v\f\r", ch) != NULL;
|
||||
}
|
||||
|
||||
static int h5_skip_white(h5_state_t* hs)
|
||||
{
|
||||
char ch;
|
||||
while (hs->pos < hs->len) {
|
||||
ch = hs->s[hs->pos];
|
||||
switch (ch) {
|
||||
case 0x00: /* IE only */
|
||||
case 0x20:
|
||||
case 0x09:
|
||||
case 0x0A:
|
||||
case 0x0B: /* IE only */
|
||||
case 0x0C:
|
||||
case 0x0D: /* IE only */
|
||||
hs->pos += 1;
|
||||
break;
|
||||
default:
|
||||
return ch;
|
||||
}
|
||||
}
|
||||
return CHAR_EOF;
|
||||
}
|
||||
|
||||
static int h5_state_eof(h5_state_t* hs)
|
||||
{
|
||||
/* eliminate unused function argument warning */
|
||||
(void)hs;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int h5_state_data(h5_state_t* hs)
|
||||
{
|
||||
const char* idx;
|
||||
|
||||
TRACE();
|
||||
assert(hs->len >= hs->pos);
|
||||
idx = (const char*) memchr(hs->s + hs->pos, CHAR_LT, hs->len - hs->pos);
|
||||
if (idx == NULL) {
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = hs->len - hs->pos;
|
||||
hs->token_type = DATA_TEXT;
|
||||
hs->state = h5_state_eof;
|
||||
if (hs->token_len == 0) {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_type = DATA_TEXT;
|
||||
hs->token_len = (size_t)(idx - hs->s) - hs->pos;
|
||||
hs->pos = (size_t)(idx - hs->s) + 1;
|
||||
hs->state = h5_state_tag_open;
|
||||
if (hs->token_len == 0) {
|
||||
return h5_state_tag_open(hs);
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 12 2.4.8
|
||||
*/
|
||||
static int h5_state_tag_open(h5_state_t* hs)
|
||||
{
|
||||
char ch;
|
||||
|
||||
TRACE();
|
||||
ch = hs->s[hs->pos];
|
||||
if (ch == CHAR_BANG) {
|
||||
hs->pos += 1;
|
||||
return h5_state_markup_declaration_open(hs);
|
||||
} else if (ch == CHAR_SLASH) {
|
||||
hs->pos += 1;
|
||||
hs->is_close = 1;
|
||||
return h5_state_end_tag_open(hs);
|
||||
} else if (ch == CHAR_QUESTION) {
|
||||
hs->pos += 1;
|
||||
return h5_state_bogus_comment(hs);
|
||||
} else if (ch == CHAR_PERCENT) {
|
||||
/* this is not in spec.. alternative comment format used
|
||||
by IE <= 9 and Safari < 4.0.3 */
|
||||
hs->pos += 1;
|
||||
return h5_state_bogus_comment2(hs);
|
||||
} else if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) {
|
||||
return h5_state_tag_name(hs);
|
||||
} else if (ch == CHAR_NULL) {
|
||||
/* IE-ism NULL characters are ignored */
|
||||
return h5_state_tag_name(hs);
|
||||
} else {
|
||||
/* user input mistake in configuring state */
|
||||
if (hs->pos == 0) {
|
||||
return h5_state_data(hs);
|
||||
}
|
||||
hs->token_start = hs->s + hs->pos - 1;
|
||||
hs->token_len = 1;
|
||||
hs->token_type = DATA_TEXT;
|
||||
hs->state = h5_state_data;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 12.2.4.9
|
||||
*/
|
||||
static int h5_state_end_tag_open(h5_state_t* hs)
|
||||
{
|
||||
char ch;
|
||||
|
||||
TRACE();
|
||||
|
||||
if (hs->pos >= hs->len) {
|
||||
return 0;
|
||||
}
|
||||
ch = hs->s[hs->pos];
|
||||
if (ch == CHAR_GT) {
|
||||
return h5_state_data(hs);
|
||||
} else if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) {
|
||||
return h5_state_tag_name(hs);
|
||||
}
|
||||
|
||||
hs->is_close = 0;
|
||||
return h5_state_bogus_comment(hs);
|
||||
}
|
||||
/*
|
||||
*
|
||||
*/
|
||||
static int h5_state_tag_name_close(h5_state_t* hs)
|
||||
{
|
||||
TRACE();
|
||||
hs->is_close = 0;
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = 1;
|
||||
hs->token_type = TAG_NAME_CLOSE;
|
||||
hs->pos += 1;
|
||||
if (hs->pos < hs->len) {
|
||||
hs->state = h5_state_data;
|
||||
} else {
|
||||
hs->state = h5_state_eof;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 12.2.4.10
|
||||
*/
|
||||
static int h5_state_tag_name(h5_state_t* hs)
|
||||
{
|
||||
char ch;
|
||||
size_t pos;
|
||||
|
||||
TRACE();
|
||||
pos = hs->pos;
|
||||
while (pos < hs->len) {
|
||||
ch = hs->s[pos];
|
||||
if (ch == 0) {
|
||||
/* special non-standard case */
|
||||
/* allow nulls in tag name */
|
||||
/* some old browsers apparently allow and ignore them */
|
||||
pos += 1;
|
||||
} else if (h5_is_white(ch)) {
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = pos - hs->pos;
|
||||
hs->token_type = TAG_NAME_OPEN;
|
||||
hs->pos = pos + 1;
|
||||
hs->state = h5_state_before_attribute_name;
|
||||
return 1;
|
||||
} else if (ch == CHAR_SLASH) {
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = pos - hs->pos;
|
||||
hs->token_type = TAG_NAME_OPEN;
|
||||
hs->pos = pos + 1;
|
||||
hs->state = h5_state_self_closing_start_tag;
|
||||
return 1;
|
||||
} else if (ch == CHAR_GT) {
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = pos - hs->pos;
|
||||
if (hs->is_close) {
|
||||
hs->pos = pos + 1;
|
||||
hs->is_close = 0;
|
||||
hs->token_type = TAG_CLOSE;
|
||||
hs->state = h5_state_data;
|
||||
} else {
|
||||
hs->pos = pos;
|
||||
hs->token_type = TAG_NAME_OPEN;
|
||||
hs->state = h5_state_tag_name_close;
|
||||
}
|
||||
return 1;
|
||||
} else {
|
||||
pos += 1;
|
||||
}
|
||||
}
|
||||
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = hs->len - hs->pos;
|
||||
hs->token_type = TAG_NAME_OPEN;
|
||||
hs->state = h5_state_eof;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 12.2.4.34
|
||||
*/
|
||||
static int h5_state_before_attribute_name(h5_state_t* hs)
|
||||
{
|
||||
int ch;
|
||||
|
||||
TRACE();
|
||||
ch = h5_skip_white(hs);
|
||||
switch (ch) {
|
||||
case CHAR_EOF: {
|
||||
return 0;
|
||||
}
|
||||
case CHAR_SLASH: {
|
||||
hs->pos += 1;
|
||||
return h5_state_self_closing_start_tag(hs);
|
||||
}
|
||||
case CHAR_GT: {
|
||||
hs->state = h5_state_data;
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = 1;
|
||||
hs->token_type = TAG_NAME_CLOSE;
|
||||
hs->pos += 1;
|
||||
return 1;
|
||||
}
|
||||
default: {
|
||||
return h5_state_attribute_name(hs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int h5_state_attribute_name(h5_state_t* hs)
|
||||
{
|
||||
char ch;
|
||||
size_t pos;
|
||||
|
||||
TRACE();
|
||||
pos = hs->pos + 1;
|
||||
while (pos < hs->len) {
|
||||
ch = hs->s[pos];
|
||||
if (h5_is_white(ch)) {
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = pos - hs->pos;
|
||||
hs->token_type = ATTR_NAME;
|
||||
hs->state = h5_state_after_attribute_name;
|
||||
hs->pos = pos + 1;
|
||||
return 1;
|
||||
} else if (ch == CHAR_SLASH) {
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = pos - hs->pos;
|
||||
hs->token_type = ATTR_NAME;
|
||||
hs->state = h5_state_self_closing_start_tag;
|
||||
hs->pos = pos + 1;
|
||||
return 1;
|
||||
} else if (ch == CHAR_EQUALS) {
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = pos - hs->pos;
|
||||
hs->token_type = ATTR_NAME;
|
||||
hs->state = h5_state_before_attribute_value;
|
||||
hs->pos = pos + 1;
|
||||
return 1;
|
||||
} else if (ch == CHAR_GT) {
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = pos - hs->pos;
|
||||
hs->token_type = ATTR_NAME;
|
||||
hs->state = h5_state_tag_name_close;
|
||||
hs->pos = pos;
|
||||
return 1;
|
||||
} else {
|
||||
pos += 1;
|
||||
}
|
||||
}
|
||||
/* EOF */
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = hs->len - hs->pos;
|
||||
hs->token_type = ATTR_NAME;
|
||||
hs->state = h5_state_eof;
|
||||
hs->pos = hs->len;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 12.2.4.36
|
||||
*/
|
||||
static int h5_state_after_attribute_name(h5_state_t* hs)
|
||||
{
|
||||
int c;
|
||||
|
||||
TRACE();
|
||||
c = h5_skip_white(hs);
|
||||
switch (c) {
|
||||
case CHAR_EOF: {
|
||||
return 0;
|
||||
}
|
||||
case CHAR_SLASH: {
|
||||
hs->pos += 1;
|
||||
return h5_state_self_closing_start_tag(hs);
|
||||
}
|
||||
case CHAR_EQUALS: {
|
||||
hs->pos += 1;
|
||||
return h5_state_before_attribute_value(hs);
|
||||
}
|
||||
case CHAR_GT: {
|
||||
return h5_state_tag_name_close(hs);
|
||||
}
|
||||
default: {
|
||||
return h5_state_attribute_name(hs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 12.2.4.37
|
||||
*/
|
||||
static int h5_state_before_attribute_value(h5_state_t* hs)
|
||||
{
|
||||
int c;
|
||||
TRACE();
|
||||
|
||||
c = h5_skip_white(hs);
|
||||
|
||||
if (c == CHAR_EOF) {
|
||||
hs->state = h5_state_eof;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (c == CHAR_DOUBLE) {
|
||||
return h5_state_attribute_value_double_quote(hs);
|
||||
} else if (c == CHAR_SINGLE) {
|
||||
return h5_state_attribute_value_single_quote(hs);
|
||||
} else if (c == CHAR_TICK) {
|
||||
/* NON STANDARD IE */
|
||||
return h5_state_attribute_value_back_quote(hs);
|
||||
} else {
|
||||
return h5_state_attribute_value_no_quote(hs);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int h5_state_attribute_value_quote(h5_state_t* hs, char qchar)
|
||||
{
|
||||
const char* idx;
|
||||
|
||||
TRACE();
|
||||
|
||||
/* skip initial quote in normal case.
|
||||
* dont do this is pos == 0 since it means we have started
|
||||
* in a non-data state. given an input of '><foo
|
||||
* we want to make 0-length attribute name
|
||||
*/
|
||||
if (hs->pos > 0) {
|
||||
hs->pos += 1;
|
||||
}
|
||||
|
||||
|
||||
idx = (const char*) memchr(hs->s + hs->pos, qchar, hs->len - hs->pos);
|
||||
if (idx == NULL) {
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = hs->len - hs->pos;
|
||||
hs->token_type = ATTR_VALUE;
|
||||
hs->state = h5_state_eof;
|
||||
} else {
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = (size_t)(idx - hs->s) - hs->pos;
|
||||
hs->token_type = ATTR_VALUE;
|
||||
hs->state = h5_state_after_attribute_value_quoted_state;
|
||||
hs->pos += hs->token_len + 1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static
|
||||
int h5_state_attribute_value_double_quote(h5_state_t* hs)
|
||||
{
|
||||
TRACE();
|
||||
return h5_state_attribute_value_quote(hs, CHAR_DOUBLE);
|
||||
}
|
||||
|
||||
static
|
||||
int h5_state_attribute_value_single_quote(h5_state_t* hs)
|
||||
{
|
||||
TRACE();
|
||||
return h5_state_attribute_value_quote(hs, CHAR_SINGLE);
|
||||
}
|
||||
|
||||
static
|
||||
int h5_state_attribute_value_back_quote(h5_state_t* hs)
|
||||
{
|
||||
TRACE();
|
||||
return h5_state_attribute_value_quote(hs, CHAR_TICK);
|
||||
}
|
||||
|
||||
static int h5_state_attribute_value_no_quote(h5_state_t* hs)
|
||||
{
|
||||
char ch;
|
||||
size_t pos;
|
||||
|
||||
TRACE();
|
||||
pos = hs->pos;
|
||||
while (pos < hs->len) {
|
||||
ch = hs->s[pos];
|
||||
if (h5_is_white(ch)) {
|
||||
hs->token_type = ATTR_VALUE;
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = pos - hs->pos;
|
||||
hs->pos = pos + 1;
|
||||
hs->state = h5_state_before_attribute_name;
|
||||
return 1;
|
||||
} else if (ch == CHAR_GT) {
|
||||
hs->token_type = ATTR_VALUE;
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = pos - hs->pos;
|
||||
hs->pos = pos;
|
||||
hs->state = h5_state_tag_name_close;
|
||||
return 1;
|
||||
}
|
||||
pos += 1;
|
||||
}
|
||||
TRACE();
|
||||
/* EOF */
|
||||
hs->state = h5_state_eof;
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = hs->len - hs->pos;
|
||||
hs->token_type = ATTR_VALUE;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 12.2.4.41
|
||||
*/
|
||||
static int h5_state_after_attribute_value_quoted_state(h5_state_t* hs)
|
||||
{
|
||||
char ch;
|
||||
|
||||
TRACE();
|
||||
if (hs->pos >= hs->len) {
|
||||
return 0;
|
||||
}
|
||||
ch = hs->s[hs->pos];
|
||||
if (h5_is_white(ch)) {
|
||||
hs->pos += 1;
|
||||
return h5_state_before_attribute_name(hs);
|
||||
} else if (ch == CHAR_SLASH) {
|
||||
hs->pos += 1;
|
||||
return h5_state_self_closing_start_tag(hs);
|
||||
} else if (ch == CHAR_GT) {
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = 1;
|
||||
hs->token_type = TAG_NAME_CLOSE;
|
||||
hs->pos += 1;
|
||||
hs->state = h5_state_data;
|
||||
return 1;
|
||||
} else {
|
||||
return h5_state_before_attribute_name(hs);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 12.2.4.43
|
||||
*/
|
||||
static int h5_state_self_closing_start_tag(h5_state_t* hs)
|
||||
{
|
||||
char ch;
|
||||
|
||||
TRACE();
|
||||
if (hs->pos >= hs->len) {
|
||||
return 0;
|
||||
}
|
||||
ch = hs->s[hs->pos];
|
||||
if (ch == CHAR_GT) {
|
||||
assert(hs->pos > 0);
|
||||
hs->token_start = hs->s + hs->pos -1;
|
||||
hs->token_len = 2;
|
||||
hs->token_type = TAG_NAME_SELFCLOSE;
|
||||
hs->state = h5_state_data;
|
||||
hs->pos += 1;
|
||||
return 1;
|
||||
} else {
|
||||
return h5_state_before_attribute_name(hs);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 12.2.4.44
|
||||
*/
|
||||
static int h5_state_bogus_comment(h5_state_t* hs)
|
||||
{
|
||||
const char* idx;
|
||||
|
||||
TRACE();
|
||||
idx = (const char*) memchr(hs->s + hs->pos, CHAR_GT, hs->len - hs->pos);
|
||||
if (idx == NULL) {
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = hs->len - hs->pos;
|
||||
hs->pos = hs->len;
|
||||
hs->state = h5_state_eof;
|
||||
} else {
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = (size_t)(idx - hs->s) - hs->pos;
|
||||
hs->pos = (size_t)(idx - hs->s) + 1;
|
||||
hs->state = h5_state_data;
|
||||
}
|
||||
|
||||
hs->token_type = TAG_COMMENT;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 12.2.4.44 ALT
|
||||
*/
|
||||
static int h5_state_bogus_comment2(h5_state_t* hs)
|
||||
{
|
||||
const char* idx;
|
||||
size_t pos;
|
||||
|
||||
TRACE();
|
||||
pos = hs->pos;
|
||||
while (1) {
|
||||
idx = (const char*) memchr(hs->s + pos, CHAR_PERCENT, hs->len - pos);
|
||||
if (idx == NULL || (idx + 1 >= hs->s + hs->len)) {
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = hs->len - hs->pos;
|
||||
hs->pos = hs->len;
|
||||
hs->token_type = TAG_COMMENT;
|
||||
hs->state = h5_state_eof;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (*(idx +1) != CHAR_GT) {
|
||||
pos = (size_t)(idx - hs->s) + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* ends in %> */
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = (size_t)(idx - hs->s) - hs->pos;
|
||||
hs->pos = (size_t)(idx - hs->s) + 2;
|
||||
hs->state = h5_state_data;
|
||||
hs->token_type = TAG_COMMENT;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 8.2.4.45
|
||||
*/
|
||||
static int h5_state_markup_declaration_open(h5_state_t* hs)
|
||||
{
|
||||
size_t remaining;
|
||||
|
||||
TRACE();
|
||||
remaining = hs->len - hs->pos;
|
||||
if (remaining >= 7 &&
|
||||
/* case insensitive */
|
||||
(hs->s[hs->pos + 0] == 'D' || hs->s[hs->pos + 0] == 'd') &&
|
||||
(hs->s[hs->pos + 1] == 'O' || hs->s[hs->pos + 1] == 'o') &&
|
||||
(hs->s[hs->pos + 2] == 'C' || hs->s[hs->pos + 2] == 'c') &&
|
||||
(hs->s[hs->pos + 3] == 'T' || hs->s[hs->pos + 3] == 't') &&
|
||||
(hs->s[hs->pos + 4] == 'Y' || hs->s[hs->pos + 4] == 'y') &&
|
||||
(hs->s[hs->pos + 5] == 'P' || hs->s[hs->pos + 5] == 'p') &&
|
||||
(hs->s[hs->pos + 6] == 'E' || hs->s[hs->pos + 6] == 'e')
|
||||
) {
|
||||
return h5_state_doctype(hs);
|
||||
} else if (remaining >= 7 &&
|
||||
/* upper case required */
|
||||
hs->s[hs->pos + 0] == '[' &&
|
||||
hs->s[hs->pos + 1] == 'C' &&
|
||||
hs->s[hs->pos + 2] == 'D' &&
|
||||
hs->s[hs->pos + 3] == 'A' &&
|
||||
hs->s[hs->pos + 4] == 'T' &&
|
||||
hs->s[hs->pos + 5] == 'A' &&
|
||||
hs->s[hs->pos + 6] == '['
|
||||
) {
|
||||
hs->pos += 7;
|
||||
return h5_state_cdata(hs);
|
||||
} else if (remaining >= 2 &&
|
||||
hs->s[hs->pos + 0] == '-' &&
|
||||
hs->s[hs->pos + 1] == '-') {
|
||||
hs->pos += 2;
|
||||
return h5_state_comment(hs);
|
||||
}
|
||||
|
||||
return h5_state_bogus_comment(hs);
|
||||
}
|
||||
|
||||
/**
|
||||
* 12.2.4.48
|
||||
* 12.2.4.49
|
||||
* 12.2.4.50
|
||||
* 12.2.4.51
|
||||
* state machine spec is confusing since it can only look
|
||||
* at one character at a time but simply it's comments end by:
|
||||
* 1) EOF
|
||||
* 2) ending in -->
|
||||
* 3) ending in -!>
|
||||
*/
|
||||
static int h5_state_comment(h5_state_t* hs)
|
||||
{
|
||||
char ch;
|
||||
const char* idx;
|
||||
size_t pos;
|
||||
size_t offset;
|
||||
const char* end = hs->s + hs->len;
|
||||
|
||||
TRACE();
|
||||
pos = hs->pos;
|
||||
while (1) {
|
||||
|
||||
idx = (const char*) memchr(hs->s + pos, CHAR_DASH, hs->len - pos);
|
||||
|
||||
/* did not find anything or has less than 3 chars left */
|
||||
if (idx == NULL || idx > hs->s + hs->len - 3) {
|
||||
hs->state = h5_state_eof;
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = hs->len - hs->pos;
|
||||
hs->token_type = TAG_COMMENT;
|
||||
return 1;
|
||||
}
|
||||
offset = 1;
|
||||
|
||||
/* skip all nulls */
|
||||
while (idx + offset < end && *(idx + offset) == 0) {
|
||||
offset += 1;
|
||||
}
|
||||
if (idx + offset == end) {
|
||||
hs->state = h5_state_eof;
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = hs->len - hs->pos;
|
||||
hs->token_type = TAG_COMMENT;
|
||||
return 1;
|
||||
}
|
||||
|
||||
ch = *(idx + offset);
|
||||
if (ch != CHAR_DASH && ch != CHAR_BANG) {
|
||||
pos = (size_t)(idx - hs->s) + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* need to test */
|
||||
#if 0
|
||||
/* skip all nulls */
|
||||
while (idx + offset < end && *(idx + offset) == 0) {
|
||||
offset += 1;
|
||||
}
|
||||
if (idx + offset == end) {
|
||||
hs->state = h5_state_eof;
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = hs->len - hs->pos;
|
||||
hs->token_type = TAG_COMMENT;
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
offset += 1;
|
||||
if (idx + offset == end) {
|
||||
hs->state = h5_state_eof;
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = hs->len - hs->pos;
|
||||
hs->token_type = TAG_COMMENT;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
ch = *(idx + offset);
|
||||
if (ch != CHAR_GT) {
|
||||
pos = (size_t)(idx - hs->s) + 1;
|
||||
continue;
|
||||
}
|
||||
offset += 1;
|
||||
|
||||
/* ends in --> or -!> */
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = (size_t)(idx - hs->s) - hs->pos;
|
||||
hs->pos = (size_t)(idx + offset - hs->s);
|
||||
hs->state = h5_state_data;
|
||||
hs->token_type = TAG_COMMENT;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
static int h5_state_cdata(h5_state_t* hs)
|
||||
{
|
||||
const char* idx;
|
||||
size_t pos;
|
||||
|
||||
TRACE();
|
||||
pos = hs->pos;
|
||||
while (1) {
|
||||
idx = (const char*) memchr(hs->s + pos, CHAR_RIGHTB, hs->len - pos);
|
||||
|
||||
/* did not find anything or has less than 3 chars left */
|
||||
if (idx == NULL || idx > hs->s + hs->len - 3) {
|
||||
hs->state = h5_state_eof;
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = hs->len - hs->pos;
|
||||
hs->token_type = DATA_TEXT;
|
||||
return 1;
|
||||
} else if ( *(idx+1) == CHAR_RIGHTB && *(idx+2) == CHAR_GT) {
|
||||
hs->state = h5_state_data;
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_len = (size_t)(idx - hs->s) - hs->pos;
|
||||
hs->pos = (size_t)(idx - hs->s) + 3;
|
||||
hs->token_type = DATA_TEXT;
|
||||
return 1;
|
||||
} else {
|
||||
pos = (size_t)(idx - hs->s) + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 8.2.4.52
|
||||
* http://www.w3.org/html/wg/drafts/html/master/syntax.html#doctype-state
|
||||
*/
|
||||
static int h5_state_doctype(h5_state_t* hs)
|
||||
{
|
||||
const char* idx;
|
||||
|
||||
TRACE();
|
||||
hs->token_start = hs->s + hs->pos;
|
||||
hs->token_type = DOCTYPE;
|
||||
|
||||
idx = (const char*) memchr(hs->s + hs->pos, CHAR_GT, hs->len - hs->pos);
|
||||
if (idx == NULL) {
|
||||
hs->state = h5_state_eof;
|
||||
hs->token_len = hs->len - hs->pos;
|
||||
} else {
|
||||
hs->state = h5_state_data;
|
||||
hs->token_len = (size_t)(idx - hs->s) - hs->pos;
|
||||
hs->pos = (size_t)(idx - hs->s) + 1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
54
naxsi-0.55.3/naxsi_src/ext/libinjection/libinjection_html5.h
Normal file
54
naxsi-0.55.3/naxsi_src/ext/libinjection/libinjection_html5.h
Normal file
@@ -0,0 +1,54 @@
|
||||
#ifndef LIBINJECTION_HTML5
|
||||
#define LIBINJECTION_HTML5
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* pull in size_t */
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
enum html5_type {
|
||||
DATA_TEXT
|
||||
, TAG_NAME_OPEN
|
||||
, TAG_NAME_CLOSE
|
||||
, TAG_NAME_SELFCLOSE
|
||||
, TAG_DATA
|
||||
, TAG_CLOSE
|
||||
, ATTR_NAME
|
||||
, ATTR_VALUE
|
||||
, TAG_COMMENT
|
||||
, DOCTYPE
|
||||
};
|
||||
|
||||
enum html5_flags {
|
||||
DATA_STATE
|
||||
, VALUE_NO_QUOTE
|
||||
, VALUE_SINGLE_QUOTE
|
||||
, VALUE_DOUBLE_QUOTE
|
||||
, VALUE_BACK_QUOTE
|
||||
};
|
||||
|
||||
struct h5_state;
|
||||
typedef int (*ptr_html5_state)(struct h5_state*);
|
||||
|
||||
typedef struct h5_state {
|
||||
const char* s;
|
||||
size_t len;
|
||||
size_t pos;
|
||||
int is_close;
|
||||
ptr_html5_state state;
|
||||
const char* token_start;
|
||||
size_t token_len;
|
||||
enum html5_type token_type;
|
||||
} h5_state_t;
|
||||
|
||||
|
||||
void libinjection_h5_init(h5_state_t* hs, const char* s, size_t len, enum html5_flags);
|
||||
int libinjection_h5_next(h5_state_t* hs);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
2298
naxsi-0.55.3/naxsi_src/ext/libinjection/libinjection_sqli.c
Normal file
2298
naxsi-0.55.3/naxsi_src/ext/libinjection/libinjection_sqli.c
Normal file
File diff suppressed because it is too large
Load Diff
295
naxsi-0.55.3/naxsi_src/ext/libinjection/libinjection_sqli.h
Normal file
295
naxsi-0.55.3/naxsi_src/ext/libinjection/libinjection_sqli.h
Normal file
@@ -0,0 +1,295 @@
|
||||
/**
|
||||
* Copyright 2012, 2013 Nick Galbreath
|
||||
* nickg@client9.com
|
||||
* BSD License -- see COPYING.txt for details
|
||||
*
|
||||
* https://libinjection.client9.com/
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _LIBINJECTION_SQLI_H
|
||||
#define _LIBINJECTION_SQLI_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Pull in size_t
|
||||
*/
|
||||
#include <string.h>
|
||||
|
||||
enum sqli_flags {
|
||||
FLAG_NONE = 0
|
||||
, FLAG_QUOTE_NONE = 1 /* 1 << 0 */
|
||||
, FLAG_QUOTE_SINGLE = 2 /* 1 << 1 */
|
||||
, FLAG_QUOTE_DOUBLE = 4 /* 1 << 2 */
|
||||
|
||||
, FLAG_SQL_ANSI = 8 /* 1 << 3 */
|
||||
, FLAG_SQL_MYSQL = 16 /* 1 << 4 */
|
||||
};
|
||||
|
||||
enum lookup_type {
|
||||
LOOKUP_WORD = 1
|
||||
, LOOKUP_TYPE = 2
|
||||
, LOOKUP_OPERATOR = 3
|
||||
, LOOKUP_FINGERPRINT = 4
|
||||
};
|
||||
|
||||
struct libinjection_sqli_token {
|
||||
#ifdef SWIG
|
||||
%immutable;
|
||||
#endif
|
||||
char type;
|
||||
char str_open;
|
||||
char str_close;
|
||||
|
||||
/*
|
||||
* position and length of token
|
||||
* in original string
|
||||
*/
|
||||
size_t pos;
|
||||
size_t len;
|
||||
|
||||
/* count:
|
||||
* in type 'v', used for number of opening '@'
|
||||
* but maybe unsed in other contexts
|
||||
*/
|
||||
int count;
|
||||
|
||||
char val[32];
|
||||
};
|
||||
|
||||
typedef struct libinjection_sqli_token stoken_t;
|
||||
|
||||
/**
|
||||
* Pointer to function, takes cstr input,
|
||||
* returns '\0' for no match, else a char
|
||||
*/
|
||||
struct libinjection_sqli_state;
|
||||
typedef char (*ptr_lookup_fn)(struct libinjection_sqli_state*, int lookuptype, const char* word, size_t len);
|
||||
|
||||
struct libinjection_sqli_state {
|
||||
#ifdef SWIG
|
||||
%immutable;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* input, does not need to be null terminated.
|
||||
* it is also not modified.
|
||||
*/
|
||||
const char *s;
|
||||
|
||||
/*
|
||||
* input length
|
||||
*/
|
||||
size_t slen;
|
||||
|
||||
/*
|
||||
* How to lookup a word or fingerprint
|
||||
*/
|
||||
ptr_lookup_fn lookup;
|
||||
void* userdata;
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
int flags;
|
||||
|
||||
/*
|
||||
* pos is index in string we are at when tokenizing
|
||||
*/
|
||||
size_t pos;
|
||||
|
||||
#ifndef SWIG
|
||||
/* for SWIG.. don't use this.. use functional API instead */
|
||||
|
||||
/* MAX TOKENS + 1 since we use one extra token
|
||||
* to determine the type of the previous token
|
||||
*/
|
||||
struct libinjection_sqli_token tokenvec[8];
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Pointer to token position in tokenvec, above
|
||||
*/
|
||||
struct libinjection_sqli_token *current;
|
||||
|
||||
/*
|
||||
* fingerprint pattern c-string
|
||||
* +1 for ending null
|
||||
* Mimimum of 8 bytes to add gcc's -fstack-protector to work
|
||||
*/
|
||||
char fingerprint[8];
|
||||
|
||||
/*
|
||||
* Line number of code that said decided if the input was SQLi or
|
||||
* not. Most of the time it's line that said "it's not a matching
|
||||
* fingerprint" but there is other logic that sometimes approves
|
||||
* an input. This is only useful for debugging.
|
||||
*
|
||||
*/
|
||||
int reason;
|
||||
|
||||
/* Number of ddw (dash-dash-white) comments
|
||||
* These comments are in the form of
|
||||
* '--[whitespace]' or '--[EOF]'
|
||||
*
|
||||
* All databases treat this as a comment.
|
||||
*/
|
||||
int stats_comment_ddw;
|
||||
|
||||
/* Number of ddx (dash-dash-[notwhite]) comments
|
||||
*
|
||||
* ANSI SQL treats these are comments, MySQL treats this as
|
||||
* two unary operators '-' '-'
|
||||
*
|
||||
* If you are parsing result returns FALSE and
|
||||
* stats_comment_dd > 0, you should reparse with
|
||||
* COMMENT_MYSQL
|
||||
*
|
||||
*/
|
||||
int stats_comment_ddx;
|
||||
|
||||
/*
|
||||
* c-style comments found /x .. x/
|
||||
*/
|
||||
int stats_comment_c;
|
||||
|
||||
/* '#' operators or mysql EOL comments found
|
||||
*
|
||||
*/
|
||||
int stats_comment_hash;
|
||||
|
||||
/*
|
||||
* number of tokens folded away
|
||||
*/
|
||||
int stats_folds;
|
||||
|
||||
/*
|
||||
* total tokens processed
|
||||
*/
|
||||
int stats_tokens;
|
||||
|
||||
};
|
||||
|
||||
typedef struct libinjection_sqli_state sfilter;
|
||||
|
||||
struct libinjection_sqli_token* libinjection_sqli_get_token(
|
||||
struct libinjection_sqli_state* sqlistate, int i);
|
||||
|
||||
/*
|
||||
* Version info.
|
||||
*
|
||||
* This is moved into a function to allow SWIG and other auto-generated
|
||||
* binding to not be modified during minor release changes. We change
|
||||
* change the version number in the c source file, and not regenerated
|
||||
* the binding
|
||||
*
|
||||
* See python's normalized version
|
||||
* http://www.python.org/dev/peps/pep-0386/#normalizedversion
|
||||
*/
|
||||
const char* libinjection_version(void);
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void libinjection_sqli_init(struct libinjection_sqli_state* sql_state,
|
||||
const char* s, size_t slen,
|
||||
int flags);
|
||||
|
||||
/**
|
||||
* Main API: tests for SQLi in three possible contexts, no quotes,
|
||||
* single quote and double quote
|
||||
*
|
||||
* \param sql_state core data structure
|
||||
*
|
||||
* \return 1 (true) if SQLi, 0 (false) if benign
|
||||
*/
|
||||
int libinjection_is_sqli(struct libinjection_sqli_state* sql_state);
|
||||
|
||||
/* FOR H@CKERS ONLY
|
||||
*
|
||||
*/
|
||||
void libinjection_sqli_callback(struct libinjection_sqli_state* sql_state,
|
||||
ptr_lookup_fn fn,
|
||||
void* userdata);
|
||||
|
||||
|
||||
/*
|
||||
* Resets state, but keeps initial string and callbacks
|
||||
*/
|
||||
void libinjection_sqli_reset(struct libinjection_sqli_state* sql_state,
|
||||
int flags);
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* This detects SQLi in a single context, mostly useful for custom
|
||||
* logic and debugging.
|
||||
*
|
||||
* \param sql_state Main data structure
|
||||
* \param flags flags to adjust parsing
|
||||
*
|
||||
* \returns a pointer to sfilter.fingerprint as convenience
|
||||
* do not free!
|
||||
*
|
||||
*/
|
||||
const char* libinjection_sqli_fingerprint(struct libinjection_sqli_state* sql_state,
|
||||
int flags);
|
||||
|
||||
/**
|
||||
* The default "word" to token-type or fingerprint function. This
|
||||
* uses a ASCII case-insensitive binary tree.
|
||||
*/
|
||||
char libinjection_sqli_lookup_word(struct libinjection_sqli_state* sql_state,
|
||||
int lookup_type,
|
||||
const char* s,
|
||||
size_t slen);
|
||||
|
||||
/* Streaming tokenization interface.
|
||||
*
|
||||
* sql_state->current is updated with the current token.
|
||||
*
|
||||
* \returns 1, has a token, keep going, or 0 no tokens
|
||||
*
|
||||
*/
|
||||
int libinjection_sqli_tokenize(struct libinjection_sqli_state * sql_state);
|
||||
|
||||
/**
|
||||
* parses and folds input, up to 5 tokens
|
||||
*
|
||||
*/
|
||||
int libinjection_sqli_fold(struct libinjection_sqli_state * sql_state);
|
||||
|
||||
/** The built-in default function to match fingerprints
|
||||
* and do false negative/positive analysis. This calls the following
|
||||
* two functions. With this, you over-ride one part or the other.
|
||||
*
|
||||
* return libinjection_sqli_blacklist(sql_state) &&
|
||||
* libinject_sqli_not_whitelist(sql_state);
|
||||
*
|
||||
* \param sql_state should be filled out after libinjection_sqli_fingerprint is called
|
||||
*/
|
||||
int libinjection_sqli_check_fingerprint(struct libinjection_sqli_state * sql_state);
|
||||
|
||||
/* Given a pattern determine if it's a SQLi pattern.
|
||||
*
|
||||
* \return TRUE if sqli, false otherwise
|
||||
*/
|
||||
int libinjection_sqli_blacklist(struct libinjection_sqli_state* sql_state);
|
||||
|
||||
/* Given a positive match for a pattern (i.e. pattern is SQLi), this function
|
||||
* does additional analysis to reduce false positives.
|
||||
*
|
||||
* \return TRUE if sqli, false otherwise
|
||||
*/
|
||||
int libinjection_sqli_not_whitelist(struct libinjection_sqli_state * sql_state);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _LIBINJECTION_SQLI_H */
|
||||
9349
naxsi-0.55.3/naxsi_src/ext/libinjection/libinjection_sqli_data.h
Normal file
9349
naxsi-0.55.3/naxsi_src/ext/libinjection/libinjection_sqli_data.h
Normal file
File diff suppressed because it is too large
Load Diff
531
naxsi-0.55.3/naxsi_src/ext/libinjection/libinjection_xss.c
Normal file
531
naxsi-0.55.3/naxsi_src/ext/libinjection/libinjection_xss.c
Normal file
@@ -0,0 +1,531 @@
|
||||
|
||||
#include "libinjection.h"
|
||||
#include "libinjection_xss.h"
|
||||
#include "libinjection_html5.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
|
||||
typedef enum attribute {
|
||||
TYPE_NONE
|
||||
, TYPE_BLACK /* ban always */
|
||||
, TYPE_ATTR_URL /* attribute value takes a URL-like object */
|
||||
, TYPE_STYLE
|
||||
, TYPE_ATTR_INDIRECT /* attribute *name* is given in *value* */
|
||||
} attribute_t;
|
||||
|
||||
|
||||
static attribute_t is_black_attr(const char* s, size_t len);
|
||||
static int is_black_tag(const char* s, size_t len);
|
||||
static int is_black_url(const char* s, size_t len);
|
||||
static int cstrcasecmp_with_null(const char *a, const char *b, size_t n);
|
||||
static int html_decode_char_at(const char* src, size_t len, size_t* consumed);
|
||||
static int htmlencode_startswith(const char* prefix, const char *src, size_t n);
|
||||
|
||||
|
||||
typedef struct stringtype {
|
||||
const char* name;
|
||||
attribute_t atype;
|
||||
} stringtype_t;
|
||||
|
||||
|
||||
static const int gsHexDecodeMap[256] = {
|
||||
256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
|
||||
256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
|
||||
256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
|
||||
256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 256, 256,
|
||||
256, 256, 256, 256, 256, 10, 11, 12, 13, 14, 15, 256,
|
||||
256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
|
||||
256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
|
||||
256, 10, 11, 12, 13, 14, 15, 256, 256, 256, 256, 256,
|
||||
256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
|
||||
256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
|
||||
256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
|
||||
256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
|
||||
256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
|
||||
256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
|
||||
256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
|
||||
256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
|
||||
256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
|
||||
256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
|
||||
256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
|
||||
256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
|
||||
256, 256, 256, 256
|
||||
};
|
||||
|
||||
static int html_decode_char_at(const char* src, size_t len, size_t* consumed)
|
||||
{
|
||||
int val = 0;
|
||||
size_t i;
|
||||
int ch;
|
||||
|
||||
if (len == 0 || src == NULL) {
|
||||
*consumed = 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
*consumed = 1;
|
||||
if (*src != '&' || len < 2) {
|
||||
return (unsigned char)(*src);
|
||||
}
|
||||
|
||||
|
||||
if (*(src+1) != '#') {
|
||||
/* normally this would be for named entities
|
||||
* but for this case we don't actually care
|
||||
*/
|
||||
return '&';
|
||||
}
|
||||
|
||||
if (*(src+2) == 'x' || *(src+2) == 'X') {
|
||||
ch = (unsigned char) (*(src+3));
|
||||
ch = gsHexDecodeMap[ch];
|
||||
if (ch == 256) {
|
||||
/* degenerate case '&#[?]' */
|
||||
return '&';
|
||||
}
|
||||
val = ch;
|
||||
i = 4;
|
||||
while (i < len) {
|
||||
ch = (unsigned char) src[i];
|
||||
if (ch == ';') {
|
||||
*consumed = i + 1;
|
||||
return val;
|
||||
}
|
||||
ch = gsHexDecodeMap[ch];
|
||||
if (ch == 256) {
|
||||
*consumed = i;
|
||||
return val;
|
||||
}
|
||||
val = (val * 16) + ch;
|
||||
if (val > 0x1000FF) {
|
||||
return '&';
|
||||
}
|
||||
++i;
|
||||
}
|
||||
*consumed = i;
|
||||
return val;
|
||||
} else {
|
||||
i = 2;
|
||||
ch = (unsigned char) src[i];
|
||||
if (ch < '0' || ch > '9') {
|
||||
return '&';
|
||||
}
|
||||
val = ch - '0';
|
||||
i += 1;
|
||||
while (i < len) {
|
||||
ch = (unsigned char) src[i];
|
||||
if (ch == ';') {
|
||||
*consumed = i + 1;
|
||||
return val;
|
||||
}
|
||||
if (ch < '0' || ch > '9') {
|
||||
*consumed = i;
|
||||
return val;
|
||||
}
|
||||
val = (val * 10) + (ch - '0');
|
||||
if (val > 0x1000FF) {
|
||||
return '&';
|
||||
}
|
||||
++i;
|
||||
}
|
||||
*consumed = i;
|
||||
return val;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* view-source:
|
||||
* data:
|
||||
* javascript:
|
||||
*/
|
||||
static stringtype_t BLACKATTR[] = {
|
||||
{ "ACTION", TYPE_ATTR_URL } /* form */
|
||||
, { "ATTRIBUTENAME", TYPE_ATTR_INDIRECT } /* SVG allow indirection of attribute names */
|
||||
, { "BY", TYPE_ATTR_URL } /* SVG */
|
||||
, { "BACKGROUND", TYPE_ATTR_URL } /* IE6, O11 */
|
||||
, { "DATAFORMATAS", TYPE_BLACK } /* IE */
|
||||
, { "DATASRC", TYPE_BLACK } /* IE */
|
||||
, { "DYNSRC", TYPE_ATTR_URL } /* Obsolete img attribute */
|
||||
, { "FILTER", TYPE_STYLE } /* Opera, SVG inline style */
|
||||
, { "FORMACTION", TYPE_ATTR_URL } /* HTML5 */
|
||||
, { "FOLDER", TYPE_ATTR_URL } /* Only on A tags, IE-only */
|
||||
, { "FROM", TYPE_ATTR_URL } /* SVG */
|
||||
, { "HANDLER", TYPE_ATTR_URL } /* SVG Tiny, Opera */
|
||||
, { "HREF", TYPE_ATTR_URL }
|
||||
, { "LOWSRC", TYPE_ATTR_URL } /* Obsolete img attribute */
|
||||
, { "POSTER", TYPE_ATTR_URL } /* Opera 10,11 */
|
||||
, { "SRC", TYPE_ATTR_URL }
|
||||
, { "STYLE", TYPE_STYLE }
|
||||
, { "TO", TYPE_ATTR_URL } /* SVG */
|
||||
, { "VALUES", TYPE_ATTR_URL } /* SVG */
|
||||
, { "XLINK:HREF", TYPE_ATTR_URL }
|
||||
, { NULL, TYPE_NONE }
|
||||
};
|
||||
|
||||
/* xmlns */
|
||||
/* xml-stylesheet > <eval>, <if expr=> */
|
||||
|
||||
/*
|
||||
static const char* BLACKATTR[] = {
|
||||
"ATTRIBUTENAME",
|
||||
"BACKGROUND",
|
||||
"DATAFORMATAS",
|
||||
"HREF",
|
||||
"SCROLL",
|
||||
"SRC",
|
||||
"STYLE",
|
||||
"SRCDOC",
|
||||
NULL
|
||||
};
|
||||
*/
|
||||
|
||||
static const char* BLACKTAG[] = {
|
||||
"APPLET"
|
||||
/* , "AUDIO" */
|
||||
, "BASE"
|
||||
, "COMMENT" /* IE http://html5sec.org/#38 */
|
||||
, "EMBED"
|
||||
/* , "FORM" */
|
||||
, "FRAME"
|
||||
, "FRAMESET"
|
||||
, "HANDLER" /* Opera SVG, effectively a script tag */
|
||||
, "IFRAME"
|
||||
, "IMPORT"
|
||||
, "ISINDEX"
|
||||
, "LINK"
|
||||
, "LISTENER"
|
||||
/* , "MARQUEE" */
|
||||
, "META"
|
||||
, "NOSCRIPT"
|
||||
, "OBJECT"
|
||||
, "SCRIPT"
|
||||
, "STYLE"
|
||||
/* , "VIDEO" */
|
||||
, "VMLFRAME"
|
||||
, "XML"
|
||||
, "XSS"
|
||||
, NULL
|
||||
};
|
||||
|
||||
|
||||
static int cstrcasecmp_with_null(const char *a, const char *b, size_t n)
|
||||
{
|
||||
char ca;
|
||||
char cb;
|
||||
/* printf("Comparing to %s %.*s\n", a, (int)n, b); */
|
||||
while (n-- > 0) {
|
||||
cb = *b++;
|
||||
if (cb == '\0') continue;
|
||||
|
||||
ca = *a++;
|
||||
|
||||
if (cb >= 'a' && cb <= 'z') {
|
||||
cb -= 0x20;
|
||||
}
|
||||
/* printf("Comparing %c vs %c with %d left\n", ca, cb, (int)n); */
|
||||
if (ca != cb) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (*a == 0) {
|
||||
/* printf(" MATCH \n"); */
|
||||
return 0;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Does an HTML encoded binary string (const char*, lenght) start with
|
||||
* a all uppercase c-string (null terminated), case insenstive!
|
||||
*
|
||||
* also ignore any embedded nulls in the HTML string!
|
||||
*
|
||||
* return 1 if match / starts with
|
||||
* return 0 if not
|
||||
*/
|
||||
static int htmlencode_startswith(const char *a, const char *b, size_t n)
|
||||
{
|
||||
size_t consumed;
|
||||
int cb;
|
||||
int first = 1;
|
||||
/* printf("Comparing %s with %.*s\n", a,(int)n,b); */
|
||||
while (n > 0) {
|
||||
if (*a == 0) {
|
||||
/* printf("Match EOL!\n"); */
|
||||
return 1;
|
||||
}
|
||||
cb = html_decode_char_at(b, n, &consumed);
|
||||
b += consumed;
|
||||
n -= consumed;
|
||||
|
||||
if (first && cb <= 32) {
|
||||
/* ignore all leading whitespace and control characters */
|
||||
continue;
|
||||
}
|
||||
first = 0;
|
||||
|
||||
if (cb == 0) {
|
||||
/* always ignore null characters in user input */
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cb == 10) {
|
||||
/* always ignore vtab characters in user input */
|
||||
/* who allows this?? */
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cb >= 'a' && cb <= 'z') {
|
||||
/* upcase */
|
||||
cb -= 0x20;
|
||||
}
|
||||
|
||||
if (*a != (char) cb) {
|
||||
/* printf(" %c != %c\n", *a, cb); */
|
||||
/* mismatch */
|
||||
return 0;
|
||||
}
|
||||
a++;
|
||||
}
|
||||
|
||||
return (*a == 0) ? 1 : 0;
|
||||
}
|
||||
|
||||
static int is_black_tag(const char* s, size_t len)
|
||||
{
|
||||
const char** black;
|
||||
|
||||
if (len < 3) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
black = BLACKTAG;
|
||||
while (*black != NULL) {
|
||||
if (cstrcasecmp_with_null(*black, s, len) == 0) {
|
||||
/* printf("Got black tag %s\n", *black); */
|
||||
return 1;
|
||||
}
|
||||
black += 1;
|
||||
}
|
||||
|
||||
/* anything SVG related */
|
||||
if ((s[0] == 's' || s[0] == 'S') &&
|
||||
(s[1] == 'v' || s[1] == 'V') &&
|
||||
(s[2] == 'g' || s[2] == 'G')) {
|
||||
/* printf("Got SVG tag \n"); */
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Anything XSL(t) related */
|
||||
if ((s[0] == 'x' || s[0] == 'X') &&
|
||||
(s[1] == 's' || s[1] == 'S') &&
|
||||
(s[2] == 'l' || s[2] == 'L')) {
|
||||
/* printf("Got XSL tag\n"); */
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static attribute_t is_black_attr(const char* s, size_t len)
|
||||
{
|
||||
stringtype_t* black;
|
||||
|
||||
if (len < 2) {
|
||||
return TYPE_NONE;
|
||||
}
|
||||
|
||||
/* javascript on.* */
|
||||
if ((s[0] == 'o' || s[0] == 'O') && (s[1] == 'n' || s[1] == 'N')) {
|
||||
/* printf("Got javascript on- attribute name\n"); */
|
||||
return TYPE_BLACK;
|
||||
}
|
||||
|
||||
|
||||
if (len >= 5) {
|
||||
/* XMLNS can be used to create arbitrary tags */
|
||||
if (cstrcasecmp_with_null("XMLNS", s, 5) == 0 || cstrcasecmp_with_null("XLINK", s, 5) == 0) {
|
||||
/* printf("Got XMLNS and XLINK tags\n"); */
|
||||
return TYPE_BLACK;
|
||||
}
|
||||
}
|
||||
|
||||
black = BLACKATTR;
|
||||
while (black->name != NULL) {
|
||||
if (cstrcasecmp_with_null(black->name, s, len) == 0) {
|
||||
/* printf("Got banned attribute name %s\n", black->name); */
|
||||
return black->atype;
|
||||
}
|
||||
black += 1;
|
||||
}
|
||||
|
||||
return TYPE_NONE;
|
||||
}
|
||||
|
||||
static int is_black_url(const char* s, size_t len)
|
||||
{
|
||||
|
||||
static const char* data_url = "DATA";
|
||||
static const char* viewsource_url = "VIEW-SOURCE";
|
||||
|
||||
/* obsolete but interesting signal */
|
||||
static const char* vbscript_url = "VBSCRIPT";
|
||||
|
||||
/* covers JAVA, JAVASCRIPT, + colon */
|
||||
static const char* javascript_url = "JAVA";
|
||||
|
||||
/* skip whitespace */
|
||||
while (len > 0 && (*s <= 32 || *s >= 127)) {
|
||||
/*
|
||||
* HEY: this is a signed character.
|
||||
* We are intentionally skipping high-bit characters too
|
||||
* since they are not ascii, and Opera sometimes uses UTF8 whitespace.
|
||||
*
|
||||
* Also in EUC-JP some of the high bytes are just ignored.
|
||||
*/
|
||||
++s;
|
||||
--len;
|
||||
}
|
||||
|
||||
if (htmlencode_startswith(data_url, s, len)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (htmlencode_startswith(viewsource_url, s, len)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (htmlencode_startswith(javascript_url, s, len)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (htmlencode_startswith(vbscript_url, s, len)) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int libinjection_is_xss(const char* s, size_t len, int flags)
|
||||
{
|
||||
h5_state_t h5;
|
||||
attribute_t attr = TYPE_NONE;
|
||||
|
||||
libinjection_h5_init(&h5, s, len, (enum html5_flags) flags);
|
||||
while (libinjection_h5_next(&h5)) {
|
||||
if (h5.token_type != ATTR_VALUE) {
|
||||
attr = TYPE_NONE;
|
||||
}
|
||||
|
||||
if (h5.token_type == DOCTYPE) {
|
||||
return 1;
|
||||
} else if (h5.token_type == TAG_NAME_OPEN) {
|
||||
if (is_black_tag(h5.token_start, h5.token_len)) {
|
||||
return 1;
|
||||
}
|
||||
} else if (h5.token_type == ATTR_NAME) {
|
||||
attr = is_black_attr(h5.token_start, h5.token_len);
|
||||
} else if (h5.token_type == ATTR_VALUE) {
|
||||
/*
|
||||
* IE6,7,8 parsing works a bit differently so
|
||||
* a whole <script> or other black tag might be hiding
|
||||
* inside an attribute value under HTML5 parsing
|
||||
* See http://html5sec.org/#102
|
||||
* to avoid doing a full reparse of the value, just
|
||||
* look for "<". This probably need adjusting to
|
||||
* handle escaped characters
|
||||
*/
|
||||
/*
|
||||
if (memchr(h5.token_start, '<', h5.token_len) != NULL) {
|
||||
return 1;
|
||||
}
|
||||
*/
|
||||
|
||||
switch (attr) {
|
||||
case TYPE_NONE:
|
||||
break;
|
||||
case TYPE_BLACK:
|
||||
return 1;
|
||||
case TYPE_ATTR_URL:
|
||||
if (is_black_url(h5.token_start, h5.token_len)) {
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
case TYPE_STYLE:
|
||||
return 1;
|
||||
case TYPE_ATTR_INDIRECT:
|
||||
/* an attribute name is specified in a _value_ */
|
||||
if (is_black_attr(h5.token_start, h5.token_len)) {
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
/*
|
||||
default:
|
||||
assert(0);
|
||||
*/
|
||||
}
|
||||
attr = TYPE_NONE;
|
||||
} else if (h5.token_type == TAG_COMMENT) {
|
||||
/* IE uses a "`" as a tag ending char */
|
||||
if (memchr(h5.token_start, '`', h5.token_len) != NULL) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* IE conditional comment */
|
||||
if (h5.token_len > 3) {
|
||||
if (h5.token_start[0] == '[' &&
|
||||
(h5.token_start[1] == 'i' || h5.token_start[1] == 'I') &&
|
||||
(h5.token_start[2] == 'f' || h5.token_start[2] == 'F')) {
|
||||
return 1;
|
||||
}
|
||||
if ((h5.token_start[0] == 'x' || h5.token_start[1] == 'X') &&
|
||||
(h5.token_start[1] == 'm' || h5.token_start[1] == 'M') &&
|
||||
(h5.token_start[2] == 'l' || h5.token_start[2] == 'L')) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (h5.token_len > 5) {
|
||||
/* IE <?import pseudo-tag */
|
||||
if (cstrcasecmp_with_null("IMPORT", h5.token_start, 6) == 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* XML Entity definition */
|
||||
if (cstrcasecmp_with_null("ENTITY", h5.token_start, 6) == 0) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* wrapper
|
||||
*/
|
||||
int libinjection_xss(const char* s, size_t len)
|
||||
{
|
||||
if (libinjection_is_xss(s, len, DATA_STATE)) {
|
||||
return 1;
|
||||
}
|
||||
if (libinjection_is_xss(s, len, VALUE_NO_QUOTE)) {
|
||||
return 1;
|
||||
}
|
||||
if (libinjection_is_xss(s, len, VALUE_SINGLE_QUOTE)) {
|
||||
return 1;
|
||||
}
|
||||
if (libinjection_is_xss(s, len, VALUE_DOUBLE_QUOTE)) {
|
||||
return 1;
|
||||
}
|
||||
if (libinjection_is_xss(s, len, VALUE_BACK_QUOTE)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
21
naxsi-0.55.3/naxsi_src/ext/libinjection/libinjection_xss.h
Normal file
21
naxsi-0.55.3/naxsi_src/ext/libinjection/libinjection_xss.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#ifndef LIBINJECTION_XSS
|
||||
#define LIBINJECTION_XSS
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* HEY THIS ISN'T DONE
|
||||
*/
|
||||
|
||||
/* pull in size_t */
|
||||
|
||||
#include <string.h>
|
||||
|
||||
int libinjection_is_xss(const char* s, size_t len, int flags);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
314
naxsi-0.55.3/naxsi_src/ext/libinjection/reader.c
Normal file
314
naxsi-0.55.3/naxsi_src/ext/libinjection/reader.c
Normal file
@@ -0,0 +1,314 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "libinjection.h"
|
||||
#include "libinjection_sqli.h"
|
||||
#include "libinjection_xss.h"
|
||||
|
||||
#ifndef TRUE
|
||||
#define TRUE 1
|
||||
#endif
|
||||
#ifndef FALSE
|
||||
#define FALSE 0
|
||||
#endif
|
||||
|
||||
static int g_test_ok = 0;
|
||||
static int g_test_fail = 0;
|
||||
|
||||
typedef enum {
|
||||
MODE_SQLI,
|
||||
MODE_XSS
|
||||
} detect_mode_t;
|
||||
|
||||
static void usage(const char* argv[]);
|
||||
size_t modp_rtrim(char* str, size_t len);
|
||||
void modp_toprint(char* str, size_t len);
|
||||
void test_positive(FILE * fd, const char *fname, detect_mode_t mode,
|
||||
int flag_invert, int flag_true, int flag_quiet);
|
||||
|
||||
int urlcharmap(char ch);
|
||||
size_t modp_url_decode(char* dest, const char* s, size_t len);
|
||||
|
||||
int urlcharmap(char ch) {
|
||||
switch (ch) {
|
||||
case '0': return 0;
|
||||
case '1': return 1;
|
||||
case '2': return 2;
|
||||
case '3': return 3;
|
||||
case '4': return 4;
|
||||
case '5': return 5;
|
||||
case '6': return 6;
|
||||
case '7': return 7;
|
||||
case '8': return 8;
|
||||
case '9': return 9;
|
||||
case 'a': case 'A': return 10;
|
||||
case 'b': case 'B': return 11;
|
||||
case 'c': case 'C': return 12;
|
||||
case 'd': case 'D': return 13;
|
||||
case 'e': case 'E': return 14;
|
||||
case 'f': case 'F': return 15;
|
||||
default:
|
||||
return 256;
|
||||
}
|
||||
}
|
||||
|
||||
size_t modp_url_decode(char* dest, const char* s, size_t len)
|
||||
{
|
||||
const char* deststart = dest;
|
||||
|
||||
size_t i = 0;
|
||||
int d = 0;
|
||||
while (i < len) {
|
||||
switch (s[i]) {
|
||||
case '+':
|
||||
*dest++ = ' ';
|
||||
i += 1;
|
||||
break;
|
||||
case '%':
|
||||
if (i+2 < len) {
|
||||
d = (urlcharmap(s[i+1]) << 4) | urlcharmap(s[i+2]);
|
||||
if ( d < 256) {
|
||||
*dest = (char) d;
|
||||
dest++;
|
||||
i += 3; /* loop will increment one time */
|
||||
} else {
|
||||
*dest++ = '%';
|
||||
i += 1;
|
||||
}
|
||||
} else {
|
||||
*dest++ = '%';
|
||||
i += 1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
*dest++ = s[i];
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
*dest = '\0';
|
||||
return (size_t)(dest - deststart); /* compute "strlen" of dest */
|
||||
}
|
||||
|
||||
void modp_toprint(char* str, size_t len)
|
||||
{
|
||||
size_t i;
|
||||
for (i = 0; i < len; ++i) {
|
||||
if (str[i] < 32 || str[i] > 126) {
|
||||
str[i] = '?';
|
||||
}
|
||||
}
|
||||
}
|
||||
size_t modp_rtrim(char* str, size_t len)
|
||||
{
|
||||
while (len) {
|
||||
char c = str[len -1];
|
||||
if (c == ' ' || c == '\n' || c == '\t' || c == '\r') {
|
||||
str[len -1] = '\0';
|
||||
len -= 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
void test_positive(FILE * fd, const char *fname, detect_mode_t mode,
|
||||
int flag_invert, int flag_true, int flag_quiet)
|
||||
{
|
||||
char linebuf[8192];
|
||||
int issqli;
|
||||
int linenum = 0;
|
||||
size_t len;
|
||||
sfilter sf;
|
||||
|
||||
while (fgets(linebuf, sizeof(linebuf), fd)) {
|
||||
linenum += 1;
|
||||
len = modp_rtrim(linebuf, strlen(linebuf));
|
||||
if (len == 0) {
|
||||
continue;
|
||||
}
|
||||
if (linebuf[0] == '#') {
|
||||
continue;
|
||||
}
|
||||
|
||||
len = modp_url_decode(linebuf, linebuf, len);
|
||||
issqli = 0;
|
||||
switch (mode) {
|
||||
case MODE_SQLI: {
|
||||
libinjection_sqli_init(&sf, linebuf, len, 0);
|
||||
issqli = libinjection_is_sqli(&sf);
|
||||
break;
|
||||
}
|
||||
case MODE_XSS: {
|
||||
issqli = libinjection_xss(linebuf, len);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
|
||||
if (issqli) {
|
||||
g_test_ok += 1;
|
||||
} else {
|
||||
g_test_fail += 1;
|
||||
}
|
||||
|
||||
if (!flag_quiet) {
|
||||
if ((issqli && flag_true && ! flag_invert) ||
|
||||
(!issqli && flag_true && flag_invert) ||
|
||||
!flag_true) {
|
||||
|
||||
modp_toprint(linebuf, len);
|
||||
|
||||
switch (mode) {
|
||||
case MODE_SQLI: {
|
||||
/*
|
||||
* if we didn't find a SQLi and fingerprint from
|
||||
* sqlstats is is 'sns' or 'snsns' then redo using
|
||||
* plain context
|
||||
*/
|
||||
if (!issqli && (strcmp(sf.fingerprint, "sns") == 0 ||
|
||||
strcmp(sf.fingerprint, "snsns") == 0)) {
|
||||
libinjection_sqli_fingerprint(&sf, 0);
|
||||
}
|
||||
|
||||
fprintf(stdout, "%s\t%d\t%s\t%s\t%s\n",
|
||||
fname, linenum,
|
||||
(issqli ? "True" : "False"), sf.fingerprint, linebuf);
|
||||
break;
|
||||
}
|
||||
case MODE_XSS: {
|
||||
fprintf(stdout, "%s\t%d\t%s\t%s\n",
|
||||
fname, linenum,
|
||||
(issqli ? "True" : "False"), linebuf);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void usage(const char* argv[])
|
||||
{
|
||||
fprintf(stdout, "usage: %s [flags] [files...]\n", argv[0]);
|
||||
fprintf(stdout, "%s\n", "");
|
||||
fprintf(stdout, "%s\n", "-q --quiet : quiet mode");
|
||||
fprintf(stdout, "%s\n", "-m --max-fails : number of failed cases need to fail entire test");
|
||||
fprintf(stdout, "%s\n", "-s INTEGER : repeat each test N time "
|
||||
"(for performance testing)");
|
||||
fprintf(stdout, "%s\n", "-t : only print positive matches");
|
||||
fprintf(stdout, "%s\n", "-x --mode-xss : test input for XSS");
|
||||
fprintf(stdout, "%s\n", "-i --invert : invert test logic "
|
||||
"(input is tested for being safe)");
|
||||
|
||||
fprintf(stdout, "%s\n", "");
|
||||
fprintf(stdout, "%s\n", "-? -h -help --help : this page");
|
||||
fprintf(stdout, "%s\n", "");
|
||||
}
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
/*
|
||||
* invert output, by
|
||||
*/
|
||||
int flag_invert = FALSE;
|
||||
|
||||
/*
|
||||
* don't print anything.. useful for
|
||||
* performance monitors, gprof.
|
||||
*/
|
||||
int flag_quiet = FALSE;
|
||||
|
||||
/*
|
||||
* only print postive results
|
||||
* with invert, only print negative results
|
||||
*/
|
||||
int flag_true = FALSE;
|
||||
detect_mode_t mode = MODE_SQLI;
|
||||
|
||||
int flag_slow = 1;
|
||||
int count = 0;
|
||||
int max = -1;
|
||||
|
||||
int i, j;
|
||||
int offset = 1;
|
||||
|
||||
while (offset < argc) {
|
||||
if (strcmp(argv[offset], "-?") == 0 ||
|
||||
strcmp(argv[offset], "-h") == 0 ||
|
||||
strcmp(argv[offset], "-help") == 0 ||
|
||||
strcmp(argv[offset], "--help") == 0) {
|
||||
usage(argv);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (strcmp(argv[offset], "-i") == 0) {
|
||||
offset += 1;
|
||||
flag_invert = TRUE;
|
||||
} else if (strcmp(argv[offset], "-q") == 0 ||
|
||||
strcmp(argv[offset], "--quiet") == 0) {
|
||||
offset += 1;
|
||||
flag_quiet = TRUE;
|
||||
} else if (strcmp(argv[offset], "-t") == 0) {
|
||||
offset += 1;
|
||||
flag_true = TRUE;
|
||||
} else if (strcmp(argv[offset], "-s") == 0) {
|
||||
offset += 1;
|
||||
flag_slow = 100;
|
||||
} else if (strcmp(argv[offset], "-m") == 0 ||
|
||||
strcmp(argv[offset], "--max-fails") == 0) {
|
||||
offset += 1;
|
||||
max = atoi(argv[offset]);
|
||||
offset += 1;
|
||||
} else if (strcmp(argv[offset], "-x") == 0 ||
|
||||
strcmp(argv[offset], "--mode-xss") == 0) {
|
||||
mode = MODE_XSS;
|
||||
offset += 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (offset == argc) {
|
||||
test_positive(stdin, "stdin", mode, flag_invert, flag_true, flag_quiet);
|
||||
} else {
|
||||
for (j = 0; j < flag_slow; ++j) {
|
||||
for (i = offset; i < argc; ++i) {
|
||||
FILE* fd = fopen(argv[i], "r");
|
||||
if (fd) {
|
||||
test_positive(fd, argv[i], mode, flag_invert, flag_true, flag_quiet);
|
||||
fclose(fd);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!flag_quiet) {
|
||||
fprintf(stdout, "%s", "\n");
|
||||
fprintf(stdout, "SQLI : %d\n", g_test_ok);
|
||||
fprintf(stdout, "SAFE : %d\n", g_test_fail);
|
||||
fprintf(stdout, "TOTAL : %d\n", g_test_ok + g_test_fail);
|
||||
}
|
||||
|
||||
if (max == -1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
count = g_test_ok;
|
||||
if (flag_invert) {
|
||||
count = g_test_fail;
|
||||
}
|
||||
|
||||
if (count > max) {
|
||||
printf("\nThreshold is %d, got %d, failing.\n", max, count);
|
||||
return 1;
|
||||
} else {
|
||||
printf("\nThreshold is %d, got %d, passing.\n", max, count);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
144
naxsi-0.55.3/naxsi_src/ext/libinjection/sqli_cli.c
Normal file
144
naxsi-0.55.3/naxsi_src/ext/libinjection/sqli_cli.c
Normal file
@@ -0,0 +1,144 @@
|
||||
/**
|
||||
* Copyright 2012, 2013 Nick Galbreath
|
||||
* nickg@client9.com
|
||||
* BSD License -- see COPYING.txt for details
|
||||
*
|
||||
* This is for testing against files in ../data/ *.txt
|
||||
* Reads from stdin or a list of files, and emits if a line
|
||||
* is a SQLi attack or not, and does basic statistics
|
||||
*
|
||||
*/
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "libinjection.h"
|
||||
#include "libinjection_sqli.h"
|
||||
|
||||
void print_string(stoken_t* t);
|
||||
void print_var(stoken_t* t);
|
||||
void print_token(stoken_t *t);
|
||||
|
||||
void print_string(stoken_t* t)
|
||||
{
|
||||
/* print opening quote */
|
||||
if (t->str_open != '\0') {
|
||||
printf("%c", t->str_open);
|
||||
}
|
||||
|
||||
/* print content */
|
||||
printf("%s", t->val);
|
||||
|
||||
/* print closing quote */
|
||||
if (t->str_close != '\0') {
|
||||
printf("%c", t->str_close);
|
||||
}
|
||||
}
|
||||
|
||||
void print_var(stoken_t* t)
|
||||
{
|
||||
if (t->count >= 1) {
|
||||
printf("%c", '@');
|
||||
}
|
||||
if (t->count == 2) {
|
||||
printf("%c", '@');
|
||||
}
|
||||
print_string(t);
|
||||
}
|
||||
|
||||
void print_token(stoken_t *t) {
|
||||
printf("%c ", t->type);
|
||||
switch (t->type) {
|
||||
case 's':
|
||||
print_string(t);
|
||||
break;
|
||||
case 'v':
|
||||
print_var(t);
|
||||
break;
|
||||
default:
|
||||
printf("%s", t->val);
|
||||
}
|
||||
printf("%s", "\n");
|
||||
}
|
||||
|
||||
int main(int argc, const char* argv[])
|
||||
{
|
||||
size_t slen;
|
||||
char* copy;
|
||||
|
||||
int flags = 0;
|
||||
int fold = 0;
|
||||
int detect = 0;
|
||||
|
||||
int i;
|
||||
int count;
|
||||
int offset = 1;
|
||||
int issqli;
|
||||
|
||||
sfilter sf;
|
||||
|
||||
if (argc < 2) {
|
||||
fprintf(stderr, "need more args\n");
|
||||
return 1;
|
||||
}
|
||||
while (1) {
|
||||
if (strcmp(argv[offset], "-m") == 0) {
|
||||
flags |= FLAG_SQL_MYSQL;
|
||||
offset += 1;
|
||||
}
|
||||
else if (strcmp(argv[offset], "-f") == 0 || strcmp(argv[offset], "--fold") == 0) {
|
||||
fold = 1;
|
||||
offset += 1;
|
||||
} else if (strcmp(argv[offset], "-d") == 0 || strcmp(argv[offset], "--detect") == 0) {
|
||||
detect = 1;
|
||||
offset += 1;
|
||||
} else if (strcmp(argv[offset], "-ca") == 0) {
|
||||
flags |= FLAG_SQL_ANSI;
|
||||
offset += 1;
|
||||
} else if (strcmp(argv[offset], "-cm") == 0) {
|
||||
flags |= FLAG_SQL_MYSQL;
|
||||
offset += 1;
|
||||
} else if (strcmp(argv[offset], "-q0") == 0) {
|
||||
flags |= FLAG_QUOTE_NONE;
|
||||
offset += 1;
|
||||
} else if (strcmp(argv[offset], "-q1") == 0) {
|
||||
flags |= FLAG_QUOTE_SINGLE;
|
||||
offset += 1;
|
||||
} else if (strcmp(argv[offset], "-q2") == 0) {
|
||||
flags |= FLAG_QUOTE_DOUBLE;
|
||||
offset += 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* ATTENTION: argv is a C-string, null terminated. We copy this
|
||||
* to it's own location, WITHOUT null byte. This way, valgrind
|
||||
* can see if we run past the buffer.
|
||||
*/
|
||||
|
||||
slen = strlen(argv[offset]);
|
||||
copy = (char* ) malloc(slen);
|
||||
memcpy(copy, argv[offset], slen);
|
||||
libinjection_sqli_init(&sf, copy, slen, flags);
|
||||
|
||||
if (detect == 1) {
|
||||
issqli = libinjection_is_sqli(&sf);
|
||||
if (issqli) {
|
||||
printf("%s\n", sf.fingerprint);
|
||||
}
|
||||
} else if (fold == 1) {
|
||||
count = libinjection_sqli_fold(&sf);
|
||||
for (i = 0; i < count; ++i) {
|
||||
print_token(&(sf.tokenvec[i]));
|
||||
}
|
||||
} else {
|
||||
while (libinjection_sqli_tokenize(&sf)) {
|
||||
print_token(sf.current);
|
||||
}
|
||||
}
|
||||
|
||||
free(copy);
|
||||
|
||||
return 0;
|
||||
}
|
||||
68
naxsi-0.55.3/naxsi_src/ext/libinjection/test_speed_sqli.c
Normal file
68
naxsi-0.55.3/naxsi_src/ext/libinjection/test_speed_sqli.c
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* A not very good test for performance. This is mostly useful in
|
||||
* testing performance -regressions-
|
||||
*
|
||||
*/
|
||||
#include <time.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "libinjection.h"
|
||||
#include "libinjection_sqli.h"
|
||||
int testIsSQL(void);
|
||||
|
||||
int testIsSQL(void)
|
||||
{
|
||||
const char* const s[] = {
|
||||
"123 LIKE -1234.5678E+2;",
|
||||
"APPLE 19.123 'FOO' \"BAR\"",
|
||||
"/* BAR */ UNION ALL SELECT (2,3,4)",
|
||||
"1 || COS(+0X04) --FOOBAR",
|
||||
"dog apple @cat banana bar",
|
||||
"dog apple cat \"banana \'bar",
|
||||
"102 TABLE CLOTH",
|
||||
"(1001-'1') union select 1,2,3,4 from credit_cards",
|
||||
NULL
|
||||
};
|
||||
const int imax = 1000000;
|
||||
int i, j;
|
||||
size_t slen;
|
||||
sfilter sf;
|
||||
clock_t t0,t1;
|
||||
double total;
|
||||
int tps;
|
||||
|
||||
t0 = clock();
|
||||
for (i = imax, j=0; i != 0; --i, ++j) {
|
||||
if (s[j] == NULL) {
|
||||
j = 0;
|
||||
}
|
||||
|
||||
slen = strlen(s[j]);
|
||||
libinjection_sqli_init(&sf, s[j], slen, FLAG_QUOTE_NONE | FLAG_SQL_ANSI);
|
||||
libinjection_is_sqli(&sf);
|
||||
}
|
||||
|
||||
t1 = clock();
|
||||
total = (double) (t1 - t0) / (double) CLOCKS_PER_SEC;
|
||||
tps = (int)((double) imax / total);
|
||||
return tps;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
const int mintps = 450000;
|
||||
int tps = testIsSQL();
|
||||
|
||||
printf("\nTPS : %d\n\n", tps);
|
||||
|
||||
if (tps < mintps) {
|
||||
printf("FAIL: %d < %d\n", tps, mintps);
|
||||
/* FAIL */
|
||||
return 1;
|
||||
} else {
|
||||
printf("OK: %d > %d\n", tps, mintps);
|
||||
/* OK */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
84
naxsi-0.55.3/naxsi_src/ext/libinjection/test_speed_xss.c
Normal file
84
naxsi-0.55.3/naxsi_src/ext/libinjection/test_speed_xss.c
Normal file
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* A not very good test for performance. This is mostly useful in
|
||||
* testing performance -regressions-
|
||||
*
|
||||
*/
|
||||
#include <time.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "libinjection.h"
|
||||
int testIsSQL(void);
|
||||
|
||||
int testIsSQL(void)
|
||||
{
|
||||
const char* const s[] = {
|
||||
"<script>alert(1);</script>",
|
||||
"><script>alert(1);</script>"
|
||||
"x ><script>alert(1);</script>",
|
||||
"' ><script>alert(1);</script>",
|
||||
"\"><script>alert(1);</script>",
|
||||
"red;</style><script>alert(1);</script>",
|
||||
"red;}</style><script>alert(1);</script>",
|
||||
"red;\"/><script>alert(1);</script>",
|
||||
"');}</style><script>alert(1);</script>",
|
||||
"onerror=alert(1)>",
|
||||
"x onerror=alert(1);>",
|
||||
"x' onerror=alert(1);>",
|
||||
"x\" onerror=alert(1);>",
|
||||
"<a href=\"javascript:alert(1)\">",
|
||||
"<a href='javascript:alert(1)'>",
|
||||
"<a href=javascript:alert(1)>",
|
||||
"<a href = javascript:alert(1); >",
|
||||
"<a href=\" javascript:alert(1);\" >",
|
||||
"<a href=\"JAVASCRIPT:alert(1);\" >",
|
||||
"123 LIKE -1234.5678E+2;",
|
||||
"APPLE 19.123 'FOO' \"BAR\"",
|
||||
"/* BAR */ UNION ALL SELECT (2,3,4)",
|
||||
"1 || COS(+0X04) --FOOBAR",
|
||||
"dog apple @cat banana bar",
|
||||
"dog apple cat \"banana \'bar",
|
||||
"102 TABLE CLOTH",
|
||||
"(1001-'1') union select 1,2,3,4 from credit_cards",
|
||||
NULL
|
||||
};
|
||||
const int imax = 1000000;
|
||||
int i, j;
|
||||
size_t slen;
|
||||
clock_t t0,t1;
|
||||
double total;
|
||||
int tps;
|
||||
|
||||
t0 = clock();
|
||||
for (i = imax, j=0; i != 0; --i, ++j) {
|
||||
if (s[j] == NULL) {
|
||||
j = 0;
|
||||
}
|
||||
|
||||
slen = strlen(s[j]);
|
||||
libinjection_xss(s[j], slen);
|
||||
}
|
||||
|
||||
t1 = clock();
|
||||
total = (double) (t1 - t0) / (double) CLOCKS_PER_SEC;
|
||||
tps = (int)((double) imax / total);
|
||||
return tps;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
const int mintps = 500000;
|
||||
int tps = testIsSQL();
|
||||
|
||||
printf("\nTPS : %d\n\n", tps);
|
||||
|
||||
if (tps < 500000) {
|
||||
printf("FAIL: %d < %d\n", tps, mintps);
|
||||
/* FAIL */
|
||||
return 1;
|
||||
} else {
|
||||
printf("OK: %d > %d\n", tps, mintps);
|
||||
/* OK */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
312
naxsi-0.55.3/naxsi_src/ext/libinjection/testdriver.c
Normal file
312
naxsi-0.55.3/naxsi_src/ext/libinjection/testdriver.c
Normal file
@@ -0,0 +1,312 @@
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <glob.h>
|
||||
#include "libinjection.h"
|
||||
#include "libinjection_sqli.h"
|
||||
#include "libinjection_html5.h"
|
||||
#include "libinjection_xss.h"
|
||||
|
||||
static char g_test[8096];
|
||||
static char g_input[8096];
|
||||
static char g_expected[8096];
|
||||
|
||||
size_t modp_rtrim(char* str, size_t len);
|
||||
size_t print_string(char* buf, size_t len, stoken_t* t);
|
||||
size_t print_var(char* buf, size_t len, stoken_t* t);
|
||||
size_t print_token(char* buf, size_t len, stoken_t *t);
|
||||
int read_file(const char* fname, int flags, int testtype);
|
||||
const char* h5_type_to_string(enum html5_type x);
|
||||
size_t print_html5_token(char* buf, size_t len, h5_state_t* hs);
|
||||
|
||||
size_t modp_rtrim(char* str, size_t len)
|
||||
{
|
||||
while (len) {
|
||||
char c = str[len -1];
|
||||
if (c == ' ' || c == '\n' || c == '\t' || c == '\r') {
|
||||
str[len -1] = '\0';
|
||||
len -= 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
size_t print_string(char* buf, size_t len, stoken_t* t)
|
||||
{
|
||||
int slen = 0;
|
||||
|
||||
/* print opening quote */
|
||||
if (t->str_open != '\0') {
|
||||
slen = sprintf(buf + len, "%c", t->str_open);
|
||||
assert(slen >= 0);
|
||||
len += (size_t) slen;
|
||||
}
|
||||
|
||||
/* print content */
|
||||
slen = sprintf(buf + len, "%s", t->val);
|
||||
assert(slen >= 0);
|
||||
len += (size_t) slen;
|
||||
|
||||
/* print closing quote */
|
||||
if (t->str_close != '\0') {
|
||||
slen = sprintf(buf + len, "%c", t->str_close);
|
||||
assert(slen >= 0);
|
||||
len += (size_t) slen;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
size_t print_var(char* buf, size_t len, stoken_t* t)
|
||||
{
|
||||
int slen = 0;
|
||||
if (t->count >= 1) {
|
||||
slen = sprintf(buf + len, "%c", '@');
|
||||
assert(slen >= 0);
|
||||
len += (size_t) slen;
|
||||
}
|
||||
if (t->count == 2) {
|
||||
slen = sprintf(buf + len, "%c", '@');
|
||||
assert(slen >= 0);
|
||||
len += (size_t) slen;
|
||||
}
|
||||
return print_string(buf, len, t);
|
||||
}
|
||||
|
||||
const char* h5_type_to_string(enum html5_type x)
|
||||
{
|
||||
switch (x) {
|
||||
case DATA_TEXT: return "DATA_TEXT";
|
||||
case TAG_NAME_OPEN: return "TAG_NAME_OPEN";
|
||||
case TAG_NAME_CLOSE: return "TAG_NAME_CLOSE";
|
||||
case TAG_NAME_SELFCLOSE: return "TAG_NAME_SELFCLOSE";
|
||||
case TAG_DATA: return "TAG_DATA";
|
||||
case TAG_CLOSE: return "TAG_CLOSE";
|
||||
case ATTR_NAME: return "ATTR_NAME";
|
||||
case ATTR_VALUE: return "ATTR_VALUE";
|
||||
case TAG_COMMENT: return "TAG_COMMENT";
|
||||
case DOCTYPE: return "DOCTYPE";
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
size_t print_html5_token(char* buf, size_t len, h5_state_t* hs)
|
||||
{
|
||||
int slen;
|
||||
char* tmp = (char*) malloc(hs->token_len + 1);
|
||||
memcpy(tmp, hs->token_start, hs->token_len);
|
||||
/* TODO.. encode to be printable */
|
||||
tmp[hs->token_len] = '\0';
|
||||
|
||||
slen = sprintf(buf + len, "%s,%d,%s\n",
|
||||
h5_type_to_string(hs->token_type),
|
||||
(int) hs->token_len,
|
||||
tmp);
|
||||
len += (size_t) slen;
|
||||
free(tmp);
|
||||
return len;
|
||||
}
|
||||
|
||||
size_t print_token(char* buf, size_t len, stoken_t *t)
|
||||
{
|
||||
int slen;
|
||||
|
||||
slen = sprintf(buf + len, "%c ", t->type);
|
||||
assert(slen >= 0);
|
||||
len += (size_t) slen;
|
||||
switch (t->type) {
|
||||
case 's':
|
||||
len = print_string(buf, len, t);
|
||||
break;
|
||||
case 'v':
|
||||
len = print_var(buf, len, t);
|
||||
break;
|
||||
default:
|
||||
slen = sprintf(buf + len, "%s", t->val);
|
||||
assert(slen >= 0);
|
||||
len += (size_t) slen;
|
||||
}
|
||||
slen = sprintf(buf + len, "%c", '\n');
|
||||
assert(slen >= 0);
|
||||
len += (size_t) slen;
|
||||
return len;
|
||||
}
|
||||
|
||||
int read_file(const char* fname, int flags, int testtype)
|
||||
{
|
||||
int count = 0;
|
||||
FILE *fp = NULL;
|
||||
char linebuf[8192];
|
||||
char g_actual[8192];
|
||||
char* bufptr = NULL;
|
||||
size_t slen;
|
||||
char* copy;
|
||||
sfilter sf;
|
||||
int ok = 1;
|
||||
int num_tokens;
|
||||
int issqli;
|
||||
int i;
|
||||
|
||||
g_test[0] = '\0';
|
||||
g_input[0] = '\0';
|
||||
g_expected[0] = '\0';
|
||||
|
||||
fp = fopen(fname, "r");
|
||||
while(fgets(linebuf, sizeof(linebuf), fp) != NULL) {
|
||||
if (count == 0 && strcmp(linebuf, "--TEST--\n") == 0) {
|
||||
bufptr = g_test;
|
||||
count = 1;
|
||||
} else if (count == 1 && strcmp(linebuf, "--INPUT--\n") == 0) {
|
||||
bufptr = g_input;
|
||||
count = 2;
|
||||
} else if (count == 2 && strcmp(linebuf, "--EXPECTED--\n") == 0) {
|
||||
bufptr = g_expected;
|
||||
count = 3;
|
||||
} else {
|
||||
assert(bufptr != NULL);
|
||||
strcat(bufptr, linebuf);
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
if (count != 3) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
g_expected[modp_rtrim(g_expected, strlen(g_expected))] = '\0';
|
||||
g_input[modp_rtrim(g_input, strlen(g_input))] = '\0';
|
||||
|
||||
|
||||
slen = strlen(g_input);
|
||||
copy = (char* ) malloc(slen);
|
||||
memcpy(copy, g_input, slen);
|
||||
|
||||
g_actual[0] = '\0';
|
||||
if (testtype == 0) {
|
||||
/*
|
||||
* print sqli tokenization only
|
||||
*/
|
||||
libinjection_sqli_init(&sf, copy, slen, flags);
|
||||
libinjection_sqli_callback(&sf, NULL, NULL);
|
||||
slen =0;
|
||||
while (libinjection_sqli_tokenize(&sf) == 1) {
|
||||
slen = print_token(g_actual, slen, sf.current);
|
||||
}
|
||||
} else if (testtype == 1) {
|
||||
/*
|
||||
* testing tokenization + folding
|
||||
*/
|
||||
libinjection_sqli_init(&sf, copy, slen, flags);
|
||||
libinjection_sqli_callback(&sf, NULL, NULL);
|
||||
slen =0;
|
||||
num_tokens = libinjection_sqli_fold(&sf);
|
||||
for (i = 0; i < num_tokens; ++i) {
|
||||
slen = print_token(g_actual, slen, libinjection_sqli_get_token(&sf, i));
|
||||
}
|
||||
} else if (testtype == 2) {
|
||||
/**
|
||||
* test sqli detection
|
||||
*/
|
||||
char buf[100];
|
||||
issqli = libinjection_sqli(copy, slen, buf);
|
||||
if (issqli) {
|
||||
sprintf(g_actual, "%s", buf);
|
||||
}
|
||||
} else if (testtype == 3) {
|
||||
/*
|
||||
* test html5 tokenization only
|
||||
*/
|
||||
|
||||
h5_state_t hs;
|
||||
libinjection_h5_init(&hs, copy, slen, DATA_STATE);
|
||||
slen = 0;
|
||||
while (libinjection_h5_next(&hs)) {
|
||||
slen = print_html5_token(g_actual, slen, &hs);
|
||||
}
|
||||
} else if (testtype == 4) {
|
||||
/*
|
||||
* test XSS detection
|
||||
*/
|
||||
sprintf(g_actual, "%d", libinjection_xss(copy, slen));
|
||||
} else {
|
||||
fprintf(stderr, "Got stange testtype value of %d\n", testtype);
|
||||
assert(0);
|
||||
}
|
||||
|
||||
g_actual[modp_rtrim(g_actual, strlen(g_actual))] = '\0';
|
||||
|
||||
if (strcmp(g_expected, g_actual) != 0) {
|
||||
printf("INPUT: \n%s\n==\n", g_input);
|
||||
printf("EXPECTED: \n%s\n==\n", g_expected);
|
||||
printf("GOT: \n%s\n==\n", g_actual);
|
||||
ok = 0;
|
||||
}
|
||||
|
||||
free(copy);
|
||||
return ok;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
int offset = 1;
|
||||
int i;
|
||||
int ok;
|
||||
int count = 0;
|
||||
int count_fail = 0;
|
||||
int flags = 0;
|
||||
int testtype = 0;
|
||||
int quiet = 0;
|
||||
|
||||
const char* fname;
|
||||
while (1) {
|
||||
if (strcmp(argv[offset], "-q") == 0 || strcmp(argv[offset], "--quiet") == 0) {
|
||||
quiet = 1;
|
||||
offset += 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
printf("%s\n", libinjection_version());
|
||||
|
||||
for (i = offset; i < argc; ++i) {
|
||||
fname = argv[i];
|
||||
count += 1;
|
||||
if (strstr(fname, "test-tokens-")) {
|
||||
flags = FLAG_QUOTE_NONE | FLAG_SQL_ANSI;
|
||||
testtype = 0;
|
||||
} else if (strstr(fname, "test-folding-")) {
|
||||
flags = FLAG_QUOTE_NONE | FLAG_SQL_ANSI;
|
||||
testtype = 1;
|
||||
} else if (strstr(fname, "test-sqli-")) {
|
||||
flags = FLAG_NONE;
|
||||
testtype = 2;
|
||||
} else if (strstr(fname, "test-html5-")) {
|
||||
flags = FLAG_NONE;
|
||||
testtype = 3;
|
||||
} else if (strstr(fname, "test-xss-")) {
|
||||
flags = FLAG_NONE;
|
||||
testtype = 4;
|
||||
} else {
|
||||
fprintf(stderr, "Unknown test type: %s, failing\n", fname);
|
||||
count_fail += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
ok = read_file(fname, flags, testtype);
|
||||
if (ok) {
|
||||
if (! quiet) {
|
||||
fprintf(stderr, "%s: ok\n", fname);
|
||||
}
|
||||
} else {
|
||||
count_fail += 1;
|
||||
if (! quiet) {
|
||||
fprintf(stderr, "%s: fail\n", fname);
|
||||
}
|
||||
}
|
||||
}
|
||||
return count > 0 && count_fail > 0;
|
||||
}
|
||||
617
naxsi-0.55.3/naxsi_src/naxsi.h
Normal file
617
naxsi-0.55.3/naxsi_src/naxsi.h
Normal file
@@ -0,0 +1,617 @@
|
||||
/*
|
||||
* NAXSI, a web application firewall for NGINX
|
||||
* Copyright (C) 2016, Thibault 'bui' Koechlin
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give
|
||||
* permission to link the code of portions of this program with the
|
||||
* OpenSSL library under certain conditions as described in each
|
||||
* individual source file, and distribute linked combinations
|
||||
* including the two.
|
||||
* You must obey the GNU General Public License in all respects
|
||||
* for all of the code used other than OpenSSL. If you modify
|
||||
* file(s) with this exception, you may extend this exception to your
|
||||
* version of the file(s), but you are not obligated to do so. If you
|
||||
* do not wish to do so, delete this exception statement from your
|
||||
* version. If you delete this exception statement from all source
|
||||
* files in the program, then also delete it here.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __FOO_H__
|
||||
#define __FOO_H__
|
||||
|
||||
#define NAXSI_VERSION "0.55.3"
|
||||
|
||||
#include <nginx.h>
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
#include <ngx_event.h>
|
||||
#include <ngx_md5.h>
|
||||
#include <ngx_http_core_module.h>
|
||||
#include <pcre.h>
|
||||
#include <ctype.h>
|
||||
#include "ext/libinjection/libinjection_sqli.h"
|
||||
#include "ext/libinjection/libinjection_xss.h"
|
||||
|
||||
|
||||
extern ngx_module_t ngx_http_naxsi_module;
|
||||
|
||||
/*
|
||||
** as the #ifdef #endif for debug are getting really messy ...
|
||||
** Bellow are all the possibles debug defines. To enable associated feature
|
||||
** debug, just set it to 1. Do not comment the actual define except if you
|
||||
** know all the associated debug calls are deleted.
|
||||
** The idea is that the compiler will optimize out the do { if (0) ... } while (0);
|
||||
*/
|
||||
|
||||
#define _naxsi_rawbody 0
|
||||
#define _debug_basestr_ruleset 0
|
||||
#define _debug_custom_score 0
|
||||
#define _debug_body_parse 0
|
||||
#define _debug_cfg_parse_one_rule 0
|
||||
#define _debug_zone 0
|
||||
#define _debug_extensive_log 0
|
||||
#define _debug_loc_conf 0
|
||||
#define _debug_main_conf 0
|
||||
#define _debug_mechanics 0
|
||||
#define _debug_json 0
|
||||
#define _debug_modifier 0
|
||||
#define _debug_payload_handler 0
|
||||
#define _debug_post_heavy 0
|
||||
#define _debug_rawbody 0
|
||||
#define _debug_readconf 0
|
||||
#define _debug_rx 0
|
||||
#define _debug_score 0
|
||||
#define _debug_spliturl_ruleset 0
|
||||
#define _debug_whitelist_compat 0
|
||||
#define _debug_whitelist 0
|
||||
#define _debug_whitelist_heavy 0
|
||||
#define _debug_whitelist_light 0
|
||||
#define wl_debug_rx 0
|
||||
|
||||
#ifndef __NAXSI_DEBUG
|
||||
#define __NAXSI_DEBUG
|
||||
#define NX_DEBUG(FEATURE, DEF, LOG, ST, ...) do { if (FEATURE) ngx_log_debug(DEF, LOG, ST, __VA_ARGS__); } while (0)
|
||||
#endif
|
||||
|
||||
#ifndef __NAXSI_LOG_DEBUG
|
||||
#define __NAXSI_LOG_DEBUG
|
||||
#define NX_LOG_DEBUG(FEATURE, DEF, LOG, ST, ...) do { if (FEATURE) ngx_conf_log_error(DEF, LOG, ST, __VA_ARGS__); } while (0)
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** Here is globally how the structures are organized :
|
||||
**
|
||||
** [[ngx_http_dummy_main_conf_t]] is the main structure for the module.
|
||||
** it contains the core rules and a set of ngx_http_dummy_loc_conf_t,
|
||||
** for each NGINX location.
|
||||
** ---
|
||||
** [[ngx_http_dummy_loc_conf_t]] is the main structure for any NGINX
|
||||
** locations, that is - a web site. It contains both pointers to the core
|
||||
** rules, as well as whitelists, scores, denied_url and all flags. all
|
||||
** the data of a nginx location is held into the loc_conf_t struct.
|
||||
** The sets of rules are actually containted into [[ngx_http_rule_t]] structs.
|
||||
** ---
|
||||
** [[ngx_http_rule_t]] structs are used to hold any info about a rule, as well
|
||||
** as whitelists. (whitelists is just a 'kind' of rule).
|
||||
**
|
||||
*/
|
||||
|
||||
/*
|
||||
** basic rule can have 4 (so far) kind of matching mechanisms
|
||||
** RX
|
||||
** STR
|
||||
** LIBINJ_XSS
|
||||
** LIBINJ_SQL
|
||||
*/
|
||||
enum DETECT_MECHANISM {
|
||||
NONE = -1,
|
||||
RX,
|
||||
STR,
|
||||
LIBINJ_XSS,
|
||||
LIBINJ_SQL
|
||||
};
|
||||
|
||||
enum MATCH_TYPE {
|
||||
URI_ONLY=0,
|
||||
NAME_ONLY,
|
||||
MIXED
|
||||
};
|
||||
|
||||
enum DUMMY_MATCH_ZONE {
|
||||
HEADERS=0,
|
||||
URL,
|
||||
ARGS,
|
||||
BODY,
|
||||
RAW_BODY,
|
||||
FILE_EXT,
|
||||
UNKNOWN
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
** struct used to store a specific match zone
|
||||
** in conf : MATCH_ZONE:[GET_VAR|HEADER|POST_VAR]:VAR_NAME:
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
/* match in [name] var of body */
|
||||
ngx_flag_t body_var:1;
|
||||
/* match in [name] var of headers */
|
||||
ngx_flag_t headers_var:1;
|
||||
/* match in [name] var of args */
|
||||
ngx_flag_t args_var:1;
|
||||
/* match on URL [name] */
|
||||
ngx_flag_t specific_url:1;
|
||||
ngx_str_t target;
|
||||
/* to be used for regexed match zones */
|
||||
ngx_regex_compile_t *target_rx;
|
||||
ngx_uint_t hash;
|
||||
|
||||
} ngx_http_custom_rule_location_t;
|
||||
|
||||
|
||||
/*
|
||||
** WhiteList Rules Definition :
|
||||
** A whitelist contains :
|
||||
** - an URI
|
||||
**
|
||||
** - one or several sets containing :
|
||||
** - an variable name ('foo') associated with a zone ($GET_VAR:foo)
|
||||
** - one or several rules id to whitelist
|
||||
*/
|
||||
|
||||
typedef struct
|
||||
{
|
||||
/* match in full body (POST DATA) */
|
||||
ngx_flag_t body:1;
|
||||
/* match in [name] var of body */
|
||||
ngx_flag_t body_var:1;
|
||||
/* match in all headers */
|
||||
ngx_flag_t headers:1;
|
||||
/* match in [name] var of headers */
|
||||
ngx_flag_t headers_var:1;
|
||||
/* match in URI */
|
||||
ngx_flag_t url:1;
|
||||
/* match in args (bla.php?<ARGS>) */
|
||||
ngx_flag_t args:1;
|
||||
/* match in [name] var of args */
|
||||
ngx_flag_t args_var:1;
|
||||
/* match on a global flag : weird_request, big_body etc. */
|
||||
ngx_flag_t flags:1;
|
||||
/* match on file upload extension */
|
||||
ngx_flag_t file_ext:1;
|
||||
/* set if defined "custom" match zone (GET_VAR/POST_VAR/...) */
|
||||
ngx_array_t *ids;
|
||||
ngx_str_t *name;
|
||||
} ngx_http_whitelist_location_t;
|
||||
|
||||
|
||||
/*
|
||||
** this struct is used to aggregate all whitelist
|
||||
** that point to the same URI or the same VARNAME
|
||||
** all the "subrules" will then be stored in the "whitelist_locations"
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
/*ngx_http_whitelist_location_t **/
|
||||
ngx_array_t *whitelist_locations;
|
||||
/* zone to wich the WL applies */
|
||||
enum DUMMY_MATCH_ZONE zone;
|
||||
/* if the "name" is only an url, specify it */
|
||||
int uri_only:1;
|
||||
/* does the rule targets the name
|
||||
instead of the content ?*/
|
||||
int target_name;
|
||||
|
||||
ngx_str_t *name;
|
||||
ngx_int_t hash;
|
||||
ngx_array_t *ids;
|
||||
} ngx_http_whitelist_rule_t;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* basic rule */
|
||||
typedef struct
|
||||
{
|
||||
ngx_str_t *str; // string
|
||||
ngx_regex_compile_t *rx; // or regex
|
||||
/*
|
||||
** basic rule can have 4 (so far) kind of matching mechanisms :
|
||||
** RX, STR, LIBINJ_XSS, LIBINJ_SQL
|
||||
*/
|
||||
enum DETECT_MECHANISM match_type;
|
||||
/* is the match zone a regex or a string (hashtable) */
|
||||
ngx_int_t rx_mz;
|
||||
/* ~~~~~ match zones ~~~~~~ */
|
||||
ngx_int_t zone;
|
||||
/* match in full body (POST DATA) */
|
||||
ngx_flag_t body:1;
|
||||
ngx_flag_t raw_body:1;
|
||||
ngx_flag_t body_var:1;
|
||||
/* match in all headers */
|
||||
ngx_flag_t headers:1;
|
||||
ngx_flag_t headers_var:1;
|
||||
/* match in URI */
|
||||
ngx_flag_t url:1;
|
||||
/* match in args (bla.php?<ARGS>) */
|
||||
ngx_flag_t args:1;
|
||||
ngx_flag_t args_var:1;
|
||||
/* match on flags (weird_uri, big_body etc. */
|
||||
ngx_flag_t flags:1;
|
||||
/* match on file upload extension */
|
||||
ngx_flag_t file_ext:1;
|
||||
/* set if defined "custom" match zone (GET_VAR/POST_VAR/...) */
|
||||
ngx_flag_t custom_location:1;
|
||||
ngx_int_t custom_location_only;
|
||||
/* does the rule targets variable name instead ? */
|
||||
ngx_int_t target_name;
|
||||
|
||||
/* custom location match zones list (GET_VAR/POST_VAR ...) */
|
||||
ngx_array_t *custom_locations;
|
||||
/* ~~~~~~~ specific flags ~~~~~~~~~ */
|
||||
ngx_flag_t negative:1;
|
||||
} ngx_http_basic_rule_t;
|
||||
|
||||
|
||||
|
||||
/* define for RULE TYPE in rule_t */
|
||||
#define BR 1
|
||||
|
||||
/* flags used for 'custom match rules', like $XSS > 7 */
|
||||
#define SUP 1
|
||||
#define SUP_OR_EQUAL 2
|
||||
#define INF 3
|
||||
#define INF_OR_EQUAL 4
|
||||
|
||||
/*
|
||||
** This struct is used to store custom scores at runtime.
|
||||
** ie : $XSS = 7
|
||||
** tag is the $XSS and sc_score is 7
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
ngx_str_t *sc_tag;
|
||||
ngx_int_t sc_score;
|
||||
ngx_flag_t block:1;
|
||||
ngx_flag_t allow:1;
|
||||
ngx_flag_t drop:1;
|
||||
ngx_flag_t log:1;
|
||||
} ngx_http_special_score_t;
|
||||
|
||||
/*
|
||||
** This one is very related to the previous one,
|
||||
** it's used to store a score rule comparison.
|
||||
** ie : $XSS > 7
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
ngx_str_t sc_tag;
|
||||
ngx_int_t sc_score;
|
||||
ngx_int_t cmp;
|
||||
ngx_flag_t block:1;
|
||||
ngx_flag_t allow:1;
|
||||
ngx_flag_t drop:1;
|
||||
ngx_flag_t log:1;
|
||||
} ngx_http_check_rule_t;
|
||||
|
||||
/* TOP level rule structure */
|
||||
typedef struct
|
||||
{
|
||||
/* type of the rule */
|
||||
ngx_int_t type;
|
||||
/* simply put a flag if it's a wlr,
|
||||
wl_id array will be used to store the whitelisted IDs */
|
||||
ngx_flag_t whitelist:1;
|
||||
ngx_array_t *wlid_array;
|
||||
/* "common" data for all rules */
|
||||
ngx_int_t rule_id;
|
||||
ngx_str_t *log_msg; // a specific log message
|
||||
ngx_int_t score; //also handles DENY and ALLOW
|
||||
|
||||
/* List of scores increased on rule match. */
|
||||
ngx_array_t *sscores;
|
||||
ngx_flag_t sc_block:1; //
|
||||
ngx_flag_t sc_allow:1; //
|
||||
// end of specific score tag stuff
|
||||
ngx_flag_t block:1;
|
||||
ngx_flag_t allow:1;
|
||||
ngx_flag_t drop:1;
|
||||
ngx_flag_t log:1;
|
||||
/* pointers on specific rule stuff */
|
||||
ngx_http_basic_rule_t *br;
|
||||
} ngx_http_rule_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
ngx_array_t *get_rules; /*ngx_http_rule_t*/
|
||||
ngx_array_t *body_rules;
|
||||
ngx_array_t *header_rules;
|
||||
ngx_array_t *generic_rules;
|
||||
ngx_array_t *raw_body_rules;
|
||||
|
||||
ngx_array_t *locations; /*ngx_http_dummy_loc_conf_t*/
|
||||
ngx_log_t *log;
|
||||
|
||||
} ngx_http_dummy_main_conf_t;
|
||||
|
||||
|
||||
/* TOP level configuration structure */
|
||||
typedef struct
|
||||
{
|
||||
/*
|
||||
** basicrule / mainrules, sorted by target zone
|
||||
*/
|
||||
ngx_array_t *get_rules;
|
||||
ngx_array_t *body_rules;
|
||||
ngx_array_t *raw_body_rules;
|
||||
ngx_array_t *header_rules;
|
||||
ngx_array_t *generic_rules;
|
||||
ngx_array_t *check_rules;
|
||||
/* raw array of whitelisted rules */
|
||||
ngx_array_t *whitelist_rules;
|
||||
/* raw array of transformed whitelists */
|
||||
ngx_array_t *tmp_wlr;
|
||||
/* raw array of regex-mz whitelists */
|
||||
ngx_array_t *rxmz_wlr;
|
||||
/* hash table of whitelisted URL rules */
|
||||
ngx_hash_t *wlr_url_hash;
|
||||
/* hash table of whitelisted ARGS rules */
|
||||
ngx_hash_t *wlr_args_hash;
|
||||
/* hash table of whitelisted BODY rules */
|
||||
ngx_hash_t *wlr_body_hash;
|
||||
/* hash table of whitelisted HEADERS rules */
|
||||
ngx_hash_t *wlr_headers_hash;
|
||||
/* rules that are globally disabled in one location */
|
||||
ngx_array_t *disabled_rules;
|
||||
/* counters for both processed requests and
|
||||
blocked requests, used in naxsi_fmt */
|
||||
size_t request_processed;
|
||||
size_t request_blocked;
|
||||
ngx_int_t error;
|
||||
ngx_array_t *persistant_data;
|
||||
ngx_flag_t extensive:1;
|
||||
ngx_flag_t learning:1;
|
||||
ngx_flag_t enabled:1;
|
||||
ngx_flag_t force_disabled:1;
|
||||
ngx_flag_t pushed:1;
|
||||
ngx_flag_t libinjection_sql_enabled:1;
|
||||
ngx_flag_t libinjection_xss_enabled:1;
|
||||
ngx_str_t *denied_url;
|
||||
/* precomputed hash for dynamic variable lookup,
|
||||
variable themselves are boolean */
|
||||
ngx_uint_t flag_enable_h;
|
||||
ngx_uint_t flag_learning_h;
|
||||
ngx_uint_t flag_post_action_h;
|
||||
ngx_uint_t flag_extensive_log_h;
|
||||
/* precomputed hash for
|
||||
libinjection dynamic flags */
|
||||
ngx_uint_t flag_libinjection_xss_h;
|
||||
ngx_uint_t flag_libinjection_sql_h;
|
||||
|
||||
} ngx_http_dummy_loc_conf_t;
|
||||
|
||||
|
||||
/*
|
||||
** used to store sets of matched rules during runtime
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
/* matched in [name] var of body */
|
||||
ngx_flag_t body_var:1;
|
||||
/* matched in [name] var of headers */
|
||||
ngx_flag_t headers_var:1;
|
||||
/* matched in [name] var of args */
|
||||
ngx_flag_t args_var:1;
|
||||
/* matched on URL */
|
||||
ngx_flag_t url:1;
|
||||
/* matched in filename [name] of args*/
|
||||
ngx_flag_t file_ext:1;
|
||||
/* matched within the 'NAME' */
|
||||
ngx_flag_t target_name:1;
|
||||
|
||||
ngx_str_t *name;
|
||||
ngx_http_rule_t *rule;
|
||||
} ngx_http_matched_rule_t;
|
||||
|
||||
/*
|
||||
** Context structure
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
ngx_array_t *special_scores;
|
||||
ngx_int_t score;
|
||||
/* blocking flags */
|
||||
ngx_flag_t log:1;
|
||||
ngx_flag_t block:1;
|
||||
ngx_flag_t allow:1;
|
||||
ngx_flag_t drop:1;
|
||||
/* state */
|
||||
ngx_flag_t wait_for_body:1;
|
||||
ngx_flag_t ready:1;
|
||||
ngx_flag_t over:1;
|
||||
/* matched rules */
|
||||
ngx_array_t *matched;
|
||||
/* runtime flags (modifiers) */
|
||||
ngx_flag_t learning:1;
|
||||
ngx_flag_t enabled:1;
|
||||
ngx_flag_t post_action:1;
|
||||
ngx_flag_t extensive_log:1;
|
||||
/* did libinjection sql/xss matched ? */
|
||||
ngx_flag_t libinjection_sql:1;
|
||||
ngx_flag_t libinjection_xss:1;
|
||||
} ngx_http_request_ctx_t;
|
||||
|
||||
/*
|
||||
** this structure is used only for json parsing.
|
||||
*/
|
||||
typedef struct ngx_http_nx_json_s {
|
||||
ngx_str_t json;
|
||||
u_char *src;
|
||||
ngx_int_t off, len;
|
||||
u_char c;
|
||||
int depth;
|
||||
ngx_http_request_t *r;
|
||||
ngx_http_request_ctx_t *ctx;
|
||||
ngx_str_t ckey;
|
||||
ngx_http_dummy_main_conf_t *main_cf;
|
||||
ngx_http_dummy_loc_conf_t *loc_cf;
|
||||
} ngx_json_t;
|
||||
|
||||
|
||||
|
||||
#define TOP_DENIED_URL_T "DeniedUrl"
|
||||
#define TOP_LEARNING_FLAG_T "LearningMode"
|
||||
#define TOP_ENABLED_FLAG_T "SecRulesEnabled"
|
||||
#define TOP_DISABLED_FLAG_T "SecRulesDisabled"
|
||||
#define TOP_CHECK_RULE_T "CheckRule"
|
||||
#define TOP_BASIC_RULE_T "BasicRule"
|
||||
#define TOP_MAIN_BASIC_RULE_T "MainRule"
|
||||
#define TOP_LIBINJECTION_SQL_T "LibInjectionSql"
|
||||
#define TOP_LIBINJECTION_XSS_T "LibInjectionXss"
|
||||
|
||||
/* nginx-style names */
|
||||
#define TOP_DENIED_URL_N "denied_url"
|
||||
#define TOP_LEARNING_FLAG_N "learning_mode"
|
||||
#define TOP_ENABLED_FLAG_N "rules_enabled"
|
||||
#define TOP_DISABLED_FLAG_N "rules_disabled"
|
||||
#define TOP_CHECK_RULE_N "check_rule"
|
||||
#define TOP_BASIC_RULE_N "basic_rule"
|
||||
#define TOP_MAIN_BASIC_RULE_N "main_rule"
|
||||
#define TOP_LIBINJECTION_SQL_N "libinjection_sql"
|
||||
#define TOP_LIBINJECTION_XSS_N "libinjection_xss"
|
||||
|
||||
|
||||
/*possible 'tokens' in rule */
|
||||
#define ID_T "id:"
|
||||
#define SCORE_T "s:"
|
||||
#define MSG_T "msg:"
|
||||
#define RX_T "rx:"
|
||||
#define STR_T "str:"
|
||||
#define MATCH_ZONE_T "mz:"
|
||||
#define WHITELIST_T "wl:"
|
||||
#define LIBINJ_XSS_T "d:libinj_xss"
|
||||
#define LIBINJ_SQL_T "d:libinj_sql"
|
||||
#define NEGATIVE_T "negative"
|
||||
|
||||
/*
|
||||
** name of hardcoded variables to
|
||||
** change behavior of naxsi at runtime
|
||||
*/
|
||||
#define RT_EXTENSIVE_LOG "naxsi_extensive_log"
|
||||
#define RT_ENABLE "naxsi_flag_enable"
|
||||
#define RT_LEARNING "naxsi_flag_learning"
|
||||
#define RT_POST_ACTION "naxsi_flag_post_action"
|
||||
#define RT_LIBINJECTION_SQL "naxsi_flag_libinjection_sql"
|
||||
#define RT_LIBINJECTION_XSS "naxsi_flag_libinjection_xss"
|
||||
|
||||
|
||||
/*
|
||||
** To avoid getting DoS'ed, define max depth
|
||||
** for JSON parser, as it is recursive
|
||||
*/
|
||||
#define JSON_MAX_DEPTH 10
|
||||
|
||||
|
||||
void *ngx_http_dummy_cfg_parse_one_rule(ngx_conf_t *cf,
|
||||
ngx_str_t *value,
|
||||
ngx_http_rule_t *rule,
|
||||
ngx_int_t nb_elem);
|
||||
char *strfaststr(unsigned char *haystack, unsigned int hl,
|
||||
unsigned char *needle, unsigned int nl);
|
||||
char *strnchr(const char *s, int c, int len);
|
||||
ngx_int_t ngx_http_dummy_create_hashtables_n(ngx_http_dummy_loc_conf_t *dlc,
|
||||
ngx_conf_t *cf);
|
||||
void ngx_http_dummy_data_parse(ngx_http_request_ctx_t *ctx,
|
||||
ngx_http_request_t *r);
|
||||
ngx_int_t ngx_http_output_forbidden_page(ngx_http_request_ctx_t *ctx,
|
||||
ngx_http_request_t *r);
|
||||
int nx_check_ids(ngx_int_t match_id, ngx_array_t *wl_ids);
|
||||
int naxsi_unescape(ngx_str_t *str);
|
||||
|
||||
void ngx_http_dummy_json_parse(ngx_http_request_ctx_t *ctx,
|
||||
ngx_http_request_t *r,
|
||||
u_char *src,
|
||||
u_int len);
|
||||
|
||||
void ngx_http_libinjection(ngx_pool_t *pool,
|
||||
ngx_str_t *name,
|
||||
ngx_str_t *value,
|
||||
ngx_http_request_ctx_t *ctx,
|
||||
ngx_http_request_t *req,
|
||||
enum DUMMY_MATCH_ZONE zone);
|
||||
/*
|
||||
** JSON parsing prototypes.
|
||||
*/
|
||||
ngx_int_t ngx_http_nx_json_forward(ngx_json_t *js) ;
|
||||
ngx_int_t ngx_http_nx_json_seek(ngx_json_t *js, unsigned char seek);
|
||||
ngx_int_t ngx_http_nx_json_quoted(ngx_json_t *js, ngx_str_t *ve);
|
||||
ngx_int_t ngx_http_nx_json_array(ngx_json_t *js);
|
||||
ngx_int_t ngx_http_nx_json_val(ngx_json_t *js);
|
||||
ngx_int_t ngx_http_nx_json_obj(ngx_json_t *js);
|
||||
|
||||
|
||||
/*
|
||||
** naxsi_runtime
|
||||
**
|
||||
*/
|
||||
|
||||
void ngx_http_dummy_update_current_ctx_status(ngx_http_request_ctx_t *ctx,
|
||||
ngx_http_dummy_loc_conf_t *cf,
|
||||
ngx_http_request_t *r);
|
||||
int ngx_http_process_basic_rule_buffer(ngx_str_t *str, ngx_http_rule_t *rl,
|
||||
ngx_int_t *match);
|
||||
void ngx_http_dummy_payload_handler(ngx_http_request_t *r);
|
||||
int ngx_http_basestr_ruleset_n(ngx_pool_t *pool,
|
||||
ngx_str_t *name,
|
||||
ngx_str_t *value,
|
||||
ngx_array_t *rules,
|
||||
ngx_http_request_t *req,
|
||||
ngx_http_request_ctx_t *ctx,
|
||||
enum DUMMY_MATCH_ZONE zone);
|
||||
void ngx_http_dummy_body_parse(ngx_http_request_ctx_t *ctx,
|
||||
ngx_http_request_t *r,
|
||||
ngx_http_dummy_loc_conf_t *cf,
|
||||
ngx_http_dummy_main_conf_t *main_cf);
|
||||
void naxsi_log_offending(ngx_str_t *name, ngx_str_t *val, ngx_http_request_t *req,
|
||||
ngx_http_rule_t *rule, enum DUMMY_MATCH_ZONE zone, ngx_int_t target_name);
|
||||
|
||||
int ngx_http_apply_rulematch_v_n(ngx_http_rule_t *r, ngx_http_request_ctx_t *ctx,
|
||||
ngx_http_request_t *req, ngx_str_t *name,
|
||||
ngx_str_t *value, enum DUMMY_MATCH_ZONE zone,
|
||||
ngx_int_t nb_match, ngx_int_t target_name);
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** externs for internal rules that requires it.
|
||||
*/
|
||||
extern ngx_http_rule_t *nx_int__libinject_sql;
|
||||
extern ngx_http_rule_t *nx_int__libinject_xss;
|
||||
|
||||
/*libinjection_xss wrapper not exported by libinject_xss.h.*/
|
||||
int libinjection_xss(const char* s, size_t len);
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
593
naxsi-0.55.3/naxsi_src/naxsi_config.c
Normal file
593
naxsi-0.55.3/naxsi_src/naxsi_config.c
Normal file
@@ -0,0 +1,593 @@
|
||||
/*
|
||||
* NAXSI, a web application firewall for NGINX
|
||||
* Copyright (C) 2016, Thibault 'bui' Koechlin
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give
|
||||
* permission to link the code of portions of this program with the
|
||||
* OpenSSL library under certain conditions as described in each
|
||||
* individual source file, and distribute linked combinations
|
||||
* including the two.
|
||||
* You must obey the GNU General Public License in all respects
|
||||
* for all of the code used other than OpenSSL. If you modify
|
||||
* file(s) with this exception, you may extend this exception to your
|
||||
* version of the file(s), but you are not obligated to do so. If you
|
||||
* do not wish to do so, delete this exception statement from your
|
||||
* version. If you delete this exception statement from all source
|
||||
* files in the program, then also delete it here.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "naxsi.h"
|
||||
/*
|
||||
** TOP LEVEL configuration parsing code
|
||||
*/
|
||||
/*
|
||||
** code to parse FLAGS and OPTIONS on each line.
|
||||
*/
|
||||
void *dummy_id(ngx_conf_t *r, ngx_str_t *tmp, ngx_http_rule_t *rule);
|
||||
void *dummy_score(ngx_conf_t *r, ngx_str_t *tmp, ngx_http_rule_t *rule);
|
||||
void *dummy_msg(ngx_conf_t *r, ngx_str_t *tmp, ngx_http_rule_t *rule);
|
||||
void *dummy_rx(ngx_conf_t *r, ngx_str_t *tmp, ngx_http_rule_t *rule);
|
||||
void *dummy_zone(ngx_conf_t *r, ngx_str_t *tmp, ngx_http_rule_t *rule);
|
||||
void *dummy_str(ngx_conf_t *r, ngx_str_t *tmp, ngx_http_rule_t *rule);
|
||||
void *dummy_negative(ngx_conf_t *r, ngx_str_t *tmp, ngx_http_rule_t *rule);
|
||||
void *dummy_libinj_xss(ngx_conf_t *r, ngx_str_t *tmp, ngx_http_rule_t *rule);
|
||||
void *dummy_libinj_sql(ngx_conf_t *r, ngx_str_t *tmp, ngx_http_rule_t *rule);
|
||||
void *dummy_whitelist(ngx_conf_t *r, ngx_str_t *tmp, ngx_http_rule_t *rule);
|
||||
/*
|
||||
** Structures related to the configuration parser
|
||||
*/
|
||||
typedef struct {
|
||||
char *prefix;
|
||||
void *(*pars)(ngx_conf_t *, ngx_str_t *, ngx_http_rule_t *);
|
||||
} ngx_http_dummy_parser_t;
|
||||
|
||||
|
||||
|
||||
static ngx_http_dummy_parser_t rule_parser[] = {
|
||||
{ID_T, dummy_id},
|
||||
{SCORE_T, dummy_score},
|
||||
{MSG_T, dummy_msg},
|
||||
{RX_T, dummy_rx},
|
||||
{STR_T, dummy_str},
|
||||
{LIBINJ_XSS_T, dummy_libinj_xss},
|
||||
{LIBINJ_SQL_T, dummy_libinj_sql},
|
||||
{MATCH_ZONE_T, dummy_zone},
|
||||
{NEGATIVE_T, dummy_negative},
|
||||
{WHITELIST_T, dummy_whitelist},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
|
||||
|
||||
void *
|
||||
dummy_negative(ngx_conf_t *r, ngx_str_t *tmp, ngx_http_rule_t *rule)
|
||||
{
|
||||
rule->br->negative = 1;
|
||||
return (NGX_CONF_OK);
|
||||
}
|
||||
|
||||
void *
|
||||
dummy_libinj_xss(ngx_conf_t *r, ngx_str_t *tmp, ngx_http_rule_t *rule)
|
||||
{
|
||||
rule->br->match_type = LIBINJ_XSS;
|
||||
return (NGX_CONF_OK);
|
||||
}
|
||||
|
||||
void *
|
||||
dummy_libinj_sql(ngx_conf_t *r, ngx_str_t *tmp, ngx_http_rule_t *rule)
|
||||
{
|
||||
rule->br->match_type = LIBINJ_SQL;
|
||||
return (NGX_CONF_OK);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void *
|
||||
dummy_score(ngx_conf_t *r, ngx_str_t *tmp, ngx_http_rule_t *rule)
|
||||
{
|
||||
int score, len;
|
||||
char *tmp_ptr, *tmp_end;
|
||||
ngx_http_special_score_t *sc;
|
||||
|
||||
rule->score = 0;
|
||||
rule->block = 0;
|
||||
rule->allow = 0;
|
||||
rule->drop = 0;
|
||||
tmp_ptr = (char *) (tmp->data + strlen(SCORE_T));
|
||||
NX_LOG_DEBUG(_debug_score, NGX_LOG_EMERG, r, 0,
|
||||
"XX-(debug) dummy score (%V)",
|
||||
tmp);
|
||||
/*allocate scores array*/
|
||||
if (!rule->sscores) {
|
||||
rule->sscores = ngx_array_create(r->pool, 1, sizeof(ngx_http_special_score_t));
|
||||
}
|
||||
|
||||
while (*tmp_ptr) {
|
||||
if (tmp_ptr[0] == '$') {
|
||||
NX_LOG_DEBUG(_debug_score, NGX_LOG_EMERG, r, 0,
|
||||
"XX-(debug) special scoring rule (%s)",
|
||||
tmp_ptr);
|
||||
tmp_end = strchr(tmp_ptr, ':');
|
||||
if (!tmp_end)
|
||||
return (NGX_CONF_ERROR);
|
||||
len = tmp_end - tmp_ptr;
|
||||
if (len <= 0)
|
||||
return (NGX_CONF_ERROR);
|
||||
sc = ngx_array_push(rule->sscores);
|
||||
if (!sc)
|
||||
return (NGX_CONF_ERROR);
|
||||
sc->sc_tag = ngx_pcalloc(r->pool, sizeof(ngx_str_t));
|
||||
if (!sc->sc_tag)
|
||||
return (NGX_CONF_ERROR);
|
||||
sc->sc_tag->data = ngx_pcalloc(r->pool, len+1);
|
||||
if (!sc->sc_tag->data)
|
||||
return (NGX_CONF_ERROR);
|
||||
//memset(rule->sc_tag->data, 0, len+1);
|
||||
memcpy(sc->sc_tag->data, tmp_ptr, len);
|
||||
sc->sc_tag->len = len;
|
||||
sc->sc_score = atoi(tmp_end+1);
|
||||
NX_LOG_DEBUG(_debug_score, NGX_LOG_EMERG, r, 0,
|
||||
"XX-(debug) special scoring (%V) => (%d)",
|
||||
sc->sc_tag, sc->sc_score);
|
||||
|
||||
/* move to end of score. */
|
||||
while ( /*don't overflow*/((unsigned int)((unsigned char *)tmp_ptr - tmp->data)) < tmp->len &&
|
||||
/*and seek for next score */ *tmp_ptr != ',')
|
||||
++tmp_ptr;
|
||||
}
|
||||
else if (tmp_ptr[0] == ',')
|
||||
++tmp_ptr;
|
||||
else if (!strcasecmp(tmp_ptr, "BLOCK")) {
|
||||
rule->block = 1;
|
||||
tmp_ptr += 5;
|
||||
}
|
||||
else if (!strcasecmp(tmp_ptr, "DROP")) {
|
||||
rule->drop = 1;
|
||||
tmp_ptr += 4;
|
||||
}
|
||||
else if (!strcasecmp(tmp_ptr, "ALLOW")) {
|
||||
rule->allow = 1;
|
||||
tmp_ptr += 5;
|
||||
}
|
||||
else if (!strcasecmp(tmp_ptr, "LOG")) {
|
||||
rule->log = 1;
|
||||
tmp_ptr += 3;
|
||||
}
|
||||
|
||||
//or maybe you just want to assign a score
|
||||
else if ( (tmp_ptr[0] >= '0' && tmp_ptr[0] <= '9') || tmp_ptr[0] == '-') {
|
||||
score = atoi((const char *)tmp->data+2);
|
||||
rule->score = score;
|
||||
break;
|
||||
}
|
||||
else
|
||||
return (NGX_CONF_ERROR);
|
||||
}
|
||||
#if defined(_debug_score) && _debug_score != 0
|
||||
unsigned int z;
|
||||
ngx_http_special_score_t *scr;
|
||||
scr = rule->sscores->elts;
|
||||
if (rule->sscores) {
|
||||
for (z = 0; z < rule->sscores->nelts; z++) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, r, 0,
|
||||
"XX-score n°%d special scoring (%V) => (%d)",
|
||||
z, scr[z].sc_tag, scr[z].sc_score);
|
||||
|
||||
}
|
||||
}
|
||||
else
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, r, 0,
|
||||
"XX-no custom scores for this rule.");
|
||||
#endif
|
||||
return (NGX_CONF_OK);
|
||||
}
|
||||
|
||||
void *
|
||||
dummy_zone(ngx_conf_t *r, ngx_str_t *tmp, ngx_http_rule_t *rule)
|
||||
{
|
||||
int tmp_len, has_zone=0;
|
||||
ngx_http_custom_rule_location_t *custom_rule;
|
||||
char *tmp_ptr, *tmp_end;
|
||||
|
||||
|
||||
if (!rule->br)
|
||||
return (NGX_CONF_ERROR);
|
||||
|
||||
tmp_ptr = (char *) tmp->data+strlen(MATCH_ZONE_T);
|
||||
while (*tmp_ptr) {
|
||||
|
||||
if (tmp_ptr[0] == '|')
|
||||
tmp_ptr++;
|
||||
/* match global zones */
|
||||
if (!strncmp(tmp_ptr, "RAW_BODY", strlen("RAW_BODY"))) {
|
||||
rule->br->raw_body = 1;
|
||||
tmp_ptr += strlen("RAW_BODY");
|
||||
has_zone = 1;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
if (!strncmp(tmp_ptr, "BODY", strlen("BODY"))) {
|
||||
rule->br->body = 1;
|
||||
tmp_ptr += strlen("BODY");
|
||||
has_zone = 1;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
if (!strncmp(tmp_ptr, "HEADERS", strlen("HEADERS"))) {
|
||||
rule->br->headers = 1;
|
||||
tmp_ptr += strlen("HEADERS");
|
||||
has_zone = 1;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
if (!strncmp(tmp_ptr, "URL", strlen("URL"))) {
|
||||
rule->br->url = 1;
|
||||
tmp_ptr += strlen("URL");
|
||||
has_zone = 1;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
if (!strncmp(tmp_ptr, "ARGS", strlen("ARGS"))) {
|
||||
rule->br->args = 1;
|
||||
tmp_ptr += strlen("ARGS");
|
||||
has_zone = 1;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
/* match against variable name*/
|
||||
if (!strncmp(tmp_ptr, "NAME", strlen("NAME"))) {
|
||||
rule->br->target_name = 1;
|
||||
tmp_ptr += strlen("NAME");
|
||||
has_zone = 1;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
/* for file_ext, just push'em in the body rules.
|
||||
when multipart parsing comes in, it'll tag the zone as
|
||||
FILE_EXT as the rule will be pushed in body rules it'll be
|
||||
checked !*/
|
||||
if (!strncmp(tmp_ptr, "FILE_EXT", strlen("FILE_EXT"))) {
|
||||
rule->br->file_ext = 1;
|
||||
rule->br->body = 1;
|
||||
tmp_ptr += strlen("FILE_EXT");
|
||||
has_zone = 1;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
/* custom match zones */
|
||||
#define MZ_GET_VAR_T "$ARGS_VAR:"
|
||||
#define MZ_HEADER_VAR_T "$HEADERS_VAR:"
|
||||
#define MZ_POST_VAR_T "$BODY_VAR:"
|
||||
#define MZ_SPECIFIC_URL_T "$URL:"
|
||||
//probably a custom zone
|
||||
if (tmp_ptr[0] == '$') {
|
||||
// tag as a custom_location rule.
|
||||
rule->br->custom_location = 1;
|
||||
if (!rule->br->custom_locations) {
|
||||
rule->br->custom_locations = ngx_array_create(r->pool, 1,
|
||||
sizeof(ngx_http_custom_rule_location_t));
|
||||
if (!rule->br->custom_locations)
|
||||
return (NGX_CONF_ERROR);
|
||||
}
|
||||
custom_rule = ngx_array_push(rule->br->custom_locations);
|
||||
if (!custom_rule)
|
||||
return (NGX_CONF_ERROR);
|
||||
memset(custom_rule, 0, sizeof(ngx_http_custom_rule_location_t));
|
||||
if (!strncmp(tmp_ptr, MZ_GET_VAR_T, strlen(MZ_GET_VAR_T))) {
|
||||
has_zone = 1;
|
||||
custom_rule->args_var = 1;
|
||||
rule->br->args_var = 1;
|
||||
tmp_ptr += strlen(MZ_GET_VAR_T);
|
||||
}
|
||||
else if (!strncmp(tmp_ptr, MZ_POST_VAR_T,
|
||||
strlen(MZ_POST_VAR_T))) {
|
||||
has_zone = 1;
|
||||
custom_rule->body_var = 1;
|
||||
rule->br->body_var = 1;
|
||||
tmp_ptr += strlen(MZ_POST_VAR_T);
|
||||
}
|
||||
else if (!strncmp(tmp_ptr, MZ_HEADER_VAR_T,
|
||||
strlen(MZ_HEADER_VAR_T))) {
|
||||
has_zone = 1;
|
||||
custom_rule->headers_var = 1;
|
||||
rule->br->headers_var = 1;
|
||||
tmp_ptr += strlen(MZ_HEADER_VAR_T);
|
||||
}
|
||||
else if (!strncmp(tmp_ptr, MZ_SPECIFIC_URL_T,
|
||||
strlen(MZ_SPECIFIC_URL_T))) {
|
||||
custom_rule->specific_url = 1;
|
||||
tmp_ptr += strlen(MZ_SPECIFIC_URL_T);
|
||||
}
|
||||
else
|
||||
/* add support for regex-style match zones.
|
||||
** this whole function should be rewritten as it's getting
|
||||
** messy as hell
|
||||
*/
|
||||
#define MZ_GET_VAR_X "$ARGS_VAR_X:"
|
||||
#define MZ_HEADER_VAR_X "$HEADERS_VAR_X:"
|
||||
#define MZ_POST_VAR_X "$BODY_VAR_X:"
|
||||
#define MZ_SPECIFIC_URL_X "$URL_X:"
|
||||
/*
|
||||
** if the rule is a negative rule (has an ID, not a WL field)
|
||||
** we need to pre-compile the regex for runtime.
|
||||
** Don't do it for whitelists, as its done in a separate manner.
|
||||
*/
|
||||
if (!strncmp(tmp_ptr, MZ_GET_VAR_X, strlen(MZ_GET_VAR_X))) {
|
||||
has_zone = 1;
|
||||
custom_rule->args_var = 1;
|
||||
rule->br->args_var = 1;
|
||||
rule->br->rx_mz = 1;
|
||||
tmp_ptr += strlen(MZ_GET_VAR_X);
|
||||
}
|
||||
else if (!strncmp(tmp_ptr, MZ_POST_VAR_X,
|
||||
strlen(MZ_POST_VAR_X))) {
|
||||
has_zone = 1;
|
||||
rule->br->rx_mz = 1;
|
||||
custom_rule->body_var = 1;
|
||||
rule->br->body_var = 1;
|
||||
tmp_ptr += strlen(MZ_POST_VAR_X);
|
||||
}
|
||||
else if (!strncmp(tmp_ptr, MZ_HEADER_VAR_X,
|
||||
strlen(MZ_HEADER_VAR_X))) {
|
||||
has_zone = 1;
|
||||
custom_rule->headers_var = 1;
|
||||
rule->br->headers_var = 1;
|
||||
rule->br->rx_mz = 1;
|
||||
tmp_ptr += strlen(MZ_HEADER_VAR_X);
|
||||
}
|
||||
else if (!strncmp(tmp_ptr, MZ_SPECIFIC_URL_X,
|
||||
strlen(MZ_SPECIFIC_URL_X))) {
|
||||
custom_rule->specific_url = 1;
|
||||
rule->br->rx_mz = 1;
|
||||
tmp_ptr += strlen(MZ_SPECIFIC_URL_X);
|
||||
}
|
||||
else
|
||||
return (NGX_CONF_ERROR);
|
||||
|
||||
/* else
|
||||
return (NGX_CONF_ERROR);*/
|
||||
tmp_end = strchr((const char *) tmp_ptr, '|');
|
||||
if (!tmp_end)
|
||||
tmp_end = tmp_ptr + strlen(tmp_ptr);
|
||||
tmp_len = tmp_end - tmp_ptr;
|
||||
if (tmp_len <= 0)
|
||||
return (NGX_CONF_ERROR);
|
||||
custom_rule->target.data = ngx_pcalloc(r->pool, tmp_len+1);
|
||||
if (!custom_rule->target.data)
|
||||
return (NGX_CONF_ERROR);
|
||||
custom_rule->target.len = tmp_len;
|
||||
memcpy(custom_rule->target.data, tmp_ptr, tmp_len);
|
||||
/*
|
||||
** pre-compile regex !
|
||||
*/
|
||||
if (rule->br->rx_mz == 1) {
|
||||
|
||||
custom_rule->target_rx = ngx_pcalloc(r->pool, sizeof(ngx_regex_compile_t));
|
||||
if (!custom_rule->target_rx)
|
||||
return (NGX_CONF_ERROR);
|
||||
custom_rule->target_rx->options = PCRE_CASELESS|PCRE_MULTILINE;
|
||||
custom_rule->target_rx->pattern = custom_rule->target;
|
||||
custom_rule->target_rx->pool = r->pool;
|
||||
custom_rule->target_rx->err.len = 0;
|
||||
custom_rule->target_rx->err.data = NULL;
|
||||
|
||||
if (ngx_regex_compile(custom_rule->target_rx) != NGX_OK) {
|
||||
NX_LOG_DEBUG(_debug_rx, NGX_LOG_EMERG, r, 0, "XX-FAILED RX:%V",
|
||||
custom_rule->target);
|
||||
return (NGX_CONF_ERROR);
|
||||
}
|
||||
}
|
||||
custom_rule->hash = ngx_hash_key_lc(custom_rule->target.data,
|
||||
custom_rule->target.len);
|
||||
|
||||
NX_LOG_DEBUG(_debug_zone, NGX_LOG_EMERG, r, 0, "XX- ZONE:[%V]",
|
||||
&(custom_rule->target));
|
||||
tmp_ptr += tmp_len;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
return (NGX_CONF_ERROR);
|
||||
}
|
||||
/*
|
||||
** ensure the match-zone actually returns a zone :)
|
||||
*/
|
||||
if (has_zone == 0) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, r, 0,
|
||||
"matchzone doesn't target an actual zone.");
|
||||
return (NGX_CONF_ERROR);
|
||||
}
|
||||
|
||||
return (NGX_CONF_OK);
|
||||
}
|
||||
|
||||
void *
|
||||
dummy_id(ngx_conf_t *r, ngx_str_t *tmp, ngx_http_rule_t *rule)
|
||||
{
|
||||
rule->rule_id = atoi((const char *) tmp->data+strlen(ID_T));
|
||||
return (NGX_CONF_OK);
|
||||
}
|
||||
|
||||
void *
|
||||
dummy_str(ngx_conf_t *r, ngx_str_t *tmp, ngx_http_rule_t *rule)
|
||||
{
|
||||
ngx_str_t *str;
|
||||
uint i;
|
||||
|
||||
if (!rule->br)
|
||||
return (NGX_CONF_ERROR);
|
||||
rule->br->match_type = STR;
|
||||
str = ngx_pcalloc(r->pool, sizeof(ngx_str_t));
|
||||
if (!str)
|
||||
return (NGX_CONF_ERROR);
|
||||
str->data = tmp->data + strlen(STR_T);
|
||||
str->len = tmp->len - strlen(STR_T);
|
||||
for (i = 0; i < str->len; i++)
|
||||
str->data[i] = tolower(str->data[i]);
|
||||
rule->br->str = str;
|
||||
return (NGX_CONF_OK);
|
||||
}
|
||||
|
||||
void *
|
||||
dummy_msg(ngx_conf_t *r, ngx_str_t *tmp, ngx_http_rule_t *rule)
|
||||
{
|
||||
ngx_str_t *str;
|
||||
|
||||
if (!rule->br)
|
||||
return (NGX_CONF_ERROR);
|
||||
str = ngx_pcalloc(r->pool, sizeof(ngx_str_t));
|
||||
if (!str)
|
||||
return (NGX_CONF_ERROR);
|
||||
str->data = tmp->data + strlen(STR_T);
|
||||
str->len = tmp->len - strlen(STR_T);
|
||||
rule->log_msg = str;
|
||||
return (NGX_CONF_OK);
|
||||
}
|
||||
|
||||
void *
|
||||
dummy_whitelist(ngx_conf_t *r, ngx_str_t *tmp, ngx_http_rule_t *rule)
|
||||
{
|
||||
|
||||
ngx_array_t *wl_ar;
|
||||
unsigned int i, ct;
|
||||
ngx_int_t *id;
|
||||
ngx_str_t str;
|
||||
|
||||
str.data = tmp->data + strlen(WHITELIST_T);
|
||||
str.len = tmp->len - strlen(WHITELIST_T);
|
||||
for (ct = 1, i = 0; i < str.len; i++)
|
||||
if (str.data[i] == ',')
|
||||
ct++;
|
||||
wl_ar = ngx_array_create(r->pool, ct, sizeof(ngx_int_t));
|
||||
if (!wl_ar)
|
||||
return (NGX_CONF_ERROR);
|
||||
NX_LOG_DEBUG(_debug_whitelist, NGX_LOG_EMERG, r, 0, "XX- allocated %d elems for WL", ct);
|
||||
for (i = 0; i < str.len; i++) {
|
||||
if (i == 0 || str.data[i-1] == ',') {
|
||||
id = (ngx_int_t *) ngx_array_push(wl_ar);
|
||||
if (!id)
|
||||
return (NGX_CONF_ERROR);
|
||||
*id = (ngx_int_t) atoi((const char *)str.data+i);
|
||||
}
|
||||
}
|
||||
rule->wlid_array = wl_ar;
|
||||
return (NGX_CONF_OK);
|
||||
}
|
||||
|
||||
void *
|
||||
dummy_rx(ngx_conf_t *r, ngx_str_t *tmp, ngx_http_rule_t *rule)
|
||||
{
|
||||
ngx_regex_compile_t *rgc;
|
||||
ngx_str_t ha;
|
||||
|
||||
|
||||
if (!rule->br)
|
||||
return (NGX_CONF_ERROR);
|
||||
rule->br->match_type = RX;
|
||||
//just prepare a string to hold the directive without 'rx:'
|
||||
ha.data = tmp->data+strlen(RX_T);
|
||||
ha.len = tmp->len-strlen(RX_T);
|
||||
rgc = ngx_pcalloc(r->pool, sizeof(ngx_regex_compile_t));
|
||||
if (!rgc)
|
||||
return (NGX_CONF_ERROR);
|
||||
rgc->options = PCRE_CASELESS|PCRE_MULTILINE;
|
||||
rgc->pattern = ha;
|
||||
rgc->pool = r->pool;
|
||||
rgc->err.len = 0;
|
||||
rgc->err.data = NULL;
|
||||
|
||||
if (ngx_regex_compile(rgc) != NGX_OK) {
|
||||
NX_LOG_DEBUG(_debug_rx, NGX_LOG_EMERG, r, 0, "XX-FAILED RX:%V",
|
||||
tmp);
|
||||
return (NGX_CONF_ERROR);
|
||||
}
|
||||
rule->br->rx = rgc;
|
||||
NX_LOG_DEBUG(_debug_rx, NGX_LOG_EMERG, r, 0, "XX- RX:[%V]",
|
||||
&(rule->br->rx->pattern));
|
||||
return (NGX_CONF_OK);
|
||||
}
|
||||
|
||||
/* Parse one rule line */
|
||||
/*
|
||||
** in : nb elem, value array, rule to fill
|
||||
** does : creates a rule struct from configuration line
|
||||
** For each element name matching a tag
|
||||
** (cf. rule_parser), then call the associated func.
|
||||
*/
|
||||
void *
|
||||
ngx_http_dummy_cfg_parse_one_rule(ngx_conf_t *cf,
|
||||
ngx_str_t *value,
|
||||
ngx_http_rule_t *current_rule,
|
||||
ngx_int_t nb_elem)
|
||||
{
|
||||
int i, z;
|
||||
void *ret;
|
||||
int valid;
|
||||
|
||||
if (!value || !value[0].data)
|
||||
return NGX_CONF_ERROR;
|
||||
/*
|
||||
** parse basic rule
|
||||
*/
|
||||
if (!ngx_strcmp(value[0].data, TOP_CHECK_RULE_T) ||
|
||||
!ngx_strcmp(value[0].data, TOP_CHECK_RULE_N) ||
|
||||
!ngx_strcmp(value[0].data, TOP_BASIC_RULE_T) ||
|
||||
!ngx_strcmp(value[0].data, TOP_BASIC_RULE_N) ||
|
||||
!ngx_strcmp(value[0].data, TOP_MAIN_BASIC_RULE_T) ||
|
||||
!ngx_strcmp(value[0].data, TOP_MAIN_BASIC_RULE_N)) {
|
||||
NX_LOG_DEBUG(_debug_cfg_parse_one_rule, NGX_LOG_EMERG, cf, 0, "naxsi-basic rule %V", &(value[1]));
|
||||
current_rule->type = BR;
|
||||
current_rule->br = ngx_pcalloc(cf->pool, sizeof(ngx_http_basic_rule_t));
|
||||
if (!current_rule->br)
|
||||
return (NGX_CONF_ERROR);
|
||||
}
|
||||
else {
|
||||
NX_LOG_DEBUG(_debug_cfg_parse_one_rule, NGX_LOG_EMERG, cf, 0,
|
||||
"Unknown start keyword in rule %V", &(value[1]));
|
||||
return (NGX_CONF_ERROR);
|
||||
}
|
||||
|
||||
// check each word of config line against each rule
|
||||
for(i = 1; i < nb_elem && value[i].len > 0; i++) {
|
||||
valid = 0;
|
||||
for (z = 0; rule_parser[z].pars; z++) {
|
||||
if (!ngx_strncmp(value[i].data,
|
||||
rule_parser[z].prefix,
|
||||
strlen(rule_parser[z].prefix))) {
|
||||
ret = rule_parser[z].pars(cf, &(value[i]),
|
||||
current_rule);
|
||||
if (ret != NGX_CONF_OK) {
|
||||
NX_LOG_DEBUG(_debug_cfg_parse_one_rule, NGX_LOG_EMERG, cf, 0,
|
||||
"XX-FAILED PARSING '%s'",
|
||||
value[i].data);
|
||||
return (ret);
|
||||
}
|
||||
valid = 1;
|
||||
}
|
||||
}
|
||||
if (!valid)
|
||||
return (NGX_CONF_ERROR);
|
||||
}
|
||||
/* validate the structure, and fill empty fields.*/
|
||||
if (!current_rule->log_msg)
|
||||
{
|
||||
current_rule->log_msg = ngx_pcalloc(cf->pool, sizeof(ngx_str_t));
|
||||
current_rule->log_msg->data = NULL;
|
||||
current_rule->log_msg->len = 0;
|
||||
}
|
||||
return (NGX_CONF_OK);
|
||||
}
|
||||
|
||||
|
||||
347
naxsi-0.55.3/naxsi_src/naxsi_json.c
Normal file
347
naxsi-0.55.3/naxsi_src/naxsi_json.c
Normal file
@@ -0,0 +1,347 @@
|
||||
/*
|
||||
* NAXSI, a web application firewall for NGINX
|
||||
* Copyright (C) 2016, Thibault 'bui' Koechlin
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give
|
||||
* permission to link the code of portions of this program with the
|
||||
* OpenSSL library under certain conditions as described in each
|
||||
* individual source file, and distribute linked combinations
|
||||
* including the two.
|
||||
* You must obey the GNU General Public License in all respects
|
||||
* for all of the code used other than OpenSSL. If you modify
|
||||
* file(s) with this exception, you may extend this exception to your
|
||||
* version of the file(s), but you are not obligated to do so. If you
|
||||
* do not wish to do so, delete this exception statement from your
|
||||
* version. If you delete this exception statement from all source
|
||||
* files in the program, then also delete it here.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "naxsi.h"
|
||||
|
||||
//#define _debug_json 1
|
||||
|
||||
ngx_http_rule_t nx_int__invalid_json = {/*type*/ 0, /*whitelist flag*/ 0,
|
||||
/*wl_id ptr*/ NULL, /*rule_id*/ 15,
|
||||
/*log_msg*/ NULL, /*score*/ 0,
|
||||
/*sscores*/ NULL,
|
||||
/*sc_block*/ 1, /*sc_allow*/ 0,
|
||||
/*block*/ 1, /*allow*/ 0, /*drop*/ 0, /*log*/ 0,
|
||||
/*br ptrs*/ NULL};
|
||||
|
||||
|
||||
ngx_int_t
|
||||
ngx_http_nx_json_forward(ngx_json_t *js)
|
||||
{
|
||||
while ((*(js->src+js->off) == ' ' ||
|
||||
*(js->src+js->off) == '\t' ||
|
||||
*(js->src+js->off) == '\n' ||
|
||||
*(js->src+js->off) == '\r') && js->off < js->len) {
|
||||
js->off++;
|
||||
}
|
||||
js->c = *(js->src + js->off);
|
||||
return (NGX_OK);
|
||||
}
|
||||
|
||||
/*
|
||||
** used to fast forward in json POSTS,
|
||||
** we skip whitespaces/tab/CR/LF
|
||||
*/
|
||||
ngx_int_t
|
||||
ngx_http_nx_json_seek(ngx_json_t *js, unsigned char seek)
|
||||
{
|
||||
|
||||
ngx_http_nx_json_forward(js);
|
||||
if (js->c != seek)
|
||||
return (NGX_ERROR);
|
||||
return (NGX_OK);
|
||||
}
|
||||
|
||||
/*
|
||||
** extract a quoted strings,
|
||||
** JSON spec only supports double-quoted strings,
|
||||
** so do we.
|
||||
*/
|
||||
ngx_int_t
|
||||
ngx_http_nx_json_quoted(ngx_json_t *js, ngx_str_t *ve)
|
||||
{
|
||||
u_char *vn_start, *vn_end;
|
||||
|
||||
vn_start = vn_end = NULL;
|
||||
|
||||
if (*(js->src+js->off) != '"')
|
||||
return (NGX_ERROR);
|
||||
js->off++;
|
||||
vn_start = js->src+js->off;
|
||||
/* extract varname inbetween "..."*/
|
||||
while (js->off < js->len) {
|
||||
/* skip next character if backslashed */
|
||||
if (*(js->src+js->off) == '\\') {
|
||||
js->off += 2;
|
||||
if (js->off >= js->len) break;
|
||||
}
|
||||
if (*(js->src+js->off) == '"') {
|
||||
vn_end = js->src+js->off;
|
||||
js->off++;
|
||||
break;
|
||||
}
|
||||
js->off++;
|
||||
}
|
||||
if (!vn_start || !vn_end)
|
||||
return (NGX_ERROR);
|
||||
if (!*vn_start || !*vn_end)
|
||||
return (NGX_ERROR);
|
||||
ve->data = vn_start;
|
||||
ve->len = vn_end - vn_start;
|
||||
return (NGX_OK);
|
||||
}
|
||||
|
||||
/*
|
||||
** an array is values separated by ','
|
||||
*/
|
||||
ngx_int_t
|
||||
ngx_http_nx_json_array(ngx_json_t *js) {
|
||||
ngx_int_t rc;
|
||||
|
||||
js->c = *(js->src + js->off);
|
||||
if (js->c != '[' || js->depth > JSON_MAX_DEPTH)
|
||||
return (NGX_ERROR);
|
||||
js->off++;
|
||||
do {
|
||||
rc = ngx_http_nx_json_val(js);
|
||||
/* if we cannot extract the value,
|
||||
we may have reached array end. */
|
||||
if (rc != NGX_OK)
|
||||
break;
|
||||
ngx_http_nx_json_forward(js);
|
||||
if (js->c == ',') {
|
||||
js->off++;
|
||||
ngx_http_nx_json_forward(js);
|
||||
} else break;
|
||||
} while (rc == NGX_OK);
|
||||
if (js->c != ']')
|
||||
return (NGX_ERROR);
|
||||
return (NGX_OK);
|
||||
}
|
||||
|
||||
|
||||
|
||||
ngx_int_t
|
||||
ngx_http_nx_json_val(ngx_json_t *js) {
|
||||
ngx_str_t val;
|
||||
ngx_int_t ret;
|
||||
ngx_str_t empty = ngx_string("");
|
||||
|
||||
val.data = NULL;
|
||||
val.len = 0;
|
||||
|
||||
ngx_http_nx_json_forward(js);
|
||||
if (js->c == '"') {
|
||||
ret = ngx_http_nx_json_quoted(js, &val);
|
||||
if (ret == NGX_OK)
|
||||
{
|
||||
/* parse extracted values. */
|
||||
if (js->loc_cf->body_rules)
|
||||
ngx_http_basestr_ruleset_n(js->r->pool, &js->ckey, &val,
|
||||
js->loc_cf->body_rules, js->r, js->ctx,
|
||||
BODY);
|
||||
if (js->main_cf->body_rules)
|
||||
ngx_http_basestr_ruleset_n(js->r->pool, &js->ckey, &val,
|
||||
js->main_cf->body_rules, js->r, js->ctx,
|
||||
BODY);
|
||||
NX_DEBUG(_debug_json, NGX_LOG_DEBUG_HTTP, js->r->connection->log, 0, "JSON '%V' : '%V'",
|
||||
&(js->ckey), &(val));
|
||||
}
|
||||
return (ret);
|
||||
}
|
||||
if ((js->c >= '0' && js->c <= '9') || js->c == '-') {
|
||||
val.data = js->src+js->off;
|
||||
while ( ((*(js->src+js->off) >= '0' && *(js->src+js->off) <= '9') ||
|
||||
*(js->src+js->off) == '.' || *(js->src+js->off) == '-') && js->off < js->len) {
|
||||
val.len++;
|
||||
js->off++;
|
||||
}
|
||||
/* parse extracted values. */
|
||||
if (js->loc_cf->body_rules)
|
||||
ngx_http_basestr_ruleset_n(js->r->pool, &js->ckey, &val,
|
||||
js->loc_cf->body_rules, js->r, js->ctx,
|
||||
BODY);
|
||||
if (js->main_cf->body_rules)
|
||||
ngx_http_basestr_ruleset_n(js->r->pool, &js->ckey, &val,
|
||||
js->main_cf->body_rules, js->r, js->ctx,
|
||||
BODY);
|
||||
NX_DEBUG(_debug_json, NGX_LOG_DEBUG_HTTP, js->r->connection->log, 0, "JSON '%V' : '%V'",
|
||||
&(js->ckey), &(val));
|
||||
return (NGX_OK);
|
||||
}
|
||||
if (!strncasecmp((const char *) (js->src + js->off), (const char *) "true", 4) ||
|
||||
!strncasecmp((const char *) (js->src + js->off), (const char *) "false", 5) ||
|
||||
!strncasecmp((const char *) (js->src + js->off), (const char *) "null", 4)) {
|
||||
js->c = *(js->src + js->off);
|
||||
/* we don't check static values, do we ?! */
|
||||
val.data = js->src+js->off;
|
||||
if (js->c == 'F' || js->c == 'f') {
|
||||
js->off += 5;
|
||||
val.len = 5;
|
||||
}
|
||||
else {
|
||||
js->off += 4;
|
||||
val.len = 4;
|
||||
}
|
||||
/* parse extracted values. */
|
||||
if (js->loc_cf->body_rules)
|
||||
ngx_http_basestr_ruleset_n(js->r->pool, &js->ckey, &val,
|
||||
js->loc_cf->body_rules, js->r, js->ctx,
|
||||
BODY);
|
||||
if (js->main_cf->body_rules)
|
||||
ngx_http_basestr_ruleset_n(js->r->pool, &js->ckey, &val,
|
||||
js->main_cf->body_rules, js->r, js->ctx,
|
||||
BODY);
|
||||
NX_DEBUG(_debug_json, NGX_LOG_DEBUG_HTTP, js->r->connection->log, 0, "JSON '%V' : '%V'",
|
||||
&(js->ckey), &(val));
|
||||
return (NGX_OK);
|
||||
}
|
||||
|
||||
if (js->c == '[') {
|
||||
ret = ngx_http_nx_json_array(js);
|
||||
if (js->c != ']')
|
||||
return (NGX_ERROR);
|
||||
js->off++;
|
||||
return (ret);
|
||||
}
|
||||
if (js->c == '{') {
|
||||
/*
|
||||
** if sub-struct, parse key without value :
|
||||
** "foobar" : { "bar" : [1,2,3]} => "foobar" parsed alone.
|
||||
** this is to avoid "foobar" left unparsed, as we won't have
|
||||
** key/value here with "foobar" as a key.
|
||||
*/
|
||||
if (js->loc_cf->body_rules)
|
||||
ngx_http_basestr_ruleset_n(js->r->pool, &js->ckey, &empty,
|
||||
js->loc_cf->body_rules, js->r, js->ctx,
|
||||
BODY);
|
||||
if (js->main_cf->body_rules)
|
||||
ngx_http_basestr_ruleset_n(js->r->pool, &js->ckey, &empty,
|
||||
js->main_cf->body_rules, js->r, js->ctx,
|
||||
BODY);
|
||||
ret = ngx_http_nx_json_obj(js);
|
||||
ngx_http_nx_json_forward(js);
|
||||
if (js->c != '}')
|
||||
return (NGX_ERROR);
|
||||
js->off++;
|
||||
return (ret);
|
||||
}
|
||||
return (NGX_ERROR);
|
||||
}
|
||||
|
||||
|
||||
ngx_int_t
|
||||
ngx_http_nx_json_obj(ngx_json_t *js)
|
||||
{
|
||||
js->c = *(js->src + js->off);
|
||||
|
||||
if (js->c != '{' || js->depth > JSON_MAX_DEPTH)
|
||||
return (NGX_ERROR);
|
||||
js->off++;
|
||||
|
||||
do {
|
||||
ngx_http_nx_json_forward(js);
|
||||
/* check subs (arrays, objects) */
|
||||
switch (js->c) {
|
||||
case '[': /* array */
|
||||
js->depth++;
|
||||
ngx_http_nx_json_array(js);
|
||||
if (ngx_http_nx_json_seek(js, ']'))
|
||||
return (NGX_ERROR);
|
||||
js->off++;
|
||||
js->depth--;
|
||||
break;
|
||||
case '{': /* sub-object */
|
||||
js->depth++;
|
||||
ngx_http_nx_json_obj(js);
|
||||
if (js->c != '}')
|
||||
return (NGX_ERROR);
|
||||
js->off++;
|
||||
js->depth--;
|
||||
break;
|
||||
case '"': /* key : value, extract and parse. */
|
||||
if (ngx_http_nx_json_quoted(js, &(js->ckey)) != NGX_OK)
|
||||
return (NGX_ERROR);
|
||||
if (ngx_http_nx_json_seek(js, ':'))
|
||||
return (NGX_ERROR);
|
||||
js->off++;
|
||||
ngx_http_nx_json_forward(js);
|
||||
if (ngx_http_nx_json_val(js) != NGX_OK)
|
||||
return (NGX_ERROR);
|
||||
}
|
||||
ngx_http_nx_json_forward(js);
|
||||
/* another element ? */
|
||||
if (js->c == ',') {
|
||||
js->off++;
|
||||
ngx_http_nx_json_forward(js);
|
||||
continue;
|
||||
|
||||
} else if (js->c == '}') {
|
||||
js->depth--;
|
||||
/* or maybe we just finished parsing this object */
|
||||
return (NGX_OK);
|
||||
} else {
|
||||
/* nothing we expected, die. */
|
||||
return (NGX_ERROR);
|
||||
}
|
||||
} while (js->off < js->len);
|
||||
|
||||
return (NGX_ERROR);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
** Parse a JSON request
|
||||
*/
|
||||
void
|
||||
ngx_http_dummy_json_parse(ngx_http_request_ctx_t *ctx,
|
||||
ngx_http_request_t *r,
|
||||
u_char *src,
|
||||
u_int len)
|
||||
{
|
||||
ngx_json_t *js;
|
||||
|
||||
|
||||
js = ngx_pcalloc(r->pool, sizeof(ngx_json_t));
|
||||
if (!js) return ;
|
||||
js->json.data = js->src = src;
|
||||
js->json.len = js->len = len;
|
||||
js->r = r;
|
||||
js->ctx = ctx;
|
||||
js->loc_cf = ngx_http_get_module_loc_conf(r, ngx_http_naxsi_module);
|
||||
js->main_cf = ngx_http_get_module_main_conf(r, ngx_http_naxsi_module);
|
||||
|
||||
if (ngx_http_nx_json_seek(js, '{')) {
|
||||
ngx_http_apply_rulematch_v_n(&nx_int__invalid_json, ctx, r, NULL, NULL, BODY, 1, 0);
|
||||
return ;
|
||||
}
|
||||
if (ngx_http_nx_json_obj(js) != NGX_OK) {
|
||||
ngx_http_apply_rulematch_v_n(&nx_int__invalid_json, ctx, r, NULL, NULL, BODY, 1, 0);
|
||||
NX_DEBUG(_debug_json, NGX_LOG_DEBUG_HTTP, js->r->connection->log, 0, "nx_json_obj returned error, apply invalid_json.");
|
||||
|
||||
}
|
||||
/* we are now on closing bracket, check for garbage. */
|
||||
js->off++;
|
||||
ngx_http_nx_json_forward(js);
|
||||
if (js->off != js->len)
|
||||
ngx_http_apply_rulematch_v_n(&nx_int__invalid_json, ctx, r, NULL, NULL, BODY, 1, 0);
|
||||
return ;
|
||||
}
|
||||
|
||||
70
naxsi-0.55.3/naxsi_src/naxsi_raw.c
Normal file
70
naxsi-0.55.3/naxsi_src/naxsi_raw.c
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* NAXSI, a web application firewall for NGINX
|
||||
* Copyright (C) 2016, Thibault 'bui' Koechlin
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give
|
||||
* permission to link the code of portions of this program with the
|
||||
* OpenSSL library under certain conditions as described in each
|
||||
* individual source file, and distribute linked combinations
|
||||
* including the two.
|
||||
* You must obey the GNU General Public License in all respects
|
||||
* for all of the code used other than OpenSSL. If you modify
|
||||
* file(s) with this exception, you may extend this exception to your
|
||||
* version of the file(s), but you are not obligated to do so. If you
|
||||
* do not wish to do so, delete this exception statement from your
|
||||
* version. If you delete this exception statement from all source
|
||||
* files in the program, then also delete it here.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "naxsi.h"
|
||||
|
||||
|
||||
void
|
||||
ngx_http_dummy_rawbody_parse(ngx_http_request_ctx_t *ctx,
|
||||
ngx_http_request_t *r,
|
||||
u_char *src,
|
||||
u_int len)
|
||||
{
|
||||
ngx_http_dummy_loc_conf_t *cf;
|
||||
ngx_str_t body;
|
||||
ngx_http_dummy_main_conf_t *main_cf;
|
||||
ngx_str_t empty = ngx_string("");
|
||||
|
||||
NX_DEBUG(_debug_rawbody, NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "XX-RAWBODY CALLED len:%d",len);
|
||||
if (len <= 0 || !src)
|
||||
return;
|
||||
cf = ngx_http_get_module_loc_conf(r, ngx_http_naxsi_module);
|
||||
main_cf = ngx_http_get_module_main_conf(r, ngx_http_naxsi_module);
|
||||
|
||||
|
||||
body.data = src;
|
||||
body.len = len;
|
||||
|
||||
naxsi_unescape(&body);
|
||||
|
||||
/* here we got val name + val content !*/
|
||||
if (cf->raw_body_rules) {
|
||||
NX_DEBUG(_debug_rawbody, NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "XX-(local) RAW BODY RULES");
|
||||
ngx_http_basestr_ruleset_n(r->pool, &empty, &body,
|
||||
cf->raw_body_rules, r, ctx, BODY);
|
||||
}
|
||||
|
||||
if (main_cf->raw_body_rules) {
|
||||
NX_DEBUG(_debug_rawbody, NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "XX-(global) RAW BODY RULES");
|
||||
ngx_http_basestr_ruleset_n(r->pool, &empty, &body,
|
||||
main_cf->raw_body_rules, r, ctx, BODY);
|
||||
}
|
||||
}
|
||||
2273
naxsi-0.55.3/naxsi_src/naxsi_runtime.c
Normal file
2273
naxsi-0.55.3/naxsi_src/naxsi_runtime.c
Normal file
File diff suppressed because it is too large
Load Diff
1161
naxsi-0.55.3/naxsi_src/naxsi_skeleton.c
Normal file
1161
naxsi-0.55.3/naxsi_src/naxsi_skeleton.c
Normal file
File diff suppressed because it is too large
Load Diff
826
naxsi-0.55.3/naxsi_src/naxsi_utils.c
Normal file
826
naxsi-0.55.3/naxsi_src/naxsi_utils.c
Normal file
@@ -0,0 +1,826 @@
|
||||
/*
|
||||
* NAXSI, a web application firewall for NGINX
|
||||
* Copyright (C) 2016, Thibault 'bui' Koechlin
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give
|
||||
* permission to link the code of portions of this program with the
|
||||
* OpenSSL library under certain conditions as described in each
|
||||
* individual source file, and distribute linked combinations
|
||||
* including the two.
|
||||
* You must obey the GNU General Public License in all respects
|
||||
* for all of the code used other than OpenSSL. If you modify
|
||||
* file(s) with this exception, you may extend this exception to your
|
||||
* version of the file(s), but you are not obligated to do so. If you
|
||||
* do not wish to do so, delete this exception statement from your
|
||||
* version. If you delete this exception statement from all source
|
||||
* files in the program, then also delete it here.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "naxsi.h"
|
||||
|
||||
|
||||
static int naxsi_unescape_uri(u_char **dst, u_char **src, size_t size, ngx_uint_t type);
|
||||
|
||||
char *
|
||||
strnchr(const char *s, int c, int len)
|
||||
{
|
||||
int cpt;
|
||||
for (cpt = 0; cpt < len && s[cpt]; cpt++)
|
||||
if (s[cpt] == c)
|
||||
return ((char *) s+cpt);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
static char *
|
||||
strncasechr(const char *s, int c, int len)
|
||||
{
|
||||
int cpt;
|
||||
for (cpt = 0; cpt < len && s[cpt]; cpt++)
|
||||
if (tolower(s[cpt]) == c)
|
||||
return ((char *) s+cpt);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
** strstr: faster, stronger, harder
|
||||
** (because strstr from libc is very slow)
|
||||
*/
|
||||
char *
|
||||
strfaststr(unsigned char *haystack, unsigned int hl,
|
||||
unsigned char *needle, unsigned int nl)
|
||||
{
|
||||
char *cpt, *found, *end;
|
||||
if (hl < nl || !haystack || !needle || !nl || !hl) return (NULL);
|
||||
cpt = (char *) haystack;
|
||||
end = (char *) haystack + hl;
|
||||
while (cpt < end) {
|
||||
found = strncasechr((const char *) cpt, (int) needle[0], hl);
|
||||
if (!found) return (NULL);
|
||||
if (nl == 1) return (found);
|
||||
if (!strncasecmp((const char *)found+1, (const char *) needle+1, nl-1))
|
||||
return ((char *) found);
|
||||
else {
|
||||
if (found+nl >= end)
|
||||
break;
|
||||
if (found+nl < end) {
|
||||
/* the haystack is shrinking */
|
||||
cpt = found+1;
|
||||
hl = (unsigned int) (end - cpt);
|
||||
}
|
||||
}
|
||||
}
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
/* unescape routine, returns number of nullbytes present */
|
||||
int naxsi_unescape(ngx_str_t *str) {
|
||||
u_char *dst, *src;
|
||||
u_int nullbytes = 0, bad = 0, i;
|
||||
|
||||
dst = str->data;
|
||||
src = str->data;
|
||||
|
||||
bad = naxsi_unescape_uri(&src, &dst,
|
||||
str->len, 0);
|
||||
str->len = src - str->data;
|
||||
//tmp hack fix, avoid %00 & co (null byte) encoding :p
|
||||
for (i = 0; i < str->len; i++)
|
||||
if (str->data[i] == 0x0)
|
||||
{
|
||||
nullbytes++;
|
||||
str->data[i] = '0';
|
||||
}
|
||||
return (nullbytes+bad);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Patched ngx_unescape_uri :
|
||||
** The original one does not care if the character following % is in valid range.
|
||||
** For example, with the original one :
|
||||
** '%uff' -> 'uff'
|
||||
*/
|
||||
static int
|
||||
naxsi_unescape_uri(u_char **dst, u_char **src, size_t size, ngx_uint_t type)
|
||||
{
|
||||
u_char *d, *s, ch, c, decoded;
|
||||
int bad = 0;
|
||||
|
||||
enum {
|
||||
sw_usual = 0,
|
||||
sw_quoted,
|
||||
sw_quoted_second
|
||||
} state;
|
||||
|
||||
d = *dst;
|
||||
s = *src;
|
||||
|
||||
state = 0;
|
||||
decoded = 0;
|
||||
|
||||
while (size--) {
|
||||
|
||||
ch = *s++;
|
||||
|
||||
switch (state) {
|
||||
case sw_usual:
|
||||
if (ch == '%') {
|
||||
state = sw_quoted;
|
||||
break;
|
||||
}
|
||||
|
||||
*d++ = ch;
|
||||
break;
|
||||
|
||||
case sw_quoted:
|
||||
|
||||
if (ch >= '0' && ch <= '9') {
|
||||
decoded = (u_char) (ch - '0');
|
||||
state = sw_quoted_second;
|
||||
break;
|
||||
}
|
||||
|
||||
c = (u_char) (ch | 0x20);
|
||||
if (c >= 'a' && c <= 'f') {
|
||||
decoded = (u_char) (c - 'a' + 10);
|
||||
state = sw_quoted_second;
|
||||
break;
|
||||
}
|
||||
|
||||
/* the invalid quoted character */
|
||||
bad++;
|
||||
state = sw_usual;
|
||||
*d++ = '%';
|
||||
*d++ = ch;
|
||||
break;
|
||||
|
||||
case sw_quoted_second:
|
||||
|
||||
state = sw_usual;
|
||||
|
||||
if (ch >= '0' && ch <= '9') {
|
||||
ch = (u_char) ((decoded << 4) + ch - '0');
|
||||
|
||||
*d++ = ch;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
c = (u_char) (ch | 0x20);
|
||||
if (c >= 'a' && c <= 'f') {
|
||||
ch = (u_char) ((decoded << 4) + c - 'a' + 10);
|
||||
|
||||
*d++ = ch;
|
||||
|
||||
break;
|
||||
}
|
||||
/* the invalid quoted character */
|
||||
/* as it happened in the 2nd part of quoted character,
|
||||
we need to restore the decoded char as well. */
|
||||
*d++ = '%';
|
||||
*d++ = *(s - 2);
|
||||
*d++ = *(s - 1);
|
||||
bad++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
*dst = d;
|
||||
*src = s;
|
||||
|
||||
return (bad);
|
||||
}
|
||||
|
||||
|
||||
/* push rule into disabled rules. */
|
||||
static ngx_int_t
|
||||
ngx_http_wlr_push_disabled(ngx_conf_t *cf, ngx_http_dummy_loc_conf_t *dlc,
|
||||
ngx_http_rule_t *curr) {
|
||||
ngx_http_rule_t **dr;
|
||||
|
||||
if (!dlc->disabled_rules)
|
||||
dlc->disabled_rules = ngx_array_create(cf->pool, 4,
|
||||
sizeof(ngx_http_rule_t *));
|
||||
if (!dlc->disabled_rules)
|
||||
return (NGX_ERROR);
|
||||
dr = ngx_array_push(dlc->disabled_rules);
|
||||
if (!dr)
|
||||
return (NGX_ERROR);
|
||||
*dr = (ngx_http_rule_t *) curr;
|
||||
return (NGX_OK);
|
||||
}
|
||||
|
||||
/* merge the two rules into father_wl, meaning
|
||||
ids. Not locations, as we are getting rid of it */
|
||||
static ngx_int_t
|
||||
ngx_http_wlr_merge(ngx_conf_t *cf, ngx_http_whitelist_rule_t *father_wl,
|
||||
ngx_http_rule_t *curr) {
|
||||
uint i;
|
||||
ngx_int_t *tmp_ptr;
|
||||
|
||||
NX_LOG_DEBUG(_debug_whitelist,
|
||||
NGX_LOG_EMERG, cf, 0, "[naxsi] merging similar wl(s)");
|
||||
|
||||
|
||||
if (!father_wl->ids)
|
||||
{
|
||||
father_wl->ids = ngx_array_create(cf->pool, 3, sizeof(ngx_int_t));
|
||||
if (!father_wl->ids)
|
||||
return (NGX_ERROR);
|
||||
}
|
||||
for (i = 0; i < curr->wlid_array->nelts; i++) {
|
||||
tmp_ptr = ngx_array_push(father_wl->ids);
|
||||
if (!tmp_ptr)
|
||||
return (NGX_ERROR);
|
||||
*tmp_ptr = ((ngx_int_t *)curr->wlid_array->elts)[i];
|
||||
//*tmp_ptr = curr->wlid_array->elts[i];
|
||||
}
|
||||
return (NGX_OK);
|
||||
}
|
||||
|
||||
/*check rule, returns associed zone, as well as location index.
|
||||
location index refers to $URL:bla or $ARGS_VAR:bla */
|
||||
#define custloc_array(x) ((ngx_http_custom_rule_location_t *) x)
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_wlr_identify(ngx_conf_t *cf, ngx_http_dummy_loc_conf_t *dlc,
|
||||
ngx_http_rule_t *curr, int *zone,
|
||||
int *uri_idx, int *name_idx) {
|
||||
|
||||
uint i;
|
||||
|
||||
/*
|
||||
identify global match zones (|ARGS|BODY|HEADERS|URL|FILE_EXT)
|
||||
*/
|
||||
if (curr->br->body || curr->br->body_var)
|
||||
*zone = BODY;
|
||||
else if (curr->br->headers || curr->br->headers_var)
|
||||
*zone = HEADERS;
|
||||
else if (curr->br->args || curr->br->args_var)
|
||||
*zone = ARGS;
|
||||
else if (curr->br->url) /*don't assume that named $URL means zone is URL.*/
|
||||
*zone = URL;
|
||||
else if (curr->br->file_ext)
|
||||
*zone = FILE_EXT;
|
||||
/*
|
||||
if we're facing a WL in the style $URL:/bla|ARGS (or any other zone),
|
||||
push it to
|
||||
*/
|
||||
for (i = 0; i < curr->br->custom_locations->nelts; i++) {
|
||||
/*
|
||||
locate target URL if exists ($URL:/bla|ARGS) or ($URL:/bla|$ARGS_VAR:foo)
|
||||
*/
|
||||
if (custloc_array(curr->br->custom_locations->elts)[i].specific_url) {
|
||||
NX_LOG_DEBUG(_debug_whitelist_heavy,
|
||||
NGX_LOG_EMERG, cf, 0,
|
||||
"whitelist has URI %V", &(custloc_array(curr->br->custom_locations->elts)[i].target));
|
||||
|
||||
*uri_idx = i;
|
||||
}
|
||||
/*
|
||||
identify named match zones ($ARGS_VAR:bla|$HEADERS_VAR:bla|$BODY_VAR:bla)
|
||||
*/
|
||||
if (custloc_array(curr->br->custom_locations->elts)[i].body_var) {
|
||||
NX_LOG_DEBUG(_debug_whitelist_heavy,
|
||||
NGX_LOG_EMERG, cf, 0,
|
||||
"whitelist has body_var %V", &(custloc_array(curr->br->custom_locations->elts)[i].target));
|
||||
|
||||
/*#217 : scream on incorrect rules*/
|
||||
if (*name_idx != -1) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "whitelist can't target more than one BODY item.");
|
||||
return (NGX_ERROR);
|
||||
}
|
||||
*name_idx = i;
|
||||
*zone = BODY;
|
||||
}
|
||||
if (custloc_array(curr->br->custom_locations->elts)[i].headers_var) {
|
||||
NX_LOG_DEBUG(_debug_whitelist_heavy,
|
||||
NGX_LOG_EMERG, cf, 0,
|
||||
"whitelist has header_var %V", &(custloc_array(curr->br->custom_locations->elts)[i].target));
|
||||
|
||||
/*#217 : scream on incorrect rules*/
|
||||
if (*name_idx != -1) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "whitelist can't target more than one HEADER item.");
|
||||
return (NGX_ERROR);
|
||||
}
|
||||
*name_idx = i;
|
||||
*zone = HEADERS;
|
||||
}
|
||||
if (custloc_array(curr->br->custom_locations->elts)[i].args_var) {
|
||||
NX_LOG_DEBUG(_debug_whitelist_heavy,
|
||||
NGX_LOG_EMERG, cf, 0,
|
||||
"whitelist has arg_var %V", &(custloc_array(curr->br->custom_locations->elts)[i].target));
|
||||
|
||||
/*#217 : scream on incorrect rules*/
|
||||
if (*name_idx != -1) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "whitelist can't target more than one ARGS item.");
|
||||
return (NGX_ERROR);
|
||||
}
|
||||
|
||||
*name_idx = i;
|
||||
*zone = ARGS;
|
||||
}
|
||||
}
|
||||
if (*zone == -1)
|
||||
return (NGX_ERROR);
|
||||
return (NGX_OK);
|
||||
}
|
||||
|
||||
ngx_http_whitelist_rule_t *
|
||||
ngx_http_wlr_find(ngx_conf_t *cf, ngx_http_dummy_loc_conf_t *dlc,
|
||||
ngx_http_rule_t *curr, int zone, int uri_idx,
|
||||
int name_idx, char **fullname) {
|
||||
uint i;
|
||||
|
||||
/* Create unique string for rule, and try to find it in existing rules.*/
|
||||
/*name AND uri*/
|
||||
|
||||
if (uri_idx != -1 && name_idx != -1) {
|
||||
NX_LOG_DEBUG(_debug_whitelist_heavy,
|
||||
NGX_LOG_EMERG, cf, 0,
|
||||
"whitelist has uri + name");
|
||||
|
||||
/* allocate one extra byte in case curr->br->target_name is set. */
|
||||
*fullname = ngx_pcalloc(cf->pool, custloc_array(curr->br->custom_locations->elts)[name_idx].target.len +
|
||||
custloc_array(curr->br->custom_locations->elts)[uri_idx].target.len + 3);
|
||||
/* if WL targets variable name instead of content, prefix hash with '#' */
|
||||
if (curr->br->target_name) {
|
||||
NX_LOG_DEBUG(_debug_whitelist_heavy,
|
||||
NGX_LOG_EMERG, cf, 0,
|
||||
"whitelist targets |NAME");
|
||||
|
||||
|
||||
strncat(*fullname, (const char *) "#", 1);
|
||||
}
|
||||
strncat(*fullname, (const char *) custloc_array(curr->br->custom_locations->elts)[uri_idx].target.data,
|
||||
custloc_array(curr->br->custom_locations->elts)[uri_idx].target.len);
|
||||
strncat(*fullname, (const char *) "#", 1);
|
||||
strncat(*fullname, (const char *) custloc_array(curr->br->custom_locations->elts)[name_idx].target.data,
|
||||
custloc_array(curr->br->custom_locations->elts)[name_idx].target.len);
|
||||
}
|
||||
/* only uri */
|
||||
else if (uri_idx != -1 && name_idx == -1) {
|
||||
NX_LOG_DEBUG(_debug_whitelist_heavy,
|
||||
NGX_LOG_EMERG, cf, 0,
|
||||
"whitelist has uri");
|
||||
|
||||
//XXX set flag only_uri
|
||||
*fullname = ngx_pcalloc(cf->pool, custloc_array(curr->br->custom_locations->elts)[uri_idx].target.len + 3);
|
||||
if (curr->br->target_name) {
|
||||
NX_LOG_DEBUG(_debug_whitelist_heavy,
|
||||
NGX_LOG_EMERG, cf, 0,
|
||||
"whitelist targets |NAME");
|
||||
|
||||
|
||||
strncat(*fullname, (const char *) "#", 1);
|
||||
}
|
||||
|
||||
|
||||
strncat(*fullname, (const char *) custloc_array(curr->br->custom_locations->elts)[uri_idx].target.data,
|
||||
custloc_array(curr->br->custom_locations->elts)[uri_idx].target.len);
|
||||
}
|
||||
/* only name */
|
||||
else if (name_idx != -1) {
|
||||
NX_LOG_DEBUG(_debug_whitelist_heavy,
|
||||
NGX_LOG_EMERG, cf, 0,
|
||||
"whitelist has name");
|
||||
|
||||
*fullname = ngx_pcalloc(cf->pool, custloc_array(curr->br->custom_locations->elts)[name_idx].target.len + 2);
|
||||
if (curr->br->target_name)
|
||||
strncat(*fullname, (const char *) "#", 1);
|
||||
strncat(*fullname, (const char *) custloc_array(curr->br->custom_locations->elts)[name_idx].target.data,
|
||||
custloc_array(curr->br->custom_locations->elts)[name_idx].target.len);
|
||||
}
|
||||
/* problem houston */
|
||||
else
|
||||
return (NULL);
|
||||
|
||||
for (i = 0; i < dlc->tmp_wlr->nelts; i++)
|
||||
if (!strcmp((const char *)*fullname, (const char *)((ngx_http_whitelist_rule_t *) dlc->tmp_wlr->elts)[i].name->data) &&
|
||||
((ngx_http_whitelist_rule_t *) dlc->tmp_wlr->elts)[i].zone == (uint) zone)
|
||||
{
|
||||
NX_LOG_DEBUG(_debug_whitelist_heavy,
|
||||
NGX_LOG_EMERG, cf, 0,
|
||||
"found existing 'same' WL : %V", ((ngx_http_whitelist_rule_t *) dlc->tmp_wlr->elts)[i].name);
|
||||
|
||||
return (&((ngx_http_whitelist_rule_t *)dlc->tmp_wlr->elts)[i]);
|
||||
}
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_wlr_finalize_hashtables(ngx_conf_t *cf, ngx_http_dummy_loc_conf_t *dlc) {
|
||||
int get_sz = 0, headers_sz = 0, body_sz = 0, uri_sz = 0;
|
||||
ngx_array_t *get_ar = NULL, *headers_ar = NULL, *body_ar = NULL, *uri_ar = NULL;
|
||||
ngx_hash_key_t *arr_node;
|
||||
ngx_hash_init_t hash_init;
|
||||
uint i;
|
||||
|
||||
NX_LOG_DEBUG(_debug_whitelist_heavy,
|
||||
NGX_LOG_EMERG, cf, 0,
|
||||
"finalizing hashtables");
|
||||
|
||||
|
||||
for (i = 0; i < dlc->tmp_wlr->nelts; i++) {
|
||||
switch (((ngx_http_whitelist_rule_t *) dlc->tmp_wlr->elts)[i].zone) {
|
||||
case FILE_EXT:
|
||||
case BODY:
|
||||
body_sz++;
|
||||
break;
|
||||
case HEADERS:
|
||||
headers_sz++;
|
||||
break;
|
||||
case URL:
|
||||
uri_sz++;
|
||||
break;
|
||||
case ARGS:
|
||||
get_sz++;
|
||||
break;
|
||||
case UNKNOWN:
|
||||
default:
|
||||
return (NGX_ERROR);
|
||||
}
|
||||
}
|
||||
NX_LOG_DEBUG(_debug_whitelist_heavy,
|
||||
NGX_LOG_EMERG, cf, 0,
|
||||
"nb items : body:%d headers:%d uri:%d get:%d",
|
||||
body_sz, headers_sz, uri_sz, get_sz);
|
||||
|
||||
|
||||
if (get_sz)
|
||||
get_ar = ngx_array_create(cf->pool, get_sz, sizeof(ngx_hash_key_t));
|
||||
if (headers_sz)
|
||||
headers_ar = ngx_array_create(cf->pool, headers_sz, sizeof(ngx_hash_key_t));
|
||||
if (body_sz)
|
||||
body_ar = ngx_array_create(cf->pool, body_sz, sizeof(ngx_hash_key_t));
|
||||
if (uri_sz)
|
||||
uri_ar = ngx_array_create(cf->pool, uri_sz, sizeof(ngx_hash_key_t));
|
||||
for (i = 0; i < dlc->tmp_wlr->nelts; i++) {
|
||||
switch (((ngx_http_whitelist_rule_t *) dlc->tmp_wlr->elts)[i].zone) {
|
||||
case FILE_EXT:
|
||||
case BODY:
|
||||
arr_node = (ngx_hash_key_t*) ngx_array_push(body_ar);
|
||||
break;
|
||||
case HEADERS:
|
||||
arr_node = (ngx_hash_key_t*) ngx_array_push(headers_ar);
|
||||
break;
|
||||
case URL:
|
||||
arr_node = (ngx_hash_key_t*) ngx_array_push(uri_ar);
|
||||
break;
|
||||
case ARGS:
|
||||
arr_node = (ngx_hash_key_t*) ngx_array_push(get_ar);
|
||||
break;
|
||||
default:
|
||||
return (NGX_ERROR);
|
||||
}
|
||||
if (!arr_node)
|
||||
return (NGX_ERROR);
|
||||
ngx_memset(arr_node, 0, sizeof(ngx_hash_key_t));
|
||||
arr_node->key = *(((ngx_http_whitelist_rule_t *) dlc->tmp_wlr->elts)[i].name);
|
||||
arr_node->key_hash = ngx_hash_key_lc(((ngx_http_whitelist_rule_t *) dlc->tmp_wlr->elts)[i].name->data,
|
||||
((ngx_http_whitelist_rule_t *) dlc->tmp_wlr->elts)[i].name->len);
|
||||
arr_node->value = (void *) &(((ngx_http_whitelist_rule_t *) dlc->tmp_wlr->elts)[i]);
|
||||
#ifdef SPECIAL__debug_whitelist_heavy
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "pushing new WL, zone:%d, target:%V, %d IDs",
|
||||
((ngx_http_whitelist_rule_t *) dlc->tmp_wlr->elts)[i].zone , ((ngx_http_whitelist_rule_t *) dlc->tmp_wlr->elts)[i].name,
|
||||
((ngx_http_whitelist_rule_t *) dlc->tmp_wlr->elts)[i].ids->nelts);
|
||||
unsigned int z;
|
||||
for (z = 0; z < ((ngx_http_whitelist_rule_t *) dlc->tmp_wlr->elts)[i].ids->nelts; z++)
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "id:%d",
|
||||
((int *)((ngx_http_whitelist_rule_t *) dlc->tmp_wlr->elts)[i].ids->elts)[z]);
|
||||
#endif
|
||||
}
|
||||
hash_init.key = &ngx_hash_key_lc;
|
||||
hash_init.pool = cf->pool;
|
||||
hash_init.temp_pool = NULL;
|
||||
hash_init.max_size = 1024;
|
||||
hash_init.bucket_size = 512;
|
||||
|
||||
if (body_ar) {
|
||||
dlc->wlr_body_hash = (ngx_hash_t*) ngx_pcalloc(cf->pool, sizeof(ngx_hash_t));
|
||||
hash_init.hash = dlc->wlr_body_hash;
|
||||
hash_init.name = "wlr_body_hash";
|
||||
if (ngx_hash_init(&hash_init, (ngx_hash_key_t*) body_ar->elts,
|
||||
body_ar->nelts) != NGX_OK) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "$BODY hashtable init failed"); /* LCOV_EXCL_LINE */
|
||||
return (NGX_ERROR); /* LCOV_EXCL_LINE */
|
||||
}
|
||||
else
|
||||
NX_LOG_DEBUG(_debug_whitelist, NGX_LOG_EMERG, cf, 0, "$BODY hashtable init successed !");
|
||||
}
|
||||
if (uri_ar) {
|
||||
dlc->wlr_url_hash = (ngx_hash_t*) ngx_pcalloc(cf->pool, sizeof(ngx_hash_t));
|
||||
hash_init.hash = dlc->wlr_url_hash;
|
||||
hash_init.name = "wlr_url_hash";
|
||||
if (ngx_hash_init(&hash_init, (ngx_hash_key_t*) uri_ar->elts,
|
||||
uri_ar->nelts) != NGX_OK) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "$URL hashtable init failed"); /* LCOV_EXCL_LINE */
|
||||
return (NGX_ERROR); /* LCOV_EXCL_LINE */
|
||||
}
|
||||
else
|
||||
NX_LOG_DEBUG(_debug_whitelist, NGX_LOG_EMERG, cf, 0, "$URL hashtable init successed !");
|
||||
|
||||
}
|
||||
if (get_ar) {
|
||||
dlc->wlr_args_hash = (ngx_hash_t*) ngx_pcalloc(cf->pool, sizeof(ngx_hash_t));
|
||||
hash_init.hash = dlc->wlr_args_hash;
|
||||
hash_init.name = "wlr_args_hash";
|
||||
if (ngx_hash_init(&hash_init, (ngx_hash_key_t*) get_ar->elts,
|
||||
get_ar->nelts) != NGX_OK) { /* LCOV_EXCL_LINE */
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "$ARGS hashtable init failed"); /* LCOV_EXCL_LINE */
|
||||
return (NGX_ERROR);
|
||||
}
|
||||
else
|
||||
NX_LOG_DEBUG(_debug_whitelist, NGX_LOG_EMERG, cf, 0, "$ARGS hashtable init successed %d !",
|
||||
dlc->wlr_args_hash->size);
|
||||
|
||||
}
|
||||
if (headers_ar) {
|
||||
dlc->wlr_headers_hash = (ngx_hash_t*) ngx_pcalloc(cf->pool, sizeof(ngx_hash_t));
|
||||
hash_init.hash = dlc->wlr_headers_hash;
|
||||
hash_init.name = "wlr_headers_hash";
|
||||
if (ngx_hash_init(&hash_init, (ngx_hash_key_t*) headers_ar->elts,
|
||||
headers_ar->nelts) != NGX_OK) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "$HEADERS hashtable init failed"); /* LCOV_EXCL_LINE */
|
||||
return (NGX_ERROR); /* LCOV_EXCL_LINE */
|
||||
}
|
||||
else
|
||||
NX_LOG_DEBUG(_debug_whitelist, NGX_LOG_EMERG, cf, 0, "$HEADERS hashtable init successed %d !",
|
||||
dlc->wlr_headers_hash->size);
|
||||
|
||||
}
|
||||
return (NGX_OK);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** This function will take the whitelist basicrules generated during the configuration
|
||||
** parsing phase, and aggregate them to build hashtables according to the matchzones.
|
||||
**
|
||||
** As whitelist can be in the form :
|
||||
** "mz:$URL:bla|$ARGS_VAR:foo"
|
||||
** "mz:$URL:bla|ARGS"
|
||||
** "mz:$HEADERS_VAR:Cookie"
|
||||
** ...
|
||||
**
|
||||
** So, we will aggregate all the rules that are pointing to the same URL together,
|
||||
** as well as rules targetting the same argument name / zone.
|
||||
*/
|
||||
|
||||
ngx_int_t
|
||||
ngx_http_dummy_create_hashtables_n(ngx_http_dummy_loc_conf_t *dlc,
|
||||
ngx_conf_t *cf)
|
||||
{
|
||||
int zone, uri_idx, name_idx, ret;
|
||||
ngx_http_rule_t *curr_r/*, *father_r*/;
|
||||
ngx_http_whitelist_rule_t *father_wlr;
|
||||
ngx_http_rule_t **rptr;
|
||||
ngx_regex_compile_t *rgc;
|
||||
char *fullname;
|
||||
uint i;
|
||||
|
||||
if (!dlc->whitelist_rules || dlc->whitelist_rules->nelts < 1) {
|
||||
NX_LOG_DEBUG(_debug_whitelist_heavy ,
|
||||
NGX_LOG_EMERG, cf, 0,
|
||||
"No whitelist registred, but it's your call.");
|
||||
|
||||
return (NGX_OK);
|
||||
}
|
||||
NX_LOG_DEBUG(_debug_whitelist_heavy,
|
||||
NGX_LOG_EMERG, cf, 0,
|
||||
"Building whitelist hashtables, %d items in list",
|
||||
dlc->whitelist_rules->nelts);
|
||||
|
||||
dlc->tmp_wlr = ngx_array_create(cf->pool, dlc->whitelist_rules->nelts,
|
||||
sizeof(ngx_http_whitelist_rule_t));
|
||||
/* iterate through each stored whitelist rule. */
|
||||
for (i = 0; i < dlc->whitelist_rules->nelts; i++) {
|
||||
uri_idx = name_idx = zone = -1;
|
||||
/*a whitelist is in fact just another basic_rule_t */
|
||||
curr_r = &(((ngx_http_rule_t*)(dlc->whitelist_rules->elts))[i]);
|
||||
NX_LOG_DEBUG(_debug_whitelist_heavy,
|
||||
NGX_LOG_EMERG, cf, 0,
|
||||
"Processing wl %d/%p", i, curr_r);
|
||||
|
||||
/*no custom location at all means that the rule is disabled */
|
||||
if (!curr_r->br->custom_locations) {
|
||||
NX_LOG_DEBUG(_debug_whitelist_heavy,
|
||||
NGX_LOG_EMERG, cf, 0,
|
||||
"WL %d is a disable rule.", i);
|
||||
|
||||
if (ngx_http_wlr_push_disabled(cf, dlc, curr_r) == NGX_ERROR)
|
||||
return (NGX_ERROR);
|
||||
continue;
|
||||
}
|
||||
ret = ngx_http_wlr_identify(cf, dlc, curr_r, &zone, &uri_idx, &name_idx);
|
||||
if (ret != NGX_OK) /* LCOV_EXCL_START */
|
||||
{
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"Following whitelist doesn't target any zone or is incorrect :");
|
||||
if (name_idx != -1)
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "whitelist target name : %V",
|
||||
&(custloc_array(curr_r->br->custom_locations->elts)[name_idx].target));
|
||||
else
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "whitelist has no target name.");
|
||||
if (uri_idx != -1)
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "whitelist target uri : %V",
|
||||
&(custloc_array(curr_r->br->custom_locations->elts)[uri_idx].target));
|
||||
else
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "whitelists has no target uri.");
|
||||
return (NGX_ERROR);
|
||||
} /* LCOV_EXCL_STOP */
|
||||
curr_r->br->zone = zone;
|
||||
/*
|
||||
** Handle regular-expression-matchzone rules :
|
||||
** Store them in a separate linked list, parsed
|
||||
** at runtime.
|
||||
*/
|
||||
if (curr_r->br->rx_mz == 1) {
|
||||
if (!dlc->rxmz_wlr) {
|
||||
dlc->rxmz_wlr = ngx_array_create(cf->pool, 1,
|
||||
sizeof(ngx_http_rule_t *));
|
||||
if (!dlc->rxmz_wlr) return (NGX_ERROR); /* LCOV_EXCL_LINE */
|
||||
}
|
||||
if (name_idx != -1 && !custloc_array(curr_r->br->custom_locations->elts)[name_idx].target_rx) {
|
||||
custloc_array(curr_r->br->custom_locations->elts)[name_idx].target_rx =
|
||||
ngx_pcalloc(cf->pool, sizeof(ngx_regex_compile_t));
|
||||
rgc = custloc_array(curr_r->br->custom_locations->elts)[name_idx].target_rx;
|
||||
rgc->options = PCRE_CASELESS|PCRE_MULTILINE;
|
||||
rgc->pattern = custloc_array(curr_r->br->custom_locations->elts)[name_idx].target;
|
||||
rgc->pool = cf->pool;
|
||||
rgc->err.len = 0;
|
||||
rgc->err.data = NULL;
|
||||
//custloc_array(curr_r->br->custom_locations->elts)[name_idx].target;
|
||||
if (ngx_regex_compile(rgc) != NGX_OK)
|
||||
return (NGX_ERROR);
|
||||
}
|
||||
if (uri_idx != -1 && !custloc_array(curr_r->br->custom_locations->elts)[uri_idx].target_rx) {
|
||||
custloc_array(curr_r->br->custom_locations->elts)[uri_idx].target_rx =
|
||||
ngx_pcalloc(cf->pool, sizeof(ngx_regex_compile_t));
|
||||
rgc = custloc_array(curr_r->br->custom_locations->elts)[uri_idx].target_rx;
|
||||
rgc->options = PCRE_CASELESS|PCRE_MULTILINE;
|
||||
rgc->pattern = custloc_array(curr_r->br->custom_locations->elts)[uri_idx].target;
|
||||
rgc->pool = cf->pool;
|
||||
rgc->err.len = 0;
|
||||
rgc->err.data = NULL;
|
||||
//custloc_array(curr_r->br->custom_locations->elts)[name_idx].target;
|
||||
if (ngx_regex_compile(rgc) != NGX_OK)
|
||||
return (NGX_ERROR);
|
||||
}
|
||||
|
||||
rptr = ngx_array_push(dlc->rxmz_wlr);
|
||||
if (!rptr)
|
||||
return (NGX_ERROR);
|
||||
*rptr = curr_r;
|
||||
continue;
|
||||
}
|
||||
/*
|
||||
** Handle static match-zones for hashtables
|
||||
*/
|
||||
father_wlr = ngx_http_wlr_find(cf, dlc, curr_r, zone, uri_idx, name_idx, (char **) &fullname);
|
||||
if (!father_wlr) {
|
||||
NX_LOG_DEBUG(_debug_whitelist_heavy,
|
||||
NGX_LOG_EMERG, cf, 0,
|
||||
"creating fresh WL [%s].", fullname);
|
||||
|
||||
/* creates a new whitelist rule in the right place.
|
||||
setup name and zone, create a new (empty) whitelist_location, as well
|
||||
as a new (empty) id aray. */
|
||||
father_wlr = ngx_array_push(dlc->tmp_wlr);
|
||||
if (!father_wlr)
|
||||
return (NGX_ERROR);
|
||||
memset(father_wlr, 0, sizeof(ngx_http_whitelist_rule_t));
|
||||
father_wlr->name = ngx_pcalloc(cf->pool, sizeof(ngx_str_t));
|
||||
if (!father_wlr->name)
|
||||
return (NGX_ERROR);
|
||||
father_wlr->name->len = strlen((const char *) fullname);
|
||||
father_wlr->name->data = (unsigned char *) fullname;
|
||||
father_wlr->zone = zone;
|
||||
/* If there is URI and no name idx, specify it,
|
||||
so that WL system won't get fooled by an argname like an URL */
|
||||
if (uri_idx != -1 && name_idx == -1)
|
||||
father_wlr->uri_only = 1;
|
||||
/* If target_name is present in son, report it. */
|
||||
if (curr_r->br->target_name)
|
||||
father_wlr->target_name = curr_r->br->target_name;
|
||||
}
|
||||
/*merges the two whitelist rules together, including custom_locations. */
|
||||
if (ngx_http_wlr_merge(cf, father_wlr, curr_r) != NGX_OK)
|
||||
return (NGX_ERROR);
|
||||
}
|
||||
|
||||
/* and finally, build the hashtables for various zones. */
|
||||
if (ngx_http_wlr_finalize_hashtables(cf, dlc) != NGX_OK)
|
||||
return (NGX_ERROR);
|
||||
/* TODO : Free old whitelist_rules (dlc->whitelist_rules)*/
|
||||
return (NGX_OK);
|
||||
}
|
||||
|
||||
/*
|
||||
function used for intensive log if dynamic flag is set.
|
||||
Output format :
|
||||
ip=<ip>&server=<server>&uri=<uri>&id=<id>&zone=<zone>&content=<content>
|
||||
*/
|
||||
|
||||
static const char *dummy_match_zones[] = {
|
||||
"HEADERS",
|
||||
"URL",
|
||||
"ARGS",
|
||||
"BODY",
|
||||
"FILE_EXT",
|
||||
"UNKNOWN",
|
||||
NULL
|
||||
};
|
||||
|
||||
|
||||
void naxsi_log_offending(ngx_str_t *name, ngx_str_t *val, ngx_http_request_t *req, ngx_http_rule_t *rule,
|
||||
enum DUMMY_MATCH_ZONE zone, ngx_int_t target_name) {
|
||||
ngx_str_t tmp_uri, tmp_val, tmp_name;
|
||||
ngx_str_t empty=ngx_string("");
|
||||
|
||||
//encode uri
|
||||
tmp_uri.len = req->uri.len + (2 * ngx_escape_uri(NULL, req->uri.data, req->uri.len,
|
||||
NGX_ESCAPE_ARGS));
|
||||
tmp_uri.data = ngx_pcalloc(req->pool, tmp_uri.len+1);
|
||||
if (tmp_uri.data == NULL)
|
||||
return ;
|
||||
ngx_escape_uri(tmp_uri.data, req->uri.data, req->uri.len, NGX_ESCAPE_ARGS);
|
||||
//encode val
|
||||
if (val->len <= 0)
|
||||
tmp_val = empty;
|
||||
else {
|
||||
tmp_val.len = val->len + (2 * ngx_escape_uri(NULL, val->data, val->len,
|
||||
NGX_ESCAPE_ARGS));
|
||||
tmp_val.data = ngx_pcalloc(req->pool, tmp_val.len+1);
|
||||
if (tmp_val.data == NULL)
|
||||
return ;
|
||||
ngx_escape_uri(tmp_val.data, val->data, val->len, NGX_ESCAPE_ARGS);
|
||||
}
|
||||
//encode name
|
||||
if (name->len <= 0)
|
||||
tmp_name = empty;
|
||||
else {
|
||||
tmp_name.len = name->len + (2 * ngx_escape_uri(NULL, name->data, name->len,
|
||||
NGX_ESCAPE_ARGS));
|
||||
tmp_name.data = ngx_pcalloc(req->pool, tmp_name.len+1);
|
||||
if (tmp_name.data == NULL)
|
||||
return ;
|
||||
ngx_escape_uri(tmp_name.data, name->data, name->len, NGX_ESCAPE_ARGS);
|
||||
}
|
||||
|
||||
ngx_log_error(NGX_LOG_ERR, req->connection->log, 0,
|
||||
"NAXSI_EXLOG: ip=%V&server=%V&uri=%V&id=%d&zone=%s%s&var_name=%V&content=%V",
|
||||
&(req->connection->addr_text), &(req->headers_in.server),
|
||||
&(tmp_uri), rule->rule_id, dummy_match_zones[zone], target_name?"|NAME":"", &(tmp_name), &(tmp_val));
|
||||
|
||||
if (tmp_val.len > 0)
|
||||
ngx_pfree(req->pool, tmp_val.data);
|
||||
if (tmp_name.len > 0)
|
||||
ngx_pfree(req->pool, tmp_name.data);
|
||||
if (tmp_uri.len > 0)
|
||||
ngx_pfree(req->pool, tmp_uri.data);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Used to check matched rule ID against wl IDs
|
||||
** Returns 1 if rule is whitelisted, 0 else
|
||||
*/
|
||||
int nx_check_ids(ngx_int_t match_id, ngx_array_t *wl_ids) {
|
||||
|
||||
int negative=0;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < wl_ids->nelts; i++) {
|
||||
if ( ((ngx_int_t *)wl_ids->elts)[i] == match_id)
|
||||
return (1);
|
||||
if ( ((ngx_int_t *)wl_ids->elts)[i] == 0)
|
||||
return (1);
|
||||
/* manage negative whitelists, except for internal rules */
|
||||
if ( ((ngx_int_t *)wl_ids->elts)[i] < 0 && match_id >= 1000) {
|
||||
negative = 1;
|
||||
/* negative wl excludes this one.*/
|
||||
if (match_id == -((ngx_int_t *)wl_ids->elts)[i]) {
|
||||
return (0);
|
||||
}
|
||||
}
|
||||
}
|
||||
return (negative == 1);
|
||||
}
|
||||
36
naxsi-0.55.3/naxsi_src/nginx.conf.example
Normal file
36
naxsi-0.55.3/naxsi_src/nginx.conf.example
Normal file
@@ -0,0 +1,36 @@
|
||||
master_process off;
|
||||
worker_processes 1;
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
events {
|
||||
worker_connections 1024;
|
||||
}
|
||||
http {
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
include mime.types;
|
||||
default_type application/octet-stream;
|
||||
sendfile on;
|
||||
keepalive_timeout 65;
|
||||
server {
|
||||
listen 4242;
|
||||
server_name localhost;
|
||||
location / {
|
||||
LearningMode;
|
||||
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_log /tmp/ngx_error.log debug;
|
||||
access_log /tmp/ngx_access.log;
|
||||
root html;
|
||||
index index.html index.htm;
|
||||
}
|
||||
error_page 500 502 503 504 /50x.html;
|
||||
location = /50x.html {
|
||||
root html;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
826
naxsi-0.55.3/naxsi_utils.c
Normal file
826
naxsi-0.55.3/naxsi_utils.c
Normal file
@@ -0,0 +1,826 @@
|
||||
/*
|
||||
* NAXSI, a web application firewall for NGINX
|
||||
* Copyright (C) 2016, Thibault 'bui' Koechlin
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give
|
||||
* permission to link the code of portions of this program with the
|
||||
* OpenSSL library under certain conditions as described in each
|
||||
* individual source file, and distribute linked combinations
|
||||
* including the two.
|
||||
* You must obey the GNU General Public License in all respects
|
||||
* for all of the code used other than OpenSSL. If you modify
|
||||
* file(s) with this exception, you may extend this exception to your
|
||||
* version of the file(s), but you are not obligated to do so. If you
|
||||
* do not wish to do so, delete this exception statement from your
|
||||
* version. If you delete this exception statement from all source
|
||||
* files in the program, then also delete it here.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "naxsi.h"
|
||||
|
||||
|
||||
static int naxsi_unescape_uri(u_char **dst, u_char **src, size_t size, ngx_uint_t type);
|
||||
|
||||
char *
|
||||
strnchr(const char *s, int c, int len)
|
||||
{
|
||||
int cpt;
|
||||
for (cpt = 0; cpt < len && s[cpt]; cpt++)
|
||||
if (s[cpt] == c)
|
||||
return ((char *) s+cpt);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
static char *
|
||||
strncasechr(const char *s, int c, int len)
|
||||
{
|
||||
int cpt;
|
||||
for (cpt = 0; cpt < len && s[cpt]; cpt++)
|
||||
if (tolower(s[cpt]) == c)
|
||||
return ((char *) s+cpt);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
** strstr: faster, stronger, harder
|
||||
** (because strstr from libc is very slow)
|
||||
*/
|
||||
char *
|
||||
strfaststr(unsigned char *haystack, unsigned int hl,
|
||||
unsigned char *needle, unsigned int nl)
|
||||
{
|
||||
char *cpt, *found, *end;
|
||||
if (hl < nl || !haystack || !needle || !nl || !hl) return (NULL);
|
||||
cpt = (char *) haystack;
|
||||
end = (char *) haystack + hl;
|
||||
while (cpt < end) {
|
||||
found = strncasechr((const char *) cpt, (int) needle[0], hl);
|
||||
if (!found) return (NULL);
|
||||
if (nl == 1) return (found);
|
||||
if (!strncasecmp((const char *)found+1, (const char *) needle+1, nl-1))
|
||||
return ((char *) found);
|
||||
else {
|
||||
if (found+nl >= end)
|
||||
break;
|
||||
if (found+nl < end) {
|
||||
/* the haystack is shrinking */
|
||||
cpt = found+1;
|
||||
hl = (unsigned int) (end - cpt);
|
||||
}
|
||||
}
|
||||
}
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
/* unescape routine, returns number of nullbytes present */
|
||||
int naxsi_unescape(ngx_str_t *str) {
|
||||
u_char *dst, *src;
|
||||
u_int nullbytes = 0, bad = 0, i;
|
||||
|
||||
dst = str->data;
|
||||
src = str->data;
|
||||
|
||||
bad = naxsi_unescape_uri(&src, &dst,
|
||||
str->len, 0);
|
||||
str->len = src - str->data;
|
||||
//tmp hack fix, avoid %00 & co (null byte) encoding :p
|
||||
for (i = 0; i < str->len; i++)
|
||||
if (str->data[i] == 0x0)
|
||||
{
|
||||
nullbytes++;
|
||||
str->data[i] = '0';
|
||||
}
|
||||
return (nullbytes+bad);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Patched ngx_unescape_uri :
|
||||
** The original one does not care if the character following % is in valid range.
|
||||
** For example, with the original one :
|
||||
** '%uff' -> 'uff'
|
||||
*/
|
||||
static int
|
||||
naxsi_unescape_uri(u_char **dst, u_char **src, size_t size, ngx_uint_t type)
|
||||
{
|
||||
u_char *d, *s, ch, c, decoded;
|
||||
int bad = 0;
|
||||
|
||||
enum {
|
||||
sw_usual = 0,
|
||||
sw_quoted,
|
||||
sw_quoted_second
|
||||
} state;
|
||||
|
||||
d = *dst;
|
||||
s = *src;
|
||||
|
||||
state = 0;
|
||||
decoded = 0;
|
||||
|
||||
while (size--) {
|
||||
|
||||
ch = *s++;
|
||||
|
||||
switch (state) {
|
||||
case sw_usual:
|
||||
if (ch == '%') {
|
||||
state = sw_quoted;
|
||||
break;
|
||||
}
|
||||
|
||||
*d++ = ch;
|
||||
break;
|
||||
|
||||
case sw_quoted:
|
||||
|
||||
if (ch >= '0' && ch <= '9') {
|
||||
decoded = (u_char) (ch - '0');
|
||||
state = sw_quoted_second;
|
||||
break;
|
||||
}
|
||||
|
||||
c = (u_char) (ch | 0x20);
|
||||
if (c >= 'a' && c <= 'f') {
|
||||
decoded = (u_char) (c - 'a' + 10);
|
||||
state = sw_quoted_second;
|
||||
break;
|
||||
}
|
||||
|
||||
/* the invalid quoted character */
|
||||
bad++;
|
||||
state = sw_usual;
|
||||
*d++ = '%';
|
||||
*d++ = ch;
|
||||
break;
|
||||
|
||||
case sw_quoted_second:
|
||||
|
||||
state = sw_usual;
|
||||
|
||||
if (ch >= '0' && ch <= '9') {
|
||||
ch = (u_char) ((decoded << 4) + ch - '0');
|
||||
|
||||
*d++ = ch;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
c = (u_char) (ch | 0x20);
|
||||
if (c >= 'a' && c <= 'f') {
|
||||
ch = (u_char) ((decoded << 4) + c - 'a' + 10);
|
||||
|
||||
*d++ = ch;
|
||||
|
||||
break;
|
||||
}
|
||||
/* the invalid quoted character */
|
||||
/* as it happened in the 2nd part of quoted character,
|
||||
we need to restore the decoded char as well. */
|
||||
*d++ = '%';
|
||||
*d++ = *(s - 2);
|
||||
*d++ = *(s - 1);
|
||||
bad++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
*dst = d;
|
||||
*src = s;
|
||||
|
||||
return (bad);
|
||||
}
|
||||
|
||||
|
||||
/* push rule into disabled rules. */
|
||||
static ngx_int_t
|
||||
ngx_http_wlr_push_disabled(ngx_conf_t *cf, ngx_http_dummy_loc_conf_t *dlc,
|
||||
ngx_http_rule_t *curr) {
|
||||
ngx_http_rule_t **dr;
|
||||
|
||||
if (!dlc->disabled_rules)
|
||||
dlc->disabled_rules = ngx_array_create(cf->pool, 4,
|
||||
sizeof(ngx_http_rule_t *));
|
||||
if (!dlc->disabled_rules)
|
||||
return (NGX_ERROR);
|
||||
dr = ngx_array_push(dlc->disabled_rules);
|
||||
if (!dr)
|
||||
return (NGX_ERROR);
|
||||
*dr = (ngx_http_rule_t *) curr;
|
||||
return (NGX_OK);
|
||||
}
|
||||
|
||||
/* merge the two rules into father_wl, meaning
|
||||
ids. Not locations, as we are getting rid of it */
|
||||
static ngx_int_t
|
||||
ngx_http_wlr_merge(ngx_conf_t *cf, ngx_http_whitelist_rule_t *father_wl,
|
||||
ngx_http_rule_t *curr) {
|
||||
uint i;
|
||||
ngx_int_t *tmp_ptr;
|
||||
|
||||
NX_LOG_DEBUG(_debug_whitelist,
|
||||
NGX_LOG_EMERG, cf, 0, "[naxsi] merging similar wl(s)");
|
||||
|
||||
|
||||
if (!father_wl->ids)
|
||||
{
|
||||
father_wl->ids = ngx_array_create(cf->pool, 3, sizeof(ngx_int_t));
|
||||
if (!father_wl->ids)
|
||||
return (NGX_ERROR);
|
||||
}
|
||||
for (i = 0; i < curr->wlid_array->nelts; i++) {
|
||||
tmp_ptr = ngx_array_push(father_wl->ids);
|
||||
if (!tmp_ptr)
|
||||
return (NGX_ERROR);
|
||||
*tmp_ptr = ((ngx_int_t *)curr->wlid_array->elts)[i];
|
||||
//*tmp_ptr = curr->wlid_array->elts[i];
|
||||
}
|
||||
return (NGX_OK);
|
||||
}
|
||||
|
||||
/*check rule, returns associed zone, as well as location index.
|
||||
location index refers to $URL:bla or $ARGS_VAR:bla */
|
||||
#define custloc_array(x) ((ngx_http_custom_rule_location_t *) x)
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_wlr_identify(ngx_conf_t *cf, ngx_http_dummy_loc_conf_t *dlc,
|
||||
ngx_http_rule_t *curr, int *zone,
|
||||
int *uri_idx, int *name_idx) {
|
||||
|
||||
uint i;
|
||||
|
||||
/*
|
||||
identify global match zones (|ARGS|BODY|HEADERS|URL|FILE_EXT)
|
||||
*/
|
||||
if (curr->br->body || curr->br->body_var)
|
||||
*zone = BODY;
|
||||
else if (curr->br->headers || curr->br->headers_var)
|
||||
*zone = HEADERS;
|
||||
else if (curr->br->args || curr->br->args_var)
|
||||
*zone = ARGS;
|
||||
else if (curr->br->url) /*don't assume that named $URL means zone is URL.*/
|
||||
*zone = URL;
|
||||
else if (curr->br->file_ext)
|
||||
*zone = FILE_EXT;
|
||||
/*
|
||||
if we're facing a WL in the style $URL:/bla|ARGS (or any other zone),
|
||||
push it to
|
||||
*/
|
||||
for (i = 0; i < curr->br->custom_locations->nelts; i++) {
|
||||
/*
|
||||
locate target URL if exists ($URL:/bla|ARGS) or ($URL:/bla|$ARGS_VAR:foo)
|
||||
*/
|
||||
if (custloc_array(curr->br->custom_locations->elts)[i].specific_url) {
|
||||
NX_LOG_DEBUG(_debug_whitelist_heavy,
|
||||
NGX_LOG_EMERG, cf, 0,
|
||||
"whitelist has URI %V", &(custloc_array(curr->br->custom_locations->elts)[i].target));
|
||||
|
||||
*uri_idx = i;
|
||||
}
|
||||
/*
|
||||
identify named match zones ($ARGS_VAR:bla|$HEADERS_VAR:bla|$BODY_VAR:bla)
|
||||
*/
|
||||
if (custloc_array(curr->br->custom_locations->elts)[i].body_var) {
|
||||
NX_LOG_DEBUG(_debug_whitelist_heavy,
|
||||
NGX_LOG_EMERG, cf, 0,
|
||||
"whitelist has body_var %V", &(custloc_array(curr->br->custom_locations->elts)[i].target));
|
||||
|
||||
/*#217 : scream on incorrect rules*/
|
||||
if (*name_idx != -1) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "whitelist can't target more than one BODY item.");
|
||||
return (NGX_ERROR);
|
||||
}
|
||||
*name_idx = i;
|
||||
*zone = BODY;
|
||||
}
|
||||
if (custloc_array(curr->br->custom_locations->elts)[i].headers_var) {
|
||||
NX_LOG_DEBUG(_debug_whitelist_heavy,
|
||||
NGX_LOG_EMERG, cf, 0,
|
||||
"whitelist has header_var %V", &(custloc_array(curr->br->custom_locations->elts)[i].target));
|
||||
|
||||
/*#217 : scream on incorrect rules*/
|
||||
if (*name_idx != -1) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "whitelist can't target more than one HEADER item.");
|
||||
return (NGX_ERROR);
|
||||
}
|
||||
*name_idx = i;
|
||||
*zone = HEADERS;
|
||||
}
|
||||
if (custloc_array(curr->br->custom_locations->elts)[i].args_var) {
|
||||
NX_LOG_DEBUG(_debug_whitelist_heavy,
|
||||
NGX_LOG_EMERG, cf, 0,
|
||||
"whitelist has arg_var %V", &(custloc_array(curr->br->custom_locations->elts)[i].target));
|
||||
|
||||
/*#217 : scream on incorrect rules*/
|
||||
if (*name_idx != -1) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "whitelist can't target more than one ARGS item.");
|
||||
return (NGX_ERROR);
|
||||
}
|
||||
|
||||
*name_idx = i;
|
||||
*zone = ARGS;
|
||||
}
|
||||
}
|
||||
if (*zone == -1)
|
||||
return (NGX_ERROR);
|
||||
return (NGX_OK);
|
||||
}
|
||||
|
||||
ngx_http_whitelist_rule_t *
|
||||
ngx_http_wlr_find(ngx_conf_t *cf, ngx_http_dummy_loc_conf_t *dlc,
|
||||
ngx_http_rule_t *curr, int zone, int uri_idx,
|
||||
int name_idx, char **fullname) {
|
||||
uint i;
|
||||
|
||||
/* Create unique string for rule, and try to find it in existing rules.*/
|
||||
/*name AND uri*/
|
||||
|
||||
if (uri_idx != -1 && name_idx != -1) {
|
||||
NX_LOG_DEBUG(_debug_whitelist_heavy,
|
||||
NGX_LOG_EMERG, cf, 0,
|
||||
"whitelist has uri + name");
|
||||
|
||||
/* allocate one extra byte in case curr->br->target_name is set. */
|
||||
*fullname = ngx_pcalloc(cf->pool, custloc_array(curr->br->custom_locations->elts)[name_idx].target.len +
|
||||
custloc_array(curr->br->custom_locations->elts)[uri_idx].target.len + 3);
|
||||
/* if WL targets variable name instead of content, prefix hash with '#' */
|
||||
if (curr->br->target_name) {
|
||||
NX_LOG_DEBUG(_debug_whitelist_heavy,
|
||||
NGX_LOG_EMERG, cf, 0,
|
||||
"whitelist targets |NAME");
|
||||
|
||||
|
||||
strncat(*fullname, (const char *) "#", 1);
|
||||
}
|
||||
strncat(*fullname, (const char *) custloc_array(curr->br->custom_locations->elts)[uri_idx].target.data,
|
||||
custloc_array(curr->br->custom_locations->elts)[uri_idx].target.len);
|
||||
strncat(*fullname, (const char *) "#", 1);
|
||||
strncat(*fullname, (const char *) custloc_array(curr->br->custom_locations->elts)[name_idx].target.data,
|
||||
custloc_array(curr->br->custom_locations->elts)[name_idx].target.len);
|
||||
}
|
||||
/* only uri */
|
||||
else if (uri_idx != -1 && name_idx == -1) {
|
||||
NX_LOG_DEBUG(_debug_whitelist_heavy,
|
||||
NGX_LOG_EMERG, cf, 0,
|
||||
"whitelist has uri");
|
||||
|
||||
//XXX set flag only_uri
|
||||
*fullname = ngx_pcalloc(cf->pool, custloc_array(curr->br->custom_locations->elts)[uri_idx].target.len + 3);
|
||||
if (curr->br->target_name) {
|
||||
NX_LOG_DEBUG(_debug_whitelist_heavy,
|
||||
NGX_LOG_EMERG, cf, 0,
|
||||
"whitelist targets |NAME");
|
||||
|
||||
|
||||
strncat(*fullname, (const char *) "#", 1);
|
||||
}
|
||||
|
||||
|
||||
strncat(*fullname, (const char *) custloc_array(curr->br->custom_locations->elts)[uri_idx].target.data,
|
||||
custloc_array(curr->br->custom_locations->elts)[uri_idx].target.len);
|
||||
}
|
||||
/* only name */
|
||||
else if (name_idx != -1) {
|
||||
NX_LOG_DEBUG(_debug_whitelist_heavy,
|
||||
NGX_LOG_EMERG, cf, 0,
|
||||
"whitelist has name");
|
||||
|
||||
*fullname = ngx_pcalloc(cf->pool, custloc_array(curr->br->custom_locations->elts)[name_idx].target.len + 2);
|
||||
if (curr->br->target_name)
|
||||
strncat(*fullname, (const char *) "#", 1);
|
||||
strncat(*fullname, (const char *) custloc_array(curr->br->custom_locations->elts)[name_idx].target.data,
|
||||
custloc_array(curr->br->custom_locations->elts)[name_idx].target.len);
|
||||
}
|
||||
/* problem houston */
|
||||
else
|
||||
return (NULL);
|
||||
|
||||
for (i = 0; i < dlc->tmp_wlr->nelts; i++)
|
||||
if (!strcmp((const char *)*fullname, (const char *)((ngx_http_whitelist_rule_t *) dlc->tmp_wlr->elts)[i].name->data) &&
|
||||
((ngx_http_whitelist_rule_t *) dlc->tmp_wlr->elts)[i].zone == (uint) zone)
|
||||
{
|
||||
NX_LOG_DEBUG(_debug_whitelist_heavy,
|
||||
NGX_LOG_EMERG, cf, 0,
|
||||
"found existing 'same' WL : %V", ((ngx_http_whitelist_rule_t *) dlc->tmp_wlr->elts)[i].name);
|
||||
|
||||
return (&((ngx_http_whitelist_rule_t *)dlc->tmp_wlr->elts)[i]);
|
||||
}
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_wlr_finalize_hashtables(ngx_conf_t *cf, ngx_http_dummy_loc_conf_t *dlc) {
|
||||
int get_sz = 0, headers_sz = 0, body_sz = 0, uri_sz = 0;
|
||||
ngx_array_t *get_ar = NULL, *headers_ar = NULL, *body_ar = NULL, *uri_ar = NULL;
|
||||
ngx_hash_key_t *arr_node;
|
||||
ngx_hash_init_t hash_init;
|
||||
uint i;
|
||||
|
||||
NX_LOG_DEBUG(_debug_whitelist_heavy,
|
||||
NGX_LOG_EMERG, cf, 0,
|
||||
"finalizing hashtables");
|
||||
|
||||
|
||||
for (i = 0; i < dlc->tmp_wlr->nelts; i++) {
|
||||
switch (((ngx_http_whitelist_rule_t *) dlc->tmp_wlr->elts)[i].zone) {
|
||||
case FILE_EXT:
|
||||
case BODY:
|
||||
body_sz++;
|
||||
break;
|
||||
case HEADERS:
|
||||
headers_sz++;
|
||||
break;
|
||||
case URL:
|
||||
uri_sz++;
|
||||
break;
|
||||
case ARGS:
|
||||
get_sz++;
|
||||
break;
|
||||
case UNKNOWN:
|
||||
default:
|
||||
return (NGX_ERROR);
|
||||
}
|
||||
}
|
||||
NX_LOG_DEBUG(_debug_whitelist_heavy,
|
||||
NGX_LOG_EMERG, cf, 0,
|
||||
"nb items : body:%d headers:%d uri:%d get:%d",
|
||||
body_sz, headers_sz, uri_sz, get_sz);
|
||||
|
||||
|
||||
if (get_sz)
|
||||
get_ar = ngx_array_create(cf->pool, get_sz, sizeof(ngx_hash_key_t));
|
||||
if (headers_sz)
|
||||
headers_ar = ngx_array_create(cf->pool, headers_sz, sizeof(ngx_hash_key_t));
|
||||
if (body_sz)
|
||||
body_ar = ngx_array_create(cf->pool, body_sz, sizeof(ngx_hash_key_t));
|
||||
if (uri_sz)
|
||||
uri_ar = ngx_array_create(cf->pool, uri_sz, sizeof(ngx_hash_key_t));
|
||||
for (i = 0; i < dlc->tmp_wlr->nelts; i++) {
|
||||
switch (((ngx_http_whitelist_rule_t *) dlc->tmp_wlr->elts)[i].zone) {
|
||||
case FILE_EXT:
|
||||
case BODY:
|
||||
arr_node = (ngx_hash_key_t*) ngx_array_push(body_ar);
|
||||
break;
|
||||
case HEADERS:
|
||||
arr_node = (ngx_hash_key_t*) ngx_array_push(headers_ar);
|
||||
break;
|
||||
case URL:
|
||||
arr_node = (ngx_hash_key_t*) ngx_array_push(uri_ar);
|
||||
break;
|
||||
case ARGS:
|
||||
arr_node = (ngx_hash_key_t*) ngx_array_push(get_ar);
|
||||
break;
|
||||
default:
|
||||
return (NGX_ERROR);
|
||||
}
|
||||
if (!arr_node)
|
||||
return (NGX_ERROR);
|
||||
ngx_memset(arr_node, 0, sizeof(ngx_hash_key_t));
|
||||
arr_node->key = *(((ngx_http_whitelist_rule_t *) dlc->tmp_wlr->elts)[i].name);
|
||||
arr_node->key_hash = ngx_hash_key_lc(((ngx_http_whitelist_rule_t *) dlc->tmp_wlr->elts)[i].name->data,
|
||||
((ngx_http_whitelist_rule_t *) dlc->tmp_wlr->elts)[i].name->len);
|
||||
arr_node->value = (void *) &(((ngx_http_whitelist_rule_t *) dlc->tmp_wlr->elts)[i]);
|
||||
#ifdef SPECIAL__debug_whitelist_heavy
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "pushing new WL, zone:%d, target:%V, %d IDs",
|
||||
((ngx_http_whitelist_rule_t *) dlc->tmp_wlr->elts)[i].zone , ((ngx_http_whitelist_rule_t *) dlc->tmp_wlr->elts)[i].name,
|
||||
((ngx_http_whitelist_rule_t *) dlc->tmp_wlr->elts)[i].ids->nelts);
|
||||
unsigned int z;
|
||||
for (z = 0; z < ((ngx_http_whitelist_rule_t *) dlc->tmp_wlr->elts)[i].ids->nelts; z++)
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "id:%d",
|
||||
((int *)((ngx_http_whitelist_rule_t *) dlc->tmp_wlr->elts)[i].ids->elts)[z]);
|
||||
#endif
|
||||
}
|
||||
hash_init.key = &ngx_hash_key_lc;
|
||||
hash_init.pool = cf->pool;
|
||||
hash_init.temp_pool = NULL;
|
||||
hash_init.max_size = 1024;
|
||||
hash_init.bucket_size = 512;
|
||||
|
||||
if (body_ar) {
|
||||
dlc->wlr_body_hash = (ngx_hash_t*) ngx_pcalloc(cf->pool, sizeof(ngx_hash_t));
|
||||
hash_init.hash = dlc->wlr_body_hash;
|
||||
hash_init.name = "wlr_body_hash";
|
||||
if (ngx_hash_init(&hash_init, (ngx_hash_key_t*) body_ar->elts,
|
||||
body_ar->nelts) != NGX_OK) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "$BODY hashtable init failed"); /* LCOV_EXCL_LINE */
|
||||
return (NGX_ERROR); /* LCOV_EXCL_LINE */
|
||||
}
|
||||
else
|
||||
NX_LOG_DEBUG(_debug_whitelist, NGX_LOG_EMERG, cf, 0, "$BODY hashtable init successed !");
|
||||
}
|
||||
if (uri_ar) {
|
||||
dlc->wlr_url_hash = (ngx_hash_t*) ngx_pcalloc(cf->pool, sizeof(ngx_hash_t));
|
||||
hash_init.hash = dlc->wlr_url_hash;
|
||||
hash_init.name = "wlr_url_hash";
|
||||
if (ngx_hash_init(&hash_init, (ngx_hash_key_t*) uri_ar->elts,
|
||||
uri_ar->nelts) != NGX_OK) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "$URL hashtable init failed"); /* LCOV_EXCL_LINE */
|
||||
return (NGX_ERROR); /* LCOV_EXCL_LINE */
|
||||
}
|
||||
else
|
||||
NX_LOG_DEBUG(_debug_whitelist, NGX_LOG_EMERG, cf, 0, "$URL hashtable init successed !");
|
||||
|
||||
}
|
||||
if (get_ar) {
|
||||
dlc->wlr_args_hash = (ngx_hash_t*) ngx_pcalloc(cf->pool, sizeof(ngx_hash_t));
|
||||
hash_init.hash = dlc->wlr_args_hash;
|
||||
hash_init.name = "wlr_args_hash";
|
||||
if (ngx_hash_init(&hash_init, (ngx_hash_key_t*) get_ar->elts,
|
||||
get_ar->nelts) != NGX_OK) { /* LCOV_EXCL_LINE */
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "$ARGS hashtable init failed"); /* LCOV_EXCL_LINE */
|
||||
return (NGX_ERROR);
|
||||
}
|
||||
else
|
||||
NX_LOG_DEBUG(_debug_whitelist, NGX_LOG_EMERG, cf, 0, "$ARGS hashtable init successed %d !",
|
||||
dlc->wlr_args_hash->size);
|
||||
|
||||
}
|
||||
if (headers_ar) {
|
||||
dlc->wlr_headers_hash = (ngx_hash_t*) ngx_pcalloc(cf->pool, sizeof(ngx_hash_t));
|
||||
hash_init.hash = dlc->wlr_headers_hash;
|
||||
hash_init.name = "wlr_headers_hash";
|
||||
if (ngx_hash_init(&hash_init, (ngx_hash_key_t*) headers_ar->elts,
|
||||
headers_ar->nelts) != NGX_OK) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "$HEADERS hashtable init failed"); /* LCOV_EXCL_LINE */
|
||||
return (NGX_ERROR); /* LCOV_EXCL_LINE */
|
||||
}
|
||||
else
|
||||
NX_LOG_DEBUG(_debug_whitelist, NGX_LOG_EMERG, cf, 0, "$HEADERS hashtable init successed %d !",
|
||||
dlc->wlr_headers_hash->size);
|
||||
|
||||
}
|
||||
return (NGX_OK);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** This function will take the whitelist basicrules generated during the configuration
|
||||
** parsing phase, and aggregate them to build hashtables according to the matchzones.
|
||||
**
|
||||
** As whitelist can be in the form :
|
||||
** "mz:$URL:bla|$ARGS_VAR:foo"
|
||||
** "mz:$URL:bla|ARGS"
|
||||
** "mz:$HEADERS_VAR:Cookie"
|
||||
** ...
|
||||
**
|
||||
** So, we will aggregate all the rules that are pointing to the same URL together,
|
||||
** as well as rules targetting the same argument name / zone.
|
||||
*/
|
||||
|
||||
ngx_int_t
|
||||
ngx_http_dummy_create_hashtables_n(ngx_http_dummy_loc_conf_t *dlc,
|
||||
ngx_conf_t *cf)
|
||||
{
|
||||
int zone, uri_idx, name_idx, ret;
|
||||
ngx_http_rule_t *curr_r/*, *father_r*/;
|
||||
ngx_http_whitelist_rule_t *father_wlr;
|
||||
ngx_http_rule_t **rptr;
|
||||
ngx_regex_compile_t *rgc;
|
||||
char *fullname;
|
||||
uint i;
|
||||
|
||||
if (!dlc->whitelist_rules || dlc->whitelist_rules->nelts < 1) {
|
||||
NX_LOG_DEBUG(_debug_whitelist_heavy ,
|
||||
NGX_LOG_EMERG, cf, 0,
|
||||
"No whitelist registred, but it's your call.");
|
||||
|
||||
return (NGX_OK);
|
||||
}
|
||||
NX_LOG_DEBUG(_debug_whitelist_heavy,
|
||||
NGX_LOG_EMERG, cf, 0,
|
||||
"Building whitelist hashtables, %d items in list",
|
||||
dlc->whitelist_rules->nelts);
|
||||
|
||||
dlc->tmp_wlr = ngx_array_create(cf->pool, dlc->whitelist_rules->nelts,
|
||||
sizeof(ngx_http_whitelist_rule_t));
|
||||
/* iterate through each stored whitelist rule. */
|
||||
for (i = 0; i < dlc->whitelist_rules->nelts; i++) {
|
||||
uri_idx = name_idx = zone = -1;
|
||||
/*a whitelist is in fact just another basic_rule_t */
|
||||
curr_r = &(((ngx_http_rule_t*)(dlc->whitelist_rules->elts))[i]);
|
||||
NX_LOG_DEBUG(_debug_whitelist_heavy,
|
||||
NGX_LOG_EMERG, cf, 0,
|
||||
"Processing wl %d/%p", i, curr_r);
|
||||
|
||||
/*no custom location at all means that the rule is disabled */
|
||||
if (!curr_r->br->custom_locations) {
|
||||
NX_LOG_DEBUG(_debug_whitelist_heavy,
|
||||
NGX_LOG_EMERG, cf, 0,
|
||||
"WL %d is a disable rule.", i);
|
||||
|
||||
if (ngx_http_wlr_push_disabled(cf, dlc, curr_r) == NGX_ERROR)
|
||||
return (NGX_ERROR);
|
||||
continue;
|
||||
}
|
||||
ret = ngx_http_wlr_identify(cf, dlc, curr_r, &zone, &uri_idx, &name_idx);
|
||||
if (ret != NGX_OK) /* LCOV_EXCL_START */
|
||||
{
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"Following whitelist doesn't target any zone or is incorrect :");
|
||||
if (name_idx != -1)
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "whitelist target name : %V",
|
||||
&(custloc_array(curr_r->br->custom_locations->elts)[name_idx].target));
|
||||
else
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "whitelist has no target name.");
|
||||
if (uri_idx != -1)
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "whitelist target uri : %V",
|
||||
&(custloc_array(curr_r->br->custom_locations->elts)[uri_idx].target));
|
||||
else
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "whitelists has no target uri.");
|
||||
return (NGX_ERROR);
|
||||
} /* LCOV_EXCL_STOP */
|
||||
curr_r->br->zone = zone;
|
||||
/*
|
||||
** Handle regular-expression-matchzone rules :
|
||||
** Store them in a separate linked list, parsed
|
||||
** at runtime.
|
||||
*/
|
||||
if (curr_r->br->rx_mz == 1) {
|
||||
if (!dlc->rxmz_wlr) {
|
||||
dlc->rxmz_wlr = ngx_array_create(cf->pool, 1,
|
||||
sizeof(ngx_http_rule_t *));
|
||||
if (!dlc->rxmz_wlr) return (NGX_ERROR); /* LCOV_EXCL_LINE */
|
||||
}
|
||||
if (name_idx != -1 && !custloc_array(curr_r->br->custom_locations->elts)[name_idx].target_rx) {
|
||||
custloc_array(curr_r->br->custom_locations->elts)[name_idx].target_rx =
|
||||
ngx_pcalloc(cf->pool, sizeof(ngx_regex_compile_t));
|
||||
rgc = custloc_array(curr_r->br->custom_locations->elts)[name_idx].target_rx;
|
||||
rgc->options = PCRE_CASELESS|PCRE_MULTILINE;
|
||||
rgc->pattern = custloc_array(curr_r->br->custom_locations->elts)[name_idx].target;
|
||||
rgc->pool = cf->pool;
|
||||
rgc->err.len = 0;
|
||||
rgc->err.data = NULL;
|
||||
//custloc_array(curr_r->br->custom_locations->elts)[name_idx].target;
|
||||
if (ngx_regex_compile(rgc) != NGX_OK)
|
||||
return (NGX_ERROR);
|
||||
}
|
||||
if (uri_idx != -1 && !custloc_array(curr_r->br->custom_locations->elts)[uri_idx].target_rx) {
|
||||
custloc_array(curr_r->br->custom_locations->elts)[uri_idx].target_rx =
|
||||
ngx_pcalloc(cf->pool, sizeof(ngx_regex_compile_t));
|
||||
rgc = custloc_array(curr_r->br->custom_locations->elts)[uri_idx].target_rx;
|
||||
rgc->options = PCRE_CASELESS|PCRE_MULTILINE;
|
||||
rgc->pattern = custloc_array(curr_r->br->custom_locations->elts)[uri_idx].target;
|
||||
rgc->pool = cf->pool;
|
||||
rgc->err.len = 0;
|
||||
rgc->err.data = NULL;
|
||||
//custloc_array(curr_r->br->custom_locations->elts)[name_idx].target;
|
||||
if (ngx_regex_compile(rgc) != NGX_OK)
|
||||
return (NGX_ERROR);
|
||||
}
|
||||
|
||||
rptr = ngx_array_push(dlc->rxmz_wlr);
|
||||
if (!rptr)
|
||||
return (NGX_ERROR);
|
||||
*rptr = curr_r;
|
||||
continue;
|
||||
}
|
||||
/*
|
||||
** Handle static match-zones for hashtables
|
||||
*/
|
||||
father_wlr = ngx_http_wlr_find(cf, dlc, curr_r, zone, uri_idx, name_idx, (char **) &fullname);
|
||||
if (!father_wlr) {
|
||||
NX_LOG_DEBUG(_debug_whitelist_heavy,
|
||||
NGX_LOG_EMERG, cf, 0,
|
||||
"creating fresh WL [%s].", fullname);
|
||||
|
||||
/* creates a new whitelist rule in the right place.
|
||||
setup name and zone, create a new (empty) whitelist_location, as well
|
||||
as a new (empty) id aray. */
|
||||
father_wlr = ngx_array_push(dlc->tmp_wlr);
|
||||
if (!father_wlr)
|
||||
return (NGX_ERROR);
|
||||
memset(father_wlr, 0, sizeof(ngx_http_whitelist_rule_t));
|
||||
father_wlr->name = ngx_pcalloc(cf->pool, sizeof(ngx_str_t));
|
||||
if (!father_wlr->name)
|
||||
return (NGX_ERROR);
|
||||
father_wlr->name->len = strlen((const char *) fullname);
|
||||
father_wlr->name->data = (unsigned char *) fullname;
|
||||
father_wlr->zone = zone;
|
||||
/* If there is URI and no name idx, specify it,
|
||||
so that WL system won't get fooled by an argname like an URL */
|
||||
if (uri_idx != -1 && name_idx == -1)
|
||||
father_wlr->uri_only = 1;
|
||||
/* If target_name is present in son, report it. */
|
||||
if (curr_r->br->target_name)
|
||||
father_wlr->target_name = curr_r->br->target_name;
|
||||
}
|
||||
/*merges the two whitelist rules together, including custom_locations. */
|
||||
if (ngx_http_wlr_merge(cf, father_wlr, curr_r) != NGX_OK)
|
||||
return (NGX_ERROR);
|
||||
}
|
||||
|
||||
/* and finally, build the hashtables for various zones. */
|
||||
if (ngx_http_wlr_finalize_hashtables(cf, dlc) != NGX_OK)
|
||||
return (NGX_ERROR);
|
||||
/* TODO : Free old whitelist_rules (dlc->whitelist_rules)*/
|
||||
return (NGX_OK);
|
||||
}
|
||||
|
||||
/*
|
||||
function used for intensive log if dynamic flag is set.
|
||||
Output format :
|
||||
ip=<ip>&server=<server>&uri=<uri>&id=<id>&zone=<zone>&content=<content>
|
||||
*/
|
||||
|
||||
static const char *dummy_match_zones[] = {
|
||||
"HEADERS",
|
||||
"URL",
|
||||
"ARGS",
|
||||
"BODY",
|
||||
"FILE_EXT",
|
||||
"UNKNOWN",
|
||||
NULL
|
||||
};
|
||||
|
||||
|
||||
void naxsi_log_offending(ngx_str_t *name, ngx_str_t *val, ngx_http_request_t *req, ngx_http_rule_t *rule,
|
||||
enum DUMMY_MATCH_ZONE zone, ngx_int_t target_name) {
|
||||
ngx_str_t tmp_uri, tmp_val, tmp_name;
|
||||
ngx_str_t empty=ngx_string("");
|
||||
|
||||
//encode uri
|
||||
tmp_uri.len = req->uri.len + (2 * ngx_escape_uri(NULL, req->uri.data, req->uri.len,
|
||||
NGX_ESCAPE_ARGS));
|
||||
tmp_uri.data = ngx_pcalloc(req->pool, tmp_uri.len+1);
|
||||
if (tmp_uri.data == NULL)
|
||||
return ;
|
||||
ngx_escape_uri(tmp_uri.data, req->uri.data, req->uri.len, NGX_ESCAPE_ARGS);
|
||||
//encode val
|
||||
if (val->len <= 0)
|
||||
tmp_val = empty;
|
||||
else {
|
||||
tmp_val.len = val->len + (2 * ngx_escape_uri(NULL, val->data, val->len,
|
||||
NGX_ESCAPE_ARGS));
|
||||
tmp_val.data = ngx_pcalloc(req->pool, tmp_val.len+1);
|
||||
if (tmp_val.data == NULL)
|
||||
return ;
|
||||
ngx_escape_uri(tmp_val.data, val->data, val->len, NGX_ESCAPE_ARGS);
|
||||
}
|
||||
//encode name
|
||||
if (name->len <= 0)
|
||||
tmp_name = empty;
|
||||
else {
|
||||
tmp_name.len = name->len + (2 * ngx_escape_uri(NULL, name->data, name->len,
|
||||
NGX_ESCAPE_ARGS));
|
||||
tmp_name.data = ngx_pcalloc(req->pool, tmp_name.len+1);
|
||||
if (tmp_name.data == NULL)
|
||||
return ;
|
||||
ngx_escape_uri(tmp_name.data, name->data, name->len, NGX_ESCAPE_ARGS);
|
||||
}
|
||||
|
||||
ngx_log_error(NGX_LOG_ERR, req->connection->log, 0,
|
||||
"NAXSI_EXLOG: ip=%V&server=%V&uri=%V&id=%d&zone=%s%s&var_name=%V&content=%V",
|
||||
&(req->connection->addr_text), &(req->headers_in.server),
|
||||
&(tmp_uri), rule->rule_id, dummy_match_zones[zone], target_name?"|NAME":"", &(tmp_name), &(tmp_val));
|
||||
|
||||
if (tmp_val.len > 0)
|
||||
ngx_pfree(req->pool, tmp_val.data);
|
||||
if (tmp_name.len > 0)
|
||||
ngx_pfree(req->pool, tmp_name.data);
|
||||
if (tmp_uri.len > 0)
|
||||
ngx_pfree(req->pool, tmp_uri.data);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Used to check matched rule ID against wl IDs
|
||||
** Returns 1 if rule is whitelisted, 0 else
|
||||
*/
|
||||
int nx_check_ids(ngx_int_t match_id, ngx_array_t *wl_ids) {
|
||||
|
||||
int negative=0;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < wl_ids->nelts; i++) {
|
||||
if ( ((ngx_int_t *)wl_ids->elts)[i] == match_id)
|
||||
return (1);
|
||||
if ( ((ngx_int_t *)wl_ids->elts)[i] == 0)
|
||||
return (1);
|
||||
/* manage negative whitelists, except for internal rules */
|
||||
if ( ((ngx_int_t *)wl_ids->elts)[i] < 0 && match_id >= 1000) {
|
||||
negative = 1;
|
||||
/* negative wl excludes this one.*/
|
||||
if (match_id == -((ngx_int_t *)wl_ids->elts)[i]) {
|
||||
return (0);
|
||||
}
|
||||
}
|
||||
}
|
||||
return (negative == 1);
|
||||
}
|
||||
36
naxsi-0.55.3/nginx.conf.example
Normal file
36
naxsi-0.55.3/nginx.conf.example
Normal file
@@ -0,0 +1,36 @@
|
||||
master_process off;
|
||||
worker_processes 1;
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
events {
|
||||
worker_connections 1024;
|
||||
}
|
||||
http {
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
include mime.types;
|
||||
default_type application/octet-stream;
|
||||
sendfile on;
|
||||
keepalive_timeout 65;
|
||||
server {
|
||||
listen 4242;
|
||||
server_name localhost;
|
||||
location / {
|
||||
LearningMode;
|
||||
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_log /tmp/ngx_error.log debug;
|
||||
access_log /tmp/ngx_access.log;
|
||||
root html;
|
||||
index index.html index.htm;
|
||||
}
|
||||
error_page 500 502 503 504 /50x.html;
|
||||
location = /50x.html {
|
||||
root html;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
399
naxsi-0.55.3/nxapi/README.md
Normal file
399
naxsi-0.55.3/nxapi/README.md
Normal file
@@ -0,0 +1,399 @@
|
||||
# A quick word
|
||||
|
||||
nxapi/nxtool is the new learning tool, that attempts to perform the following :
|
||||
* Events import : Importing naxsi events into an elasticsearch database
|
||||
* Whitelist generation : Generate whitelists, from templates rather than from purely statistical aspects
|
||||
* Events management : Allow tagging of events into database to exclude them from wl gen process
|
||||
* Reporting : Display information about current DB content
|
||||
|
||||
# Configuration file : nxapi.json
|
||||
|
||||
nxapi uses a JSON file for its settings, such as :
|
||||
|
||||
|
||||
$ cat nxapi.json
|
||||
|
||||
|
||||
{
|
||||
# elasticsearch setup, must point to the right instance.
|
||||
"elastic" : {
|
||||
"host" : "127.0.0.1:9200",
|
||||
"index" : "nxapi",
|
||||
"doctype" : "events",
|
||||
"default_ttl" : "7200",
|
||||
"max_size" : "1000"
|
||||
},
|
||||
# filter used for any issued requests, you shouldn't modify it yet
|
||||
"global_filters" : {
|
||||
"whitelisted" : "false"
|
||||
},
|
||||
# global warning and global success rules, used to distinguish good and 'bad' whitelists
|
||||
"global_warning_rules" : {
|
||||
"rule_uri" : [ ">", "5" ],
|
||||
"rule_var_name" : [ ">", "5" ],
|
||||
"rule_ip" : ["<=", 10 ],
|
||||
"global_rule_ip_ratio" : ["<", 5]
|
||||
},
|
||||
"global_success_rules" : {
|
||||
"global_rule_ip_ratio" : [">=", 30],
|
||||
"rule_ip" : [">=", 10]
|
||||
},
|
||||
# path to naxsi core rules, path to template files,
|
||||
# path to geoloc database.
|
||||
"naxsi" : {
|
||||
"rules_path" : "/etc/nginx/naxsi_core.rules",
|
||||
"template_path" : "tpl/",
|
||||
"geoipdb_path" : "nx_datas/country2coords.txt"
|
||||
},
|
||||
# controls default colors and verbosity behavior
|
||||
"output" : {
|
||||
"colors" : "true",
|
||||
"verbosity" : "5"
|
||||
}
|
||||
}
|
||||
|
||||
# Prequisites
|
||||
|
||||
## Set up ElasticSearch
|
||||
* Download the archive with the binary files from https://www.elastic.co/downloads/elasticsearch
|
||||
* Extract the archive
|
||||
* Start ElasticSearch by executing `bin/elasticsearch` in the extracted folder
|
||||
* Check whether ElasticSearch is running correctly:
|
||||
`curl -XGET http://localhost:9200/`
|
||||
* Add a nxapi index with the following command:
|
||||
`curl -XPUT 'http://localhost:9200/nxapi/'`
|
||||
|
||||
## Populating ElasticSearch with data
|
||||
* Enable learning mode
|
||||
* Browse website to generate data in the logfile
|
||||
* Change into nxapi directory
|
||||
* Load the data from the log file into ElasticSearch with the following command:
|
||||
`./nxtool.py -c nxapi.json --files=/PATH/TO/LOGFILE.LOG`
|
||||
* Check if data was added correctly:
|
||||
`curl -XPOST "http://localhost:9200/nxapi/events/_search?pretty" -d '{}' `
|
||||
* Check if nxtool sees it correctly:
|
||||
`./nxtool.py -c nxapi.json -x`
|
||||
# Simple usage approach
|
||||
|
||||
##1. Get info about db
|
||||
|
||||
$ ./nxtool.py -x --colors -c nxapi.json
|
||||
Will issue a summary of database content, including :
|
||||
|
||||
* Ratio between tagged/untagged events.
|
||||
|
||||
Tagging of events is an important notion that allows you to know how well you are doing on learning.
|
||||
Let's say you just started learning. You will have a tag ratio of 0%, which means you didn't write any
|
||||
whitelists for recent events. Once you start generating whitelists, you can provide those (`-w /tmp/wl.cf --tag`)
|
||||
and nxapi will mark those events in the database as whitelisted, excluding them from future generation process.
|
||||
It allows you to speed up the generation process, but mainly to know how well you dealt with recent false positives.
|
||||
|
||||
You can also use the tagging mechanism to exclude obvious attack patterns from learning. If X.Y.Z.W keeps hammering my website and polluting my log, I can provide nxapi with the ip (`-i /tmp/ips.txt --tag`) to tag and exclude them from process.
|
||||
|
||||
* Top servers.
|
||||
A TOP10 list of dst hosts raising the most exceptions.
|
||||
* Top URI(s).
|
||||
A TOP10 list of dst URIs raising the most exceptions. It is very useful in combination with --filter to generate whitelists for specific URI(s).
|
||||
* Top Zones.
|
||||
List of most active zones of exceptions.
|
||||
|
||||
##2. Generate whitelists
|
||||
Let's say I had the following output :
|
||||
|
||||
./nxtool.py -c nxapi.json -x --colors
|
||||
# Whitelist(ing) ratio :
|
||||
# false 79.96 % (total:196902/246244)
|
||||
# true 20.04 % (total:49342/246244)
|
||||
# Top servers :
|
||||
# www.x1.fr 21.93 % (total:43181/196915)
|
||||
# www.x2.fr 15.21 % (total:29945/196915)
|
||||
...
|
||||
# Top URI(s) :
|
||||
# /foo/bar/test 8.55 % (total:16831/196915)
|
||||
# /user/register 5.62 % (total:11060/196915)
|
||||
# /index.php/ 4.26 % (total:8385/196915)
|
||||
...
|
||||
# Top Zone(s) :
|
||||
# BODY 41.29 % (total:81309/196924)
|
||||
# HEADERS 23.2 % (total:45677/196924)
|
||||
# BODY|NAME 16.88 % (total:33243/196924)
|
||||
# ARGS 12.47 % (total:24566/196924)
|
||||
# URL 5.56 % (total:10947/196924)
|
||||
# ARGS|NAME 0.4 % (total:787/196924)
|
||||
# FILE_EXT 0.2 % (total:395/196924)
|
||||
# Top Peer(s) :
|
||||
# ...
|
||||
|
||||
I want to generate whitelists for x1.fr, so I will get more precise statistics first :
|
||||
|
||||
./nxtool.py -c nxapi.json -x --colors -s www.x1.fr
|
||||
...
|
||||
# Top URI(s) :
|
||||
# /foo/bar/test 8.55 % (total:16831/196915)
|
||||
# /index.php/ 4.26 % (total:8385/196915)
|
||||
...
|
||||
|
||||
I will then attempt to generate whitelists for the `/foo/bar/test` page, that seems to trigger most events :
|
||||
|
||||
`Take note of the --filter option, that allows me to work whitelists only for this URI.
|
||||
Filters can specify any field : var_name, zone, uri, id, whitelisted, content, country, date ...
|
||||
However, take care, they don't support regexp yet.
|
||||
Take note as well of --slack usage, that allows to ignore success/warning criterias, as my website has too few
|
||||
visitors, making legitimate exceptions appear as false positives.`
|
||||
|
||||
./nxtool.py -c nxapi.json -s www.x1.fr -f --filter 'uri /foo/bar/test' --slack
|
||||
...
|
||||
#msg: A generic whitelist, true for the whole uri
|
||||
#Rule (1303) html close tag
|
||||
#total hits 126
|
||||
#content : lyiuqhfnp,+<a+href="http://preemptivelove.org/">Cialis+forum</a>,+KKSXJyE,+[url=http://preemptivelove.org/]Viagra+or+cialis[/url],+XGRgnjn,+http
|
||||
#content : 4ThLQ6++<a+href="http://aoeymqcqbdby.com/">aoeymqcqbdby</a>,+[url=http://ndtofuvzhpgq.com/]ndtofuvzhpgq[/url],+[link..
|
||||
#peers : x.y.z.w
|
||||
...
|
||||
#uri : /faq/
|
||||
#var_name : numcommande
|
||||
#var_name : comment
|
||||
...
|
||||
# success : global_rule_ip_ratio is 58.82
|
||||
# warnings : rule_ip is 10
|
||||
BasicRule wl:1303 "mz:$URL:/foo/bar/test|BODY";
|
||||
|
||||
|
||||
nxtool attempts to provide extra information to allow user to decides wether it's a false positive :
|
||||
* content : actual HTTP content, only present if $naxsi_extensive_log is set to 1
|
||||
* uri : example(s) of URI on which the event was triggered
|
||||
* var_name : example(s) of variable names in which the content was triggered
|
||||
* success and warnings : nxapi will provide you with scoring information (see 'scores').
|
||||
|
||||
##3. Interactive whitelist generation
|
||||
|
||||
Another way of creating whitelists is to use the -g option. This option provide
|
||||
an interactive way to generate whitelists. This option use the EDITOR env
|
||||
variable and uses it to iterate over all the servers available inside your elastic
|
||||
search instance (if the EDITOR env variable isn't set it will try to use `vi`.
|
||||
You can either delete or comment with a `#` at the beginning the line you don't
|
||||
want to keep. After the server selection, it will iterate on each available uri
|
||||
and zone for earch server. If you want to use regex, only available for uri,
|
||||
you can add a `?` at the beginning of each line where you want to use a regex:
|
||||
|
||||
uri /fr/foo/ ...
|
||||
?uri /[a-z]{2,}/foo ...
|
||||
|
||||
The -g options once all the selection is done, will attempt to generate the wl
|
||||
with the same behaviour as -f option, and write the result inside the path the
|
||||
typical output when generating wl is:
|
||||
|
||||
generating wl with filters {u'whitelisted': u'false', 'uri': '/fr/foo', 'server': 'x.com'}
|
||||
Writing in file: /tmp/server_x.com_0.wl
|
||||
|
||||
As you can see you'll see each filter and each file for each selections.
|
||||
|
||||
##4. Tagging events
|
||||
|
||||
Once I chose the whitelists that I think are appropriate, I will write them in a whitelist file.
|
||||
Then, I can tag corresponding events :
|
||||
nxtool.py -c nxapi.json -w /tmp/whitelist.conf --tag
|
||||
|
||||
And then, if I look at the report again, I will see a bump in the tagged ratio of events.
|
||||
Once the ratio is high enough or the most active URLs & IPs are false positives, it's done!
|
||||
|
||||
|
||||
# Tips and tricks for whitelist generation
|
||||
|
||||
* `--filter`
|
||||
|
||||
--filter is your friend, especially if you have a lot of exceptions.
|
||||
By narrowing the search field for whitelists, it will increase speed, and reduce false positives.
|
||||
|
||||
* use `-t` instead of `-f`
|
||||
|
||||
-f is the "dumb" generation mode, where all templates will be attempted.
|
||||
if you provide something like `-t "ARGS/*"` only templates specific to ARGS whitelists will be attempted.
|
||||
|
||||
* Create your own templates
|
||||
|
||||
If you manage applications that do share code/framework/technology, you will quickly find yourself
|
||||
generating the same wl again and again. Stop that! Write your own templates, improving generation time,
|
||||
accuracy and reducing false positives. Take a practical example:
|
||||
I'm dealing with magento, like a *lot*. One of the recurring patterns is the "onepage" checkout, so I created specific templates:
|
||||
|
||||
{
|
||||
"_success" : { "rule_ip" : [ ">", "1"]},
|
||||
"_msg" : "Magento checkout page (BODY|NAME)",
|
||||
"?uri" : "/checkout/onepage/.*",
|
||||
"zone" : "BODY|NAME",
|
||||
"id" : "1310 OR 1311"
|
||||
}
|
||||
|
||||
|
||||
# Supported options
|
||||
|
||||
## Scope/Filtering options
|
||||
|
||||
`-s SERVER, --server=SERVER`
|
||||
|
||||
Restrict context of whitelist generation or stats display to specific FQDN.
|
||||
|
||||
`--filter=FILTER`
|
||||
|
||||
A filter (in the form of a dict) to merge with
|
||||
existing templates/filters: 'uri /foobar zone BODY'.
|
||||
You can combine several filters, for example : `--filter "country FR" --filter "uri /foobar"`.
|
||||
|
||||
|
||||
## Whitelist generation options
|
||||
|
||||
`-t TEMPLATE, --template=TEMPLATE`
|
||||
|
||||
Given a path to a template file, attempt to generate matching whitelists.
|
||||
Possible whitelists will be tested versus database, only the ones with "good" scores will be kept.
|
||||
if TEMPLATE starts with a '/' it's treated as an absolute path. Else, it's expanded starting in tpl/ directory.
|
||||
|
||||
`-f, --full-auto`
|
||||
|
||||
Attempts whitelist generation for all templates present in rules_path.
|
||||
|
||||
`--slack`
|
||||
|
||||
Sets nxtool to ignore scores and display all generated whitelists.
|
||||
|
||||
|
||||
## Tagging options
|
||||
|
||||
`-w WL_FILE, --whitelist-path=WL_FILE`
|
||||
|
||||
Given a whitelist file, finds matching events in database.
|
||||
|
||||
`-i IPS, --ip-path=IPS`
|
||||
|
||||
Given a list of ips (separatated by \n), finds matching events in database.
|
||||
|
||||
`--tag`
|
||||
|
||||
Performs tagging. If not specified, matching events are simply displayed.
|
||||
|
||||
|
||||
## Statistics generation options
|
||||
|
||||
`-x, --stats`
|
||||
|
||||
Generate statistics about current database.
|
||||
|
||||
## Importing data
|
||||
|
||||
**Note:** All acquisition features expect naxsi EXLOG/FMT content.
|
||||
|
||||
|
||||
` --files=FILES_IN Path to log files to parse.̀`
|
||||
|
||||
|
||||
Supports glob, gz bz2, ie. --files "/var/log/nginx/*mysite.com*error.log*"
|
||||
|
||||
|
||||
`--fifo=FIFO_IN Path to a FIFO to be created & read from. [infinite]`
|
||||
Creates a FIFO, increases F_SETPIPE_SZ, and reads on it. mostly useful for reading directly from syslog/nginx logs.
|
||||
|
||||
`--stdin Read from stdin.`
|
||||
|
||||
`--no-timeout Disable timeout on read operations (stdin/fifo).̀
|
||||
|
||||
|
||||
# Understanding templates
|
||||
|
||||
Templates do have a central role within nxapi.
|
||||
By default only generic ones are provided, you should create your own.
|
||||
First, look at a generic one to understand how it works :
|
||||
|
||||
{
|
||||
"zone" : "HEADERS",
|
||||
"var_name" : "cookie",
|
||||
"id" : "?"
|
||||
}
|
||||
|
||||
Here is how nxtool will use this to generate whitelists:
|
||||
1. extract global_filters from nxapi.json, and create the base ES filter :
|
||||
{ "whitelisted" : "false" }
|
||||
2. merge base ES filter with provided cmd line filter (--filter, -s www.x1.fr)
|
||||
{ "whitelisted" : "false", "server" : "www.x1.fr" }
|
||||
3. For each static field of the template, merge it in base ES filter :
|
||||
{ "whitelisted" : "false", "server" : "www.x1.fr", "zone" : "HEADERS", "var_name" : "cookie" }
|
||||
4. For each field to be expanded (value is `?`) :
|
||||
4.1. select all possible values for this field (id) matching base ES filter, (ie. 1000 and 1001 here)
|
||||
4.2. attempt to generate a whitelist for each possible value, and evaluate its scores.
|
||||
{ "whitelisted" : "false", "server" : "www.x1.fr", "zone" : "HEADERS", "var_name" : "cookie", "id" : "1000"}
|
||||
{ "whitelisted" : "false", "server" : "www.x1.fr", "zone" : "HEADERS", "var_name" : "cookie", "id" : "1001"}
|
||||
5. For each final set that provided results, output a whitelist.
|
||||
|
||||
|
||||
Templates support :
|
||||
* `"field" : "value"` : A static value that must be present in exception for template to be true.
|
||||
* `"field" : "?"` : A value that must be expanded from database content (while matching static&global filters).
|
||||
unique values for "field" will then be used for whitelist generation (one whitelist per unique value).
|
||||
* `"?field" : "regexp"` : A regular expression for a field content that will be searched in database.
|
||||
unique values matching regexp for "field" will then be used for whitelist generation (one whitelist per unique value).
|
||||
* `"_statics" : { "field" : "value" }` : A static value to be used at whitelist generation time. Does not take part in search process,
|
||||
only at 'output' time. ie. `"_statics" : { "id" : "0" }` is the only way to have a whitelist outputing a 'wl:0'.
|
||||
* `"_msg" : "string" ` : a text message to help the user understand the template purpose.
|
||||
* `"_success" : { ... }` : A dict supplied to overwrite/complete 'global' scoring rules.
|
||||
* `"_warnings" : { ... }` : A dict supplied to overwrite/complete 'global' scoring rules.
|
||||
|
||||
|
||||
# Understanding scoring
|
||||
|
||||
Scoring mechanism :
|
||||
* Scoring mechanism is a very trivial approach, relying on three kinds of "scoring" expressions : _success, _warning, _deny.
|
||||
* Whenever a _success rule is met while generating a whitelist, it will INCREASE the "score" of the whitelist by 1.
|
||||
* Whenever a _warning rule is met while generating a whitelist, it will DECREASE the "score" of the whitelist by 1.
|
||||
* Whenever a _deny rule is met while generating a whitelist, it will disable the whitelist output.
|
||||
|
||||
_note:_
|
||||
In order to understand scoring mechanism, it is crucial to tell the difference between a template and a rule.
|
||||
A template is a .json file which can match many events. A rule is usually a subpart of a template results.
|
||||
For example, if we have this data :
|
||||
|
||||
[ {"id" : 1, "zone" : HEADERS, ip:A.A.A.A},
|
||||
{"id" : 2, "zone" : HEADERS, ip:A.A.A.A},
|
||||
{"id" : 1, "zone" : ARGS, ip:A.B.C.D}
|
||||
]
|
||||
|
||||
|
||||
And this template :
|
||||
|
||||
{"id" : 1, "zone" : "?"}
|
||||
|
||||
Well, template_ip would be 2, as 2 peers triggered events with ID:1.
|
||||
However, rule_ip would be 1, as the two generated rules ('id:1 mz:ARGS' and 'id:1 mz:HEADERS'),
|
||||
were triggered each by one unique peer.
|
||||
|
||||
If --slack is present, scoring is ignored, and all possible whitelists are displayed.
|
||||
In normal conditions, whitelists with more than 0 points are displayed.
|
||||
The default filters enabled in nxapi, from nxapi.json :
|
||||
|
||||
|
||||
"global_warning_rules" : {
|
||||
"rule_ip" : ["<=", 10 ],
|
||||
"global_rule_ip_ratio" : ["<", 5]
|
||||
},
|
||||
"global_success_rules" : {
|
||||
"global_rule_ip_ratio" : [">=", 10],
|
||||
"rule_ip" : [">=", 10]
|
||||
},
|
||||
"global_deny_rules" : {
|
||||
"global_rule_ip_ratio" : ["<", 2]
|
||||
},
|
||||
|
||||
|
||||
* rule_N <= X : "at least" X uniq(N) where present in the specific events from which the WL is generated.
|
||||
* '"rule_ip" : ["<=", 10 ],' : True if less than 10 unique IPs hit the event
|
||||
* '"rule_var_name" : [ "<=", "5" ]' : True if less than 5 unique variable names hit the event
|
||||
* template_N <= X : "at least" X uniq(N) where present in the specific events from which the WL is generated.
|
||||
* Note the difference with "rule_X" rules.
|
||||
* global_rule_ip_ratio < X : "at least" X% of the users that triggered events triggered this one as well.
|
||||
* however, ration can theorically apply to anything, just ip_ratio is the most common.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
318
naxsi-0.55.3/nxapi/naxsi_kibana.dash
Normal file
318
naxsi-0.55.3/nxapi/naxsi_kibana.dash
Normal file
@@ -0,0 +1,318 @@
|
||||
{
|
||||
"title": "naxsi-current+inspect (last 1 hour)",
|
||||
"services": {
|
||||
"query": {
|
||||
"idQueue": [
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4
|
||||
],
|
||||
"list": {
|
||||
"0": {
|
||||
"id": 0,
|
||||
"type": "topN",
|
||||
"query": "*",
|
||||
"alias": "",
|
||||
"color": "#6ED0E0",
|
||||
"pin": false,
|
||||
"enable": true,
|
||||
"field": "server",
|
||||
"size": 10,
|
||||
"union": "AND"
|
||||
}
|
||||
},
|
||||
"ids": [
|
||||
0
|
||||
]
|
||||
},
|
||||
"filter": {
|
||||
"idQueue": [
|
||||
0,
|
||||
1,
|
||||
2
|
||||
],
|
||||
"list": {
|
||||
"0": {
|
||||
"type": "time",
|
||||
"field": "date",
|
||||
"from": "now-1h",
|
||||
"to": "now",
|
||||
"mandate": "must",
|
||||
"active": true,
|
||||
"alias": "",
|
||||
"id": 0
|
||||
},
|
||||
"1": {
|
||||
"type": "querystring",
|
||||
"query": "*preprod*",
|
||||
"mandate": "mustNot",
|
||||
"active": true,
|
||||
"alias": "",
|
||||
"id": 1
|
||||
}
|
||||
},
|
||||
"ids": [
|
||||
0,
|
||||
1
|
||||
]
|
||||
}
|
||||
},
|
||||
"rows": [
|
||||
{
|
||||
"title": "events",
|
||||
"height": "250px",
|
||||
"editable": true,
|
||||
"collapse": false,
|
||||
"collapsable": true,
|
||||
"panels": [
|
||||
{
|
||||
"error": false,
|
||||
"span": 3,
|
||||
"editable": true,
|
||||
"type": "terms",
|
||||
"loadingEditor": false,
|
||||
"queries": {
|
||||
"mode": "all",
|
||||
"ids": [
|
||||
0
|
||||
]
|
||||
},
|
||||
"field": "server",
|
||||
"exclude": [],
|
||||
"missing": true,
|
||||
"other": true,
|
||||
"size": 30,
|
||||
"order": "count",
|
||||
"style": {
|
||||
"font-size": "10pt"
|
||||
},
|
||||
"donut": false,
|
||||
"tilt": false,
|
||||
"labels": true,
|
||||
"arrangement": "vertical",
|
||||
"chart": "bar",
|
||||
"counter_pos": "below",
|
||||
"spyable": true,
|
||||
"title": "sites",
|
||||
"tmode": "terms",
|
||||
"tstat": "total",
|
||||
"valuefield": ""
|
||||
},
|
||||
{
|
||||
"span": 9,
|
||||
"editable": true,
|
||||
"type": "histogram",
|
||||
"loadingEditor": false,
|
||||
"mode": "count",
|
||||
"time_field": "date",
|
||||
"queries": {
|
||||
"mode": "all",
|
||||
"ids": [
|
||||
0
|
||||
]
|
||||
},
|
||||
"value_field": null,
|
||||
"auto_int": false,
|
||||
"resolution": 100,
|
||||
"interval": "1m",
|
||||
"intervals": [
|
||||
"auto",
|
||||
"1s",
|
||||
"1m",
|
||||
"5m",
|
||||
"10m",
|
||||
"30m",
|
||||
"1h",
|
||||
"3h",
|
||||
"12h",
|
||||
"1d",
|
||||
"1w",
|
||||
"1M",
|
||||
"1y"
|
||||
],
|
||||
"fill": 1,
|
||||
"linewidth": 3,
|
||||
"timezone": "browser",
|
||||
"spyable": true,
|
||||
"zoomlinks": true,
|
||||
"bars": true,
|
||||
"stack": false,
|
||||
"points": false,
|
||||
"lines": false,
|
||||
"legend": true,
|
||||
"x-axis": true,
|
||||
"y-axis": true,
|
||||
"percentage": false,
|
||||
"interactive": false,
|
||||
"options": true,
|
||||
"tooltip": {
|
||||
"value_type": "individual",
|
||||
"query_as_alias": true
|
||||
},
|
||||
"title": "history",
|
||||
"scale": 1,
|
||||
"y_format": "none",
|
||||
"grid": {
|
||||
"max": null,
|
||||
"min": 0
|
||||
},
|
||||
"annotate": {
|
||||
"enable": false,
|
||||
"query": "*",
|
||||
"size": 20,
|
||||
"field": "_type",
|
||||
"sort": [
|
||||
"_score",
|
||||
"desc"
|
||||
]
|
||||
},
|
||||
"pointradius": 5,
|
||||
"show_query": true,
|
||||
"legend_counts": true,
|
||||
"zerofill": true,
|
||||
"derivative": false
|
||||
}
|
||||
],
|
||||
"notice": false
|
||||
},
|
||||
{
|
||||
"title": "timelines",
|
||||
"height": "150px",
|
||||
"editable": true,
|
||||
"collapse": false,
|
||||
"collapsable": true,
|
||||
"panels": [
|
||||
{
|
||||
"error": false,
|
||||
"span": 12,
|
||||
"editable": true,
|
||||
"type": "table",
|
||||
"loadingEditor": false,
|
||||
"size": 100,
|
||||
"pages": 5,
|
||||
"offset": 0,
|
||||
"sort": [
|
||||
"date",
|
||||
"desc"
|
||||
],
|
||||
"overflow": "min-height",
|
||||
"fields": [
|
||||
"server",
|
||||
"uri",
|
||||
"zone",
|
||||
"var_name",
|
||||
"ip",
|
||||
"id",
|
||||
"content",
|
||||
"date"
|
||||
],
|
||||
"highlight": [
|
||||
null
|
||||
],
|
||||
"sortable": true,
|
||||
"header": true,
|
||||
"paging": true,
|
||||
"field_list": false,
|
||||
"all_fields": false,
|
||||
"trimFactor": 300,
|
||||
"localTime": false,
|
||||
"timeField": "date",
|
||||
"spyable": true,
|
||||
"queries": {
|
||||
"mode": "all",
|
||||
"ids": [
|
||||
0
|
||||
]
|
||||
},
|
||||
"style": {
|
||||
"font-size": "9pt"
|
||||
},
|
||||
"normTimes": true
|
||||
}
|
||||
],
|
||||
"notice": false
|
||||
}
|
||||
],
|
||||
"editable": true,
|
||||
"failover": false,
|
||||
"index": {
|
||||
"interval": "none",
|
||||
"pattern": "[logstash-]YYYY.MM.DD",
|
||||
"default": "nxapi",
|
||||
"warm_fields": true
|
||||
},
|
||||
"style": "dark",
|
||||
"panel_hints": true,
|
||||
"pulldowns": [
|
||||
{
|
||||
"type": "query",
|
||||
"collapse": true,
|
||||
"notice": false,
|
||||
"enable": true,
|
||||
"query": "*",
|
||||
"pinned": true,
|
||||
"history": [
|
||||
"*",
|
||||
"www.forum-fic.com"
|
||||
],
|
||||
"remember": 10
|
||||
},
|
||||
{
|
||||
"type": "filtering",
|
||||
"collapse": false,
|
||||
"notice": true,
|
||||
"enable": true
|
||||
}
|
||||
],
|
||||
"nav": [
|
||||
{
|
||||
"type": "timepicker",
|
||||
"collapse": false,
|
||||
"notice": false,
|
||||
"enable": true,
|
||||
"status": "Stable",
|
||||
"time_options": [
|
||||
"5m",
|
||||
"15m",
|
||||
"1h",
|
||||
"6h",
|
||||
"12h",
|
||||
"24h",
|
||||
"2d",
|
||||
"7d",
|
||||
"30d"
|
||||
],
|
||||
"refresh_intervals": [
|
||||
"5s",
|
||||
"10s",
|
||||
"30s",
|
||||
"1m",
|
||||
"5m",
|
||||
"15m",
|
||||
"30m",
|
||||
"1h",
|
||||
"2h",
|
||||
"1d"
|
||||
],
|
||||
"timefield": "date",
|
||||
"now": true,
|
||||
"filter_id": 0
|
||||
}
|
||||
],
|
||||
"loader": {
|
||||
"save_gist": true,
|
||||
"save_elasticsearch": true,
|
||||
"save_local": true,
|
||||
"save_default": true,
|
||||
"save_temp": true,
|
||||
"save_temp_ttl_enable": true,
|
||||
"save_temp_ttl": "30d",
|
||||
"load_gist": false,
|
||||
"load_elasticsearch": true,
|
||||
"load_elasticsearch_size": 20,
|
||||
"load_local": false,
|
||||
"hide": false
|
||||
},
|
||||
"refresh": "10s"
|
||||
}
|
||||
261
naxsi-0.55.3/nxapi/nx_datas/country2coords.txt
Normal file
261
naxsi-0.55.3/nxapi/nx_datas/country2coords.txt
Normal file
@@ -0,0 +1,261 @@
|
||||
AD:42.5462450,1.6015540
|
||||
AE:23.4240760,53.8478180
|
||||
AF:33.939110,67.7099530
|
||||
AG:47.38766640,8.25542950
|
||||
AI:18.2205540,-63.06861499999999
|
||||
AL:32.31823140,-86.9022980
|
||||
AM:-3.41684270,-65.85606460
|
||||
AN:12.2260790,-69.0600870
|
||||
AO:47.5162310,14.5500720
|
||||
AQ:-82.86275189999999,-135.0
|
||||
AR:35.201050,-91.83183339999999
|
||||
AS:-14.2709720,-170.1322170
|
||||
AT:47.5162310,14.5500720
|
||||
AU:-25.2743980,133.7751360
|
||||
AW:12.521110,-69.9683380
|
||||
AX:60.33854850,20.27125850
|
||||
AZ:34.04892810,-111.09373110
|
||||
BA:43.9158860,17.6790760
|
||||
BB:13.1938870,-59.5431980
|
||||
BD:23.6849940,90.3563310
|
||||
BE:50.5038870,4.4699360
|
||||
BF:12.2383330,-1.5615930
|
||||
BG:42.7338830,25.485830
|
||||
BH:-19.91906770,-43.93857470
|
||||
BI:-3.3730560,29.9188860
|
||||
BJ:9.307689999999999,2.3158340
|
||||
BM:32.3213840,-64.75736999999999
|
||||
BN:4.5352770,114.7276690
|
||||
BO:7.95517910,-11.74099460
|
||||
BR:-14.2350040,-51.925280
|
||||
BS:25.034280,-77.39627999999999
|
||||
BT:27.5141620,90.4336010
|
||||
BV:47.65806030,-94.87917419999999
|
||||
BW:-22.3284740,24.6848660
|
||||
BY:53.7098070,27.9533890
|
||||
BZ:17.1898770,-88.49764999999999
|
||||
CA:36.7782610,-119.41793240
|
||||
CC:-26.58576560,-60.95400730
|
||||
CD:-4.0383330,21.7586640
|
||||
CF:6.611110999999999,20.9394440
|
||||
CG:-0.2280210,15.8276590
|
||||
CH:46.8181880,8.227511999999999
|
||||
CI:7.539988999999999,-5.547079999999999
|
||||
CK:-21.2367360,-159.7776710
|
||||
CL:-35.6751470,-71.5429690
|
||||
CM:7.369721999999999,12.3547220
|
||||
CN:35.861660,104.1953970
|
||||
CO:39.55005070,-105.78206740
|
||||
CR:9.748916999999999,-83.7534280
|
||||
CS:39.56441050,16.25221430
|
||||
CU:21.5217570,-77.7811670
|
||||
CV:16.0020820,-24.0131970
|
||||
CX:-10.4475250,105.6904490
|
||||
CY:35.1264130,33.4298590
|
||||
CZ:49.81749199999999,15.4729620
|
||||
DE:51.165691,10.451526
|
||||
DJ:11.8251380,42.5902750
|
||||
DK:56.263920,9.5017850
|
||||
DM:15.4149990,-61.37097600000001
|
||||
DO:18.7356930,-70.1626510
|
||||
DZ:28.0338860,1.6596260
|
||||
EC:-32.29684020,26.4193890
|
||||
EE:58.5952720,25.0136070
|
||||
EG:26.8205530,30.8024980
|
||||
EH:24.2155270,-12.8858340
|
||||
ER:15.1793840,39.7823340
|
||||
ES:-19.18342290,-40.30886260
|
||||
ET:9.145000000000001,40.4896730
|
||||
FI:61.92410999999999,25.7481510
|
||||
FJ:-17.7133710,178.0650320
|
||||
FK:-51.7962530,-59.5236130
|
||||
FM:-25.39459690,-58.73736339999999
|
||||
FO:-25.39459690,-58.73736339999999
|
||||
FR:46.2276380,2.2137490
|
||||
FX:27.9026210,-82.7447310
|
||||
GA:32.15743510,-82.90712300000001
|
||||
GB:55.3780510,-3.4359730
|
||||
GD:12.11650,-61.67899999999999
|
||||
GE:52.0451550,5.871823399999999
|
||||
GF:3.9338890,-53.1257820
|
||||
GH:7.9465270,-1.0231940
|
||||
GI:36.1377410,-5.3453740
|
||||
GL:71.7069360,-42.6043030
|
||||
GM:13.4431820,-15.3101390
|
||||
GN:9.9455870,-9.6966450
|
||||
GP:-26.27075930,28.11226790
|
||||
GQ:1.6508010,10.2678950
|
||||
GR:39.0742080,21.8243120
|
||||
GS:-54.4295790,-36.5879090
|
||||
GT:15.7834710,-90.23075899999999
|
||||
GU:13.4443040,144.7937310
|
||||
GW:11.8037490,-15.1804130
|
||||
GY:4.8604160,-58.930180
|
||||
HK:22.3964280,114.1094970
|
||||
HM:-53.081810,73.50415799999999
|
||||
HN:15.1999990,-86.2419050
|
||||
HR:45.10,15.20
|
||||
HT:18.9711870,-72.28521499999999
|
||||
HU:47.1624940,19.5033040
|
||||
ID:44.06820190,-114.74204080
|
||||
IE:53.412910,-8.243890
|
||||
IL:40.63312490,-89.39852830
|
||||
IN:40.26719410,-86.13490190
|
||||
IO:-6.3431940,71.8765190
|
||||
IQ:33.2231910,43.6792910
|
||||
IR:32.4279080,53.6880460
|
||||
IS:64.96305099999999,-19.0208350
|
||||
IT:41.871940,12.567380
|
||||
JM:18.1095810,-77.29750799999999
|
||||
JO:30.5851640,36.2384140
|
||||
JP:36.2048240,138.2529240
|
||||
KE:-0.0235590,37.9061930
|
||||
KG:41.204380,74.7660980
|
||||
KH:12.5656790,104.9909630
|
||||
KI:-3.3704170,-168.7340390
|
||||
KM:-11.8750010,43.8722190
|
||||
KN:17.3578220,-62.7829980
|
||||
KP:40.3398520,127.5100930
|
||||
KR:35.9077570,127.7669220
|
||||
KW:29.311660,47.4817660
|
||||
KY:37.83933320,-84.27001790
|
||||
KZ:48.0195730,66.92368399999999
|
||||
LA:31.24482340,-92.14502449999999
|
||||
LB:33.8547210,35.8622850
|
||||
LC:45.93829410,9.3857290
|
||||
LI:51.44272380,6.06087260
|
||||
LK:7.873053999999999,80.77179699999999
|
||||
LR:6.4280550,-9.429499000000002
|
||||
LS:-29.6099880,28.2336080
|
||||
LT:55.1694380,23.8812750
|
||||
LU:49.8152730,6.129582999999999
|
||||
LV:56.8796350,24.6031890
|
||||
LY:26.33510,17.2283310
|
||||
MA:42.40721070,-71.38243740
|
||||
MC:43.73841760000001,7.424615799999999
|
||||
MD:39.04575490,-76.64127119999999
|
||||
MG:-17.9301780,-43.79084530
|
||||
MH:19.75147980,75.71388840
|
||||
MK:41.6086350,21.7452750
|
||||
ML:17.5706920,-3.9961660
|
||||
MM:21.9139650,95.95622299999999
|
||||
MN:46.7295530,-94.68589980
|
||||
MO:37.96425290,-91.83183339999999
|
||||
MP:-25.5653360,30.52790960
|
||||
MQ:14.6415280,-61.0241740
|
||||
MR:21.007890,-10.9408350
|
||||
MS:32.35466790,-89.39852830
|
||||
MT:46.87968220,-110.36256580
|
||||
MU:-20.3484040,57.55215200000001
|
||||
MV:3.2027780,73.220680
|
||||
MW:-13.2543080,34.3015250
|
||||
MX:23.6345010,-102.5527840
|
||||
MY:4.2104840,101.9757660
|
||||
MZ:-18.6656950,35.5295620
|
||||
NA:-22.957640,18.490410
|
||||
NC:35.75957310,-79.01929969999999
|
||||
NE:41.49253740,-99.90181310
|
||||
NF:-29.0408350,167.9547120
|
||||
NG:9.0819990,8.675276999999999
|
||||
NI:12.8654160,-85.2072290
|
||||
NL:53.13550910,-57.66043640
|
||||
NO:48.10807699999999,15.80495580
|
||||
NP:28.3948570,84.12400799999999
|
||||
NR:-0.5227780,166.9315030
|
||||
NU:70.29977110,-83.10757690
|
||||
NZ:-40.9005570,174.8859710
|
||||
OM:21.5125830,55.9232550
|
||||
PA:41.20332160,-77.19452470
|
||||
PE:46.5107120,-63.41681359999999
|
||||
PF:-17.6797420,-149.4068430
|
||||
PG:5.263234100000001,100.48462270
|
||||
PH:12.8797210,121.7740170
|
||||
PK:30.3753210,69.34511599999999
|
||||
PL:51.9194380,19.1451360
|
||||
PM:46.9419360,-56.271110
|
||||
PN:-24.7036150,-127.4393080
|
||||
PR:-25.25208880,-52.02154150
|
||||
PS:31.9521620,35.2331540
|
||||
PT:39.39987199999999,-8.2244540
|
||||
PW:7.514979999999999,134.582520
|
||||
PY:-23.4425030,-58.4438320
|
||||
QA:25.3548260,51.1838840
|
||||
RE:-21.1151410,55.5363840
|
||||
RO:45.9431610,24.966760
|
||||
RU:61.524010,105.3187560
|
||||
RW:-1.9402780,29.8738880
|
||||
SA:23.8859420,45.0791620
|
||||
SB:-9.645709999999999,160.1561940
|
||||
SC:33.8360810,-81.16372450
|
||||
SD:43.96951480,-99.90181310
|
||||
SE:60.12816100000001,18.6435010
|
||||
SG:1.3520830,103.8198360
|
||||
SH:54.20907680,9.5889410
|
||||
SI:46.1512410,14.9954630
|
||||
SJ:-30.87245870,-68.52471489999999
|
||||
SK:52.93991590,-106.45086390
|
||||
SL:-33.87690180,-66.23671720
|
||||
SM:43.942360,12.4577770
|
||||
SN:14.4974010,-14.4523620
|
||||
SO:5.1521490,46.1996160
|
||||
SR:3.9193050,-56.0277830
|
||||
ST:0.186360,6.613080999999999
|
||||
SU:30.6516520,104.0759310
|
||||
SV:46.8181880,8.227511999999999
|
||||
SY:34.80207499999999,38.9968150
|
||||
SZ:-26.5225030,31.4658660
|
||||
TC:21.6940250,-71.7979280
|
||||
TD:15.4541660,18.7322070
|
||||
TF:-53.86711170,-69.2972140
|
||||
TG:8.6195430,0.8247820
|
||||
TH:15.8700320,100.9925410
|
||||
TJ:38.8610340,71.2760930
|
||||
TK:-8.967362999999999,-171.8558810
|
||||
TL:-8.8742170,125.7275390
|
||||
TM:38.9697190,59.5562780
|
||||
TN:35.51749130,-86.58044730
|
||||
TO:-11.40987370,-48.71914229999999
|
||||
TP:37.87774020,12.71351210
|
||||
TR:38.9637450,35.2433220
|
||||
TT:10.6918030,-61.2225030
|
||||
TV:45.78572920,12.19702880
|
||||
TW:23.697810,120.9605150
|
||||
TZ:-6.3690280,34.8888220
|
||||
UA:48.3794330,31.165580
|
||||
UG:1.3733330,32.2902750
|
||||
UK:55.3780510,-3.4359730
|
||||
UM:14.00451050,-176.70562750
|
||||
US:37.090240,-95.7128910
|
||||
UY:-32.5227790,-55.7658350
|
||||
UZ:41.3774910,64.5852620
|
||||
VA:37.43157340,-78.65689420
|
||||
VC:12.9843050,-61.2872280
|
||||
VE:6.423750,-66.589730
|
||||
VG:18.4206950,-64.6399680
|
||||
VI:18.3357650,-64.89633499999999
|
||||
VN:14.0583240,108.2771990
|
||||
VU:-15.3767060,166.9591580
|
||||
WF:-13.7687520,-177.1560970
|
||||
WS:-13.7590290,-172.1046290
|
||||
YE:15.5527270,48.5163880
|
||||
YT:64.28232740,-135.0
|
||||
YU:39.8408430,114.5889030
|
||||
ZA:-30.5594820,22.9375060
|
||||
ZM:-13.1338970,27.8493320
|
||||
ZR:51.80736570,5.70867610
|
||||
ZW:-19.0154380,29.1548570
|
||||
BIZ:42.91333330,44.17611110
|
||||
COM:45.81203170,9.085614999999999
|
||||
EDU:38.5333020,-121.7879780
|
||||
GOV:-12.27130120,136.82331380
|
||||
INT:36.13685970,-80.22767949999999
|
||||
MIL:60.164480,132.6396450
|
||||
NET:58.05874000000001,138.2498550
|
||||
ORG:30.06971249999999,-93.79811680
|
||||
PRO:41.82904250,-94.15938679999999
|
||||
AERO:54.85890260,10.38748130
|
||||
ARPA:39.70400050000001,45.12065270000001
|
||||
COOP:34.14125450,-118.37270070
|
||||
INFO:3.134430,101.686250
|
||||
NAME:27.70287990,85.32163220
|
||||
NATO:50.8762830,4.4219710
|
||||
38
naxsi-0.55.3/nxapi/nxapi.json
Normal file
38
naxsi-0.55.3/nxapi/nxapi.json
Normal file
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"elastic" : {
|
||||
"host" : "127.0.0.1:9200",
|
||||
"use_ssl" : false,
|
||||
"index" : "nxapi",
|
||||
"doctype" : "events",
|
||||
"default_ttl" : "7200",
|
||||
"max_size" : "1000",
|
||||
"version" : "2"
|
||||
},
|
||||
"syslogd": {
|
||||
"host" : "0.0.0.0",
|
||||
"port" : "51400"
|
||||
},
|
||||
"global_filters" : {
|
||||
"whitelisted" : "false"
|
||||
},
|
||||
"global_warning_rules" : {
|
||||
"rule_ip" : ["<=", 10 ],
|
||||
"global_rule_ip_ratio" : ["<", 5]
|
||||
},
|
||||
"global_success_rules" : {
|
||||
"global_rule_ip_ratio" : [">=", 10],
|
||||
"rule_ip" : [">=", 10]
|
||||
},
|
||||
"global_deny_rules" : {
|
||||
"global_rule_ip_ratio" : ["<", 2]
|
||||
},
|
||||
"naxsi" : {
|
||||
"rules_path" : "/etc/nginx/naxsi_core.rules",
|
||||
"template_path" : [ "tpl/"],
|
||||
"geoipdb_path" : "nx_datas/country2coords.txt"
|
||||
},
|
||||
"output" : {
|
||||
"colors" : "true",
|
||||
"verbosity" : "5"
|
||||
}
|
||||
}
|
||||
0
naxsi-0.55.3/nxapi/nxapi/__init__.py
Normal file
0
naxsi-0.55.3/nxapi/nxapi/__init__.py
Normal file
540
naxsi-0.55.3/nxapi/nxapi/nxparse.py
Normal file
540
naxsi-0.55.3/nxapi/nxapi/nxparse.py
Normal file
@@ -0,0 +1,540 @@
|
||||
# Parses a line of log, and potentially returns a dict of dict.
|
||||
import sys
|
||||
import pprint
|
||||
import time
|
||||
import glob
|
||||
import logging
|
||||
import string
|
||||
import urlparse
|
||||
import itertools
|
||||
import gzip
|
||||
import bz2
|
||||
from select import select
|
||||
from functools import partial
|
||||
import datetime
|
||||
#import urllib2 as urllib
|
||||
import json
|
||||
import copy
|
||||
from elasticsearch.helpers import bulk
|
||||
import os
|
||||
import socket
|
||||
|
||||
class NxReader():
|
||||
""" Feeds the given injector from logfiles """
|
||||
def __init__(self, acquire_fct, stdin=False, lglob=[], fd=None,
|
||||
stdin_timeout=5, syslog=None, syslogport=None, sysloghost=None):
|
||||
self.acquire_fct = acquire_fct
|
||||
self.files = []
|
||||
self.timeout = stdin_timeout
|
||||
self.stdin = False
|
||||
self.fd = fd
|
||||
self.syslog = syslog
|
||||
self.syslogport = syslogport
|
||||
self.sysloghost = sysloghost
|
||||
if stdin is not False:
|
||||
logging.warning("Using stdin")
|
||||
self.stdin = True
|
||||
return
|
||||
if len(lglob) > 0:
|
||||
for regex in lglob:
|
||||
self.files.extend(glob.glob(regex))
|
||||
logging.warning("List of files :"+str(self.files))
|
||||
if self.fd is not None:
|
||||
logging.warning("Reading from supplied FD (fifo ?)")
|
||||
if self.syslog is not None:
|
||||
logging.warning("Reading from syslog socket")
|
||||
|
||||
def read_fd(self, fd):
|
||||
if self.timeout is not None:
|
||||
rlist, _, _ = select([fd], [], [], self.timeout)
|
||||
else:
|
||||
rlist, _, _ = select([fd], [], [])
|
||||
success = discard = not_nx = malformed = 0
|
||||
if rlist:
|
||||
s = fd.readline()
|
||||
if s == '':
|
||||
return s
|
||||
self.acquire_fct(s)
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def read_syslog(self, syslog):
|
||||
if self.syslogport is not None:
|
||||
host = self.sysloghost
|
||||
port = int(self.syslogport)
|
||||
else:
|
||||
print "Unable to get syslog host and port"
|
||||
sys.exit(1)
|
||||
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
|
||||
try:
|
||||
s.bind((host,port))
|
||||
s.listen(10)
|
||||
except socket.error as msg:
|
||||
print 'Bind failed. Error Code : ' + str(msg[0]) + ' Message ' + msg[1]
|
||||
pass
|
||||
print "Listening for syslog incoming "+host+" port "+ str(self.syslogport)
|
||||
conn, addr = s.accept()
|
||||
syslog = conn.recv(1024)
|
||||
if syslog == '':
|
||||
return False
|
||||
conn.send(syslog)
|
||||
self.acquire_fct(syslog)
|
||||
return True
|
||||
|
||||
def read_files(self):
|
||||
if self.fd is not None:
|
||||
while True:
|
||||
ret = self.read_fd(self.fd)
|
||||
if ret == '':
|
||||
return False
|
||||
return 0
|
||||
if self.syslog is not None:
|
||||
ret = ""
|
||||
while self.read_syslog(self.syslog) is True:
|
||||
pass
|
||||
return 0
|
||||
|
||||
count = 0
|
||||
total = 0
|
||||
for lfile in self.files:
|
||||
success = not_nx = discard = malformed = fragmented = reunited = 0
|
||||
logging.info("Importing file "+lfile)
|
||||
try:
|
||||
if lfile.endswith(".gz"):
|
||||
print "GZ open"
|
||||
fd = gzip.open(lfile, "rb")
|
||||
elif lfile.endswith(".bz2"):
|
||||
print "BZ2 open"
|
||||
fd = bz2.BZ2File(lfile, "r")
|
||||
else:
|
||||
print "log open"
|
||||
fd = open(lfile, "r")
|
||||
except:
|
||||
logging.critical("Unable to open file : "+lfile)
|
||||
return 1
|
||||
for line in fd:
|
||||
self.acquire_fct(line)
|
||||
fd.close()
|
||||
return 0
|
||||
|
||||
|
||||
class NxParser():
|
||||
def __init__(self):
|
||||
# output date format
|
||||
self.out_date_format = "%Y/%m/%d %H:%M:%S"
|
||||
# Start of Data / End of data marker
|
||||
self.sod_marker = [' [error] ', ' [debug] ']
|
||||
self.eod_marker = [', client: ', '']
|
||||
# naxsi data keywords
|
||||
self.naxsi_keywords = [" NAXSI_FMT: ", " NAXSI_EXLOG: "]
|
||||
# keep track of fragmented lines (seed_start=X seed_end=X)
|
||||
self.reunited_lines = 0
|
||||
self.fragmented_lines = 0
|
||||
self.multiline_buf = {}
|
||||
# store generated objects
|
||||
self.dict_buf = []
|
||||
self.bad_line = 0
|
||||
|
||||
def unify_date(self, date):
|
||||
""" tries to parse a text date,
|
||||
returns date object or None on error """
|
||||
idx = 0
|
||||
res = ""
|
||||
supported_formats = [
|
||||
"%b %d %H:%M:%S",
|
||||
"%b %d %H:%M:%S",
|
||||
"%Y/%m/%d %H:%M:%S",
|
||||
"%Y-%m-%d %H:%M:%S",
|
||||
"%Y-%m-%dT%H:%M:%S"
|
||||
# "%Y-%m-%dT%H:%M:%S+%:z"
|
||||
]
|
||||
while date[idx] == " " or date[idx] == "\t":
|
||||
idx += 1
|
||||
success = 0
|
||||
for date_format in supported_formats:
|
||||
nb_sp = date_format.count(" ")
|
||||
clean_date = string.join(date.split(" ")[:nb_sp+1], " ")
|
||||
# strptime does not support numeric time zone, hack.
|
||||
idx = clean_date.find("+")
|
||||
if idx != -1:
|
||||
clean_date = clean_date[:idx]
|
||||
try:
|
||||
x = time.strptime(clean_date, date_format)
|
||||
z = time.strftime(self.out_date_format, x)
|
||||
success = 1
|
||||
break
|
||||
except:
|
||||
#print "'"+clean_date+"' not in format '"+date_format+"'"
|
||||
pass
|
||||
if success == 0:
|
||||
logging.critical("Unable to parse date format :'"+date+"'")
|
||||
return None
|
||||
return z
|
||||
|
||||
# returns line, ready for parsing.
|
||||
# returns none if line contains no naxsi data
|
||||
def clean_line(self, line):
|
||||
""" returns an array of [date, "NAXSI_..."] from a
|
||||
raw log line. 2nd item starts at first naxsi keyword
|
||||
found. """
|
||||
ret = [None, None]
|
||||
|
||||
# Don't try to parse if no naxsi keyword is found
|
||||
for word in self.naxsi_keywords:
|
||||
idx = line.find(word)
|
||||
if idx != -1:
|
||||
break
|
||||
if idx == -1:
|
||||
return None
|
||||
|
||||
line = line.rstrip('\n')
|
||||
for mark in self.sod_marker:
|
||||
date_end = line.find(mark)
|
||||
if date_end != -1:
|
||||
break
|
||||
for mark in self.eod_marker:
|
||||
if mark == '':
|
||||
data_end = len(line)
|
||||
break
|
||||
data_end = line.find(mark)
|
||||
if data_end != -1:
|
||||
break
|
||||
if date_end == -1 or data_end == 1:
|
||||
self.bad_line += 1
|
||||
return None
|
||||
ret[0] = self.unify_date(line[:date_end])
|
||||
chunk = line[date_end:data_end]
|
||||
md = None
|
||||
for word in self.naxsi_keywords:
|
||||
idx = chunk.find(word)
|
||||
if (idx != -1):
|
||||
ret[1] = chunk[idx+len(word):]
|
||||
if ret[1] is None:
|
||||
self.bad_line += 1
|
||||
return None
|
||||
return ret
|
||||
# attempts to clean and parse a line
|
||||
def parse_raw_line(self, line):
|
||||
clean_dict = self.clean_line(line)
|
||||
if clean_dict is None:
|
||||
logging.debug("not a naxsi line")
|
||||
return None
|
||||
nlist = self.parse_line(clean_dict[1])
|
||||
if nlist is None:
|
||||
return None
|
||||
return {'date' : clean_dict[0], 'events' : nlist}
|
||||
def parse_line(self, line):
|
||||
ndict = self.tokenize_log(line)
|
||||
if ndict is None:
|
||||
logging.critical("Unable to tokenize line "+line)
|
||||
return None
|
||||
nlist = self.demult_exception(ndict)
|
||||
return nlist
|
||||
def demult_exception(self, event):
|
||||
demult = []
|
||||
if event.get('seed_start') and event.get('seed_end') is None:
|
||||
#First line of a multiline naxsi fmt
|
||||
self.multiline_buf[event['seed_start']] = event
|
||||
self.fragmented_lines += 1
|
||||
return demult
|
||||
elif event.get('seed_start') and event.get('seed_end'):
|
||||
# naxsi fmt is very long, at least 3 lines
|
||||
self.fragmented_lines += 1
|
||||
if self.multiline_buf.get(event['seed_end']) is None:
|
||||
logging.critical("Orphans end {0} / start {1}".format(event['seed_end'],
|
||||
event['seed_start']))
|
||||
return demult
|
||||
self.multiline_buf[event['seed_end']].update(event)
|
||||
self.multiline_buf[event['seed_start']] = self.multiline_buf[event['seed_end']]
|
||||
del self.multiline_buf[event['seed_end']]
|
||||
return demult
|
||||
elif event.get('seed_start') is None and event.get('seed_end'):
|
||||
# last line of the naxsi_fmt, just update the dict, and parse it like a normal line
|
||||
if self.multiline_buf.get(event['seed_end']) is None:
|
||||
logging.critical('Got a line with seed_end {0}, but i cant find a matching seed_start...\nLine will probably be incomplete'.format(event['seed_end']))
|
||||
return demult
|
||||
self.fragmented_lines += 1
|
||||
self.reunited_lines += 1
|
||||
self.multiline_buf[event['seed_end']].update(event)
|
||||
event = self.multiline_buf[event['seed_end']]
|
||||
del self.multiline_buf[event['seed_end']]
|
||||
entry = {}
|
||||
|
||||
for x in ['uri', 'server', 'content', 'ip', 'date', 'var_name', 'country']:
|
||||
entry[x] = event.get(x, '')
|
||||
clean = entry
|
||||
|
||||
# NAXSI_EXLOG lines only have one triple (zone,id,var_name), but has non-empty content
|
||||
if 'zone' in event.keys():
|
||||
if 'var_name' in event.keys():
|
||||
entry['var_name'] = event['var_name']
|
||||
entry['zone'] = event['zone']
|
||||
entry['id'] = event['id']
|
||||
demult.append(entry)
|
||||
return demult
|
||||
|
||||
# NAXSI_FMT can have many (zone,id,var_name), but does not have content
|
||||
# we iterate over triples.
|
||||
elif 'zone0' in event.keys():
|
||||
commit = True
|
||||
for i in itertools.count():
|
||||
entry = copy.deepcopy(clean)
|
||||
zn = ''
|
||||
vn = ''
|
||||
rn = ''
|
||||
if 'var_name' + str(i) in event.keys():
|
||||
entry['var_name'] = event['var_name' + str(i)]
|
||||
if 'zone' + str(i) in event.keys():
|
||||
entry['zone'] = event['zone' + str(i)]
|
||||
else:
|
||||
commit = False
|
||||
break
|
||||
if 'id' + str(i) in event.keys():
|
||||
entry['id'] = event['id' + str(i)]
|
||||
else:
|
||||
commit = False
|
||||
break
|
||||
if commit is True:
|
||||
demult.append(entry)
|
||||
else:
|
||||
logging.warning("Malformed/incomplete event [missing subfield]")
|
||||
logging.info(pprint.pformat(event))
|
||||
return demult
|
||||
return demult
|
||||
else:
|
||||
logging.warning("Malformed/incomplete event [no zone]")
|
||||
logging.info(pprint.pformat(event))
|
||||
return demult
|
||||
|
||||
def tokenize_log(self, line):
|
||||
"""Parses a naxsi exception to a dict,
|
||||
1 on error, 0 on success"""
|
||||
odict = urlparse.parse_qs(line)
|
||||
# one value per key, reduce.
|
||||
for x in odict.keys():
|
||||
odict[x][0] = odict[x][0].replace('\n', "\\n")
|
||||
odict[x][0] = odict[x][0].replace('\r', "\\r")
|
||||
odict[x] = odict[x][0]
|
||||
# check for incomplete/truncated lines
|
||||
if 'zone0' in odict.keys():
|
||||
for i in itertools.count():
|
||||
is_z = is_id = False
|
||||
if 'zone' + str(i) in odict.keys():
|
||||
is_z = True
|
||||
if 'id' + str(i) in odict.keys():
|
||||
is_id = True
|
||||
if is_z is True and is_id is True:
|
||||
continue
|
||||
if is_z is False and is_id is False:
|
||||
break
|
||||
# clean our mess if we have to.
|
||||
try:
|
||||
del (odict['zone' + str(i)])
|
||||
del (odict['id' + str(i)])
|
||||
del (odict['var_name' + str(i)])
|
||||
except:
|
||||
pass
|
||||
break
|
||||
return odict
|
||||
|
||||
|
||||
class NxInjector():
|
||||
def __init__(self, auto_commit_limit=400):
|
||||
self.nlist = []
|
||||
self.auto_commit = auto_commit_limit
|
||||
self.total_objs = 0
|
||||
self.total_commits = 0
|
||||
# optional
|
||||
def get_ready(self):
|
||||
pass
|
||||
def insert(self, obj):
|
||||
self.nlist.append(obj)
|
||||
if self.auto_commit > 0 and len(self.nlist) > self.auto_commit:
|
||||
return self.commit()
|
||||
return True
|
||||
def commit(self):
|
||||
return False
|
||||
def stop(self):
|
||||
self.commit()
|
||||
pass
|
||||
|
||||
|
||||
class ESInject(NxInjector):
|
||||
def __init__(self, es, cfg, auto_commit_limit=400):
|
||||
#
|
||||
# self.nlist = []
|
||||
# self.auto_commit = auto_commit_limit
|
||||
# super(ESInject, self).__init__(value=20)
|
||||
NxInjector.__init__(self, auto_commit_limit)
|
||||
self.es = es
|
||||
self.cfg = cfg
|
||||
self.es_version = cfg["elastic"]["version"]
|
||||
# self.host = host
|
||||
# self.index = index
|
||||
# self.collection = collection
|
||||
# self.login = login
|
||||
# self.password = password
|
||||
self.set_mappings()
|
||||
|
||||
|
||||
# def esreq(self, pidx_uri, data, method="PUT"):
|
||||
# try:
|
||||
# body = json.dumps(data)
|
||||
# except:
|
||||
# print "Unable to dumps data :"+data
|
||||
# return False
|
||||
# try:
|
||||
# print "=>>"+"http://"+self.host+"/"+self.index+pidx_uri
|
||||
# req = urllib.Request("http://"+self.host+"/"+self.index+pidx_uri, data=body)
|
||||
# f = urllib.urlopen(req)
|
||||
# resp = f.read()
|
||||
# print resp
|
||||
# f.close()
|
||||
# except:
|
||||
# # import traceback
|
||||
# # print 'generic exception: ' + traceback.format_exc()
|
||||
# # print "!!Unexpected error:", sys.exc_info()[0]
|
||||
# #print resp
|
||||
# logging.critical("Unable to emit request.")
|
||||
# sys.exit(-1)
|
||||
|
||||
# return False
|
||||
# return True
|
||||
def set_mappings(self):
|
||||
if self.es_version == '5':
|
||||
try:
|
||||
self.es.indices.create(
|
||||
index=self.cfg["elastic"]["index"],
|
||||
ignore=400 # Ignore 400 cause by IndexAlreadyExistsException when creating an index
|
||||
)
|
||||
except Exception as idxadd_error:
|
||||
print "Unable to create the index/collection for ES 5.X: "+self.cfg["elastic"]["index"]+" "+self.cfg["elastic"]["doctype"]+ ", Error: " + str(idxadd_error)
|
||||
try:
|
||||
self.es.indices.put_mapping(
|
||||
index=self.cfg["elastic"]["index"],
|
||||
doc_type=self.cfg["elastic"]["doctype"],
|
||||
body={
|
||||
"events" : {
|
||||
# * (Note: The _timestamp and _ttl fields were deprecated and are now removed in ES 5.X.
|
||||
# deleting documents from an index is very expensive compared to deleting whole indexes.
|
||||
# That is why time based indexes are recommended over this sort of thing and why
|
||||
# _ttl was deprecated in the first place)
|
||||
#"_ttl" : { "enabled" : "true", "default" : "4d" },
|
||||
"properties" : { "var_name" : {"type": "keyword"},
|
||||
"uri" : {"type": "keyword"},
|
||||
"zone" : {"type": "keyword"},
|
||||
"server" : {"type": "keyword"},
|
||||
"whitelisted" : {"type" : "keyword"},
|
||||
"ip" : {"type" : "keyword"}
|
||||
}
|
||||
}
|
||||
})
|
||||
except Exception as mapset_error:
|
||||
print "Unable to set mapping on index/collection for ES 5.X: "+self.cfg["elastic"]["index"]+" "+self.cfg["elastic"]["doctype"]+", Error: "+str(mapset_error)
|
||||
return
|
||||
else:
|
||||
try:
|
||||
self.es.create(
|
||||
index=self.cfg["elastic"]["index"],
|
||||
doc_type=self.cfg["elastic"]["doctype"],
|
||||
# id=repo_name,
|
||||
body={},
|
||||
ignore=409 # 409 - conflict - would be returned if the document is already there
|
||||
)
|
||||
except Exception as idxadd_error:
|
||||
print "Unable to create the index/collection : "+self.cfg["elastic"]["index"]+" "+self.cfg["elastic"]["doctype"]+", Error: "+str(idxadd_error)
|
||||
return
|
||||
try:
|
||||
self.es.indices.put_mapping(
|
||||
index=self.cfg["elastic"]["index"],
|
||||
doc_type=self.cfg["elastic"]["doctype"],
|
||||
body={
|
||||
"events" : {
|
||||
"_ttl" : { "enabled" : "true", "default" : "4d" },
|
||||
"properties" : { "var_name" : {"type": "string", "index":"not_analyzed"},
|
||||
"uri" : {"type": "string", "index":"not_analyzed"},
|
||||
"zone" : {"type": "string", "index":"not_analyzed"},
|
||||
"server" : {"type": "string", "index":"not_analyzed"},
|
||||
"whitelisted" : {"type" : "string", "index":"not_analyzed"},
|
||||
"content" : {"type" : "string", "index":"not_analyzed"},
|
||||
"ip" : { "type" : "string", "index":"not_analyzed"}
|
||||
}
|
||||
}
|
||||
})
|
||||
except Exception as mapset_error:
|
||||
print "Unable to set mapping on index/collection : "+self.cfg["elastic"]["index"]+" "+self.cfg["elastic"]["doctype"]+", Error: "+str(mapset_error)
|
||||
return
|
||||
|
||||
|
||||
def commit(self):
|
||||
"""Process list of dict (yes) and push them to DB """
|
||||
self.total_objs += len(self.nlist)
|
||||
count = 0
|
||||
full_body = ""
|
||||
items = []
|
||||
for evt_array in self.nlist:
|
||||
for entry in evt_array['events']:
|
||||
items.append({"index" : {}})
|
||||
entry['whitelisted'] = "false"
|
||||
entry['comments'] = "import:"+str(datetime.datetime.now())
|
||||
# go utf-8 ?
|
||||
for x in entry.keys():
|
||||
if isinstance(entry[x], basestring):
|
||||
entry[x] = unicode(entry[x], errors='replace')
|
||||
items.append(entry)
|
||||
count += 1
|
||||
mapfunc = partial(json.dumps, ensure_ascii=False)
|
||||
try:
|
||||
full_body = "\n".join(map(mapfunc,items)) + "\n"
|
||||
except:
|
||||
print "Unexpected error:", sys.exc_info()[0]
|
||||
print "Unable to json.dumps : "
|
||||
pprint.pprint(items)
|
||||
bulk(self.es, items, index=self.cfg["elastic"]["index"], doc_type="events", raise_on_error=True)
|
||||
self.total_commits += count
|
||||
logging.debug("Written "+str(self.total_commits)+" events")
|
||||
print "Written "+str(self.total_commits)+" events"
|
||||
del self.nlist[0:len(self.nlist)]
|
||||
|
||||
|
||||
class NxGeoLoc():
|
||||
def __init__(self, cfg):
|
||||
self.cfg = cfg
|
||||
try:
|
||||
import GeoIP
|
||||
except ImportError:
|
||||
logging.warning("""Python's GeoIP module is not present.
|
||||
'World Map' reports won't work,
|
||||
and you can't use per-country filters.""")
|
||||
raise
|
||||
if not os.path.isfile(self.cfg["naxsi"]["geoipdb_path"]):
|
||||
logging.error("Unable to load GeoIPdb.")
|
||||
raise ValueError
|
||||
self.gi = GeoIP.new(GeoIP.GEOIP_MEMORY_CACHE)
|
||||
def cc2ll(self, country):
|
||||
""" translates countrycode to lagitude, longitude """
|
||||
# pun intended
|
||||
coord = [37.090240,-95.7128910]
|
||||
try:
|
||||
fd = open(self.cfg["naxsi"]["geoipdb_path"], "r")
|
||||
except:
|
||||
return "Unable to open GeoLoc database, please check your setup."
|
||||
fd.seek(0)
|
||||
for cn in fd:
|
||||
if cn.startswith(country+":"):
|
||||
x = cn[len(country)+1:-1]
|
||||
ar = x.split(',')
|
||||
coord[0] = float(ar[1])
|
||||
coord[1] = float(ar[0])
|
||||
break
|
||||
return coord
|
||||
def ip2cc(self, ip):
|
||||
""" translates an IP to a country code """
|
||||
country = self.gi.country_code_by_addr(ip)
|
||||
# pun intended
|
||||
if country is None or len(country) < 2:
|
||||
country = "CN"
|
||||
return country
|
||||
def ip2ll(self, ip):
|
||||
return self.cc2ll(self.ip2cc(ip))
|
||||
741
naxsi-0.55.3/nxapi/nxapi/nxtransform.py
Normal file
741
naxsi-0.55.3/nxapi/nxapi/nxtransform.py
Normal file
@@ -0,0 +1,741 @@
|
||||
import logging
|
||||
import json
|
||||
import copy
|
||||
import operator
|
||||
import os
|
||||
import pprint
|
||||
import shlex
|
||||
import datetime
|
||||
import glob
|
||||
import sys
|
||||
|
||||
from nxtypificator import Typificator
|
||||
|
||||
class NxConfig():
|
||||
""" Simple configuration loader """
|
||||
cfg = {}
|
||||
def __init__(self, fname):
|
||||
try:
|
||||
self.cfg = (json.loads(open(fname).read()))
|
||||
except:
|
||||
logging.critical("Unable to open/parse configuration file.")
|
||||
raise ValueError
|
||||
|
||||
class NxRating():
|
||||
""" A class that is used to check success criterias of rule.
|
||||
attempts jit querying + caching """
|
||||
def __init__(self, cfg, es, tr):
|
||||
self.tr = tr
|
||||
self.cfg = cfg
|
||||
self.es = es
|
||||
self.esq = {
|
||||
'global' : None,
|
||||
'template' : None,
|
||||
'rule' : None}
|
||||
self.stats = {
|
||||
'global' : {},
|
||||
'template' : {},
|
||||
'rule' : {}
|
||||
}
|
||||
self.global_warnings = cfg["global_warning_rules"]
|
||||
self.global_success = cfg["global_success_rules"]
|
||||
self.global_deny = cfg["global_deny_rules"]
|
||||
def drop(self):
|
||||
""" clears all existing stats """
|
||||
self.stats['template'] = {}
|
||||
self.stats['global'] = {}
|
||||
self.stats['rule'] = {}
|
||||
def refresh_scope(self, scope, esq):
|
||||
""" drops all datas for a named scope """
|
||||
if scope not in self.esq.keys():
|
||||
print "Unknown scope ?!"+scope
|
||||
self.esq[scope] = esq
|
||||
self.stats[scope] = {}
|
||||
def query_ratio(self, scope, scope_small, score, force_refresh):
|
||||
""" wrapper to calculate ratio between two vals, rounded float """
|
||||
#print "ratio :"+str(self.get(scope_small, score))+" / "+str( self.get(scope, score))
|
||||
ratio = round( (float(self.get(scope_small, score)) / self.get(scope, score)) * 100.0, 2)
|
||||
return ratio
|
||||
def get(self, scope, score, scope_small=None, force_refresh=False):
|
||||
""" fetch a value from self.stats or query ES """
|
||||
#print "#GET:"+scope+"_?"+str(scope_small)+"?_"+score+" = ?"
|
||||
if scope not in self.stats.keys():
|
||||
#print "unknown scope :"+scope
|
||||
return None
|
||||
if scope_small is not None:
|
||||
return self.query_ratio(scope, scope_small, score, force_refresh)
|
||||
elif score in self.stats[scope].keys() and force_refresh is False:
|
||||
return self.stats[scope][score]
|
||||
else:
|
||||
if score is not 'total':
|
||||
self.stats[scope][score] = self.tr.fetch_uniques(self.esq[scope], score)['total']
|
||||
else:
|
||||
res = self.tr.search(self.esq[scope])
|
||||
self.stats[scope][score] = res['hits']['total']
|
||||
|
||||
return self.stats[scope][score]
|
||||
def check_rule_score(self, tpl):
|
||||
""" wrapper to check_score, TOFIX ? """
|
||||
return self.check_score(tpl_success=tpl.get('_success', None),
|
||||
tpl_warnings=tpl.get('_warnings', None),
|
||||
tpl_deny=tpl.get('_deny', None))
|
||||
def check_score(self, tpl_success=None, tpl_warnings=None, tpl_deny=None):
|
||||
# pprint.pprint(self.stats)
|
||||
debug = False
|
||||
success = []
|
||||
warning = []
|
||||
deny = False
|
||||
failed_tests = {"success" : [], "warnings" : []}
|
||||
glb_success = self.global_success
|
||||
glb_warnings = self.global_warnings
|
||||
glb_deny = self.global_deny
|
||||
|
||||
for sdeny in [tpl_deny, glb_deny]:
|
||||
if sdeny is None:
|
||||
continue
|
||||
for k in sdeny.keys():
|
||||
res = self.check_rule(k, sdeny[k])
|
||||
if res['check'] is True:
|
||||
# print "WE SHOULD DENY THAT"
|
||||
deny = True
|
||||
break
|
||||
for scheck in [glb_success, tpl_success]:
|
||||
if scheck is None:
|
||||
continue
|
||||
for k in scheck.keys():
|
||||
res = self.check_rule(k, scheck[k])
|
||||
if res['check'] is True:
|
||||
if debug is True:
|
||||
print "[SUCCESS] OK, on "+k+" vs "+str(res['curr'])+", check :"+str(scheck[k][0])+" - "+str(scheck[k][1])
|
||||
success.append({'key' : k, 'criteria' : scheck[k], 'curr' : res['curr']})
|
||||
else:
|
||||
if debug is True:
|
||||
print "[SUCCESS] KO, on "+k+" vs "+str(res['curr'])+", check :"+str(scheck[k][0])+" - "+str(scheck[k][1])
|
||||
failed_tests["success"].append({'key' : k, 'criteria' : scheck[k], 'curr' : res['curr']})
|
||||
|
||||
for fcheck in [glb_warnings, tpl_warnings]:
|
||||
if fcheck is None:
|
||||
continue
|
||||
for k in fcheck.keys():
|
||||
res = self.check_rule(k, fcheck[k])
|
||||
if res['check'] is True:
|
||||
if debug is True:
|
||||
print "[WARNINGS] TRIGGERED, on "+k+" vs "+str(res['curr'])+", check :"+str(fcheck[k][0])+" - "+str(fcheck[k][1])
|
||||
warning.append({'key' : k, 'criteria' : fcheck[k], 'curr' : res['curr']})
|
||||
else:
|
||||
if debug is True:
|
||||
print "[WARNINGS] NOT TRIGGERED, on "+k+" vs "+str(res['curr'])+", check :"+str(fcheck[k][0])+" - "+str(fcheck[k][1])
|
||||
failed_tests["warnings"].append({'key' : k, 'criteria' : fcheck[k], 'curr' : res['curr']})
|
||||
x = { 'success' : success,
|
||||
'warnings' : warning,
|
||||
'failed_tests' : failed_tests,
|
||||
'deny' : deny}
|
||||
return x
|
||||
def check_rule(self, label, check_rule):
|
||||
""" check met/failed success/warning criterias
|
||||
of a given template vs a set of results """
|
||||
check = check_rule[0]
|
||||
beat = check_rule[1]
|
||||
if label.find("var_name") != -1:
|
||||
label = label.replace("var_name", "var-name")
|
||||
items = label.split('_')
|
||||
for x in range(len(items)):
|
||||
items[x] = items[x].replace("var-name", "var_name")
|
||||
|
||||
if len(items) == 2:
|
||||
scope = items[0]
|
||||
score = items[1]
|
||||
x = self.get(scope, score)
|
||||
# print "scope:"+str(scope)+" score:"+str(score)
|
||||
return {'curr' : x, 'check' : check( int(self.get(scope, score)), int(beat))}
|
||||
elif len(items) == 4:
|
||||
scope = items[0]
|
||||
scope_small = items[1]
|
||||
score = items[2]
|
||||
x = self.get(scope, score, scope_small=scope_small)
|
||||
#Xpprint.pprint()
|
||||
return {'curr' : x, 'check' : check(int(self.get(scope, score, scope_small=scope_small)), int(beat))}
|
||||
else:
|
||||
print "cannot understand rule ("+label+"):",
|
||||
pprint.pprint(check_rule)
|
||||
return { 'curr' : 0, 'check' : False }
|
||||
|
||||
class NxTranslate():
|
||||
""" Transform Whitelists, template into
|
||||
ElasticSearch queries, and vice-versa, conventions :
|
||||
esq : elasticsearch query
|
||||
tpl : template
|
||||
cr : core rule
|
||||
wl : whitelist """
|
||||
def __init__(self, es, cfg):
|
||||
self.es = es
|
||||
self.debug = True
|
||||
self.cfg = cfg.cfg
|
||||
self.cfg["global_warning_rules"] = self.normalize_checks(self.cfg["global_warning_rules"])
|
||||
self.cfg["global_success_rules"] = self.normalize_checks(self.cfg["global_success_rules"])
|
||||
self.cfg["global_deny_rules"] = self.normalize_checks(self.cfg["global_deny_rules"])
|
||||
self.core_msg = {}
|
||||
# by default, es queries will return 1000 results max
|
||||
self.es_max_size = self.cfg.get("elastic").get("max_size", 1000)
|
||||
print "# size :"+str(self.es_max_size)
|
||||
# purely for output coloring
|
||||
self.red = u'{0}'
|
||||
self.grn = u'{0}'
|
||||
self.blu = u'{0}'
|
||||
if self.cfg["output"]["colors"] == "true":
|
||||
self.red = u"\033[91m{0}\033[0m"
|
||||
self.grn = u"\033[92m{0}\033[0m"
|
||||
self.blu = u"\033[94m{0}\033[0m"
|
||||
# Attempt to parse provided core rules file
|
||||
self.load_cr_file(self.cfg["naxsi"]["rules_path"])
|
||||
|
||||
def full_auto(self, to_fill_list=None):
|
||||
""" Loads all tpl within template_path
|
||||
If templates has hit, peers or url(s) ratio > 15%,
|
||||
attempts to generate whitelists.
|
||||
Only displays the wl that did not raise warnings, ranked by success"""
|
||||
|
||||
# gather total IPs, total URIs, total hit count
|
||||
scoring = NxRating(self.cfg, self.es, self)
|
||||
|
||||
strict = True
|
||||
if self.cfg.get("naxsi").get("strict", "") == "false":
|
||||
strict = False
|
||||
|
||||
scoring.refresh_scope("global", self.cfg["global_filters"])
|
||||
if scoring.get("global", "ip") <= 0:
|
||||
return []
|
||||
output = []
|
||||
for sdir in self.cfg["naxsi"]["template_path"]:
|
||||
for root, dirs, files in os.walk(sdir):
|
||||
for file in files:
|
||||
if file.endswith(".tpl"):
|
||||
output.append("# {0}{1}/{2} ".format(
|
||||
self.grn.format(" template :"),
|
||||
root,
|
||||
file
|
||||
))
|
||||
template = self.load_tpl_file(root+"/"+file)
|
||||
scoring.refresh_scope('template', self.tpl2esq(template))
|
||||
output.append("Nb of hits : {0}".format(scoring.get('template', 'total')))
|
||||
if scoring.get('template', 'total') > 0:
|
||||
output.append('{0}'.format(self.grn.format("# template matched, generating all rules.")))
|
||||
whitelists = self.gen_wl(template, rule={})
|
||||
# x add here
|
||||
output.append('{0}'.format(len(whitelists))+" whitelists ...")
|
||||
for genrule in whitelists:
|
||||
scoring.refresh_scope('rule', genrule['rule'])
|
||||
results = scoring.check_rule_score(template)
|
||||
# XX1
|
||||
if (len(results['success']) > len(results['warnings']) and results["deny"] == False) or self.cfg["naxsi"]["strict"] == "false":
|
||||
# print "?deny "+str(results['deny'])
|
||||
try:
|
||||
str_genrule = '{0}'.format(self.grn.format(self.tpl2wl(genrule['rule']).encode('utf-8', 'replace'), template))
|
||||
except UnicodeDecodeError:
|
||||
logging.warning('WARNING: Unprocessable string found in the elastic search')
|
||||
output.append(self.fancy_display(genrule, results, template))
|
||||
output.append(str_genrule)
|
||||
if to_fill_list is not None:
|
||||
genrule.update({'genrule': str_genrule})
|
||||
to_fill_list.append(genrule)
|
||||
return output
|
||||
|
||||
def wl_on_type(self):
|
||||
for rule in Typificator(self.es, self.cfg).get_rules():
|
||||
print 'BasicRule negative "rx:{0}" "msg:{1}" "mz:${2}_VAR:{3}" "s:BLOCK";'.format(*rule)
|
||||
|
||||
def fancy_display(self, full_wl, scores, template=None):
|
||||
output = []
|
||||
if template is not None and '_msg' in template.keys():
|
||||
output.append("#msg: {0}\n".format(template['_msg']))
|
||||
rid = full_wl['rule'].get('id', "0")
|
||||
output.append("#Rule ({0}) {1}\n".format(rid, self.core_msg.get(rid, 'Unknown ..')))
|
||||
if self.cfg["output"]["verbosity"] >= 4:
|
||||
output.append("#total hits {0}\n".format(full_wl['total_hits']))
|
||||
for x in ["content", "peers", "uri", "var_name"]:
|
||||
if x not in full_wl.keys():
|
||||
continue
|
||||
for y in full_wl[x]:
|
||||
output.append("#{0} : {1}\n".format(x, unicode(y).encode("utf-8", 'replace')))
|
||||
return ''.join(output)
|
||||
|
||||
# pprint.pprint(scores)
|
||||
for x in scores['success']:
|
||||
print "# success : "+self.grn.format(str(x['key'])+" is "+str(x['curr']))
|
||||
for x in scores['warnings']:
|
||||
print "# warnings : "+self.grn.format(str(x['key'])+" is "+str(x['curr']))
|
||||
|
||||
pass
|
||||
def expand_tpl_path(self, template):
|
||||
""" attempts to convert stuff to valid tpl paths.
|
||||
if it starts with / or . it will consider it's a relative/absolute path,
|
||||
else, that it's a regex on tpl names. """
|
||||
clean_tpls = []
|
||||
tpl_files = []
|
||||
if template.startswith('/') or template.startswith('.'):
|
||||
tpl_files.extend(glob.glob(template))
|
||||
else:
|
||||
for sdir in self.cfg['naxsi']['template_path']:
|
||||
tpl_files.extend(glob.glob(sdir +"/"+template))
|
||||
for x in tpl_files:
|
||||
if x.endswith(".tpl") and x not in clean_tpls:
|
||||
clean_tpls.append(x)
|
||||
return clean_tpls
|
||||
|
||||
def load_tpl_file(self, tpl):
|
||||
""" open, json.loads a tpl file,
|
||||
cleanup data, return dict. """
|
||||
try:
|
||||
x = open(tpl)
|
||||
except:
|
||||
logging.error("Unable to open tpl file.")
|
||||
return None
|
||||
tpl_s = ""
|
||||
for l in x.readlines():
|
||||
if l.startswith('#'):
|
||||
continue
|
||||
else:
|
||||
tpl_s += l
|
||||
try:
|
||||
template = json.loads(tpl_s)
|
||||
except:
|
||||
logging.error("Unable to load json from '"+tpl_s+"'")
|
||||
return None
|
||||
if '_success' in template.keys():
|
||||
template['_success'] = self.normalize_checks(template['_success'])
|
||||
if '_warnings' in template.keys():
|
||||
template['_warnings'] = self.normalize_checks(template['_warnings'])
|
||||
if '_deny' in template.keys():
|
||||
template['_deny'] = self.normalize_checks(template['_deny'])
|
||||
#return self.tpl_append_gfilter(template)
|
||||
return template
|
||||
def load_wl_file(self, wlf):
|
||||
""" Loads a file of whitelists,
|
||||
convert them to ES queries,
|
||||
and returns them as a list """
|
||||
esql = []
|
||||
try:
|
||||
wlfd = open(wlf, "r")
|
||||
except:
|
||||
logging.error("Unable to open whitelist file.")
|
||||
return None
|
||||
for wl in wlfd:
|
||||
[res, esq] = self.wl2esq(wl)
|
||||
if res is True:
|
||||
esql.append(esq)
|
||||
if len(esql) > 0:
|
||||
return esql
|
||||
return None
|
||||
def load_cr_file(self, cr_file):
|
||||
""" parses naxsi's core rule file, to
|
||||
decorate output with "msg:" field content """
|
||||
core_msg = {}
|
||||
core_msg['0'] = "id:0 is wildcard (all rules) whitelist."
|
||||
try:
|
||||
fd = open(cr_file, 'r')
|
||||
for i in fd:
|
||||
if i.startswith('MainRule') or i.startswith('#@MainRule'):
|
||||
pos = i.find('id:')
|
||||
pos_msg = i.find('msg:')
|
||||
self.core_msg[i[pos + 3:i[pos + 3].find(';') - 1]] = i[pos_msg + 4:][:i[pos_msg + 4:].find('"')]
|
||||
fd.close()
|
||||
except:
|
||||
logging.warning("Unable to open rules file")
|
||||
def tpl2esq(self, ob, full=True):
|
||||
''' receives template or a rule, returns a valid
|
||||
ElasticSearch query '''
|
||||
qr = {
|
||||
"query" : { "bool" : { "must" : [ ]} },
|
||||
"size" : self.es_max_size
|
||||
}
|
||||
# A hack in case we were inadvertently given an esq
|
||||
if 'query' in ob.keys():
|
||||
return ob
|
||||
for k in ob.keys():
|
||||
if k.startswith("_"):
|
||||
continue
|
||||
# if key starts with '?' :
|
||||
# use content for search, but use content from exceptions to generate WL
|
||||
if k[0] == '?':
|
||||
k = k[1:]
|
||||
qr['query']['bool']['must'].append({"regexp" : { k : ob['?'+k] }})
|
||||
# wildcard
|
||||
elif ob[k] == '?':
|
||||
pass
|
||||
else:
|
||||
qr['query']['bool']['must'].append({"match" : { k : ob[k]}})
|
||||
|
||||
qr = self.append_gfilter(qr)
|
||||
return qr
|
||||
def append_gfilter(self, esq):
|
||||
""" append global filters parameters
|
||||
to and existing elasticsearch query """
|
||||
for x in self.cfg["global_filters"]:
|
||||
if x.startswith('?'):
|
||||
x = x[1:]
|
||||
if {"regexp" : { x : self.cfg["global_filters"]['?'+x] }} not in esq['query']['bool']['must']:
|
||||
esq['query']['bool']['must'].append({"regexp" : { x : self.cfg["global_filters"]['?'+x] }})
|
||||
else:
|
||||
if {"match" : { x : self.cfg["global_filters"][x] }} not in esq['query']['bool']['must']:
|
||||
esq['query']['bool']['must'].append({"match" : { x : self.cfg["global_filters"][x] }})
|
||||
return esq
|
||||
def tpl_append_gfilter(self, tpl):
|
||||
for x in self.cfg["global_filters"]:
|
||||
tpl[x] = self.cfg["global_filters"][x]
|
||||
return tpl
|
||||
def wl2esq(self, raw_line):
|
||||
""" parses a fulltext naxsi whitelist,
|
||||
and outputs the matching es query (ie. for tagging),
|
||||
returns [True|False, error_string|ESquery] """
|
||||
esq = {
|
||||
"query" : { "bool" : { "must" : [ ]} },
|
||||
"size" : self.es_max_size
|
||||
}
|
||||
wl_id = ""
|
||||
mz_str = ""
|
||||
# do some pre-check to ensure it's a valid line
|
||||
if raw_line.startswith("#"):
|
||||
return [False, "commented out"]
|
||||
if raw_line.find("BasicRule") == -1:
|
||||
return [False, "not a BasicRule"]
|
||||
# split line
|
||||
strings = shlex.split(raw_line)
|
||||
# bug #194 - drop everything after the first chunk starting with a '#' (inline comments)
|
||||
for x in strings:
|
||||
if x.startswith('#'):
|
||||
strings = strings[:strings.index(x)]
|
||||
# more checks
|
||||
if len(strings) < 3:
|
||||
return [False, "empty/incomplete line"]
|
||||
if strings[0].startswith('#'):
|
||||
return [False, "commented line"]
|
||||
if strings[0] != "BasicRule":
|
||||
return [False, "not a BasicRule, keyword '"+strings[0]+"'"]
|
||||
if strings[len(strings) - 1].endswith(';'):
|
||||
strings[len(strings) - 1] = strings[len(strings) - 1][:-1]
|
||||
for x in strings:
|
||||
if x.startswith("wl:"):
|
||||
wl_id = x[3:]
|
||||
# if ID contains "," replace them with OR for ES query
|
||||
wl_id = wl_id.replace(",", " OR ")
|
||||
# if ID != 0 add it, otherwise, it's a wildcard!
|
||||
if wl_id != "0":
|
||||
# if IDs are negative, we must exclude all IDs except
|
||||
# those ones.
|
||||
if wl_id.find("-") != -1:
|
||||
wl_id = wl_id.replace("-", "")
|
||||
#print "Negative query."
|
||||
if not 'must_not' in esq['query']['bool'].keys():
|
||||
esq['query']['bool']['must_not'] = []
|
||||
esq['query']['bool']['must_not'].append({"match" : { "id" : wl_id}})
|
||||
else:
|
||||
esq['query']['bool']['must'].append({"match" : { "id" : wl_id}})
|
||||
if x.startswith("mz:"):
|
||||
mz_str = x[3:]
|
||||
[res, filters] = self.parse_mz(mz_str, esq)
|
||||
if res is False:
|
||||
return [False, "matchzone parsing failed."]
|
||||
esq = self.append_gfilter(esq)
|
||||
return [True, filters]
|
||||
def parse_mz(self, mz_str, esq):
|
||||
""" parses a match zone from BasicRule, and updates
|
||||
es query accordingly. Removes ^/$ chars from regexp """
|
||||
forbidden_rx_chars = "^$"
|
||||
kw = mz_str.split("|")
|
||||
tpl = esq['query']['bool']['must']
|
||||
uri = ""
|
||||
zone = ""
|
||||
var_name = ""
|
||||
t_name = False
|
||||
# |NAME flag
|
||||
if "NAME" in kw:
|
||||
t_name = True
|
||||
kw.remove("NAME")
|
||||
for k in kw:
|
||||
# named var
|
||||
if k.startswith('$'):
|
||||
k = k[1:]
|
||||
try:
|
||||
[zone, var_name] = k.split(':')
|
||||
except:
|
||||
return [False, "Incoherent zone : "+k]
|
||||
# *_VAR:<string>
|
||||
if zone.endswith("_VAR"):
|
||||
zone = zone[:-4]
|
||||
if t_name is True:
|
||||
zone += "|NAME"
|
||||
tpl.append({"match" : { "zone" : zone}})
|
||||
tpl.append({"match" : { "var_name" : var_name}})
|
||||
# *_VAR_X:<regexp>
|
||||
elif zone.endswith("_VAR_X"):
|
||||
zone = zone[:-6]
|
||||
if t_name is True:
|
||||
zone += "|NAME"
|
||||
tpl.append({"match" : { "zone" : zone}})
|
||||
#.translate(string.maketrans(chars, newchars))
|
||||
tpl.append({"regexp" : { "var_name" : var_name.translate(None, forbidden_rx_chars)}})
|
||||
# URL_X:<regexp>
|
||||
elif zone == "URL_X":
|
||||
zone = zone[:-2]
|
||||
tpl.append({"regexp" : { "uri" : var_name.translate(None, forbidden_rx_chars)}})
|
||||
# URL:<string>
|
||||
elif zone == "URL":
|
||||
tpl.append({"match" : { "uri" : var_name }})
|
||||
else:
|
||||
print "huh, what's that ? "+zone
|
||||
|
||||
# |<ZONE>
|
||||
else:
|
||||
if k not in ["HEADERS", "BODY", "URL", "ARGS", "FILE_EXT"]:
|
||||
return [False, "Unknown zone : '"+k+"'"]
|
||||
zone = k
|
||||
if t_name is True:
|
||||
zone += "|NAME"
|
||||
tpl.append({"match" : {"zone" : zone}})
|
||||
# print "RULE :"
|
||||
# pprint.pprint(esq)
|
||||
return [True, esq]
|
||||
def tpl2wl(self, rule, template=None):
|
||||
""" transforms a rule/esq
|
||||
to a valid BasicRule. """
|
||||
tname = False
|
||||
zone = ""
|
||||
if template is not None and '_statics' in template.keys():
|
||||
for x in template['_statics'].keys():
|
||||
rule[x] = template['_statics'][x]
|
||||
|
||||
wl = "BasicRule "
|
||||
wl += " wl:"+str(rule.get('id', 0)).replace("OR", ",").replace("|", ",").replace(" ", "")
|
||||
|
||||
wl += ' "mz:'
|
||||
|
||||
if rule.get('uri', None) is not None:
|
||||
wl += "$URL:"+rule['uri']
|
||||
wl += "|"
|
||||
# whitelist targets name
|
||||
if rule.get('zone', '').endswith("|NAME"):
|
||||
tname = True
|
||||
zone = rule['zone'][:-5]
|
||||
else:
|
||||
zone = rule['zone']
|
||||
|
||||
if rule.get('var_name', '') not in ['', '?'] and zone != "FILE_EXT":
|
||||
wl += "$"+zone+"_VAR:"+rule['var_name']
|
||||
else:
|
||||
wl += zone
|
||||
|
||||
if tname is True:
|
||||
wl += "|NAME"
|
||||
|
||||
wl += '";'
|
||||
return wl
|
||||
def fetch_top(self, template, field, limit=10):
|
||||
""" fetch top items for a given field,
|
||||
clears the field if exists in gfilters """
|
||||
x = None
|
||||
if field in template.keys():
|
||||
x = template[field]
|
||||
del template[field]
|
||||
esq = self.tpl2esq(template)
|
||||
if x is not None:
|
||||
template[field] = x
|
||||
if self.cfg["elastic"].get("version", None) == "1":
|
||||
esq['facets'] = { "facet_results" : {"terms": { "field": field, "size" : self.es_max_size} }}
|
||||
elif self.cfg["elastic"].get("version", None) in ["2", "5"]:
|
||||
esq['aggregations'] = { "agg1" : {"terms": { "field": field, "size" : self.es_max_size} }}
|
||||
else:
|
||||
print "Unknown / Unspecified ES version in nxapi.json : {0}".format(self.cfg["elastic"].get("version", "#UNDEFINED"))
|
||||
sys.exit(1)
|
||||
|
||||
res = self.search(esq)
|
||||
|
||||
if self.cfg["elastic"].get("version", None) == "1":
|
||||
total = res['facets']['facet_results']['total']
|
||||
elif self.cfg["elastic"].get("version", None) in ["2", "5"]:
|
||||
total = res['hits']['total']
|
||||
else:
|
||||
print "Unknown / Unspecified ES version in nxapi.json : {0}".format(self.cfg["elastic"].get("version", "#UNDEFINED"))
|
||||
sys.exit(1)
|
||||
|
||||
count = 0
|
||||
ret = []
|
||||
if self.cfg["elastic"].get("version", None) == "1":
|
||||
for x in res['facets']['facet_results']['terms']:
|
||||
ret.append('{0} {1}% (total: {2}/{3})'.format(x['term'], round((float(x['count']) / total) * 100, 2), x['count'], total))
|
||||
count += 1
|
||||
if count > limit:
|
||||
break
|
||||
elif self.cfg["elastic"].get("version", None) in ["2", "5"]:
|
||||
for x in res['aggregations']['agg1']['buckets']:
|
||||
ret.append('{0} {1}% (total: {2}/{3})'.format(x['key'], round((float(x['doc_count']) / total) * 100, 2), x['doc_count'], total))
|
||||
count += 1
|
||||
if count > limit:
|
||||
break
|
||||
else:
|
||||
print "Unknown / Unspecified ES version in nxapi.json : {0}".format(self.cfg["elastic"].get("version", "#UNDEFINED"))
|
||||
sys.exit(1)
|
||||
return ret
|
||||
|
||||
def fetch_uniques(self, rule, key):
|
||||
""" shortcut function to gather unique
|
||||
values and their associated match count """
|
||||
uniques = []
|
||||
esq = self.tpl2esq(rule)
|
||||
#
|
||||
if self.cfg["elastic"].get("version", None) == "1":
|
||||
esq['facets'] = { "facet_results" : {"terms": { "field": key, "size" : 50000} }}
|
||||
elif self.cfg["elastic"].get("version", None) in ["2", "5"]:
|
||||
esq['aggregations'] = { "agg1" : {"terms": { "field": key, "size" : 50000} }}
|
||||
else:
|
||||
print "Unknown / Unspecified ES version in nxapi.json : {0}".format(self.cfg["elastic"].get("version", "#UNDEFINED"))
|
||||
sys.exit(1)
|
||||
|
||||
res = self.search(esq)
|
||||
if self.cfg["elastic"].get("version", None) == "1":
|
||||
for x in res['facets']['facet_results']['terms']:
|
||||
if x['term'] not in uniques:
|
||||
uniques.append(x['term'])
|
||||
elif self.cfg["elastic"].get("version", None) in ["2", "5"]:
|
||||
for x in res['aggregations']['agg1']['buckets']:
|
||||
if x['key'] not in uniques:
|
||||
uniques.append(x['key'])
|
||||
else:
|
||||
print "Unknown / Unspecified ES version in nxapi.json : {0}".format(self.cfg["elastic"].get("version", "#UNDEFINED"))
|
||||
sys.exit(1)
|
||||
|
||||
return { 'list' : uniques, 'total' : len(uniques) }
|
||||
def index(self, body, eid):
|
||||
return self.es.index(index=self.cfg["elastic"]["index"], doc_type=self.cfg["elastic"]["doctype"], body=body, id=eid)
|
||||
def search(self, esq, stats=False):
|
||||
""" search wrapper with debug """
|
||||
debug = False
|
||||
|
||||
if debug is True:
|
||||
print "#SEARCH:PARAMS:index="+self.cfg["elastic"]["index"]+", doc_type="+self.cfg["elastic"]["doctype"]+", body=",
|
||||
print "#SEARCH:QUERY:",
|
||||
pprint.pprint (esq)
|
||||
if len(esq["query"]["bool"]["must"]) == 0:
|
||||
del esq["query"]
|
||||
x = self.es.search(index=self.cfg["elastic"]["index"], doc_type=self.cfg["elastic"]["doctype"], body=esq)
|
||||
if debug is True:
|
||||
print "#RESULT:",
|
||||
pprint.pprint(x)
|
||||
return x
|
||||
def normalize_checks(self, tpl):
|
||||
""" replace check signs (<, >, <=, >=) by
|
||||
operator.X in a dict-form tpl """
|
||||
replace = {
|
||||
'>' : operator.gt,
|
||||
'<' : operator.lt,
|
||||
'>=' : operator.ge,
|
||||
'<=' : operator.le
|
||||
}
|
||||
|
||||
for tpl_key in tpl.keys():
|
||||
for token in replace.keys():
|
||||
if tpl[tpl_key][0] == token:
|
||||
tpl[tpl_key][0] = replace[token]
|
||||
return tpl
|
||||
def tag_events(self, esq, msg, tag=False):
|
||||
""" tag events with msg + tstamp if they match esq """
|
||||
count = 0
|
||||
total_events = 0
|
||||
esq["size"] = "0"
|
||||
print "TAG RULE :",
|
||||
pprint.pprint(esq)
|
||||
x = self.search(esq)
|
||||
total_events = int(str(x["hits"]["total"]))
|
||||
print str(self.grn.format(total_events)) + " items to be tagged ..."
|
||||
size = int(x['hits']['total'])
|
||||
if size > 20000:
|
||||
size = size / 100
|
||||
elif size > 100:
|
||||
size = size / 10
|
||||
while count < total_events:
|
||||
esq["size"] = size
|
||||
esq["from"] = 0
|
||||
res = self.search(esq)
|
||||
# Iterate through matched evts to tag them.
|
||||
if int(res['hits']['total']) == 0:
|
||||
break
|
||||
for item in res['hits']['hits']:
|
||||
eid = item['_id']
|
||||
body = item['_source']
|
||||
cm = item['_source']['comments']
|
||||
body['comments'] += ","+msg+":"+str(datetime.datetime.now())
|
||||
body['whitelisted'] = "true"
|
||||
if tag is True:
|
||||
self.index(body, eid)
|
||||
else:
|
||||
print eid+",",
|
||||
count += 1
|
||||
print "Tagged {0} events out of {1}".format(count, total_events)
|
||||
if total_events - count < size:
|
||||
size = total_events - count
|
||||
print ""
|
||||
#--
|
||||
if not tag or tag is False:
|
||||
return 0
|
||||
else:
|
||||
return count
|
||||
|
||||
|
||||
def gen_wl(self, tpl, rule={}):
|
||||
""" recursive whitelist generation function,
|
||||
returns a list of all possible witelists. """
|
||||
retlist = []
|
||||
for tpl_key in tpl.keys():
|
||||
if tpl_key in rule.keys():
|
||||
continue
|
||||
if tpl_key[0] in ['_', '?']:
|
||||
continue
|
||||
if tpl[tpl_key] == '?':
|
||||
continue
|
||||
rule[tpl_key] = tpl[tpl_key]
|
||||
for tpl_key in tpl.keys():
|
||||
if tpl_key.startswith('_'):
|
||||
continue
|
||||
elif tpl_key.startswith('?'):
|
||||
if tpl_key[1:] in rule.keys():
|
||||
continue
|
||||
unique_vals = self.fetch_uniques(rule, tpl_key[1:])['list']
|
||||
for uval in unique_vals:
|
||||
rule[tpl_key[1:]] = uval
|
||||
retlist += self.gen_wl(tpl, copy.copy(rule))
|
||||
return retlist
|
||||
elif tpl[tpl_key] == '?':
|
||||
if tpl_key in rule.keys():
|
||||
continue
|
||||
unique_vals = self.fetch_uniques(rule, tpl_key)['list']
|
||||
for uval in unique_vals:
|
||||
rule[tpl_key] = uval
|
||||
retlist += self.gen_wl(tpl, copy.copy(rule))
|
||||
return retlist
|
||||
elif tpl_key not in rule.keys():
|
||||
rule[tpl_key] = tpl[tpl_key]
|
||||
retlist += self.gen_wl(tpl, copy.copy(rule))
|
||||
return retlist
|
||||
|
||||
esq = self.tpl2esq(rule)
|
||||
res = self.search(esq)
|
||||
if res['hits']['total'] > 0:
|
||||
clist = []
|
||||
peers = []
|
||||
uri = []
|
||||
var_name = []
|
||||
|
||||
for x in res['hits']['hits']:
|
||||
if len(x.get("_source").get("ip", "")) > 0 and x.get("_source").get("ip", "") not in peers:
|
||||
peers.append(x["_source"]["ip"])
|
||||
if len(x.get("_source").get("uri", "")) > 0 and x.get("_source").get("uri", "") not in uri:
|
||||
uri.append(x["_source"]["uri"])
|
||||
if len(x.get("_source").get("var_name", "")) > 0 and x.get("_source").get("var_name", "") not in var_name:
|
||||
var_name.append(x["_source"]["var_name"])
|
||||
if len(x.get("_source").get("content", "")) > 0 and x.get("_source").get("content", "") not in clist:
|
||||
clist.append(x["_source"]["content"])
|
||||
if len(clist) >= 5:
|
||||
break
|
||||
retlist.append({'rule' : rule, 'content' : clist[:5], 'total_hits' : res['hits']['total'], 'peers' : peers[:5], 'uri' : uri[:5],
|
||||
'var_name' : var_name[:5]})
|
||||
return retlist
|
||||
return []
|
||||
101
naxsi-0.55.3/nxapi/nxapi/nxtypificator.py
Normal file
101
naxsi-0.55.3/nxapi/nxapi/nxtypificator.py
Normal file
@@ -0,0 +1,101 @@
|
||||
'''
|
||||
This modules generate types for url parameters.
|
||||
'''
|
||||
import re
|
||||
import sys
|
||||
import collections
|
||||
from elasticsearch import Elasticsearch
|
||||
|
||||
# Each regexp is a subset of the next one
|
||||
REGEXPS = [
|
||||
[r'^$', 'empty'],
|
||||
[r'^[01]$', 'boolean'],
|
||||
[r'^\d+$', 'integer'],
|
||||
[r'^#[0-9a-f]+$', 'colour'], # hex + '#'
|
||||
[r'^[0-9a-f]+$', 'hexadecimal'],
|
||||
[r'^[0-9a-z]+$', 'alphanum'],
|
||||
[r'^https?://([0-9a-z-.]+\.)+[\w?+-=&/ ]+$', 'url'], # like http://pouet.net?hello=1&id=3
|
||||
[r'^\w+$', 'alphanumdash'],
|
||||
[r'^[0-9a-z?&=+_-]+$', 'url parameter'],
|
||||
[r'^[\w[] ,&=+-]+$', 'array'],
|
||||
[r'^[' + r'\s\w' + r'!$%^&*()[]:;@~#?/.,' + r']+$', 'plaintext'],
|
||||
[r'', 'none'], # untypables parameters
|
||||
]
|
||||
|
||||
|
||||
class Typificator(object):
|
||||
''' Classes that:
|
||||
1. Fetch data from ES
|
||||
2. Generate types for parameters
|
||||
3. Returns a dict of dict
|
||||
'''
|
||||
def __init__(self, es, cfg):
|
||||
self.es_instance = es
|
||||
self.cfg = cfg
|
||||
|
||||
def __get_data(self, nb_samples=1e5):
|
||||
''' Get (in a lazy way) data from the ES instance
|
||||
'''
|
||||
data = set()
|
||||
position = 0
|
||||
size = min(10000, nb_samples) # if nb_samples if inferiour to our size, we'll get it in a single request.
|
||||
while nb_samples:
|
||||
if not data:
|
||||
body = {'query': {}}
|
||||
for k,v in self.cfg['global_filters'].iteritems():
|
||||
body['query'].update({'match':{k:v}})
|
||||
data = self.es_instance.search(index=self.cfg["elastic"]["index"], doc_type='events',
|
||||
size=size, from_=position,
|
||||
body=body)
|
||||
data = data['hits']['hits'] # we don't care about metadata
|
||||
if not data: # we got all data from ES
|
||||
return
|
||||
position += size
|
||||
nb_samples -= size
|
||||
for log in data:
|
||||
yield log['_source']
|
||||
|
||||
def get_rules(self, nb_samples=1e5):
|
||||
''' Generate (in a lazy way) types for parameters
|
||||
'''
|
||||
# Thank you defaultdict <3
|
||||
# rules = {zone1: {var1:0, var2:0}, zone2: {var6:0, ...}, ...}
|
||||
rules = collections.defaultdict(lambda: collections.defaultdict(int))
|
||||
|
||||
# Compile regexp for speed
|
||||
regexps = [re.compile(reg, re.IGNORECASE) for reg, _ in REGEXPS]
|
||||
|
||||
for line in self.__get_data(nb_samples):
|
||||
try: # some events are fucked up^w^w empty
|
||||
#naxsi inverts the var_name and the content
|
||||
#when a rule match on var_name
|
||||
if line['zone'].endswith('|NAME'):
|
||||
continue
|
||||
zone = line['zone']
|
||||
content = line['content']
|
||||
var_name = line['var_name']
|
||||
except KeyError as e:
|
||||
print 'Error with : {0} ({1})'.format(line, e)
|
||||
continue
|
||||
|
||||
if not var_name: # No types for empty varnames.
|
||||
continue
|
||||
|
||||
# Bump regexps until one matches
|
||||
# Since every regexp is a subset of the next one,
|
||||
# this works great.
|
||||
while not regexps[rules[zone][var_name]].match(content):
|
||||
rules[zone][var_name] += 1
|
||||
|
||||
for zone, zone_data in rules.iteritems():
|
||||
for var_name, index in zone_data.iteritems():
|
||||
if index < len(REGEXPS) - 1: # Don't return untyped things
|
||||
yield [REGEXPS[index][0], REGEXPS[index][1], zone, var_name]
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
nb_samples = 1e6 if len(sys.argv) == 1 else int(sys.argv[1])
|
||||
|
||||
for rule in Typificator().get_rules(nb_samples):
|
||||
print 'TypeRule "rx:{0}" "msg:typed ({1}) parameter" "mz:${2}_VAR:{3}"'.format(rule[0], rule[1], rule[2], rule[3])
|
||||
479
naxsi-0.55.3/nxapi/nxtool.py
Executable file
479
naxsi-0.55.3/nxapi/nxtool.py
Executable file
@@ -0,0 +1,479 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import glob, fcntl, termios
|
||||
import sys
|
||||
import socket
|
||||
import elasticsearch
|
||||
import time
|
||||
import os
|
||||
import tempfile
|
||||
import subprocess
|
||||
import json
|
||||
|
||||
from collections import defaultdict
|
||||
from optparse import OptionParser, OptionGroup
|
||||
from nxapi.nxtransform import *
|
||||
from nxapi.nxparse import *
|
||||
|
||||
F_SETPIPE_SZ = 1031 # Linux 2.6.35+
|
||||
F_GETPIPE_SZ = 1032 # Linux 2.6.35+
|
||||
|
||||
|
||||
def open_fifo(fifo):
|
||||
try:
|
||||
os.mkfifo(fifo)
|
||||
except OSError:
|
||||
print "Fifo ["+fifo+"] already exists (non fatal)."
|
||||
except Exception, e:
|
||||
print "Unable to create fifo ["+fifo+"]"
|
||||
try:
|
||||
print "Opening fifo ... will return when data is available."
|
||||
fifo_fd = open(fifo, 'r')
|
||||
fcntl.fcntl(fifo_fd, F_SETPIPE_SZ, 1000000)
|
||||
print "Pipe (modified) size : "+str(fcntl.fcntl(fifo_fd, F_GETPIPE_SZ))
|
||||
except Exception, e:
|
||||
print "Unable to create fifo, error: "+str(e)
|
||||
return None
|
||||
return fifo_fd
|
||||
|
||||
def macquire(line):
|
||||
z = parser.parse_raw_line(line)
|
||||
# add data str and country
|
||||
if z is not None:
|
||||
for event in z['events']:
|
||||
event['date'] = z['date']
|
||||
try:
|
||||
event['coords'] = geoloc.ip2ll(event['ip'])
|
||||
event['country'] = geoloc.ip2cc(event['ip'])
|
||||
except NameError:
|
||||
pass
|
||||
# print "Got data :)"
|
||||
# pprint.pprint(z)
|
||||
#print ".",
|
||||
print z
|
||||
injector.insert(z)
|
||||
else:
|
||||
pass
|
||||
#print "No data ? "+line
|
||||
#print ""
|
||||
|
||||
|
||||
|
||||
opt = OptionParser()
|
||||
# group : config
|
||||
p = OptionGroup(opt, "Configuration options")
|
||||
p.add_option('-c', '--config', dest="cfg_path", default="/usr/local/etc/nxapi.json", help="Path to nxapi.json (config).")
|
||||
p.add_option('--colors', dest="colors", action="store_false", default="true", help="Disable output colorz.")
|
||||
# p.add_option('-q', '--quiet', dest="quiet_flag", action="store_true", help="Be quiet.")
|
||||
# p.add_option('-v', '--verbose', dest="verb_flag", action="store_true", help="Be verbose.")
|
||||
opt.add_option_group(p)
|
||||
# group : in option
|
||||
p = OptionGroup(opt, "Input options (log acquisition)")
|
||||
p.add_option('--files', dest="files_in", help="Path to log files to parse.")
|
||||
p.add_option('--fifo', dest="fifo_in", help="Path to a FIFO to be created & read from. [infinite]")
|
||||
p.add_option('--stdin', dest="stdin", action="store_true", help="Read from stdin.")
|
||||
p.add_option('--no-timeout', dest="infinite_flag", action="store_true", help="Disable timeout on read operations (stdin/fifo).")
|
||||
p.add_option('--syslog', dest="syslog_in", action="store_true", help="Listen on tcp port for syslog logging.")
|
||||
opt.add_option_group(p)
|
||||
# group : filtering
|
||||
p = OptionGroup(opt, "Filtering options (for whitelist generation)")
|
||||
p.add_option('-s', '--server', dest="server", help="FQDN to which we should restrict operations.")
|
||||
p.add_option('--filter', dest="filter", action="append", help="This option specify a filter for each type of filter, filter are merge with existing templates/filters. (--filter 'uri /foobar')")
|
||||
opt.add_option_group(p)
|
||||
# group : tagging
|
||||
p = OptionGroup(opt, "Tagging options (tag existing events in database)")
|
||||
p.add_option('-w', '--whitelist-path', dest="wl_file", help="A path to whitelist file, will find matching events in DB.")
|
||||
p.add_option('-i', '--ip-path', dest="ips", help="A path to IP list file, will find matching events in DB.")
|
||||
p.add_option('--tag', dest="tag", action="store_true", help="Actually tag matching items in DB.")
|
||||
opt.add_option_group(p)
|
||||
# group : whitelist generation
|
||||
p = OptionGroup(opt, "Whitelist Generation")
|
||||
p.add_option('-f', '--full-auto', dest="full_auto", action="store_true", help="Attempt fully automatic whitelist generation process.")
|
||||
p.add_option('-t', '--template', dest="template", help="Path to template to apply.")
|
||||
p.add_option('--slack', dest="slack", action="store_false", help="Enables less strict mode.")
|
||||
p.add_option('--type', dest="type_wl", action="store_true", help="Generate whitelists based on param type")
|
||||
opt.add_option_group(p)
|
||||
# group : statistics
|
||||
p = OptionGroup(opt, "Statistics Generation")
|
||||
p.add_option('-x', '--stats', dest="stats", action="store_true", help="Generate statistics about current's db content.")
|
||||
opt.add_option_group(p)
|
||||
# group : interactive generation
|
||||
p = OptionGroup(opt, "Interactive Whitelists Generation")
|
||||
p.add_option('-g', '--interactive-generation', dest="int_gen", action="store_true", help="Use your favorite text editor for whitelist generation.")
|
||||
opt.add_option_group(p)
|
||||
|
||||
(options, args) = opt.parse_args()
|
||||
|
||||
|
||||
try:
|
||||
cfg = NxConfig(options.cfg_path)
|
||||
except ValueError:
|
||||
sys.exit(-1)
|
||||
|
||||
if options.server is not None:
|
||||
cfg.cfg["global_filters"]["server"] = options.server
|
||||
|
||||
# https://github.com/nbs-system/naxsi/issues/231
|
||||
mutally_exclusive = ['stats', 'full_auto', 'template', 'wl_file', 'ips', 'files_in', 'fifo_in', 'syslog_in']
|
||||
count=0
|
||||
for x in mutally_exclusive:
|
||||
if options.ensure_value(x, None) is not None:
|
||||
count += 1
|
||||
if count > 1:
|
||||
print "Mutually exclusive options are present (ie. import and stats), aborting."
|
||||
sys.exit(-1)
|
||||
|
||||
|
||||
cfg.cfg["output"]["colors"] = "false" if options.int_gen else str(options.colors).lower()
|
||||
cfg.cfg["naxsi"]["strict"] = str(options.slack).lower()
|
||||
|
||||
def get_filter(arg_filter):
|
||||
x = {}
|
||||
to_parse = []
|
||||
kwlist = ['server', 'uri', 'zone', 'var_name', 'ip', 'id', 'content', 'country', 'date',
|
||||
'?server', '?uri', '?var_name', '?content']
|
||||
try:
|
||||
for argstr in arg_filter:
|
||||
argstr = ' '.join(argstr.split())
|
||||
to_parse += argstr.split(' ')
|
||||
if [a for a in kwlist if a in to_parse]:
|
||||
for kw in to_parse:
|
||||
if kw in kwlist:
|
||||
x[kw] = to_parse[to_parse.index(kw)+1]
|
||||
else:
|
||||
raise
|
||||
except:
|
||||
logging.critical('option --filter must have at least one option')
|
||||
sys.exit(-1)
|
||||
return x
|
||||
|
||||
if options.filter is not None:
|
||||
cfg.cfg["global_filters"].update(get_filter(options.filter))
|
||||
|
||||
try:
|
||||
use_ssl = bool(cfg.cfg["elastic"]["use_ssl"])
|
||||
except KeyError:
|
||||
use_ssl = False
|
||||
|
||||
es = elasticsearch.Elasticsearch(cfg.cfg["elastic"]["host"], use_ssl=use_ssl)
|
||||
# Get ES version from the client and avail it at cfg
|
||||
es_version = es.info()['version'].get('number', None)
|
||||
if es_version is not None:
|
||||
cfg.cfg["elastic"]["version"] = es_version.split(".")[0]
|
||||
if cfg.cfg["elastic"].get("version", None) is None:
|
||||
print "Failed to get version from ES, Specify version ['1'/'2'/'5'] in [elasticsearch] section"
|
||||
sys.exit(-1)
|
||||
|
||||
translate = NxTranslate(es, cfg)
|
||||
|
||||
|
||||
|
||||
if options.type_wl is True:
|
||||
translate.wl_on_type()
|
||||
sys.exit(0)
|
||||
|
||||
# whitelist generation options
|
||||
if options.full_auto is True:
|
||||
translate.load_cr_file(translate.cfg["naxsi"]["rules_path"])
|
||||
results = translate.full_auto()
|
||||
if results:
|
||||
for result in results:
|
||||
print "{0}".format(result)
|
||||
else:
|
||||
print "No hits for this filter."
|
||||
sys.exit(1)
|
||||
sys.exit(0)
|
||||
|
||||
if options.template is not None:
|
||||
scoring = NxRating(cfg.cfg, es, translate)
|
||||
|
||||
tpls = translate.expand_tpl_path(options.template)
|
||||
gstats = {}
|
||||
if len(tpls) <= 0:
|
||||
print "No template matching"
|
||||
sys.exit(1)
|
||||
# prepare statistics for global scope
|
||||
scoring.refresh_scope('global', translate.tpl2esq(cfg.cfg["global_filters"]))
|
||||
for tpl_f in tpls:
|
||||
scoring.refresh_scope('rule', {})
|
||||
scoring.refresh_scope('template', {})
|
||||
|
||||
print translate.grn.format("#Loading tpl '"+tpl_f+"'")
|
||||
tpl = translate.load_tpl_file(tpl_f)
|
||||
# prepare statistics for filter scope
|
||||
scoring.refresh_scope('template', translate.tpl2esq(tpl))
|
||||
#pprint.pprint(tpl)
|
||||
print "Hits of template : "+str(scoring.get('template', 'total'))
|
||||
|
||||
whitelists = translate.gen_wl(tpl, rule={})
|
||||
print str(len(whitelists))+" whitelists ..."
|
||||
for genrule in whitelists:
|
||||
#pprint.pprint(genrule)
|
||||
scoring.refresh_scope('rule', genrule['rule'])
|
||||
scores = scoring.check_rule_score(tpl)
|
||||
if (len(scores['success']) > len(scores['warnings']) and scores['deny'] == False) or cfg.cfg["naxsi"]["strict"] == "false":
|
||||
#print "?deny "+str(scores['deny'])
|
||||
print translate.fancy_display(genrule, scores, tpl)
|
||||
print translate.grn.format(translate.tpl2wl(genrule['rule'], tpl)).encode('utf-8')
|
||||
sys.exit(0)
|
||||
|
||||
# tagging options
|
||||
|
||||
if options.wl_file is not None and options.server is None:
|
||||
print translate.red.format("Cannot tag events in database without a server name !")
|
||||
sys.exit(2)
|
||||
|
||||
if options.wl_file is not None:
|
||||
wl_files = []
|
||||
wl_files.extend(glob.glob(options.wl_file))
|
||||
count = 0
|
||||
for wlf in wl_files:
|
||||
print translate.grn.format("#Loading tpl '"+wlf+"'")
|
||||
try:
|
||||
wlfd = open(wlf, "r")
|
||||
except:
|
||||
print translate.red.format("Unable to open wl file '"+wlf+"'")
|
||||
sys.exit(-1)
|
||||
for wl in wlfd:
|
||||
[res, esq] = translate.wl2esq(wl)
|
||||
if res is True:
|
||||
count = 0
|
||||
while True:
|
||||
x = translate.tag_events(esq, "Whitelisted", tag=options.tag)
|
||||
count += x
|
||||
if x == 0:
|
||||
break
|
||||
|
||||
print translate.grn.format(str(count)) + " items tagged ..."
|
||||
count = 0
|
||||
sys.exit(0)
|
||||
|
||||
if options.ips is not None:
|
||||
ip_files = []
|
||||
ip_files.extend(glob.glob(options.ips))
|
||||
tpl = {}
|
||||
count = 0
|
||||
# esq = translate.tpl2esq(cfg.cfg["global_filters"])
|
||||
|
||||
for wlf in ip_files:
|
||||
try:
|
||||
wlfd = open(wlf, "r")
|
||||
except:
|
||||
print "Unable to open ip file '"+wlf+"'"
|
||||
sys.exit(-1)
|
||||
for wl in wlfd:
|
||||
print "=>"+wl
|
||||
tpl["ip"] = wl.strip('\n')
|
||||
esq = translate.tpl2esq(tpl)
|
||||
pprint.pprint(esq)
|
||||
pprint.pprint(tpl)
|
||||
count += translate.tag_events(esq, "BadIPS", tag=options.tag)
|
||||
print translate.grn.format(str(count)) + " items to be tagged ..."
|
||||
count = 0
|
||||
sys.exit(0)
|
||||
|
||||
# statistics
|
||||
if options.stats is True:
|
||||
print translate.red.format("# Whitelist(ing) ratio :")
|
||||
translate.fetch_top(cfg.cfg["global_filters"], "whitelisted", limit=2)
|
||||
print translate.red.format("# Top servers :")
|
||||
for e in translate.fetch_top(cfg.cfg["global_filters"], "server", limit=10):
|
||||
try:
|
||||
list_e = e.split()
|
||||
print '# {0} {1} {2}{3}'.format(translate.grn.format(list_e[0]), list_e[1], list_e[2], list_e[3])
|
||||
except:
|
||||
print "--malformed--"
|
||||
print translate.red.format("# Top URI(s) :")
|
||||
for e in translate.fetch_top(cfg.cfg["global_filters"], "uri", limit=10):
|
||||
try:
|
||||
list_e = e.split()
|
||||
print '# {0} {1} {2}{3}'.format(translate.grn.format(list_e[0]), list_e[1], list_e[2], list_e[3])
|
||||
except:
|
||||
print "--malformed--"
|
||||
print translate.red.format("# Top Zone(s) :")
|
||||
for e in translate.fetch_top(cfg.cfg["global_filters"], "zone", limit=10):
|
||||
try:
|
||||
list_e = e.split()
|
||||
print '# {0} {1} {2}{3}'.format(translate.grn.format(list_e[0]), list_e[1], list_e[2], list_e[3])
|
||||
except:
|
||||
print "--malformed--"
|
||||
print translate.red.format("# Top Peer(s) :")
|
||||
for e in translate.fetch_top(cfg.cfg["global_filters"], "ip", limit=10):
|
||||
try:
|
||||
list_e = e.split()
|
||||
print '# {0} {1} {2}{3}'.format(translate.grn.format(list_e[0]), list_e[1], list_e[2], list_e[3])
|
||||
except:
|
||||
print "--malformed--"
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
def write_generated_wl(filename, results):
|
||||
|
||||
with open('/tmp/{0}'.format(filename), 'w') as wl_file:
|
||||
for result in results:
|
||||
for key, items in result.iteritems():
|
||||
if items:
|
||||
print "{} {}".format(key, items)
|
||||
if key == 'genrule':
|
||||
wl_file.write("# {}\n{}\n".format(key, items))
|
||||
else:
|
||||
wl_file.write("# {} {}\n".format(key, items))
|
||||
wl_file.flush()
|
||||
|
||||
def ask_user_for_server_selection(editor, welcome_sentences, selection):
|
||||
with tempfile.NamedTemporaryFile(suffix='.tmp') as temporary_file:
|
||||
top_selection = translate.fetch_top(cfg.cfg["global_filters"],
|
||||
selection,
|
||||
limit=10
|
||||
)
|
||||
temporary_file.write(welcome_sentences)
|
||||
for line in top_selection:
|
||||
temporary_file.write('{0}\n'.format(line))
|
||||
temporary_file.flush()
|
||||
subprocess.call([editor, temporary_file.name])
|
||||
temporary_file.seek(len(welcome_sentences))
|
||||
ret = []
|
||||
for line in temporary_file:
|
||||
if not line.startswith('#'):
|
||||
ret.append(line.strip().split()[0])
|
||||
return ret
|
||||
|
||||
def ask_user_for_selection(editor, welcome_sentences, selection, servers):
|
||||
regex_message = "# as in the --filter option you can add ? for regex\n"
|
||||
ret = {}
|
||||
for server in servers:
|
||||
server_reminder = "server: {0}\n\n".format(server)
|
||||
ret[server] = []
|
||||
with tempfile.NamedTemporaryFile(suffix='.tmp') as temporary_file:
|
||||
temporary_file.write(welcome_sentences + regex_message + server_reminder)
|
||||
cfg.cfg["global_filters"]["server"] = server
|
||||
top_selection = translate.fetch_top(cfg.cfg["global_filters"],
|
||||
selection,
|
||||
limit=10
|
||||
)
|
||||
for line in top_selection:
|
||||
temporary_file.write('{0} {1}\n'.format(selection, line))
|
||||
temporary_file.flush()
|
||||
subprocess.call([editor, temporary_file.name])
|
||||
temporary_file.seek(len(welcome_sentences) + len(server_reminder) + len(regex_message))
|
||||
for line in temporary_file:
|
||||
if not line.startswith('#'):
|
||||
res = line.strip().split()
|
||||
ret[server].append((res[0], res[1]))
|
||||
return ret
|
||||
|
||||
def generate_wl(selection_dict):
|
||||
for key, items in selection_dict.iteritems():
|
||||
if not items:
|
||||
return False
|
||||
global_filters_context = cfg.cfg["global_filters"]
|
||||
global_filters_context["server"] = key
|
||||
for idx, (selection, item) in enumerate(items):
|
||||
global_filters_context[selection] = item
|
||||
translate.cfg["global_filters"] = global_filters_context
|
||||
print 'generating wl with filters {0}'.format(global_filters_context)
|
||||
wl_dict_list = []
|
||||
res = translate.full_auto(wl_dict_list)
|
||||
del global_filters_context[selection]
|
||||
write_generated_wl(
|
||||
"server_{0}_{1}.wl".format(
|
||||
key,
|
||||
idx if (selection == "uri") else "zone_{0}".format(item),
|
||||
),
|
||||
wl_dict_list
|
||||
)
|
||||
|
||||
if options.int_gen is True:
|
||||
editor = os.environ.get('EDITOR', 'vi')
|
||||
|
||||
welcome_sentences = '{0}\n{1}\n'.format(
|
||||
'# all deleted line or starting with a # will be ignore',
|
||||
'# if you want to use slack option you have to specify it on the command line options'
|
||||
)
|
||||
|
||||
servers = ask_user_for_server_selection(editor, welcome_sentences, "server")
|
||||
|
||||
uris = ask_user_for_selection(editor, welcome_sentences, "uri", servers)
|
||||
zones = ask_user_for_selection(editor, welcome_sentences, "zone", servers)
|
||||
|
||||
if uris:
|
||||
generate_wl(uris)
|
||||
if zones:
|
||||
generate_wl(zones)
|
||||
# in case the user let uri and zone files empty generate wl for all
|
||||
# selected server(s)
|
||||
if not uris and not zones:
|
||||
for server in servers:
|
||||
translate.cfg["global_filters"]["server"] = server
|
||||
print 'generating with filters: {0}'.format(translate.cfg["global_filters"])
|
||||
res = translate.full_auto()
|
||||
writing_generated_wl("server_{0}.wl".format(server), res)
|
||||
|
||||
sys.exit(0)
|
||||
|
||||
# input options, only setup injector if one input option is present
|
||||
if options.files_in is not None or options.fifo_in is not None or options.stdin is not None or options.syslog_in is not None:
|
||||
if options.fifo_in is not None or options.syslog_in is not None:
|
||||
injector = ESInject(es, cfg.cfg, auto_commit_limit=1)
|
||||
else:
|
||||
injector = ESInject(es, cfg.cfg)
|
||||
parser = NxParser()
|
||||
offset = time.timezone if (time.localtime().tm_isdst == 0) else time.altzone
|
||||
offset = offset / 60 / 60 * -1
|
||||
if offset < 0:
|
||||
offset = str(-offset)
|
||||
else:
|
||||
offset = str(offset)
|
||||
offset = offset.zfill(2)
|
||||
parser.out_date_format = "%Y-%m-%dT%H:%M:%S+"+offset #ES-friendly
|
||||
try:
|
||||
geoloc = NxGeoLoc(cfg.cfg)
|
||||
except:
|
||||
print "Unable to get GeoIP"
|
||||
|
||||
if options.files_in is not None:
|
||||
reader = NxReader(macquire, lglob=[options.files_in])
|
||||
reader.read_files()
|
||||
injector.stop()
|
||||
sys.exit(0)
|
||||
|
||||
if options.fifo_in is not None:
|
||||
fd = open_fifo(options.fifo_in)
|
||||
if options.infinite_flag is True:
|
||||
reader = NxReader(macquire, fd=fd, stdin_timeout=None)
|
||||
else:
|
||||
reader = NxReader(macquire, fd=fd)
|
||||
while True:
|
||||
print "start-",
|
||||
if reader.read_files() == False:
|
||||
break
|
||||
print "stop"
|
||||
print 'End of fifo input...'
|
||||
injector.stop()
|
||||
sys.exit(0)
|
||||
|
||||
if options.syslog_in is not None:
|
||||
sysloghost = cfg.cfg["syslogd"]["host"]
|
||||
syslogport = cfg.cfg["syslogd"]["port"]
|
||||
while 1:
|
||||
reader = NxReader(macquire, syslog=True, syslogport=syslogport, sysloghost=sysloghost)
|
||||
reader.read_files()
|
||||
injector.stop()
|
||||
sys.exit(0)
|
||||
|
||||
if options.stdin is True:
|
||||
if options.infinite_flag:
|
||||
reader = NxReader(macquire, lglob=[], fd=sys.stdin, stdin_timeout=None)
|
||||
else:
|
||||
reader = NxReader(macquire, lglob=[], fd=sys.stdin)
|
||||
while True:
|
||||
print "start-",
|
||||
if reader.read_files() == False:
|
||||
break
|
||||
print "stop"
|
||||
print 'End of stdin input...'
|
||||
injector.stop()
|
||||
sys.exit(0)
|
||||
|
||||
opt.print_help()
|
||||
sys.exit(0)
|
||||
1
naxsi-0.55.3/nxapi/requirements.txt
Normal file
1
naxsi-0.55.3/nxapi/requirements.txt
Normal file
@@ -0,0 +1 @@
|
||||
elasticsearch
|
||||
36
naxsi-0.55.3/nxapi/setup.py
Normal file
36
naxsi-0.55.3/nxapi/setup.py
Normal file
@@ -0,0 +1,36 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
from distutils.core import setup
|
||||
import os
|
||||
import glob
|
||||
import pprint
|
||||
|
||||
f = {}
|
||||
data_files = [('/usr/local/nxapi/', ['nx_datas/country2coords.txt']),
|
||||
('/usr/local/etc/', ['nxapi.json'])]
|
||||
#modules = []
|
||||
for dirname, dirnames, filenames in os.walk('tpl/'):
|
||||
for filename in filenames:
|
||||
if filename.endswith(".tpl"):
|
||||
print dirname+"#"+filename
|
||||
if "/usr/local/nxapi/"+dirname not in f.keys():
|
||||
|
||||
f["/usr/local/nxapi/"+dirname] = []
|
||||
|
||||
f["/usr/local/nxapi/"+dirname].append(os.path.join(dirname, filename))
|
||||
|
||||
for z in f.keys():
|
||||
data_files.append( (z, f[z]))
|
||||
|
||||
|
||||
setup(name='nxtool',
|
||||
version='1.0',
|
||||
description='Naxsi log parser, whitelist & report generator',
|
||||
author='Naxsi Dev Team',
|
||||
author_email='thibault.koechlin@nbs-system.com',
|
||||
url='http://github.com/nbs-system/naxsi',
|
||||
scripts=['nxtool.py'],
|
||||
packages=['nxapi'],
|
||||
data_files=data_files
|
||||
)
|
||||
|
||||
11
naxsi-0.55.3/nxapi/tpl/APPS/google_analytics-ARGS.tpl
Normal file
11
naxsi-0.55.3/nxapi/tpl/APPS/google_analytics-ARGS.tpl
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"var_name" : "__utmz",
|
||||
"id" : "1009 or 1010 or 1005 or 1011",
|
||||
"zone" : "ARGS",
|
||||
"_statics" : {
|
||||
"id" : "1009,1010,1005,1011"
|
||||
},
|
||||
"_msg" : "google analytics, __utmz var in ARGS"
|
||||
}
|
||||
|
||||
|
||||
8
naxsi-0.55.3/nxapi/tpl/ARGS/precise-id.tpl
Normal file
8
naxsi-0.55.3/nxapi/tpl/ARGS/precise-id.tpl
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"_msg" : "A generic, precise wl tpl (url+var+id)",
|
||||
"zone" : "ARGS",
|
||||
"var_name" : "?",
|
||||
"id" : "?",
|
||||
"uri" : "?",
|
||||
"_warnings" : { "template_uri" : [ ">=", "5"]}
|
||||
}
|
||||
11
naxsi-0.55.3/nxapi/tpl/ARGS/site-wide-id.tpl
Normal file
11
naxsi-0.55.3/nxapi/tpl/ARGS/site-wide-id.tpl
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"_msg" : "A generic, wide (id+zone) wl",
|
||||
"_success" : { "template_uri" : [ ">", "5"],
|
||||
"rule_uri" : [ ">", "5"]},
|
||||
|
||||
"_warnings" : { "rule_var_name" : [ "<=", "5" ],
|
||||
"rule_uri" : [ "<=", "5" ] },
|
||||
"_deny" : { "rule_var_name" : [ "<", "10" ] },
|
||||
"zone" : "ARGS",
|
||||
"id" : "?"
|
||||
}
|
||||
8
naxsi-0.55.3/nxapi/tpl/ARGS/url-wide-id-NAME.tpl
Normal file
8
naxsi-0.55.3/nxapi/tpl/ARGS/url-wide-id-NAME.tpl
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"_msg" : "A generic whitelist, true for the whole uri",
|
||||
"zone" : "ARGS|NAME",
|
||||
"uri" : "?",
|
||||
"id" : "?",
|
||||
"_warnings" : { "template_uri" : [ ">", "5" ] },
|
||||
"_success" : { "rule_var_name" : [ ">", "5" ] }
|
||||
}
|
||||
8
naxsi-0.55.3/nxapi/tpl/ARGS/url-wide-id.tpl
Normal file
8
naxsi-0.55.3/nxapi/tpl/ARGS/url-wide-id.tpl
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"_msg" : "A generic whitelist, true for the whole uri",
|
||||
"zone" : "ARGS",
|
||||
"uri" : "?",
|
||||
"id" : "?",
|
||||
"_deny" : { "rule_var_name" : [ "<=", "3" ]},
|
||||
"_success" : { "rule_var_name" : [ ">", "3" ]}
|
||||
}
|
||||
9
naxsi-0.55.3/nxapi/tpl/BODY/precise-id.tpl
Normal file
9
naxsi-0.55.3/nxapi/tpl/BODY/precise-id.tpl
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"_msg" : "A generic, precise wl tpl (url+var+id)",
|
||||
"zone" : "BODY",
|
||||
"var_name" : "?",
|
||||
"id" : "?",
|
||||
"uri" : "?",
|
||||
"_warnings" : { "template_uri" : [ "<", "5"],
|
||||
"template_var_name" : [ "<", "5"]}
|
||||
}
|
||||
8
naxsi-0.55.3/nxapi/tpl/BODY/site-wide-id.tpl
Normal file
8
naxsi-0.55.3/nxapi/tpl/BODY/site-wide-id.tpl
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"_msg" : "A generic, wide (id+zone) wl",
|
||||
"zone" : "BODY",
|
||||
"id" : "?",
|
||||
"_success" : { "template_uri" : [ ">", "5"],
|
||||
"template_var_name" : [ ">", "5"]},
|
||||
"_deny" : { "rule_var_name" : [ "<", "10" ] }
|
||||
}
|
||||
7
naxsi-0.55.3/nxapi/tpl/BODY/url-wide-id-BODY-NAME.tpl
Normal file
7
naxsi-0.55.3/nxapi/tpl/BODY/url-wide-id-BODY-NAME.tpl
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"_msg" : "A generic whitelist, true for the whole uri, BODY|NAME",
|
||||
"zone" : "BODY|NAME",
|
||||
"uri" : "?",
|
||||
"id" : "?",
|
||||
"_warnings" : { "template_uri" : [ ">", "5"] }
|
||||
}
|
||||
8
naxsi-0.55.3/nxapi/tpl/BODY/url-wide-id.tpl
Normal file
8
naxsi-0.55.3/nxapi/tpl/BODY/url-wide-id.tpl
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"_warnings" : { "template_uri" : [ ">", "5"]},
|
||||
"_deny" : {"rule_var_name" : ["<", "5"]},
|
||||
"_msg" : "A generic whitelist, true for the whole uri",
|
||||
"zone" : "BODY",
|
||||
"uri" : "?",
|
||||
"id" : "?"
|
||||
}
|
||||
8
naxsi-0.55.3/nxapi/tpl/BODY/var_name-wide-id.tpl
Normal file
8
naxsi-0.55.3/nxapi/tpl/BODY/var_name-wide-id.tpl
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"zone" : "BODY",
|
||||
"var_name" : "?",
|
||||
"id" : "?",
|
||||
"_msg" : "A generic rule to spot var-name specific WL",
|
||||
"_success" : { "rule_uri" : [ ">", "2"]},
|
||||
"_deny" : { "rule_uri" : ["<", "2"]}
|
||||
}
|
||||
5
naxsi-0.55.3/nxapi/tpl/HEADERS/cookies.tpl
Normal file
5
naxsi-0.55.3/nxapi/tpl/HEADERS/cookies.tpl
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"_success" : { "template_uri" : [ ">=", "5"] },
|
||||
"zone" : "HEADERS",
|
||||
"var_name" : "cookie",
|
||||
"id" : "?"}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"id" : "1002",
|
||||
"zone" : "URL",
|
||||
"_success" : { "template_uri" : [ ">", "5"],
|
||||
"rule_uri" : [ ">", "5"]}
|
||||
}
|
||||
7
naxsi-0.55.3/nxapi/tpl/URI/site-wide-id.tpl
Normal file
7
naxsi-0.55.3/nxapi/tpl/URI/site-wide-id.tpl
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"_success" : { "template_uri" : [ ">=", "5"],
|
||||
"rule_uri" : [ ">=", "5"]},
|
||||
"zone" : "URL",
|
||||
"id" : "?"
|
||||
}
|
||||
|
||||
6
naxsi-0.55.3/nxapi/tpl/URI/url-wide-id.tpl
Normal file
6
naxsi-0.55.3/nxapi/tpl/URI/url-wide-id.tpl
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"_deny" : { "template_uri" : [ ">", "5" ] },
|
||||
"uri" : "?",
|
||||
"zone" : "URL",
|
||||
"id" : "?"
|
||||
}
|
||||
1208
naxsi-0.55.3/t/00naxsi_base.t
vendored
Normal file
1208
naxsi-0.55.3/t/00naxsi_base.t
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1194
naxsi-0.55.3/t/01naxsi_whitelists.t
vendored
Normal file
1194
naxsi-0.55.3/t/01naxsi_whitelists.t
vendored
Normal file
File diff suppressed because it is too large
Load Diff
319
naxsi-0.55.3/t/02naxsi_bypass.t
vendored
Normal file
319
naxsi-0.55.3/t/02naxsi_bypass.t
vendored
Normal file
@@ -0,0 +1,319 @@
|
||||
#vi:filetype=perl
|
||||
|
||||
|
||||
# A AJOUTER :
|
||||
# TEST CASE AVEC UNE REGLE SUR UN HEADER GENERIQUE
|
||||
# La même sur des arguments :)
|
||||
|
||||
use lib 'lib';
|
||||
use Test::Nginx::Socket;
|
||||
|
||||
plan tests => repeat_each(2) * blocks();
|
||||
no_root_location();
|
||||
no_long_string();
|
||||
$ENV{TEST_NGINX_SERVROOT} = server_root();
|
||||
run_tests();
|
||||
|
||||
|
||||
__DATA__
|
||||
=== TEST 1: Basic GET request
|
||||
--- 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;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?a=buibui
|
||||
--- error_code: 200
|
||||
=== TEST 2: DENY : XSS bypass vector 1 (basic url encode)
|
||||
--- 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;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?a=%2f%3cSc%3E
|
||||
--- error_code: 412
|
||||
=== TEST 2.1: DENY : XSS bypass vector 2 (\x encode)
|
||||
--- 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 >= 2" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?a=\x2f\x3cSc\x3E
|
||||
--- error_code: 412
|
||||
|
||||
=== TEST 2.2: DENY : XSS bypass vector %00 (nullbyte)
|
||||
--- 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 >= 2" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?a=a%00<%00script
|
||||
--- error_code: 412
|
||||
|
||||
=== TEST 2.3: DENY : XSS bypass vector %00 (nullbyte) URL
|
||||
--- 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 >= 2" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /a%00aaa
|
||||
--- error_code: 400
|
||||
=== TEST 3.0: DENY : bypass vector ? (multi arg break)
|
||||
--- 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 >= 2" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?a=a?<x
|
||||
--- error_code: 412
|
||||
=== TEST 3.1: DENY : ? break (multi ?)
|
||||
--- 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 >= 2" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?a=<a?a
|
||||
--- error_code: 412
|
||||
=== TEST 4.0: malformed URIs
|
||||
--- 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 >= 2" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?&&val
|
||||
--- error_code: 200
|
||||
=== TEST 4.01: malformed URIs
|
||||
--- 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 >= 2" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?&&va<l
|
||||
--- error_code: 412
|
||||
=== TEST 4.1: malformed URIs
|
||||
--- 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 >= 2" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?val&&
|
||||
--- error_code: 412
|
||||
=== TEST 4.2: malformed URIs
|
||||
--- 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 >= 2" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?&val
|
||||
--- error_code: 200
|
||||
=== TEST 4.21: malformed URIs
|
||||
--- 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 >= 2" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?&va<l
|
||||
--- error_code: 412
|
||||
=== TEST 4.3: malformed URIs
|
||||
--- 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 >= 2" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?val&
|
||||
--- error_code: 412
|
||||
156
naxsi-0.55.3/t/03naxsi_profile.t
vendored
Normal file
156
naxsi-0.55.3/t/03naxsi_profile.t
vendored
Normal file
@@ -0,0 +1,156 @@
|
||||
#vi:filetype=perl
|
||||
|
||||
|
||||
# A AJOUTER :
|
||||
# TEST CASE AVEC UNE REGLE SUR UN HEADER GENERIQUE
|
||||
# La même sur des arguments :)
|
||||
|
||||
use lib 'lib';
|
||||
use Test::Nginx::Socket;
|
||||
|
||||
plan tests => repeat_each(2) * blocks();
|
||||
no_root_location();
|
||||
no_long_string();
|
||||
$ENV{TEST_NGINX_SERVROOT} = server_root();
|
||||
run_tests();
|
||||
|
||||
|
||||
__DATA__
|
||||
=== TEST 1: Basic GET request
|
||||
--- 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;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?a=buibui
|
||||
--- error_code: 200
|
||||
=== TEST 2: DENY : Obvious GET XSS
|
||||
--- 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;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?a="><ScRiPt>alert(1)</scRiPt>
|
||||
--- error_code: 412
|
||||
=== TEST 2.1: DENY : Obvious RFI
|
||||
--- 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 >= 2" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?a=http://evil.com/eva.txt
|
||||
--- error_code: 412
|
||||
=== TEST 2.3: DENY : Obvious LFI
|
||||
--- 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 >= 2" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?a=../../../../../bar.txt
|
||||
--- error_code: 412
|
||||
=== TEST 3: OBVIOUS GET SQL INJECTION
|
||||
--- 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;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?a=1'+Or+'1'='1
|
||||
--- error_code: 412
|
||||
=== TEST 3bis: OBVIOUS (quoteless) GET SQL INJECTION
|
||||
--- 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;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?a=1+UnIoN+SeLeCt+1
|
||||
--- error_code: 412
|
||||
1003
naxsi-0.55.3/t/04naxsi_files.t
vendored
Normal file
1003
naxsi-0.55.3/t/04naxsi_files.t
vendored
Normal file
File diff suppressed because it is too large
Load Diff
359
naxsi-0.55.3/t/05naxsi_advanced_whitelists.t
vendored
Normal file
359
naxsi-0.55.3/t/05naxsi_advanced_whitelists.t
vendored
Normal file
@@ -0,0 +1,359 @@
|
||||
#vi:filetype=perl
|
||||
|
||||
|
||||
# A AJOUTER :
|
||||
# TEST CASE AVEC UNE REGLE SUR UN HEADER GENERIQUE
|
||||
# La même sur des arguments :)
|
||||
|
||||
use lib 'lib';
|
||||
use Test::Nginx::Socket;
|
||||
|
||||
plan tests => repeat_each(2) * blocks();
|
||||
no_root_location();
|
||||
no_long_string();
|
||||
$ENV{TEST_NGINX_SERVROOT} = server_root();
|
||||
run_tests();
|
||||
__DATA__
|
||||
=== WL TEST 5.0: Two whitelists on two named arguments, same URL
|
||||
--- user_files
|
||||
>>> buixor
|
||||
eh yo
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:1998" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1998;
|
||||
MainRule "str:1999" "msg:foobar test pattern #2" "mz:ARGS" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
#LearningMode;
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:1999 "mz:$ARGS_VAR:bla|$URL:/buixor";
|
||||
BasicRule wl:1998 "mz:$ARGS_VAR:blu|$URL:/buixor";
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /buixor?bla=1999
|
||||
--- error_code: 200
|
||||
=== WL TEST 5.1: Two whitelists on two named arguments, same URL
|
||||
--- user_files
|
||||
>>> buixor
|
||||
eh yo
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:1998" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1998;
|
||||
MainRule "str:1999" "msg:foobar test pattern #2" "mz:ARGS" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
#LearningMode;
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:1999 "mz:$ARGS_VAR:bla|$URL:/buixor";
|
||||
BasicRule wl:1998 "mz:$ARGS_VAR:blu|$URL:/buixor";
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /buixor?blu=1999
|
||||
--- error_code: 412
|
||||
=== WL TEST 5.2: Two whitelists on two named arguments, same URL
|
||||
--- user_files
|
||||
>>> buixor
|
||||
eh yo
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:1998" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1998;
|
||||
MainRule "str:1999" "msg:foobar test pattern #2" "mz:ARGS" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
#LearningMode;
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:1999 "mz:$ARGS_VAR:bla|$URL:/buixor";
|
||||
BasicRule wl:1998 "mz:$ARGS_VAR:blu|$URL:/buixor";
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /buixor?bla=1999&blu=1998
|
||||
--- error_code: 200
|
||||
=== WL TEST 5.3: Two whitelists on two named arguments, same URL
|
||||
--- user_files
|
||||
>>> buixor
|
||||
eh yo
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:1998" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1998;
|
||||
MainRule "str:1999" "msg:foobar test pattern #2" "mz:ARGS" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
#LearningMode;
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:1999 "mz:$ARGS_VAR:bla|$URL:/buixor";
|
||||
BasicRule wl:1998 "mz:$ARGS_VAR:blu|$URL:/buixor";
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?buixor=1998
|
||||
--- error_code: 412
|
||||
=== WL TEST 5.4: Whitelists on ARGS/URLs that are URLencoded
|
||||
--- user_files
|
||||
>>> buixor
|
||||
eh yo
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:1998" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1998;
|
||||
MainRule "str:1999" "msg:foobar test pattern #2" "mz:ARGS" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
#LearningMode;
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:1999 "mz:$ARGS_VAR:b_@_la|$URL:/buixor";
|
||||
BasicRule wl:1998 "mz:$ARGS_VAR:blu|$URL:/buixor";
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /buixor?b_@_la=1999
|
||||
--- error_code: 200
|
||||
|
||||
=== WL TEST 5.5: Whitelists on ARGS/URLs that are URLencoded
|
||||
--- user_files
|
||||
>>> buixor
|
||||
eh yo
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:1998" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1998;
|
||||
MainRule "str:1999" "msg:foobar test pattern #2" "mz:ARGS" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
#LearningMode;
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:1999 "mz:$ARGS_VAR:b[]la|$URL:/buixor";
|
||||
BasicRule wl:1998 "mz:$ARGS_VAR:blu|$URL:/buixor";
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /buixor?b]la=1999
|
||||
--- error_code: 412
|
||||
|
||||
=== WL TEST 6: Whitelists trying to provoke collisions ($ARGS_VAR:x + $URL:x|ARGS)
|
||||
--- user_files
|
||||
>>> buixor
|
||||
eh yo
|
||||
>>> bla
|
||||
eh yo
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:1998" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1998;
|
||||
MainRule "str:1999" "msg:foobar test pattern #2" "mz:ARGS" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
#LearningMode;
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
# BasicRule wl:1999 "mz:$ARGS_VAR:/bla";
|
||||
BasicRule wl:1998 "mz:$URL:/bla|ARGS";
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /bla?1998
|
||||
--- error_code: 200
|
||||
|
||||
=== WL TEST 6.0: Whitelists trying to provoke collisions ($ARGS_VAR:x + $URL:x|ARGS)
|
||||
--- user_files
|
||||
>>> buixor
|
||||
eh yo
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:1998" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1998;
|
||||
MainRule "str:1999" "msg:foobar test pattern #2" "mz:ARGS" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
#LearningMode;
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
# BasicRule wl:1999 "mz:$ARGS_VAR:/bla";
|
||||
BasicRule wl:1998 "mz:$URL:/bla|ARGS";
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?/bla=1998
|
||||
--- error_code: 412
|
||||
|
||||
=== WL TEST 6.1: Whitelists trying to provoke collisions ($ARGS_VAR:x + $URL:x|ARGS)
|
||||
--- user_files
|
||||
>>> buixor
|
||||
eh yo
|
||||
>>> bla
|
||||
eh yo
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:1998" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1998;
|
||||
MainRule "str:1999" "msg:foobar test pattern #2" "mz:ARGS" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
#LearningMode;
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:1999 "mz:$ARGS_VAR:bla";
|
||||
BasicRule wl:1998 "mz:$URL:/bla|ARGS";
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /bla?bla=1999&toto=1998
|
||||
--- error_code: 200
|
||||
|
||||
=== WL TEST 6.2: Whitelists trying to provoke collisions ($ARGS_VAR:x + $URL:x|ARGS)
|
||||
--- user_files
|
||||
>>> buixor
|
||||
eh yo
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:1998" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1998;
|
||||
MainRule "str:1999" "msg:foobar test pattern #2" "mz:ARGS" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
#LearningMode;
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:1999 "mz:$ARGS_VAR:/bla";
|
||||
BasicRule wl:1998 "mz:$URL:/bla|ARGS";
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /buixor?/bla=1999
|
||||
--- error_code: 200
|
||||
|
||||
=== WL TEST 6.3: Whitelists trying to provoke collisions ($ARGS_VAR:x + $URL:x|ARGS)
|
||||
--- user_files
|
||||
>>> buixor
|
||||
eh yo
|
||||
>>> bla
|
||||
eh yo
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:1998" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1998;
|
||||
MainRule "str:1999" "msg:foobar test pattern #2" "mz:ARGS" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
#LearningMode;
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:1999 "mz:$ARGS_VAR:/bla";
|
||||
BasicRule wl:1998 "mz:$URL:/bla|ARGS";
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /bla?/bla=1999&bu=1998
|
||||
--- error_code: 200
|
||||
|
||||
130
naxsi-0.55.3/t/06naxsi_weirds.t
vendored
Normal file
130
naxsi-0.55.3/t/06naxsi_weirds.t
vendored
Normal file
@@ -0,0 +1,130 @@
|
||||
#vi:filetype=perl
|
||||
|
||||
|
||||
use lib 'lib';
|
||||
use Test::Nginx::Socket;
|
||||
|
||||
plan tests => repeat_each(2) * blocks();
|
||||
no_root_location();
|
||||
no_long_string();
|
||||
$ENV{TEST_NGINX_SERVROOT} = server_root();
|
||||
run_tests();
|
||||
__DATA__
|
||||
=== WL TEST 1.0: weird request in URL
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
--- config
|
||||
location / {
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?&&&&a&&&&&
|
||||
--- error_code: 412
|
||||
|
||||
=== WL TEST 1.01: weird request in URL (wl on fullzone)
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
--- config
|
||||
location / {
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:12 "mz:ARGS";
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?&&&&a&&&&&
|
||||
--- error_code: 200
|
||||
|
||||
=== WL TEST 1.02: weird request in URL (wl on zone+URL)
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
--- config
|
||||
location / {
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:12 "mz:$URL:/|ARGS";
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?&&&&a&&&&&
|
||||
--- error_code: 200
|
||||
|
||||
=== WL TEST 1.03: weird request in URL (fail wl on zone+bad URL)
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
--- config
|
||||
location / {
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:12 "mz:$URL:/a|ARGS";
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?&&&&a&&&&&
|
||||
--- error_code: 412
|
||||
|
||||
=== WL TEST 1.04: weird request in URL (fail wl on bad zone+URL)
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
--- config
|
||||
location / {
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:12 "mz:$URL:/|URL";
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?&&&&a&&&&&
|
||||
--- error_code: 412
|
||||
730
naxsi-0.55.3/t/07naxsi_argnames.t
vendored
Normal file
730
naxsi-0.55.3/t/07naxsi_argnames.t
vendored
Normal file
@@ -0,0 +1,730 @@
|
||||
#vi:filetype=perl
|
||||
|
||||
|
||||
# A AJOUTER :
|
||||
# TEST CASE AVEC UNE REGLE SUR UN HEADER GENERIQUE
|
||||
# La même sur des arguments :)
|
||||
|
||||
use lib 'lib';
|
||||
use Test::Nginx::Socket;
|
||||
|
||||
plan tests => repeat_each(2) * blocks();
|
||||
no_root_location();
|
||||
no_long_string();
|
||||
$ENV{TEST_NGINX_SERVROOT} = server_root();
|
||||
run_tests();
|
||||
__DATA__
|
||||
=== WL TEST 1.0: Obvious test in arg
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:foobar" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
#LearningMode;
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?foobar=a
|
||||
--- error_code: 412
|
||||
|
||||
=== WL TEST 1.01: Check non-collision of zone and 'name' flag
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule id:5 "str:foobar" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42";
|
||||
--- config
|
||||
location / {
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?a=foobar
|
||||
--- error_code: 412
|
||||
|
||||
=== WL TEST 1.1: Generic whitelist in ARGS_NAME
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:foobar" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
#LearningMode;
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:1999 "mz:ARGS|NAME";
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?foobar=a
|
||||
--- error_code: 200
|
||||
|
||||
|
||||
=== WL TEST 1.11: Generic whitelist in ARGS_NAME, limit
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:foobar" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
#LearningMode;
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:1999 "mz:ARGS";
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?foobar=a
|
||||
--- error_code: 412
|
||||
|
||||
=== WL TEST 1.12: Generic whitelist in ARGS_NAME, limit
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:foobar" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
#LearningMode;
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:1999 "mz:ARGS|NAME";
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?a=foobar
|
||||
--- error_code: 412
|
||||
|
||||
=== WL TEST 1.2: whitelist in ARGS_NAME+$URL
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:foobar" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
#LearningMode;
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:1999 "mz:$URL:/|ARGS|NAME";
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?foobar=a
|
||||
--- error_code: 200
|
||||
|
||||
=== WL TEST 1.21: whitelist in ARGS_NAME+$URL, limit
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:foobar" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
#LearningMode;
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:1999 "mz:$URL:/|ARGS|NAME";
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?foobar=a
|
||||
--- error_code: 200
|
||||
|
||||
=== WL TEST 1.22: whitelist in ARGS_NAME+$URL, limit
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:foobar" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
#LearningMode;
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:1999 "mz:$URL:/|ARGS|NAME";
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?a=foobar
|
||||
--- error_code: 412
|
||||
|
||||
|
||||
=== WL TEST 1.3: failed whitelist in ARGS_NAME+$URL
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:foobar" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
#LearningMode;
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:1999 "mz:$URL:/z|ARGS|NAME";
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?foobar=a
|
||||
--- error_code: 412
|
||||
|
||||
=== WL TEST 1.31: failed whitelist in ARGS_NAME+$URL
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:foobar" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
#LearningMode;
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:1999 "mz:$URL:/|ARGS|NAME";
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?a=foobar
|
||||
--- error_code: 412
|
||||
|
||||
=== WL TEST 1.32: failed whitelist in ARGS_NAME+$URL
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:foobar" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
#LearningMode;
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:1999 "mz:$URL:/|$ARGS_VAR:b|NAME";
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?b=foobar
|
||||
--- error_code: 412
|
||||
|
||||
=== WL TEST 1.33: failed whitelist in ARGS_NAME+$URL
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:foobar" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
#LearningMode;
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:1999 "mz:$URL:/|$ARGS_VAR:foobar|NAME";
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?foobar=bui
|
||||
--- error_code: 200
|
||||
|
||||
=== WL TEST 1.34: failed whitelist in ARGS_NAME+$URL
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:foobar" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1999;
|
||||
MainRule "str:foobra" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:2999;
|
||||
--- config
|
||||
location / {
|
||||
#LearningMode;
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:1999 "mz:$URL:/|$ARGS_VAR:foobar|NAME";
|
||||
BasicRule wl:2999 "mz:$URL:/|$ARGS_VAR:foobar";
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?foobar=foobra
|
||||
--- error_code: 200
|
||||
|
||||
=== WL TEST 1.35: failed whitelist in ARGS_NAME+$URL
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:foobar" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1999;
|
||||
MainRule "str:foobra" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:2999;
|
||||
--- config
|
||||
location / {
|
||||
#LearningMode;
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:1999 "mz:$URL:/|$ARGS_VAR:foobar|NAME";
|
||||
BasicRule wl:2999 "mz:$URL:/|$ARGS_VAR:foobar";
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?foobar=foobar
|
||||
--- error_code: 412
|
||||
|
||||
=== WL TEST 1.36: failed whitelist in ARGS_NAME+$URL
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:foobar" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1999;
|
||||
MainRule "str:foobra" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:2999;
|
||||
--- config
|
||||
location / {
|
||||
#LearningMode;
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:1999 "mz:$URL:/|$ARGS_VAR:foobar|NAME";
|
||||
BasicRule wl:2999 "mz:$URL:/|$ARGS_VAR:foobar";
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?foobar=foobar
|
||||
--- error_code: 412
|
||||
|
||||
|
||||
=== WL TEST 1.4: whitelist in ARGS_NAME+$URL+$ARGS_VAR
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:foobar" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
#LearningMode;
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:1999 "mz:$URL:/|$ARGS_VAR:foobar|NAME";
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?foobar=a
|
||||
--- error_code: 200
|
||||
|
||||
=== WL TEST 1.41: whitelist in ARGS_NAME+$URL+$ARGS_VAR
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:foobar" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
#LearningMode;
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:1999 "mz:$URL:/|$ARGS_VAR:foobar|NAME";
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?a=foobar
|
||||
--- error_code: 412
|
||||
|
||||
|
||||
|
||||
=== WL TEST 1.5: whitelist in ARGS_NAME+$URL+$ARGS_VAR, limit
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:foobar" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
#LearningMode;
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:1999 "mz:$URL:/|$ARGS_VAR:foobar|NAME";
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?foobar=foobar
|
||||
--- error_code: 412
|
||||
|
||||
|
||||
=== WL TEST 1.51: whitelist in ARGS_NAME+$URL+$ARGS_VAR, limit
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:foobar" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
#LearningMode;
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:1999 "mz:$URL:/|$ARGS_VAR:foobar|NAME";
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?foobar=foo
|
||||
--- error_code: 200
|
||||
|
||||
=== WL TEST 1.6: whitelist in $URL+$ARGS_VAR | NAME, (collision)
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:foobar" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
#LearningMode;
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:1999 "mz:$URL:/|$ARGS_VAR:foobar|NAME";
|
||||
BasicRule wl:1999 "mz:$URL:/|$ARGS_VAR:foobar";
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?foobar=foobar
|
||||
--- error_code: 200
|
||||
|
||||
=== WL TEST 1.6.1: whitelist in $URL+ARGS | NAME, (collision)
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:foobar" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
#LearningMode;
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:1999 "mz:$URL:/|ARGS|NAME";
|
||||
BasicRule wl:1999 "mz:$URL:/|ARGS";
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?foobar=foobar
|
||||
--- error_code: 200
|
||||
|
||||
=== WL TEST 1.6.2: whitelist in $URL+ARGS | NAME, (collision)
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:foobar" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
#LearningMode;
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:1999 "mz:$URL:/|ARGS|NAME";
|
||||
BasicRule wl:1999 "mz:$URL:/|ARGS";
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?foobar=lol
|
||||
--- error_code: 200
|
||||
=== WL TEST 1.6.3: whitelist in $URL+ARGS | NAME, (collision)
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:foobar" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
#LearningMode;
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:1999 "mz:$URL:/|ARGS|NAME";
|
||||
BasicRule wl:1999 "mz:$URL:/|ARGS";
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?lol=foobar
|
||||
--- error_code: 200
|
||||
=== WL TEST 1.6.4: whitelist in $URL+ARGS | NAME, (collision)
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:foobar" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
#LearningMode;
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:1999 "mz:$URL:/|ARGS|NAME";
|
||||
# BasicRule wl:1999 "mz:$URL:/|ARGS";
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?lol=foobar
|
||||
--- error_code: 412
|
||||
=== WL TEST 1.6.5: whitelist in $URL+ARGS | NAME, (collision)
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:foobar" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
#LearningMode;
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
# BasicRule wl:1999 "mz:$URL:/|ARGS|NAME";
|
||||
BasicRule wl:1999 "mz:$URL:/|ARGS";
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?foobar=lol
|
||||
--- error_code: 412
|
||||
=== WL TEST 1.6.6: whitelist in $URL+ARGS | NAME, (collision)
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:foobar" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
#LearningMode;
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
# BasicRule wl:1999 "mz:$URL:/|ARGS|NAME";
|
||||
BasicRule wl:1999 "mz:$URL:/|ARGS";
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?lol=foobar
|
||||
--- error_code: 200
|
||||
=== WL TEST 1.6.7: whitelist in $URL+ARGS | NAME, (collision)
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:foobar" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
#LearningMode;
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:1999 "mz:$URL:/|ARGS|NAME";
|
||||
# BasicRule wl:1999 "mz:$URL:/|ARGS";
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?lol=foobar
|
||||
--- error_code: 412
|
||||
|
||||
372
naxsi-0.55.3/t/08negative_whitelists.t
vendored
Normal file
372
naxsi-0.55.3/t/08negative_whitelists.t
vendored
Normal file
@@ -0,0 +1,372 @@
|
||||
#vi:filetype=perl
|
||||
|
||||
|
||||
# A AJOUTER :
|
||||
# TEST CASE AVEC UNE REGLE SUR UN HEADER GENERIQUE
|
||||
# La même sur des arguments :)
|
||||
|
||||
use lib 'lib';
|
||||
use Test::Nginx::Socket;
|
||||
|
||||
plan tests => repeat_each(2) * blocks();
|
||||
no_root_location();
|
||||
no_long_string();
|
||||
$ENV{TEST_NGINX_SERVROOT} = server_root();
|
||||
run_tests();
|
||||
|
||||
__DATA__
|
||||
|
||||
=== WL TEST 1.0
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule negative "str:foobar" "msg:foobar test pattern" "mz:$ARGS_VAR:b" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
#LearningMode;
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?b=toto
|
||||
--- error_code: 412
|
||||
|
||||
=== WL TEST 1.01
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule negative "str:foobar" "msg:foobar test pattern" "mz:$ARGS_VAR:b" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
#LearningMode;
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?b=foobar
|
||||
--- error_code: 200
|
||||
|
||||
=== WL TEST 1.03
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule negative "str:foobar" "msg:foobar test pattern" "mz:$URL:/|$ARGS_VAR:b" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
#LearningMode;
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /a?b=foobar
|
||||
--- error_code: 404
|
||||
|
||||
=== WL TEST 1.04
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule negative "str:foobar" "msg:foobar test pattern" "mz:$URL:/a|$ARGS_VAR:b" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
#LearningMode;
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /a?b=foobrar
|
||||
--- error_code: 412
|
||||
|
||||
|
||||
|
||||
=== WL TEST 2.0
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule negative "rx:foobar" "msg:foobar test pattern" "mz:$URL:/a|$ARGS_VAR:b" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
#LearningMode;
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /a?b=foobrar
|
||||
--- error_code: 412
|
||||
|
||||
|
||||
|
||||
=== WL TEST 2.01
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule negative "rx:foobar" "msg:foobar test pattern" "mz:$URL:/a|$ARGS_VAR:b" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
#LearningMode;
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /a?b=foobar
|
||||
--- error_code: 404
|
||||
|
||||
|
||||
=== WL TEST 2.02
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule negative "rx:^foobar" "msg:foobar test pattern" "mz:$URL:/a|$ARGS_VAR:b" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
#LearningMode;
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?b=foobar
|
||||
--- error_code: 200
|
||||
|
||||
=== WL TEST 2.03
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule negative "rx:^foobar" "msg:foobar test pattern" "mz:$URL:/a|$ARGS_VAR:b" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
#LearningMode;
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /a?b=rfoobar
|
||||
--- error_code: 412
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
=== WL TEST 2.04
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule negative "rx:^foobar" "msg:foobar test pattern" "mz:$URL:/a|$ARGS_VAR:b" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
#LearningMode;
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /a?b=foobar
|
||||
--- error_code: 404
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
=== WL TEST 2.05
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule negative "rx:^foobar$" "msg:foobar test pattern" "mz:$URL:/a|$ARGS_VAR:b" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
#LearningMode;
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /a?b=foobar
|
||||
--- error_code: 404
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
=== WL TEST 2.06
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule negative "rx:^foobar$" "msg:foobar test pattern" "mz:$URL:/a|$ARGS_VAR:b" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
#LearningMode;
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /a?b=foobara
|
||||
--- error_code: 412
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
=== WL TEST 2.07
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule negative "rx:^[0-9]+$" "msg:foobar test pattern" "mz:$URL:/a|$ARGS_VAR:b" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
#LearningMode;
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /a?b=foobara
|
||||
--- error_code: 412
|
||||
|
||||
|
||||
|
||||
=== WL TEST 2.08
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule negative "rx:^[0-9]+$" "msg:foobar test pattern" "mz:$URL:/a|$ARGS_VAR:b" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
#LearningMode;
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /a?b=1234
|
||||
--- error_code: 404
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
883
naxsi-0.55.3/t/09sqlmap_tamper.t
vendored
Normal file
883
naxsi-0.55.3/t/09sqlmap_tamper.t
vendored
Normal file
@@ -0,0 +1,883 @@
|
||||
|
||||
use lib 'lib';
|
||||
use Test::Nginx::Socket;
|
||||
|
||||
plan tests => repeat_each(2) * blocks();
|
||||
no_root_location();
|
||||
no_long_string();
|
||||
$ENV{TEST_NGINX_SERVROOT} = server_root();
|
||||
run_tests();
|
||||
|
||||
|
||||
__DATA__
|
||||
=== TODO: naxsi does not support utf8, potential bypass. Still too marginal to be worth checking
|
||||
--- main_config
|
||||
working_directory /tmp/;
|
||||
worker_rlimit_core 25M;
|
||||
--- 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;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- raw_request eval
|
||||
"GET /?a=AND+%EF%BC%871%EF%BC%87=%EF%BC%871%EF%BC%87 HTTP/1.0
|
||||
|
||||
"
|
||||
--- error_code: 200
|
||||
|
||||
|
||||
=== TEST 1: hey 2
|
||||
--- main_config
|
||||
working_directory /tmp/;
|
||||
worker_rlimit_core 25M;
|
||||
--- 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;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- raw_request eval
|
||||
"GET /?a=AND+%00%271%00%27=%00%271%00%27 HTTP/1.0
|
||||
|
||||
"
|
||||
--- error_code: 412
|
||||
|
||||
|
||||
=== TEST 1: hey 3
|
||||
--- main_config
|
||||
working_directory /tmp/;
|
||||
worker_rlimit_core 25M;
|
||||
--- 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;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- raw_request eval
|
||||
"GET /?a=AND+1=1%00 Union select 1 HTTP/1.0
|
||||
|
||||
"
|
||||
--- error_code: 412
|
||||
|
||||
|
||||
=== NOT TODO: base64, not worthing checking
|
||||
--- main_config
|
||||
working_directory /tmp/;
|
||||
worker_rlimit_core 25M;
|
||||
--- 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;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- raw_request eval
|
||||
"GET /?a=MScgQU5EIFNMRUVQKDUpIw== HTTP/1.0
|
||||
|
||||
"
|
||||
--- error_code: 200
|
||||
|
||||
|
||||
=== TEST 1: hey 5
|
||||
--- main_config
|
||||
working_directory /tmp/;
|
||||
worker_rlimit_core 25M;
|
||||
--- 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;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- raw_request eval
|
||||
"GET /?a='A+NOT+BETWEEN+0+AND+B' HTTP/1.0
|
||||
|
||||
"
|
||||
--- error_code: 412
|
||||
|
||||
|
||||
=== TEST 1: hey 6
|
||||
--- main_config
|
||||
working_directory /tmp/;
|
||||
worker_rlimit_core 25M;
|
||||
--- 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;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- raw_request eval
|
||||
"GET /?a=%2553%2545%254c%2545%2543%2554%2520%2546%2549%2545%254c%2544%2520%2546%2552%254f%254d%2520%2554%2541%2542%254c%2545 HTTP/1.0
|
||||
|
||||
"
|
||||
--- error_code: 412
|
||||
|
||||
|
||||
=== TEST 1: hey 7
|
||||
--- main_config
|
||||
working_directory /tmp/;
|
||||
worker_rlimit_core 25M;
|
||||
--- 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;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- raw_request eval
|
||||
"GET /?a=%53%45%4c%45%43%54%20%46%49%45%4c%44%20%46%52%4f%4d%20%54%41%42%4c%45 HTTP/1.0
|
||||
|
||||
"
|
||||
--- error_code: 412
|
||||
|
||||
|
||||
=== TEST 1: hey 8
|
||||
--- main_config
|
||||
working_directory /tmp/;
|
||||
worker_rlimit_core 25M;
|
||||
--- 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;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- raw_request eval
|
||||
"GET /?a=%u0053%u0045%u004c%u0045%u0043%u0054%u0020%u0046%u0049%u0045%u004c%u0044%u0020%u0046%u0052%u004f%u004d%u0020%u0054%u0041%u0042%u004c%u0045' HTTP/1.0
|
||||
|
||||
"
|
||||
--- error_code: 412
|
||||
|
||||
|
||||
=== TEST 1: hey 9
|
||||
--- main_config
|
||||
working_directory /tmp/;
|
||||
worker_rlimit_core 25M;
|
||||
--- 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;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- raw_request eval
|
||||
"GET /?a=SELECT+*+FROM+users+WHERE+id+LIKE+1 HTTP/1.0
|
||||
|
||||
"
|
||||
--- error_code: 412
|
||||
|
||||
|
||||
=== TEST 1: hey 10
|
||||
--- main_config
|
||||
working_directory /tmp/;
|
||||
worker_rlimit_core 25M;
|
||||
--- 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;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- raw_request eval
|
||||
"GET /?a=value'/*!0UNION/*!0ALL/*!0SELECT/*!0CONCAT(/*!0CHAR(58,107,112,113,58),/*!0IFNULL(CAST(/*!0CURRENT_USER()/*!0AS/*!0CHAR),/*!0CHAR(32)),/*!0CHAR(58,97,110,121,58)),+NULL,+NULL#/*!0AND+'QDWa'='QDWa HTTP/1.0
|
||||
|
||||
"
|
||||
--- error_code: 412
|
||||
|
||||
|
||||
=== TEST 1: hey 11
|
||||
--- main_config
|
||||
working_directory /tmp/;
|
||||
worker_rlimit_core 25M;
|
||||
--- 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;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- raw_request eval
|
||||
"GET /?a=IF(ISNULL(1),+2,+1) HTTP/1.0
|
||||
|
||||
"
|
||||
--- error_code: 412
|
||||
|
||||
|
||||
=== TEST 1: hey 12
|
||||
--- main_config
|
||||
working_directory /tmp/;
|
||||
worker_rlimit_core 25M;
|
||||
--- 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;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- raw_request eval
|
||||
"GET /?a=1+/*!30000AND+2>1*/-- HTTP/1.0
|
||||
|
||||
"
|
||||
--- error_code: 412
|
||||
|
||||
|
||||
=== TEST 1: hey 13
|
||||
--- main_config
|
||||
working_directory /tmp/;
|
||||
worker_rlimit_core 25M;
|
||||
--- 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;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- raw_request eval
|
||||
"GET /?a=1+/*!00000AND+2>1*/-- HTTP/1.0
|
||||
|
||||
"
|
||||
--- error_code: 412
|
||||
|
||||
|
||||
=== TEST 1: hey 14
|
||||
--- main_config
|
||||
working_directory /tmp/;
|
||||
worker_rlimit_core 25M;
|
||||
--- 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;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- raw_request eval
|
||||
"GET /?a=+UNION+++SELECT++ HTTP/1.0
|
||||
|
||||
"
|
||||
--- error_code: 412
|
||||
|
||||
|
||||
=== IIS/ASP Encoding
|
||||
--- main_config
|
||||
working_directory /tmp/;
|
||||
worker_rlimit_core 25M;
|
||||
--- 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;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- raw_request eval
|
||||
"GET /?a=%S%E%L%E%C%T+%F%I%E%L%D+%F%R%O%M+%T%A%B%L%E HTTP/1.0
|
||||
|
||||
"
|
||||
--- error_code: 412
|
||||
|
||||
|
||||
=== TEST 1: hey 16
|
||||
--- main_config
|
||||
working_directory /tmp/;
|
||||
worker_rlimit_core 25M;
|
||||
--- 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;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- raw_request eval
|
||||
"GET /?a=1 UnioN SeLEct 1 HTTP/1.0
|
||||
|
||||
"
|
||||
--- error_code: 412
|
||||
|
||||
|
||||
=== TEST 1: hey 17
|
||||
--- main_config
|
||||
working_directory /tmp/;
|
||||
worker_rlimit_core 25M;
|
||||
--- 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;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- raw_request eval
|
||||
"GET /?a=AND+1=1+and+'0having'='0having' HTTP/1.0
|
||||
|
||||
"
|
||||
--- error_code: 412
|
||||
|
||||
|
||||
=== TEST 1: hey 18
|
||||
--- main_config
|
||||
working_directory /tmp/;
|
||||
worker_rlimit_core 25M;
|
||||
--- 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;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- raw_request eval
|
||||
"GET /?a=SELECT/**/id/**/FROM/**/users HTTP/1.0
|
||||
|
||||
"
|
||||
--- error_code: 412
|
||||
|
||||
|
||||
=== TEST 1: hey 19
|
||||
--- main_config
|
||||
working_directory /tmp/;
|
||||
worker_rlimit_core 25M;
|
||||
--- 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;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- raw_request eval
|
||||
"GET /?a=1--PTTmJopxdWJ%0AAND--cWfcVRPV%0A9227=9227 HTTP/1.0
|
||||
|
||||
"
|
||||
--- error_code: 412
|
||||
|
||||
|
||||
=== TEST 1: hey 20
|
||||
--- main_config
|
||||
working_directory /tmp/;
|
||||
worker_rlimit_core 25M;
|
||||
--- 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;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- raw_request eval
|
||||
"GET /?a=1%23PTTmJopxdWJ%0AAND%23cWfcVRPV%0A9227=9227 HTTP/1.0
|
||||
|
||||
"
|
||||
--- error_code: 412
|
||||
|
||||
|
||||
=== TEST 1: hey 21
|
||||
--- main_config
|
||||
working_directory /tmp/;
|
||||
worker_rlimit_core 25M;
|
||||
--- 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;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- raw_request eval
|
||||
"GET /?a=1%23PTTmJopxdWJ%0AAND%23cWfcVRPV%0A9227=9227 HTTP/1.0
|
||||
|
||||
"
|
||||
--- error_code: 412
|
||||
|
||||
|
||||
=== TEST 1: hey 22
|
||||
--- main_config
|
||||
working_directory /tmp/;
|
||||
worker_rlimit_core 25M;
|
||||
--- 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;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- raw_request eval
|
||||
"GET /?a=SELECT%08id%02FROM%0Fusers HTTP/1.0
|
||||
|
||||
"
|
||||
--- error_code: 412
|
||||
|
||||
|
||||
=== TEST 1: hey 23
|
||||
--- main_config
|
||||
working_directory /tmp/;
|
||||
worker_rlimit_core 25M;
|
||||
--- 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;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- raw_request eval
|
||||
"GET /?a=1%23%0A9227=922%237 HTTP/1.0
|
||||
|
||||
"
|
||||
--- error_code: 412
|
||||
|
||||
|
||||
=== TEST 1: hey 24
|
||||
--- main_config
|
||||
working_directory /tmp/;
|
||||
worker_rlimit_core 25M;
|
||||
--- 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;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- raw_request eval
|
||||
"GET /?a=SELECT%0Bid%0BFROM%A0users HTTP/1.0
|
||||
|
||||
"
|
||||
--- error_code: 412
|
||||
|
||||
|
||||
=== TEST 1: hey 25
|
||||
--- main_config
|
||||
working_directory /tmp/;
|
||||
worker_rlimit_core 25M;
|
||||
--- 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;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- raw_request eval
|
||||
"GET /?a=1--%0AAND--%0A9227=9227 HTTP/1.0
|
||||
|
||||
"
|
||||
--- error_code: 412
|
||||
|
||||
|
||||
=== TEST 1: hey 26
|
||||
--- main_config
|
||||
working_directory /tmp/;
|
||||
worker_rlimit_core 25M;
|
||||
--- 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;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- raw_request eval
|
||||
"GET /?a=SELECT+id+FROM+users HTTP/1.0
|
||||
|
||||
"
|
||||
--- error_code: 412
|
||||
|
||||
|
||||
|
||||
=== TEST 1: hey 28
|
||||
--- main_config
|
||||
working_directory /tmp/;
|
||||
worker_rlimit_core 25M;
|
||||
--- 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;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- raw_request eval
|
||||
"GET /?a=1%bf%27+AND+1=1--%20 HTTP/1.0
|
||||
|
||||
"
|
||||
--- error_code: 412
|
||||
|
||||
|
||||
=== TEST 1: hey 29
|
||||
--- main_config
|
||||
working_directory /tmp/;
|
||||
worker_rlimit_core 25M;
|
||||
--- 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;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- raw_request eval
|
||||
"GET /?a=1/*!UNION*//*!ALL*//*!SELECT*//*!NULL*/,/*!NULL*/,+CONCAT(CHAR(58,104,116,116,58),IFNULL(CAST(CURRENT_USER()/*!AS*//*!CHAR*/),CHAR(32)),CHAR(58,100,114,117,58))# HTTP/1.0
|
||||
|
||||
"
|
||||
--- error_code: 412
|
||||
|
||||
|
||||
=== TEST 1: hey 30
|
||||
--- main_config
|
||||
working_directory /tmp/;
|
||||
worker_rlimit_core 25M;
|
||||
--- 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;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- raw_request eval
|
||||
"GET /?a=1/*!UNION*//*!ALL*//*!SELECT*//*!NULL*/,/*!NULL*/,/*!CONCAT*/(/*!CHAR*/(58,122,114,115,58),/*!IFNULL*/(CAST(/*!CURRENT_USER*/()/*!AS*//*!CHAR*/),/*!CHAR*/(32)),/*!CHAR*/(58,115,114,121,58))# HTTP/1.0
|
||||
|
||||
"
|
||||
--- error_code: 412
|
||||
|
||||
|
||||
423
naxsi-0.55.3/t/10naxsi_modifiers.t
vendored
Normal file
423
naxsi-0.55.3/t/10naxsi_modifiers.t
vendored
Normal file
@@ -0,0 +1,423 @@
|
||||
#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 1.0 : Runtime Learning force (per ip)
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
--- config
|
||||
if ($remote_addr = "127.0.0.1") {
|
||||
set $naxsi_flag_learning 1;
|
||||
}
|
||||
location / {
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?a=<>
|
||||
--- error_code: 200
|
||||
|
||||
=== TEST 1.01 : Runtime Learning force (absolute)
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
--- config
|
||||
set $naxsi_flag_learning 1;
|
||||
location / {
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?a=<>
|
||||
--- error_code: 200
|
||||
|
||||
|
||||
=== TEST 1.1: Runtime Learning force (fail - per ip)
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
--- config
|
||||
if ($remote_addr = "127.0.0.42") {
|
||||
set $naxsi_flag_learning 1;
|
||||
}
|
||||
|
||||
location / {
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?a=<>
|
||||
--- error_code: 412
|
||||
|
||||
=== TEST 1.2: Runtime Learning force (fail - in location)
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
--- config
|
||||
location / {
|
||||
# this will not work, as naxsi
|
||||
# is processed before var set in location.
|
||||
set $naxsi_flag_learning 1;
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?a=<>
|
||||
--- error_code: 412
|
||||
|
||||
|
||||
|
||||
=== TEST 1.3: Runtime Learning disable (per ip)
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
--- config
|
||||
if ($remote_addr = "127.0.0.1") {
|
||||
set $naxsi_flag_learning 0;
|
||||
}
|
||||
location / {
|
||||
SecRulesEnabled;
|
||||
LearningMode;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?a=<>
|
||||
--- error_code: 412
|
||||
|
||||
=== TEST 1.4: Runtime Learning disable (fail - per ip)
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
--- config
|
||||
if ($remote_addr = "127.0.0.42") {
|
||||
set $naxsi_flag_learning 0;
|
||||
}
|
||||
location / {
|
||||
SecRulesEnabled;
|
||||
LearningMode;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?a=<>
|
||||
--- error_code: 200
|
||||
|
||||
=== TEST 2.00 : Check that SecRulesDisabled correctly works
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
--- config
|
||||
location / {
|
||||
SecRulesEnabled;
|
||||
SecRulesDisabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?a=<>
|
||||
--- error_code: 200
|
||||
|
||||
=== TEST 2: Runtime disable force (absolute)
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
--- config
|
||||
set $naxsi_flag_enable 0;
|
||||
location / {
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?a=<>
|
||||
--- error_code: 200
|
||||
|
||||
=== TEST 2.2: Runtime enable force
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
--- config
|
||||
set $naxsi_flag_enable 1;
|
||||
location / {
|
||||
SecRulesEnabled;
|
||||
SecRulesDisabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?a=<>
|
||||
--- error_code: 412
|
||||
|
||||
=== TEST 2.3: Runtime enable force, with static learning (which is pointless)
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
--- config
|
||||
set $naxsi_flag_enable 1;
|
||||
location / {
|
||||
LearningMode;
|
||||
SecRulesEnabled;
|
||||
SecRulesDisabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?a=<>
|
||||
--- error_code: 200
|
||||
|
||||
=== TEST 2.4: Runtime enable + learning mode (absolute)
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
--- config
|
||||
set $naxsi_flag_learning 1;
|
||||
set $naxsi_flag_enable 1;
|
||||
location / {
|
||||
SecRulesEnabled;
|
||||
SecRulesDisabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?a=<>
|
||||
--- error_code: 200
|
||||
|
||||
=== TEST 3.0: Runtime enable + learning mode (per ip)
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
--- config
|
||||
if ($remote_addr = "127.0.0.1") {
|
||||
set $naxsi_flag_enable 1;
|
||||
set $naxsi_flag_learning 1;
|
||||
}
|
||||
location / {
|
||||
SecRulesEnabled;
|
||||
SecRulesDisabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?a=<>
|
||||
--- error_code: 200
|
||||
|
||||
=== TEST 3.1: Runtime enable + learning mode (per ip)
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
--- config
|
||||
if ($remote_addr = "127.0.0.42") {
|
||||
set $naxsi_flag_enable 1;
|
||||
set $naxsi_flag_learning 1;
|
||||
}
|
||||
location / {
|
||||
SecRulesEnabled;
|
||||
SecRulesDisabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?a=<>
|
||||
--- error_code: 200
|
||||
|
||||
=== TEST 3.2: Runtime enable + learning mode (per ip)
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
--- config
|
||||
set $naxsi_flag_enable 1;
|
||||
if ($remote_addr = "127.0.0.1") {
|
||||
set $naxsi_flag_learning 1;
|
||||
}
|
||||
location / {
|
||||
SecRulesEnabled;
|
||||
SecRulesDisabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?a=<>
|
||||
--- error_code: 200
|
||||
|
||||
=== TEST 3.3: Runtime enable (success) + learning mode (fail - per ip)
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
--- config
|
||||
set $naxsi_flag_enable 1;
|
||||
if ($remote_addr = "127.0.0.42") {
|
||||
set $naxsi_flag_learning 1;
|
||||
}
|
||||
location / {
|
||||
SecRulesEnabled;
|
||||
SecRulesDisabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?a=<>
|
||||
--- error_code: 412
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
2363
naxsi-0.55.3/t/11naxsi_newstyle_config.t
vendored
Normal file
2363
naxsi-0.55.3/t/11naxsi_newstyle_config.t
vendored
Normal file
File diff suppressed because it is too large
Load Diff
67
naxsi-0.55.3/t/12naxsi_argnames_extended.t
vendored
Normal file
67
naxsi-0.55.3/t/12naxsi_argnames_extended.t
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
#vi:filetype=perl
|
||||
|
||||
|
||||
# A AJOUTER :
|
||||
# TEST CASE AVEC UNE REGLE SUR UN HEADER GENERIQUE
|
||||
# La même sur des arguments :)
|
||||
|
||||
use lib 'lib';
|
||||
use Test::Nginx::Socket;
|
||||
|
||||
plan tests => repeat_each(2) * blocks();
|
||||
no_root_location();
|
||||
no_long_string();
|
||||
$ENV{TEST_NGINX_SERVROOT} = server_root();
|
||||
run_tests();
|
||||
__DATA__
|
||||
=== WL TEST 1.0: Obvious test in arg
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:foobar" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
#LearningMode;
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?foobar=a
|
||||
--- error_code: 412
|
||||
|
||||
=== WL TEST 1.1: Obvious test in arg
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:foobar" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
#LearningMode;
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:1999 "mz:$ARGS_VAR:foobar|NAME";
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?foobar=a
|
||||
--- error_code: 200
|
||||
|
||||
80
naxsi-0.55.3/t/13test.t
vendored
Normal file
80
naxsi-0.55.3/t/13test.t
vendored
Normal file
@@ -0,0 +1,80 @@
|
||||
# This File is used for broken tests.
|
||||
|
||||
|
||||
use lib 'lib';
|
||||
use Test::Nginx::Socket;
|
||||
|
||||
plan tests => repeat_each(2) * blocks();
|
||||
no_root_location();
|
||||
no_long_string();
|
||||
$ENV{TEST_NGINX_SERVROOT} = server_root();
|
||||
run_tests();
|
||||
__DATA__
|
||||
# This one should actually return 200, but a hashtable collision happens
|
||||
=== WL TEST 6.1: Whitelist provoking collision
|
||||
--- user_files
|
||||
>>> buixor
|
||||
eh yo
|
||||
>>> bla
|
||||
eh yo
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:1998" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1998;
|
||||
MainRule "str:1999" "msg:foobar test pattern #2" "mz:ARGS" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
#LearningMode;
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:1999 "mz:$URL:/bla|ARGS|NAME";
|
||||
BasicRule wl:1998 "mz:$URL:/bla|ARGS";
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /bla?blx=1998&1999=bla
|
||||
--- error_code: 200
|
||||
|
||||
=== WL TEST 6.2: Trigger multi-line logs
|
||||
--- user_files
|
||||
>>> buixor
|
||||
eh yo
|
||||
>>> bla
|
||||
eh yo
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:1998" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1998;
|
||||
MainRule "str:1999" "msg:foobar test pattern #2" "mz:ARGS" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
LearningMode;
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:1999 "mz:$URL:/bla|ARGS|NAME";
|
||||
BasicRule wl:1998 "mz:$URL:/bla|ARGS";
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1=1998&AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA2=1998&AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA3=1998&AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4=1998&AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5=1998&AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA6=1998&AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7=1998&AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8=1998&AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA9=1998&AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA10=1998&AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA11=1998&AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA12=1998&AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA13=1998&AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA14=1998&AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA15=1998
|
||||
--- error_code: 200
|
||||
|
||||
|
||||
742
naxsi-0.55.3/t/14json.t
vendored
Normal file
742
naxsi-0.55.3/t/14json.t
vendored
Normal file
@@ -0,0 +1,742 @@
|
||||
#vi:filetype=perl
|
||||
|
||||
use lib 'lib';
|
||||
use Test::Nginx::Socket;
|
||||
|
||||
plan tests => repeat_each(2) * blocks();
|
||||
no_root_location();
|
||||
no_long_string();
|
||||
$ENV{TEST_NGINX_SERVROOT} = server_root();
|
||||
run_tests();
|
||||
__DATA__
|
||||
=== JSON0 : Valid JSON
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
--- config
|
||||
location / {
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
error_page 405 = $uri;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- more_headers
|
||||
Content-Type: application/json
|
||||
--- request eval
|
||||
use URI::Escape;
|
||||
"POST /
|
||||
{
|
||||
\"glossary\": {
|
||||
\"title\": \"example glossary\",
|
||||
\"GlossDiv\": {
|
||||
\"title\": \"S\",
|
||||
\"GlossList\": {
|
||||
\"GlossEntry\": {
|
||||
\"ID\": \"SGML\",
|
||||
\"SortAs\": \"SGML\",
|
||||
\"GlossTerm\": \"Standard Generalized Markup Language\",
|
||||
\"Acronym\": \"SGML\",
|
||||
\"Abbrev\": \"ISO 8879:1986\",
|
||||
\"GlossDef\": {
|
||||
\"para\": \"A meta-markup language used to create markup languages such as DocBook.\",
|
||||
\"GlossSeeAlso\": [\"GML\", \"XML\"]
|
||||
},
|
||||
\"GlossSee\": \"markup\"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"
|
||||
--- error_code: 200
|
||||
=== JSON1 : invalid JSON (double closing ']')
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
--- config
|
||||
location / {
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
error_page 405 = $uri;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- more_headers
|
||||
Content-Type: application/json
|
||||
--- request eval
|
||||
use URI::Escape;
|
||||
"POST /
|
||||
{
|
||||
\"glossary\": {
|
||||
\"title\": \"example glossary\",
|
||||
\"GlossDiv\": {
|
||||
\"title\": \"S\",
|
||||
\"GlossList\": {
|
||||
\"GlossEntry\": {
|
||||
\"ID\": \"SGML\",
|
||||
\"SortAs\": \"SGML\",
|
||||
\"GlossTerm\": \"Standard Generalized Markup Language\",
|
||||
\"Acronym\": \"SGML\",
|
||||
\"Abbrev\": \"ISO 8879:1986\",
|
||||
\"GlossDef\": {
|
||||
\"para\": \"A meta-markup language used to create markup languages such as DocBook.\",
|
||||
\"GlossSeeAlso\": [\"GML\", \"XML\"]]
|
||||
},
|
||||
\"GlossSee\": \"markup\"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"
|
||||
--- error_code: 412
|
||||
|
||||
|
||||
|
||||
=== JSON2 : invalid JSON (missing closing ']')
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
--- config
|
||||
location / {
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
error_page 405 = $uri;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- more_headers
|
||||
Content-Type: application/json
|
||||
--- request eval
|
||||
use URI::Escape;
|
||||
"POST /
|
||||
{
|
||||
\"glossary\": {
|
||||
\"title\": \"example glossary\",
|
||||
\"GlossDiv\": {
|
||||
\"title\": \"S\",
|
||||
\"GlossList\": {
|
||||
\"GlossEntry\": {
|
||||
\"ID\": \"SGML\",
|
||||
\"SortAs\": \"SGML\",
|
||||
\"GlossTerm\": \"Standard Generalized Markup Language\",
|
||||
\"Acronym\": \"SGML\",
|
||||
\"Abbrev\": \"ISO 8879:1986\",
|
||||
\"GlossDef\": {
|
||||
\"para\": \"A meta-markup language used to create markup languages such as DocBook.\",
|
||||
\"GlossSeeAlso\": [\"GML\", \"XML\"
|
||||
},
|
||||
\"GlossSee\": \"markup\"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"
|
||||
--- error_code: 412
|
||||
=== JSON3 : invalid JSON (closing array with '}' instead of ']')
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
--- config
|
||||
location / {
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
error_page 405 = $uri;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- more_headers
|
||||
Content-Type: application/json
|
||||
--- request eval
|
||||
use URI::Escape;
|
||||
"POST /
|
||||
{
|
||||
\"glossary\": {
|
||||
\"title\": \"example glossary\",
|
||||
\"GlossDiv\": {
|
||||
\"title\": \"S\",
|
||||
\"GlossList\": {
|
||||
\"GlossEntry\": {
|
||||
\"ID\": \"SGML\",
|
||||
\"SortAs\": \"SGML\",
|
||||
\"GlossTerm\": \"Standard Generalized Markup Language\",
|
||||
\"Acronym\": \"SGML\",
|
||||
\"Abbrev\": \"ISO 8879:1986\",
|
||||
\"GlossDef\": {
|
||||
\"para\": \"A meta-markup language used to create markup languages such as DocBook.\",
|
||||
\"GlossSeeAlso\": [\"GML\", \"XML\"}
|
||||
},
|
||||
\"GlossSee\": \"markup\"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"
|
||||
--- error_code: 412
|
||||
=== JSON4 : invalid JSON (Missing final closing '}')
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
--- config
|
||||
location / {
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
error_page 405 = $uri;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- more_headers
|
||||
Content-Type: application/json
|
||||
--- request eval
|
||||
use URI::Escape;
|
||||
"POST /
|
||||
{
|
||||
\"glossary\": {
|
||||
\"title\": \"example glossary\",
|
||||
\"GlossDiv\": {
|
||||
\"title\": \"S\",
|
||||
\"GlossList\": {
|
||||
\"GlossEntry\": {
|
||||
\"ID\": \"SGML\",
|
||||
\"SortAs\": \"SGML\",
|
||||
\"GlossTerm\": \"Standard Generalized Markup Language\",
|
||||
\"Acronym\": \"SGML\",
|
||||
\"Abbrev\": \"ISO 8879:1986\",
|
||||
\"GlossDef\": {
|
||||
\"para\": \"A meta-markup language used to create markup languages such as DocBook.\",
|
||||
\"GlossSeeAlso\": [\"GML\", \"XML\"]
|
||||
},
|
||||
\"GlossSee\": \"markup\"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"
|
||||
--- error_code: 412
|
||||
|
||||
=== JSON5 : invalid JSON (Extra closing '}')
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
--- config
|
||||
location / {
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
error_page 405 = $uri;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- more_headers
|
||||
Content-Type: application/json
|
||||
--- request eval
|
||||
use URI::Escape;
|
||||
"POST /
|
||||
{
|
||||
\"glossary\": {
|
||||
\"title\": \"example glossary\",
|
||||
\"GlossDiv\": {
|
||||
\"title\": \"S\",
|
||||
\"GlossList\": {
|
||||
\"GlossEntry\": {
|
||||
\"ID\": \"SGML\",
|
||||
\"SortAs\": \"SGML\",
|
||||
\"GlossTerm\": \"Standard Generalized Markup Language\",
|
||||
\"Acronym\": \"SGML\",
|
||||
\"Abbrev\": \"ISO 8879:1986\",
|
||||
\"GlossDef\": {
|
||||
\"para\": \"A meta-markup language used to create markup languages such as DocBook.\",
|
||||
\"GlossSeeAlso\": [\"GML\", \"XML\"]
|
||||
},
|
||||
\"GlossSee\": \"markup\"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}}"
|
||||
--- error_code: 412
|
||||
=== JSON6 : invalid JSON (Missing ',' in array)
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
--- config
|
||||
location / {
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
error_page 405 = $uri;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- more_headers
|
||||
Content-Type: application/json
|
||||
--- request eval
|
||||
use URI::Escape;
|
||||
"POST /
|
||||
{
|
||||
\"glossary\": {
|
||||
\"title\": \"example glossary\",
|
||||
\"GlossDiv\": {
|
||||
\"title\": \"S\",
|
||||
\"GlossList\": {
|
||||
\"GlossEntry\": {
|
||||
\"ID\": \"SGML\",
|
||||
\"SortAs\": \"SGML\",
|
||||
\"GlossTerm\": \"Standard Generalized Markup Language\",
|
||||
\"Acronym\": \"SGML\",
|
||||
\"Abbrev\": \"ISO 8879:1986\",
|
||||
\"GlossDef\": {
|
||||
\"para\": \"A meta-markup language used to create markup languages such as DocBook.\",
|
||||
\"GlossSeeAlso\": [\"GML\" \"XML\"]
|
||||
},
|
||||
\"GlossSee\": \"markup\"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}"
|
||||
--- error_code: 412
|
||||
=== JSON7 : Valid JSON with empty array item (Extra ',' in array)
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
--- config
|
||||
location / {
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
error_page 405 = $uri;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- more_headers
|
||||
Content-Type: application/json
|
||||
--- request eval
|
||||
use URI::Escape;
|
||||
"POST /
|
||||
{
|
||||
\"glossary\": {
|
||||
\"title\": \"example glossary\",
|
||||
\"GlossDiv\": {
|
||||
\"title\": \"S\",
|
||||
\"GlossList\": {
|
||||
\"GlossEntry\": {
|
||||
\"ID\": \"SGML\",
|
||||
\"SortAs\": \"SGML\",
|
||||
\"GlossTerm\": \"Standard Generalized Markup Language\",
|
||||
\"Acronym\": \"SGML\",
|
||||
\"Abbrev\": \"ISO 8879:1986\",
|
||||
\"GlossDef\": {
|
||||
\"para\": \"A meta-markup language used to create markup languages such as DocBook.\",
|
||||
\"GlossSeeAlso\": [\"GML\",\"XML\",]
|
||||
},
|
||||
\"GlossSee\": \"markup\"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}"
|
||||
--- error_code: 200
|
||||
=== JSON8 : valid JSON - too deep !
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
--- config
|
||||
location / {
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
error_page 405 = $uri;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- more_headers
|
||||
Content-Type: application/json
|
||||
--- request eval
|
||||
use URI::Escape;
|
||||
"POST /
|
||||
{{{{{{{{{{{{[\"lol\"]}}}}}}}}}}}}"
|
||||
--- error_code: 412
|
||||
=== JSON9 : Valid JSON with ev0l stuff (array => var content)
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
--- config
|
||||
location / {
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
error_page 405 = $uri;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- more_headers
|
||||
Content-Type: application/json
|
||||
--- request eval
|
||||
use URI::Escape;
|
||||
"POST /
|
||||
{
|
||||
\"glossary\": {
|
||||
\"title\": \"example glossary\",
|
||||
\"GlossDiv\": {
|
||||
\"title\": \"S\",
|
||||
\"GlossList\": {
|
||||
\"GlossEntry\": {
|
||||
\"ID\": \"SGML\",
|
||||
\"SortAs\": \"SGML\",
|
||||
\"GlossTerm\": \"Standard Generalized Markup Language\",
|
||||
\"Acronym\": \"SGML\",
|
||||
\"Abbrev\": \"ISO 8879:1986\",
|
||||
\"GlossDef\": {
|
||||
\"para\": \"A meta-markup language used to create markup languages such as DocBook.\",
|
||||
\"GlossSeeAlso\": [\"G<ML\",\"XML\",]
|
||||
},
|
||||
\"GlossSee\": \"markup\"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}"
|
||||
--- error_code: 412
|
||||
=== JSON10 : Valid JSON with ev0l stuff (array => var name)
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
--- config
|
||||
location / {
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
error_page 405 = $uri;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- more_headers
|
||||
Content-Type: application/json
|
||||
--- request eval
|
||||
use URI::Escape;
|
||||
"POST /
|
||||
{
|
||||
\"glossary\": {
|
||||
\"title\": \"example glossary\",
|
||||
\"GlossDiv\": {
|
||||
\"title\": \"S\",
|
||||
\"GlossList\": {
|
||||
\"GlossEntry\": {
|
||||
\"ID\": \"SGML\",
|
||||
\"SortAs\": \"SGML\",
|
||||
\"GlossTerm\": \"Standard Generalized Markup Language\",
|
||||
\"Acronym\": \"SGML\",
|
||||
\"Abbrev\": \"ISO 8879:1986\",
|
||||
\"GlossDef\": {
|
||||
\"para\": \"A meta-markup language used to create markup languages such as DocBook.\",
|
||||
\"GlossSeeAl<so\": [\"GML\",\"XML\",]
|
||||
},
|
||||
\"GlossSee\": \"markup\"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}"
|
||||
--- error_code: 412
|
||||
=== JSON11 : Empty JSON object
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
--- config
|
||||
location / {
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
error_page 405 = $uri;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- more_headers
|
||||
Content-Type: application/json
|
||||
--- request eval
|
||||
use URI::Escape;
|
||||
"POST /
|
||||
{
|
||||
|
||||
}"
|
||||
--- error_code: 200
|
||||
=== JSON12 : malformed (closing object before array) Json
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
--- config
|
||||
location / {
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
error_page 405 = $uri;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- more_headers
|
||||
Content-Type: application/json
|
||||
--- request eval
|
||||
use URI::Escape;
|
||||
"POST /
|
||||
{
|
||||
\"fuu\" : [\"laul\", {\"die\" : \"nope\" ]}
|
||||
}"
|
||||
--- error_code: 412
|
||||
=== JSON12 : malformed (unescaped quotes)
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
--- config
|
||||
location / {
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
error_page 405 = $uri;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- more_headers
|
||||
Content-Type: application/json
|
||||
--- request eval
|
||||
use URI::Escape;
|
||||
"POST /
|
||||
{
|
||||
\"fuu\" : [\"laul\", {\"die\" : \"n\"ope\" }]
|
||||
}"
|
||||
--- error_code: 412
|
||||
|
||||
=== JSON12 : escaped quotes
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
--- config
|
||||
set $naxsi_extensive_log 1;
|
||||
location / {
|
||||
BasicRule wl:1001,1205;
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
error_page 405 = $uri;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- more_headers
|
||||
Content-Type: application/json
|
||||
--- request eval
|
||||
use URI::Escape;
|
||||
"POST /
|
||||
{
|
||||
\"fuu\" : [\"laul\", {\"die\" : \"n\\\"ope\" }]
|
||||
}"
|
||||
--- error_code: 200
|
||||
=== JSON13 : concatenation attempt (ie "foo":"bar"+eval(evil)+"foo")
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
--- config
|
||||
set $naxsi_extensive_log 1;
|
||||
location / {
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
error_page 405 = $uri;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- more_headers
|
||||
Content-Type: application/json
|
||||
--- request eval
|
||||
use URI::Escape;
|
||||
"POST /
|
||||
{
|
||||
\"fuu\" : \"oh \"+eval(evil)+\" my\"]
|
||||
}"
|
||||
--- error_code: 412
|
||||
=== JSON13 : concatenation attempt (ie "foo":"bar"+eval(evil)+"foo")
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
--- config
|
||||
set $naxsi_extensive_log 1;
|
||||
location / {
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
error_page 405 = $uri;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- more_headers
|
||||
Content-Type: application/json
|
||||
--- request eval
|
||||
use URI::Escape;
|
||||
"POST /
|
||||
{
|
||||
\"obvious\" : \"a<a\"]
|
||||
}"
|
||||
--- error_code: 412
|
||||
=== JSON14 : unfinished sub object
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
--- config
|
||||
set $naxsi_extensive_log 1;
|
||||
location / {
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
error_page 405 = $uri;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- more_headers
|
||||
Content-Type: application/json
|
||||
--- request eval
|
||||
use URI::Escape;
|
||||
"POST /
|
||||
{
|
||||
\"obvious\" : \"a<a\",
|
||||
\"fu\" : { \"aa\" : \"bb\"
|
||||
}"
|
||||
--- error_code: 412
|
||||
559
naxsi-0.55.3/t/15json_wl.t
vendored
Normal file
559
naxsi-0.55.3/t/15json_wl.t
vendored
Normal file
@@ -0,0 +1,559 @@
|
||||
use lib 'lib';
|
||||
use Test::Nginx::Socket;
|
||||
|
||||
|
||||
plan tests => repeat_each(2) * blocks();
|
||||
no_root_location();
|
||||
no_long_string();
|
||||
$ENV{TEST_NGINX_SERVROOT} = server_root();
|
||||
run_tests();
|
||||
__DATA__
|
||||
=== json wl 0.1 : no rulematch
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:foobar" "msg:foobar test pattern" "mz:BODY" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
error_page 405 = $uri;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- more_headers
|
||||
Content-Type: application/json
|
||||
--- request eval
|
||||
use URI::Escape;
|
||||
"POST /
|
||||
{
|
||||
\"lol\" : \"bar\"
|
||||
}
|
||||
"
|
||||
--- error_code: 200
|
||||
=== json wl 0.2 : rulematch
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:foobar" "msg:foobar test pattern" "mz:BODY" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
error_page 405 = $uri;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- more_headers
|
||||
Content-Type: application/json
|
||||
--- request eval
|
||||
use URI::Escape;
|
||||
"POST /
|
||||
{
|
||||
\"lol\" : \"foobar\"
|
||||
}
|
||||
"
|
||||
--- error_code: 412
|
||||
=== json wl 0.3 : rulematch + wl on full zone
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:foobar" "msg:foobar test pattern" "mz:BODY" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:1999 "mz:BODY";
|
||||
error_page 405 = $uri;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- more_headers
|
||||
Content-Type: application/json
|
||||
--- request eval
|
||||
use URI::Escape;
|
||||
"POST /
|
||||
{
|
||||
\"lol\" : \"foobar\"
|
||||
}
|
||||
"
|
||||
--- error_code: 200
|
||||
=== json wl 0.4 : rulematch + wl on zone + varname
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:foobar" "msg:foobar test pattern" "mz:BODY" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:1999 "mz:$BODY_VAR:lol";
|
||||
error_page 405 = $uri;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- more_headers
|
||||
Content-Type: application/json
|
||||
--- request eval
|
||||
use URI::Escape;
|
||||
"POST /
|
||||
{
|
||||
\"lol\" : \"foobar\"
|
||||
}
|
||||
"
|
||||
--- error_code: 200
|
||||
|
||||
=== json wl 0.5 : rulematch + wl on zone + varname + url
|
||||
--- user_files
|
||||
>>> test_uri
|
||||
eh yo
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:foobar" "msg:foobar test pattern" "mz:BODY" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:1999 "mz:$BODY_VAR:lol|$URL:/test_uri";
|
||||
error_page 405 = $uri;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- more_headers
|
||||
Content-Type: application/json
|
||||
--- request eval
|
||||
use URI::Escape;
|
||||
"POST /test_uri
|
||||
{
|
||||
\"lol\" : \"foobar\"
|
||||
}
|
||||
"
|
||||
--- error_code: 200
|
||||
|
||||
=== json wl 0.6 : rulematch + wl on zone + varname + url [fail]
|
||||
--- user_files
|
||||
>>> test_uri
|
||||
eh yo
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:foobar" "msg:foobar test pattern" "mz:BODY" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:1999 "mz:$BODY_VAR:lol|$URL:/test_uri";
|
||||
error_page 405 = $uri;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- more_headers
|
||||
Content-Type: application/json
|
||||
--- request eval
|
||||
use URI::Escape;
|
||||
"POST /
|
||||
{
|
||||
\"lol\" : \"foobar\"
|
||||
}
|
||||
"
|
||||
--- error_code: 412
|
||||
|
||||
=== json wl 0.7 : rulematch + wl on zone + varname (in sub-json element)
|
||||
--- user_files
|
||||
>>> test_uri
|
||||
eh yo
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:foobar" "msg:foobar test pattern" "mz:BODY" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:1999 "mz:$BODY_VAR:test_123|$URL:/test_uri";
|
||||
error_page 405 = $uri;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- more_headers
|
||||
Content-Type: application/json
|
||||
--- request eval
|
||||
use URI::Escape;
|
||||
"POST /test_uri
|
||||
{
|
||||
\"oh\" : [\"there\", \"is\", \"no\", \"way\"],
|
||||
\"this\" : { \"will\" : [\"work\", \"does\"],
|
||||
\"it\" : \"??\" },
|
||||
\"trigger\" : {\"test_123\" : [\"foobar\", \"will\", \"trigger\", \"it\"]},
|
||||
\"foo\" : \"baar\"
|
||||
}
|
||||
"
|
||||
--- error_code: 200
|
||||
|
||||
=== json wl 0.8 : rulematch + wl on zone + varname (in sub-json element) [fail]
|
||||
--- user_files
|
||||
>>> test_uri
|
||||
eh yo
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:foobar" "msg:foobar test pattern" "mz:BODY" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:1999 "mz:$BODY_VAR:test_123|$URL:/test_uri";
|
||||
error_page 405 = $uri;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- more_headers
|
||||
Content-Type: application/json
|
||||
--- request eval
|
||||
use URI::Escape;
|
||||
"POST /test_uri
|
||||
{
|
||||
\"oh\" : [\"there\", \"is\", \"no\", \"way\"],
|
||||
\"this\" : { \"will\" : [\"work\", \"does\"],
|
||||
\"it\" : \"??\" },
|
||||
\"trigger\" : {\"test_1234\" : [\"foobar\", \"will\", \"trigger\", \"it\"]},
|
||||
\"foo\" : \"baar\"
|
||||
}
|
||||
"
|
||||
--- error_code: 412
|
||||
=== json wl 0.9 : match in varname
|
||||
--- user_files
|
||||
>>> test_uri
|
||||
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 / {
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
error_page 405 = $uri;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- more_headers
|
||||
Content-Type: application/json
|
||||
--- request eval
|
||||
use URI::Escape;
|
||||
"POST /test_uri
|
||||
{
|
||||
\"oh\" : [\"there\", \"is\", \"no\", \"way\"],
|
||||
\"this\" : { \"will\" : [\"work\", \"does\"],
|
||||
\"it\" : \"??\" },
|
||||
\"tr<igger\" : {\"test_1234\" : [\"foobar\", \"will\", \"trigger\", \"it\"]},
|
||||
\"foo\" : \"baar\"
|
||||
}
|
||||
"
|
||||
--- error_code: 412
|
||||
=== json wl 1.0 : match in varname + wl on varname
|
||||
--- user_files
|
||||
>>> test_uri
|
||||
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 / {
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:1302 "mz:$BODY_VAR:tr<igger|NAME";
|
||||
error_page 405 = $uri;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- more_headers
|
||||
Content-Type: application/json
|
||||
--- request eval
|
||||
use URI::Escape;
|
||||
"POST /test_uri
|
||||
{
|
||||
\"oh\" : [\"there\", \"is\", \"no\", \"way\"],
|
||||
\"this\" : { \"will\" : [\"work\", \"does\"],
|
||||
\"it\" : \"??\" },
|
||||
\"tr<igger\" : {\"test_1234\" : [\"foobar\", \"will\", \"trigger\", \"it\"]},
|
||||
\"foo\" : \"baar\"
|
||||
}
|
||||
"
|
||||
--- error_code: 200
|
||||
=== json wl 1.1 : match (empty variable name)
|
||||
--- user_files
|
||||
>>> test_uri
|
||||
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 / {
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
error_page 405 = $uri;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- more_headers
|
||||
Content-Type: application/json
|
||||
--- request eval
|
||||
use URI::Escape;
|
||||
"POST /test_uri
|
||||
{
|
||||
\"\" : [\"there\", \"is\", \"no\", \"way\"]
|
||||
}
|
||||
"
|
||||
--- error_code: 200
|
||||
=== json wl 1.1 : match (no variable name)
|
||||
--- user_files
|
||||
>>> test_uri
|
||||
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 / {
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
error_page 405 = $uri;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- more_headers
|
||||
Content-Type: application/json
|
||||
--- request eval
|
||||
use URI::Escape;
|
||||
"POST /test_uri
|
||||
{
|
||||
[\"there\", \"is\", \"no\", \"way\"]
|
||||
}
|
||||
"
|
||||
--- error_code: 200
|
||||
=== json wl 2.0 : malformed json (missing opening {)
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:foobar" "msg:foobar test pattern" "mz:BODY" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
error_page 405 = $uri;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- more_headers
|
||||
Content-Type: application/json
|
||||
--- request eval
|
||||
use URI::Escape;
|
||||
"POST /
|
||||
|
||||
\"lol\" : \"bar\"
|
||||
}
|
||||
"
|
||||
--- error_code: 412
|
||||
=== json wl 2.1 : Numeric content json
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:foobar" "msg:foobar test pattern" "mz:BODY" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
error_page 405 = $uri;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- more_headers
|
||||
Content-Type: application/json
|
||||
--- request eval
|
||||
use URI::Escape;
|
||||
"POST /
|
||||
{
|
||||
\"lol\" : 372
|
||||
}
|
||||
"
|
||||
--- error_code: 200
|
||||
=== json wl 2.2 : true/false content json
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:foobar" "msg:foobar test pattern" "mz:BODY" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
error_page 405 = $uri;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- more_headers
|
||||
Content-Type: application/json
|
||||
--- request eval
|
||||
use URI::Escape;
|
||||
"POST /
|
||||
{
|
||||
\"lol\" : false,
|
||||
\"serious_stuff\" : true,
|
||||
\"extra_coverage\" : null
|
||||
}
|
||||
"
|
||||
--- error_code: 200
|
||||
|
||||
=== json wl 2.3 : malformed json
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:foobar" "msg:foobar test pattern" "mz:BODY" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
error_page 405 = $uri;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- more_headers
|
||||
Content-Type: application/json
|
||||
--- request eval
|
||||
use URI::Escape;
|
||||
"POST /
|
||||
{
|
||||
\"lol\" : false,
|
||||
\"serious_stuff\" : true,
|
||||
\"extra_coverage\" : null
|
||||
"
|
||||
--- error_code: 412
|
||||
|
||||
|
||||
574
naxsi-0.55.3/t/16rx_mz.t
vendored
Normal file
574
naxsi-0.55.3/t/16rx_mz.t
vendored
Normal file
@@ -0,0 +1,574 @@
|
||||
use lib 'lib';
|
||||
use Test::Nginx::Socket;
|
||||
|
||||
plan tests => repeat_each(2) * blocks();
|
||||
no_root_location();
|
||||
no_long_string();
|
||||
$ENV{TEST_NGINX_SERVROOT} = server_root();
|
||||
run_tests();
|
||||
__DATA__
|
||||
=== RXWL TEST 1.0: simple wide regex ($args_var)
|
||||
--- user_files
|
||||
>>> buixor
|
||||
eh yo
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:1999" "msg:foobar test pattern #1" "mz:ARGS" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
#LearningMode;
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:1999 "mz:$ARGS_VAR_X:bla";
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /buixor?bla=1999
|
||||
--- error_code: 200
|
||||
=== RXWL TEST 1.1: simple wide regex ($args_var)
|
||||
--- user_files
|
||||
>>> buixor
|
||||
eh yo
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:1999" "msg:foobar test pattern #1" "mz:ARGS" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
#LearningMode;
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:1999 "mz:$ARGS_VAR_X:bla";
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /buixor?bra=1999
|
||||
--- error_code: 412
|
||||
=== RXWL TEST 1.2: simple wide regex ($args_var)
|
||||
--- user_files
|
||||
>>> buixor
|
||||
eh yo
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:1999" "msg:foobar test pattern #1" "mz:ARGS" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
#LearningMode;
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:1999 "mz:$ARGS_VAR_X:bla";
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /buixor?aablaaa=1999
|
||||
--- error_code: 200
|
||||
=== RXWL TEST 1.3: simple end-restrictive regex ($args_var_x:..$)
|
||||
--- user_files
|
||||
>>> buixor
|
||||
eh yo
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:1999" "msg:foobar test pattern #1" "mz:ARGS" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
#LearningMode;
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:1999 "mz:$ARGS_VAR_X:bla$";
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /buixor?aabla=1999
|
||||
--- error_code: 200
|
||||
=== RXWL TEST 1.3: simple end-restrictive regex ($args_var_x:..$)
|
||||
--- user_files
|
||||
>>> buixor
|
||||
eh yo
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:1999" "msg:foobar test pattern #1" "mz:ARGS" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
#LearningMode;
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:1999 "mz:$ARGS_VAR_X:bla$";
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /buixor?aabla=1999
|
||||
--- error_code: 200
|
||||
=== RXWL TEST 1.4: simple end-restrictive regex ($args_var_x:..$)
|
||||
--- user_files
|
||||
>>> buixor
|
||||
eh yo
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:1999" "msg:foobar test pattern #1" "mz:ARGS" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
#LearningMode;
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:1999 "mz:$ARGS_VAR_X:bla$";
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /buixor?aablaa=1999
|
||||
--- error_code: 412
|
||||
=== RXWL TEST 1.5: simple begin-restrictive regex ($args_var_x:^..)
|
||||
--- user_files
|
||||
>>> buixor
|
||||
eh yo
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:1999" "msg:foobar test pattern #1" "mz:ARGS" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
#LearningMode;
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:1999 "mz:$ARGS_VAR_X:^bla";
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /buixor?blaa=1999
|
||||
--- error_code: 200
|
||||
=== RXWL TEST 1.6: simple begin-restrictive regex ($args_var_x:^..)
|
||||
--- user_files
|
||||
>>> buixor
|
||||
eh yo
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:1999" "msg:foobar test pattern #1" "mz:ARGS" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
#LearningMode;
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:1999 "mz:$ARGS_VAR_X:^bla";
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /buixor?blaa=1999
|
||||
--- error_code: 200
|
||||
=== RXWL TEST 1.7: simple begin-restrictive regex ($args_var_x:^..)
|
||||
--- user_files
|
||||
>>> buixor
|
||||
eh yo
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:1999" "msg:foobar test pattern #1" "mz:ARGS" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
#LearningMode;
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:1999 "mz:$ARGS_VAR_X:^bla";
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /buixor?ablaa=1999
|
||||
--- error_code: 412
|
||||
=== RXWL TEST 1.8: simple full-restrictive regex ($args_var_x:^..$)
|
||||
--- user_files
|
||||
>>> buixor
|
||||
eh yo
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:1999" "msg:foobar test pattern #1" "mz:ARGS" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
#LearningMode;
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:1999 "mz:$ARGS_VAR_X:^bla$";
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /buixor?abla=1999
|
||||
--- error_code: 412
|
||||
=== RXWL TEST 1.9: simple full-restrictive regex ($args_var_x:^..$)
|
||||
--- user_files
|
||||
>>> buixor
|
||||
eh yo
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:1999" "msg:foobar test pattern #1" "mz:ARGS" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
#LearningMode;
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:1999 "mz:$ARGS_VAR_X:^bla$";
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /buixor?bla=1999
|
||||
--- error_code: 200
|
||||
|
||||
=== RXWL TEST 2.0: simple wide regex ($args_var|$url)
|
||||
--- user_files
|
||||
>>> foo
|
||||
eh yo
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:1999" "msg:foobar test pattern #1" "mz:ARGS" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
#LearningMode;
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:1999 "mz:$ARGS_VAR_X:bla|$URL_X:/foo";
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /foo?bla=1999
|
||||
--- error_code: 200
|
||||
|
||||
=== RXWL TEST 2.1: simple wide regex ($args_var|$url)
|
||||
--- user_files
|
||||
>>> foo
|
||||
eh yo
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:1999" "msg:foobar test pattern #1" "mz:ARGS" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
#LearningMode;
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:1999 "mz:$ARGS_VAR_X:bla|$URL_X:/foo";
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /foz?bla=1999
|
||||
--- error_code: 412
|
||||
=== RXWL TEST 2.2: simple half-restrictive regex ($args_var|$url)
|
||||
--- user_files
|
||||
>>> foo
|
||||
eh yo
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:1999" "msg:foobar test pattern #1" "mz:ARGS" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
#LearningMode;
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:1999 "mz:$ARGS_VAR_X:^bla$|$URL_X:/foo";
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /foo?blaz=1999
|
||||
--- error_code: 412
|
||||
=== RXWL TEST 3.0: simple wide regex (url|args|name)
|
||||
--- user_files
|
||||
>>> foo
|
||||
eh yo
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:1999" "msg:foobar test pattern #1" "mz:ARGS" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
#LearningMode;
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:1999 "mz:$URL_X:/foo|ARGS|NAME";
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /foo?19991999=foo
|
||||
--- error_code: 200
|
||||
|
||||
=== RXWL TEST 3.1: simple wide regex (url|args|name)
|
||||
--- user_files
|
||||
>>> foo
|
||||
eh yo
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:1999" "msg:foobar test pattern #1" "mz:ARGS" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
#LearningMode;
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:1999 "mz:$URL_X:/foo|ARGS|NAME";
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /foo?foo=1999
|
||||
--- error_code: 412
|
||||
|
||||
=== RXWL TEST 4.0: simple restrictive+complex regex ($URL_X|URL)
|
||||
--- user_files
|
||||
>>> foo
|
||||
eh yo
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:1999" "msg:foobar test pattern #1" "mz:ARGS|URL" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
#LearningMode;
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:1999 "mz:$URL_X:^/foo_[0-9]+_$|URL";
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /foo_1999_?x=x
|
||||
--- error_code: 404
|
||||
=== RXWL TEST 4.1: simple restrictive+complex regex ($ARGS_VAR_X|NAME)
|
||||
--- user_files
|
||||
>>> foo
|
||||
eh yo
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:1999" "msg:foobar test pattern #1" "mz:ARGS|URL" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
#LearningMode;
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:1999 "mz:$ARGS_VAR_X:^foo_[0-9]+_$|NAME";
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?foo_1999_inject=x
|
||||
--- error_code: 412
|
||||
=== RXWL TEST 5.0: file ext ($URL|NAME) XXX
|
||||
--- user_files
|
||||
>>> foo
|
||||
eh yo
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:1999" "msg:foobar test pattern #1" "mz:ARGS|URL" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
#LearningMode;
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:1999 "mz:$ARGS_VAR_X:^foo_[0-9]+_$|NAME";
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?foo_1999_inject=x
|
||||
--- error_code: 412
|
||||
|
||||
=== RXWL TEST 6.0: case sensitiveness
|
||||
--- user_files
|
||||
>>> foo
|
||||
eh yo
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:abcd" "msg:foobar test pattern #1" "mz:ARGS|URL" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
#LearningMode;
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:1999 "mz:$ARGS_VAR_X:^foo_[0-9]+_$";
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?foo_1999_=ABCD
|
||||
--- error_code: 200
|
||||
99
naxsi-0.55.3/t/17case.t
vendored
Normal file
99
naxsi-0.55.3/t/17case.t
vendored
Normal file
@@ -0,0 +1,99 @@
|
||||
#vi:filetype=perl
|
||||
|
||||
|
||||
# A AJOUTER :
|
||||
# TEST CASE AVEC UNE REGLE SUR UN HEADER GENERIQUE
|
||||
# La même sur des arguments :)
|
||||
|
||||
use lib 'lib';
|
||||
use Test::Nginx::Socket;
|
||||
|
||||
plan tests => repeat_each(2) * blocks();
|
||||
no_root_location();
|
||||
no_long_string();
|
||||
$ENV{TEST_NGINX_SERVROOT} = server_root();
|
||||
run_tests();
|
||||
__DATA__
|
||||
=== WL TEST X.0: URL case sensitive wl
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:foobar" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
#LearningMode;
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:1999,1000 "mz:$URL:/foobar/tableDropdown|URL";
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /foobar/tableDropdown
|
||||
--- error_code: 404
|
||||
|
||||
=== WL TEST X.1: URL case sensitive wl
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:foobar" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
#LearningMode;
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:1000 "mz:$URL:/wp-content/plugins/ultimate-tinymce/tableDropdown/editor_plugin.js|URL";
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /wp-content/plugins/ultimate-tinymce/tableDropdown/editor_plugin.js
|
||||
--- error_code: 404
|
||||
=== WL TEST 6.3: Whitelists trying to provoke collisions
|
||||
--- user_files
|
||||
>>> buixor
|
||||
eh yo
|
||||
>>> bla
|
||||
eh yo
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:1998" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1998;
|
||||
MainRule "str:1999" "msg:foobar test pattern #2" "mz:ARGS" "s:$SQL:42" id:1999;
|
||||
--- config
|
||||
location / {
|
||||
#LearningMode;
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:1999 "mz:$ARGS_VAR:/bla";
|
||||
BasicRule wl:1998 "mz:$URL:/bla|ARGS";
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /bla?/bla=1999&bu=1998
|
||||
--- error_code: 200
|
||||
354
naxsi-0.55.3/t/18ids.t
vendored
Normal file
354
naxsi-0.55.3/t/18ids.t
vendored
Normal file
@@ -0,0 +1,354 @@
|
||||
use lib 'lib';
|
||||
use Test::Nginx::Socket;
|
||||
|
||||
plan tests => repeat_each(2) * blocks();
|
||||
no_root_location();
|
||||
no_long_string();
|
||||
$ENV{TEST_NGINX_SERVROOT} = server_root();
|
||||
run_tests();
|
||||
__DATA__
|
||||
=== ID TEST 1.0: Disabled IDs
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:1999" "msg:foobar test pattern #1" "mz:ARGS" "s:$SQL:42" id:1999;
|
||||
MainRule "str:1998" "msg:foobar test pattern #1" "mz:ARGS" "s:$SQL:42" id:1998;
|
||||
--- config
|
||||
location / {
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:1999;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?bla=1999
|
||||
--- error_code: 200
|
||||
=== ID TEST 1.1: Disabled IDs (fail)
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:1999" "msg:foobar test pattern #1" "mz:ARGS" "s:$SQL:42" id:1999;
|
||||
MainRule "str:1998" "msg:foobar test pattern #1" "mz:ARGS" "s:$SQL:42" id:1998;
|
||||
--- config
|
||||
location / {
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:1999;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?bla=1998
|
||||
--- error_code: 412
|
||||
=== ID TEST 1.2: Disabled negative IDs
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:1999" "msg:foobar test pattern #1" "mz:ARGS" "s:$SQL:42" id:1999;
|
||||
MainRule "str:1998" "msg:foobar test pattern #1" "mz:ARGS" "s:$SQL:42" id:1998;
|
||||
--- config
|
||||
location / {
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:-1999;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?bla=1998
|
||||
--- error_code: 200
|
||||
=== ID TEST 1.3: Disabled negative IDs (fail)
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:1999" "msg:foobar test pattern #1" "mz:ARGS" "s:$SQL:42" id:1999;
|
||||
MainRule "str:1998" "msg:foobar test pattern #1" "mz:ARGS" "s:$SQL:42" id:1998;
|
||||
--- config
|
||||
location / {
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:-1999;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?bla=1999
|
||||
--- error_code: 412
|
||||
=== ID TEST 1.4: Multiple Disabled negative IDs
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:1999" "msg:foobar test pattern #1" "mz:ARGS" "s:$SQL:42" id:1999;
|
||||
MainRule "str:1998" "msg:foobar test pattern #1" "mz:ARGS" "s:$SQL:42" id:1998;
|
||||
MainRule "str:1997" "msg:foobar test pattern #1" "mz:ARGS" "s:$SQL:42" id:1997;
|
||||
--- config
|
||||
location / {
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:-1999,-1998;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?bla=1997
|
||||
--- error_code: 200
|
||||
=== ID TEST 1.5: Multiple Disabled negative IDs
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:1999" "msg:foobar test pattern #1" "mz:ARGS" "s:$SQL:42" id:1999;
|
||||
MainRule "str:1998" "msg:foobar test pattern #1" "mz:ARGS" "s:$SQL:42" id:1998;
|
||||
MainRule "str:1997" "msg:foobar test pattern #1" "mz:ARGS" "s:$SQL:42" id:1997;
|
||||
--- config
|
||||
location / {
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:-1999,-1998;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?bla=1999
|
||||
--- error_code: 412
|
||||
|
||||
|
||||
=== ID TEST 2.0: BasicRule negative id test
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:1999" "msg:foobar test pattern #1" "mz:ARGS" "s:$SQL:42" id:1999;
|
||||
MainRule "str:1998" "msg:foobar test pattern #1" "mz:ARGS" "s:$SQL:42" id:1998;
|
||||
MainRule "str:1997" "msg:foobar test pattern #1" "mz:ARGS" "s:$SQL:42" id:1997;
|
||||
--- config
|
||||
location / {
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:-1999 "mz:$URL:/|$ARGS_VAR:foo";
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?foo=1999
|
||||
--- error_code: 412
|
||||
|
||||
|
||||
=== ID TEST 2.1: BasicRule negative id test (fail)
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:1999" "msg:foobar test pattern #1" "mz:ARGS" "s:$SQL:42" id:1999;
|
||||
MainRule "str:1998" "msg:foobar test pattern #1" "mz:ARGS" "s:$SQL:42" id:1998;
|
||||
MainRule "str:1997" "msg:foobar test pattern #1" "mz:ARGS" "s:$SQL:42" id:1997;
|
||||
--- config
|
||||
location / {
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:-1999 "mz:$URL:/|$ARGS_VAR:foo";
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?foo=1998
|
||||
--- error_code: 200
|
||||
|
||||
|
||||
=== ID TEST 2.2: BasicRule negative id test (fail on internal ID)
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:1999" "msg:foobar test pattern #1" "mz:ARGS" "s:$SQL:42" id:1999;
|
||||
MainRule "str:1998" "msg:foobar test pattern #1" "mz:ARGS" "s:$SQL:42" id:1998;
|
||||
MainRule "str:1997" "msg:foobar test pattern #1" "mz:ARGS" "s:$SQL:42" id:1997;
|
||||
--- config
|
||||
location / {
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:-1999 "mz:$URL:/|$ARGS_VAR:foo";
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?foo=a%00a
|
||||
--- error_code: 412
|
||||
|
||||
|
||||
=== ID TEST 3.0: Partial disabled whitelist
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:1999" "msg:foobar test pattern #1" "mz:ARGS|URL" "s:$SQL:42" id:1999;
|
||||
# MainRule "str:1998" "msg:foobar test pattern #1" "mz:ARGS" "s:$SQL:42" id:1998;
|
||||
# MainRule "str:1997" "msg:foobar test pattern #1" "mz:ARGS" "s:$SQL:42" id:1997;
|
||||
--- config
|
||||
location / {
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:1999 "mz:ARGS";
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /?foo=a1999a
|
||||
--- error_code: 200
|
||||
|
||||
=== ID TEST 3.1: Partial disabled whitelist (fail zone)
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:1999" "msg:foobar test pattern #1" "mz:ARGS|URL" "s:$SQL:42" id:1999;
|
||||
# MainRule "str:1998" "msg:foobar test pattern #1" "mz:ARGS" "s:$SQL:42" id:1998;
|
||||
# MainRule "str:1997" "msg:foobar test pattern #1" "mz:ARGS" "s:$SQL:42" id:1997;
|
||||
--- config
|
||||
location / {
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:1999 "mz:ARGS";
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- request
|
||||
GET /1999?foo=aa
|
||||
--- error_code: 412
|
||||
|
||||
=== ID TEST 4.0: header disabled rule
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:1998" "msg:foobar test pattern #1" "mz:HEADERS|ARGS" "s:$SQL:42" id:1998;
|
||||
--- config
|
||||
location / {
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- more_headers
|
||||
foo: 1998
|
||||
--- request
|
||||
GET /
|
||||
--- error_code: 412
|
||||
|
||||
=== ID TEST 4.1: header disabled rule wl
|
||||
--- main_config
|
||||
load_module /tmp/naxsi_ut/modules/ngx_http_naxsi_module.so;
|
||||
--- http_config
|
||||
include /tmp/naxsi_ut/naxsi_core.rules;
|
||||
MainRule "str:1998" "msg:foobar test pattern #1" "mz:HEADERS|ARGS" "s:$SQL:42" id:1998;
|
||||
--- config
|
||||
location / {
|
||||
SecRulesEnabled;
|
||||
DeniedUrl "/RequestDenied";
|
||||
CheckRule "$SQL >= 8" BLOCK;
|
||||
CheckRule "$RFI >= 8" BLOCK;
|
||||
CheckRule "$TRAVERSAL >= 4" BLOCK;
|
||||
CheckRule "$XSS >= 8" BLOCK;
|
||||
root $TEST_NGINX_SERVROOT/html/;
|
||||
index index.html index.htm;
|
||||
BasicRule wl:1998 "mz:HEADERS";
|
||||
}
|
||||
location /RequestDenied {
|
||||
return 412;
|
||||
}
|
||||
--- more_headers
|
||||
foo: 1998
|
||||
--- request
|
||||
GET /
|
||||
--- error_code: 200
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user