Initial Commit
This commit is contained in:
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user