mirror of
https://github.com/owasp-modsecurity/ModSecurity.git
synced 2025-09-29 19:24:29 +03:00
Updates libinjection to v3.10.0
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright 2012,2013 Nick Galbreath
|
||||
* Copyright 2012,2016 Nick Galbreath
|
||||
* nickg@client9.com
|
||||
* BSD License -- see COPYING.txt for details
|
||||
*
|
||||
@@ -18,7 +18,7 @@
|
||||
#include "libinjection_sqli.h"
|
||||
#include "libinjection_sqli_data.h"
|
||||
|
||||
#define LIBINJECTION_VERSION "3.9.1"
|
||||
#define LIBINJECTION_VERSION "3.9.2"
|
||||
|
||||
#define LIBINJECTION_SQLI_TOKEN_SIZE sizeof(((stoken_t*)(0))->val)
|
||||
#define LIBINJECTION_SQLI_MAX_TOKENS 5
|
||||
@@ -112,15 +112,11 @@ memchr2(const char *haystack, size_t haystack_len, char c0, char c1)
|
||||
}
|
||||
|
||||
while (cur < last) {
|
||||
if (cur[0] == c0) {
|
||||
if (cur[1] == c1) {
|
||||
return cur;
|
||||
} else {
|
||||
cur += 2; /* (c0 == c1) ? 1 : 2; */
|
||||
}
|
||||
} else {
|
||||
cur += 1;
|
||||
/* safe since cur < len - 1 always */
|
||||
if (cur[0] == c0 && cur[1] == c1) {
|
||||
return cur;
|
||||
}
|
||||
cur += 1;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
@@ -191,11 +187,11 @@ static int char_is_white(char ch) {
|
||||
/* ' ' space is 0x32
|
||||
'\t 0x09 \011 horizontal tab
|
||||
'\n' 0x0a \012 new line
|
||||
'\v' 0x0b \013 verical tab
|
||||
'\v' 0x0b \013 vertical tab
|
||||
'\f' 0x0c \014 new page
|
||||
'\r' 0x0d \015 carriage return
|
||||
0x00 \000 null (oracle)
|
||||
0xa0 \240 is latin1
|
||||
0xa0 \240 is Latin-1
|
||||
*/
|
||||
return strchr(" \t\n\v\f\r\240\000", ch) != NULL;
|
||||
}
|
||||
@@ -294,7 +290,7 @@ static void st_clear(stoken_t * st)
|
||||
static void st_assign_char(stoken_t * st, const char stype, size_t pos, size_t len,
|
||||
const char value)
|
||||
{
|
||||
/* done to elimiate unused warning */
|
||||
/* done to eliminate unused warning */
|
||||
(void)len;
|
||||
st->type = (char) stype;
|
||||
st->pos = pos;
|
||||
@@ -402,7 +398,7 @@ static size_t parse_eol_comment(struct libinjection_sqli_state * sf)
|
||||
}
|
||||
}
|
||||
|
||||
/** In Ansi mode, hash is an operator
|
||||
/** In ANSI mode, hash is an operator
|
||||
* In MYSQL mode, it's a EOL comment like '--'
|
||||
*/
|
||||
static size_t parse_hash(struct libinjection_sqli_state * sf)
|
||||
@@ -842,7 +838,7 @@ static size_t parse_bstring(struct libinjection_sqli_state *sf)
|
||||
|
||||
/*
|
||||
* hex literal string
|
||||
* re: [XX]'[0123456789abcdefABCDEF]*'
|
||||
* re: [xX]'[0123456789abcdefABCDEF]*'
|
||||
* mysql has requirement of having EVEN number of chars,
|
||||
* but pgsql does not
|
||||
*/
|
||||
@@ -1072,7 +1068,7 @@ static size_t parse_money(struct libinjection_sqli_state *sf)
|
||||
/* we have $foobar$ ... find it again */
|
||||
strend = my_memmem(cs+xlen+2, slen - (pos+xlen+2), cs + pos, xlen+2);
|
||||
|
||||
if (strend == NULL) {
|
||||
if (strend == NULL || ((size_t)(strend - cs) < (pos+xlen+2))) {
|
||||
/* fell off edge */
|
||||
st_assign(sf->current, TYPE_STRING, pos+xlen+2, slen - pos - xlen - 2, cs+pos+xlen+2);
|
||||
sf->current->str_open = '$';
|
||||
@@ -1104,7 +1100,6 @@ static size_t parse_number(struct libinjection_sqli_state * sf)
|
||||
const char *cs = sf->s;
|
||||
const size_t slen = sf->slen;
|
||||
size_t pos = sf->pos;
|
||||
int have_dot = 0;
|
||||
int have_e = 0;
|
||||
int have_exp = 0;
|
||||
|
||||
@@ -1136,7 +1131,6 @@ static size_t parse_number(struct libinjection_sqli_state * sf)
|
||||
}
|
||||
|
||||
if (pos < slen && cs[pos] == '.') {
|
||||
have_dot = 1;
|
||||
pos += 1;
|
||||
while (pos < slen && ISDIGIT(cs[pos])) {
|
||||
pos += 1;
|
||||
@@ -1185,7 +1179,7 @@ static size_t parse_number(struct libinjection_sqli_state * sf)
|
||||
}
|
||||
}
|
||||
|
||||
if (have_dot == 1 && have_e == 1 && have_exp == 0) {
|
||||
if (have_e == 1 && have_exp == 0) {
|
||||
/* very special form of
|
||||
* "1234.e"
|
||||
* "10.10E"
|
||||
@@ -1242,29 +1236,13 @@ int libinjection_sqli_tokenize(struct libinjection_sqli_state * sf)
|
||||
const unsigned char ch = (unsigned char) (s[*pos]);
|
||||
|
||||
/*
|
||||
* if not ascii, then continue...
|
||||
* actually probably need to just assuming
|
||||
* it's a string
|
||||
* look up the parser, and call it
|
||||
*
|
||||
* Porting Note: this is mapping of char to function
|
||||
* charparsers[ch]()
|
||||
*/
|
||||
if (ch > 127) {
|
||||
fnptr = char_parse_map[ch];
|
||||
|
||||
/* 160 or 0xA0 or octal 240 is "latin1 non-breaking space"
|
||||
* but is treated as a space in mysql.
|
||||
*/
|
||||
if (ch == 160) {
|
||||
fnptr = parse_white;
|
||||
} else {
|
||||
fnptr = parse_word;
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* look up the parser, and call it
|
||||
*
|
||||
* Porting Note: this is mapping of char to function
|
||||
* charparsers[ch]()
|
||||
*/
|
||||
fnptr = char_parse_map[ch];
|
||||
}
|
||||
*pos = (*fnptr) (sf);
|
||||
|
||||
/*
|
||||
@@ -1349,16 +1327,22 @@ static int syntax_merge_words(struct libinjection_sqli_state * sf,stoken_t * a,
|
||||
a->type == TYPE_UNION ||
|
||||
a->type == TYPE_FUNCTION ||
|
||||
a->type == TYPE_EXPRESSION ||
|
||||
a->type == TYPE_TSQL ||
|
||||
a->type == TYPE_SQLTYPE)) {
|
||||
return CHAR_NULL;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (b->type != TYPE_KEYWORD && b->type != TYPE_BAREWORD &&
|
||||
b->type != TYPE_OPERATOR && b->type != TYPE_SQLTYPE &&
|
||||
b->type != TYPE_LOGIC_OPERATOR &&
|
||||
b->type != TYPE_FUNCTION &&
|
||||
b->type != TYPE_UNION && b->type != TYPE_EXPRESSION) {
|
||||
return CHAR_NULL;
|
||||
if (!
|
||||
(b->type == TYPE_KEYWORD ||
|
||||
b->type == TYPE_BAREWORD ||
|
||||
b->type == TYPE_OPERATOR ||
|
||||
b->type == TYPE_UNION ||
|
||||
b->type == TYPE_FUNCTION ||
|
||||
b->type == TYPE_EXPRESSION ||
|
||||
b->type == TYPE_TSQL ||
|
||||
b->type == TYPE_SQLTYPE ||
|
||||
b->type == TYPE_LOGIC_OPERATOR)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
sz1 = a->len;
|
||||
@@ -1374,7 +1358,6 @@ static int syntax_merge_words(struct libinjection_sqli_state * sf,stoken_t * a,
|
||||
tmp[sz1] = ' ';
|
||||
memcpy(tmp + sz1 + 1, b->val, sz2);
|
||||
tmp[sz3] = CHAR_NULL;
|
||||
|
||||
ch = sf->lookup(sf, LOOKUP_WORD, tmp, sz3);
|
||||
|
||||
if (ch != CHAR_NULL) {
|
||||
@@ -1450,6 +1433,13 @@ int libinjection_sqli_fold(struct libinjection_sqli_state * sf)
|
||||
sf->tokenvec[2].type == TYPE_COMMA &&
|
||||
sf->tokenvec[3].type == TYPE_LEFTPARENS &&
|
||||
sf->tokenvec[4].type == TYPE_NUMBER
|
||||
) ||
|
||||
(
|
||||
sf->tokenvec[0].type == TYPE_BAREWORD &&
|
||||
sf->tokenvec[1].type == TYPE_RIGHTPARENS &&
|
||||
sf->tokenvec[2].type == TYPE_OPERATOR &&
|
||||
sf->tokenvec[3].type == TYPE_LEFTPARENS &&
|
||||
sf->tokenvec[4].type == TYPE_BAREWORD
|
||||
)
|
||||
)
|
||||
{
|
||||
@@ -1506,16 +1496,6 @@ int libinjection_sqli_fold(struct libinjection_sqli_state * sf)
|
||||
pos -= 1;
|
||||
sf->stats_folds += 1;
|
||||
continue;
|
||||
} else if (sf->tokenvec[left].type == TYPE_SEMICOLON &&
|
||||
sf->tokenvec[left+1].type == TYPE_FUNCTION &&
|
||||
cstrcasecmp("IF", sf->tokenvec[left+1].val, sf->tokenvec[left+1].len) == 0) {
|
||||
/* IF is normally a function, except in Transact-SQL where it can be used as a
|
||||
* standalone control flow operator, e.g. ; IF 1=1 ...
|
||||
* if found after a semicolon, convert from 'f' type to 'T' type
|
||||
*/
|
||||
sf->tokenvec[left+1].type = TYPE_TSQL;
|
||||
left += 2;
|
||||
continue; /* reparse everything, but we probably can advance left, and pos */
|
||||
} else if ((sf->tokenvec[left].type == TYPE_OPERATOR ||
|
||||
sf->tokenvec[left].type == TYPE_LOGIC_OPERATOR) &&
|
||||
(st_is_unary_op(&sf->tokenvec[left+1]) ||
|
||||
@@ -1539,9 +1519,22 @@ int libinjection_sqli_fold(struct libinjection_sqli_state * sf)
|
||||
left -= 1;
|
||||
}
|
||||
continue;
|
||||
} else if (sf->tokenvec[left].type == TYPE_SEMICOLON &&
|
||||
sf->tokenvec[left+1].type == TYPE_FUNCTION &&
|
||||
(sf->tokenvec[left+1].val[0] == 'I' ||
|
||||
sf->tokenvec[left+1].val[0] == 'i' ) &&
|
||||
(sf->tokenvec[left+1].val[1] == 'F' ||
|
||||
sf->tokenvec[left+1].val[1] == 'f' )) {
|
||||
/* IF is normally a function, except in Transact-SQL where it can be used as a
|
||||
* standalone control flow operator, e.g. ; IF 1=1 ...
|
||||
* if found after a semicolon, convert from 'f' type to 'T' type
|
||||
*/
|
||||
sf->tokenvec[left+1].type = TYPE_TSQL;
|
||||
/* left += 2; */
|
||||
continue; /* reparse everything, but we probably can advance left, and pos */
|
||||
} else if ((sf->tokenvec[left].type == TYPE_BAREWORD || sf->tokenvec[left].type == TYPE_VARIABLE) &&
|
||||
sf->tokenvec[left+1].type == TYPE_LEFTPARENS && (
|
||||
/* TSQL functions but common enough to be collumn names */
|
||||
/* TSQL functions but common enough to be column names */
|
||||
cstrcasecmp("USER_ID", sf->tokenvec[left].val, sf->tokenvec[left].len) == 0 ||
|
||||
cstrcasecmp("USER_NAME", sf->tokenvec[left].val, sf->tokenvec[left].len) == 0 ||
|
||||
|
||||
@@ -1564,7 +1557,7 @@ int libinjection_sqli_fold(struct libinjection_sqli_state * sf)
|
||||
|
||||
/* pos is the same
|
||||
* other conversions need to go here... for instance
|
||||
* password CAN be a function, coalese CAN be a function
|
||||
* password CAN be a function, coalesce CAN be a function
|
||||
*/
|
||||
sf->tokenvec[left].type = TYPE_FUNCTION;
|
||||
continue;
|
||||
@@ -1828,7 +1821,7 @@ int libinjection_sqli_fold(struct libinjection_sqli_state * sf)
|
||||
* 1,-sin(1) --> 1 (1)
|
||||
* Here, just do
|
||||
* 1,-sin(1) --> 1,sin(1)
|
||||
* just remove unary opartor
|
||||
* just remove unary operator
|
||||
*/
|
||||
st_copy(&sf->tokenvec[left+1], &sf->tokenvec[left+2]);
|
||||
pos -= 1;
|
||||
@@ -1852,9 +1845,21 @@ int libinjection_sqli_fold(struct libinjection_sqli_state * sf)
|
||||
pos -= 1;
|
||||
left = 0;
|
||||
continue;
|
||||
} else if ((sf->tokenvec[left].type == TYPE_FUNCTION) &&
|
||||
(sf->tokenvec[left+1].type == TYPE_LEFTPARENS) &&
|
||||
(sf->tokenvec[left+2].type != TYPE_RIGHTPARENS)) {
|
||||
/*
|
||||
* whats going on here
|
||||
* Some SQL functions like USER() have 0 args
|
||||
* if we get User(foo), then User is not a function
|
||||
* This should be expanded since it eliminated a lot of false
|
||||
* positives.
|
||||
*/
|
||||
if (cstrcasecmp("USER", sf->tokenvec[left].val, sf->tokenvec[left].len) == 0) {
|
||||
sf->tokenvec[left].type = TYPE_BAREWORD;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* no folding -- assume left-most token is
|
||||
is good, now use the existing 2 tokens --
|
||||
do not get another
|
||||
@@ -2019,7 +2024,7 @@ int libinjection_sqli_blacklist(struct libinjection_sqli_state* sql_state)
|
||||
}
|
||||
|
||||
/*
|
||||
* return TRUE if sqli, false is benign
|
||||
* return TRUE if SQLi, false is benign
|
||||
*/
|
||||
int libinjection_sqli_not_whitelist(struct libinjection_sqli_state* sql_state)
|
||||
{
|
||||
@@ -2033,10 +2038,10 @@ int libinjection_sqli_not_whitelist(struct libinjection_sqli_state* sql_state)
|
||||
|
||||
if (tlen > 1 && sql_state->fingerprint[tlen-1] == TYPE_COMMENT) {
|
||||
/*
|
||||
* if ending comment is contains 'sp_password' then it's sqli!
|
||||
* if ending comment is contains 'sp_password' then it's SQLi!
|
||||
* MS Audit log apparently ignores anything with
|
||||
* 'sp_password' in it. Unable to find primary refernece to
|
||||
* this "feature" of SQL Server but seems to be known sqli
|
||||
* 'sp_password' in it. Unable to find primary reference to
|
||||
* this "feature" of SQL Server but seems to be known SQLi
|
||||
* technique
|
||||
*/
|
||||
if (my_memmem(sql_state->s, sql_state->slen,
|
||||
@@ -2055,7 +2060,7 @@ int libinjection_sqli_not_whitelist(struct libinjection_sqli_state* sql_state)
|
||||
|
||||
if (sql_state->fingerprint[1] == TYPE_UNION) {
|
||||
if (sql_state->stats_tokens == 2) {
|
||||
/* not sure why but 1U comes up in Sqli attack
|
||||
/* not sure why but 1U comes up in SQLi attack
|
||||
* likely part of parameter splitting/etc.
|
||||
* lots of reasons why "1 union" might be normal
|
||||
* input, so beep only if other SQLi things are present
|
||||
@@ -2080,7 +2085,7 @@ int libinjection_sqli_not_whitelist(struct libinjection_sqli_state* sql_state)
|
||||
|
||||
/*
|
||||
* for fingerprint like 'nc', only comments of /x are treated
|
||||
* as SQL... ending comments of "--" and "#" are not sqli
|
||||
* as SQL... ending comments of "--" and "#" are not SQLi
|
||||
*/
|
||||
if (sql_state->tokenvec[0].type == TYPE_BAREWORD &&
|
||||
sql_state->tokenvec[1].type == TYPE_COMMENT &&
|
||||
@@ -2090,7 +2095,7 @@ int libinjection_sqli_not_whitelist(struct libinjection_sqli_state* sql_state)
|
||||
}
|
||||
|
||||
/*
|
||||
* if '1c' ends with '/x' then it's sqli
|
||||
* if '1c' ends with '/x' then it's SQLi
|
||||
*/
|
||||
if (sql_state->tokenvec[0].type == TYPE_NUMBER &&
|
||||
sql_state->tokenvec[1].type == TYPE_COMMENT &&
|
||||
@@ -2113,13 +2118,13 @@ int libinjection_sqli_not_whitelist(struct libinjection_sqli_state* sql_state)
|
||||
if (sql_state->tokenvec[0].type == TYPE_NUMBER &&
|
||||
sql_state->tokenvec[1].type == TYPE_COMMENT) {
|
||||
if (sql_state->stats_tokens > 2) {
|
||||
/* we have some folding going on, highly likely sqli */
|
||||
/* we have some folding going on, highly likely SQLi */
|
||||
sql_state->reason = __LINE__;
|
||||
return TRUE;
|
||||
}
|
||||
/*
|
||||
* we check that next character after the number is either whitespace,
|
||||
* or '/' or a '-' ==> sqli.
|
||||
* or '/' or a '-' ==> SQLi.
|
||||
*/
|
||||
ch = sql_state->s[sql_state->tokenvec[0].len];
|
||||
if ( ch <= 32 ) {
|
||||
@@ -2141,7 +2146,7 @@ int libinjection_sqli_not_whitelist(struct libinjection_sqli_state* sql_state)
|
||||
}
|
||||
|
||||
/*
|
||||
* detect obvious sqli scans.. many people put '--' in plain text
|
||||
* detect obvious SQLi scans.. many people put '--' in plain text
|
||||
* so only detect if input ends with '--', e.g. 1-- but not 1-- foo
|
||||
*/
|
||||
if ((sql_state->tokenvec[1].len > 2)
|
||||
@@ -2177,7 +2182,7 @@ int libinjection_sqli_not_whitelist(struct libinjection_sqli_state* sql_state)
|
||||
}
|
||||
|
||||
/*
|
||||
* not sqli
|
||||
* not SQLi
|
||||
*/
|
||||
sql_state->reason = __LINE__;
|
||||
return FALSE;
|
||||
@@ -2186,8 +2191,8 @@ int libinjection_sqli_not_whitelist(struct libinjection_sqli_state* sql_state)
|
||||
streq(sql_state->fingerprint, "1&1") ||
|
||||
streq(sql_state->fingerprint, "1&v") ||
|
||||
streq(sql_state->fingerprint, "1&s")) {
|
||||
/* 'sexy and 17' not sqli
|
||||
* 'sexy and 17<18' sqli
|
||||
/* 'sexy and 17' not SQLi
|
||||
* 'sexy and 17<18' SQLi
|
||||
*/
|
||||
if (sql_state->stats_tokens == 3) {
|
||||
sql_state->reason = __LINE__;
|
||||
@@ -2243,7 +2248,7 @@ int libinjection_is_sqli(struct libinjection_sqli_state * sql_state)
|
||||
size_t slen = sql_state->slen;
|
||||
|
||||
/*
|
||||
* no input? not sqli
|
||||
* no input? not SQLi
|
||||
*/
|
||||
if (slen == 0) {
|
||||
return FALSE;
|
||||
|
Reference in New Issue
Block a user