mirror of
https://github.com/owasp-modsecurity/ModSecurity.git
synced 2025-08-16 07:56:12 +03:00
Updates libinjection.
This is not yet their v3.10.0. But I belive it is close to be. See #124 at client9/libinjection for further information.
This commit is contained in:
parent
e5dbe59336
commit
53571a860d
2
CHANGES
2
CHANGES
@ -1,6 +1,8 @@
|
|||||||
DD MMM YYYY - 2.9.2 - To be released
|
DD MMM YYYY - 2.9.2 - To be released
|
||||||
------------------------------------
|
------------------------------------
|
||||||
|
|
||||||
|
* Updates libinjection to: bf234eb2f385b969c4f803b35fda53cffdd93922
|
||||||
|
[Issue #1412 - @zimmerle, @bjdijk]
|
||||||
* Avoid log flood while using SecConnEngine
|
* Avoid log flood while using SecConnEngine
|
||||||
[Issue #1436 - @victorhora]
|
[Issue #1436 - @victorhora]
|
||||||
* Make url path absolute for SecHashEngine only when it is relative
|
* Make url path absolute for SecHashEngine only when it is relative
|
||||||
|
@ -12,7 +12,6 @@
|
|||||||
|
|
||||||
|
|
||||||
#define CHAR_EOF -1
|
#define CHAR_EOF -1
|
||||||
#define CHAR_NULL 0
|
|
||||||
#define CHAR_BANG 33
|
#define CHAR_BANG 33
|
||||||
#define CHAR_DOUBLE 34
|
#define CHAR_DOUBLE 34
|
||||||
#define CHAR_PERCENT 37
|
#define CHAR_PERCENT 37
|
||||||
@ -24,7 +23,6 @@
|
|||||||
#define CHAR_GT 62
|
#define CHAR_GT 62
|
||||||
#define CHAR_QUESTION 63
|
#define CHAR_QUESTION 63
|
||||||
#define CHAR_RIGHTB 93
|
#define CHAR_RIGHTB 93
|
||||||
#define CHAR_TICK 96
|
|
||||||
|
|
||||||
/* prototypes */
|
/* prototypes */
|
||||||
|
|
||||||
@ -43,7 +41,6 @@ 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_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_double_quote(h5_state_t* hs);
|
||||||
static int h5_state_attribute_value_single_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_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_after_attribute_value_quoted_state(h5_state_t* hs);
|
||||||
static int h5_state_comment(h5_state_t* hs);
|
static int h5_state_comment(h5_state_t* hs);
|
||||||
@ -63,28 +60,16 @@ static int h5_state_doctype(h5_state_t* hs);
|
|||||||
/**
|
/**
|
||||||
* public function
|
* public function
|
||||||
*/
|
*/
|
||||||
void libinjection_h5_init(h5_state_t* hs, const char* s, size_t len, enum html5_flags flags)
|
void libinjection_h5_init(h5_state_t* hs, const char* s, size_t len, int flags)
|
||||||
{
|
{
|
||||||
memset(hs, 0, sizeof(h5_state_t));
|
memset(hs, 0, sizeof(h5_state_t));
|
||||||
hs->s = s;
|
hs->s = s;
|
||||||
hs->len = len;
|
hs->len = len;
|
||||||
|
|
||||||
switch (flags) {
|
|
||||||
case DATA_STATE:
|
|
||||||
hs->state = h5_state_data;
|
hs->state = h5_state_data;
|
||||||
break;
|
if (flags == 0) {
|
||||||
case VALUE_NO_QUOTE:
|
hs->state = h5_state_data;
|
||||||
hs->state = h5_state_before_attribute_name;
|
} else {
|
||||||
break;
|
assert(0);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,18 +85,10 @@ int libinjection_h5_next(h5_state_t* hs)
|
|||||||
/**
|
/**
|
||||||
* Everything below here is private
|
* Everything below here is private
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
static int h5_is_white(char ch)
|
static int h5_is_white(char ch)
|
||||||
{
|
{
|
||||||
/*
|
|
||||||
* \t = horizontal tab = 0x09
|
|
||||||
* \n = newline = 0x0A
|
|
||||||
* \v = vertical tab = 0x0B
|
|
||||||
* \f = form feed = 0x0C
|
|
||||||
* \r = cr = 0x0D
|
|
||||||
*/
|
|
||||||
return strchr(" \t\n\v\f\r", ch) != NULL;
|
return strchr(" \t\n\v\f\r", ch) != NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,17 +97,9 @@ static int h5_skip_white(h5_state_t* hs)
|
|||||||
char ch;
|
char ch;
|
||||||
while (hs->pos < hs->len) {
|
while (hs->pos < hs->len) {
|
||||||
ch = hs->s[hs->pos];
|
ch = hs->s[hs->pos];
|
||||||
switch (ch) {
|
if (ch == ' ') {
|
||||||
case 0x00: /* IE only */
|
|
||||||
case 0x20:
|
|
||||||
case 0x09:
|
|
||||||
case 0x0A:
|
|
||||||
case 0x0B: /* IE only */
|
|
||||||
case 0x0C:
|
|
||||||
case 0x0D: /* IE only */
|
|
||||||
hs->pos += 1;
|
hs->pos += 1;
|
||||||
break;
|
} else {
|
||||||
default:
|
|
||||||
return ch;
|
return ch;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -198,9 +167,6 @@ static int h5_state_tag_open(h5_state_t* hs)
|
|||||||
return h5_state_bogus_comment2(hs);
|
return h5_state_bogus_comment2(hs);
|
||||||
} else if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) {
|
} else if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) {
|
||||||
return h5_state_tag_name(hs);
|
return h5_state_tag_name(hs);
|
||||||
} else if (ch == CHAR_NULL) {
|
|
||||||
/* IE-ism NULL characters are ignored */
|
|
||||||
return h5_state_tag_name(hs);
|
|
||||||
} else {
|
} else {
|
||||||
/* user input mistake in configuring state */
|
/* user input mistake in configuring state */
|
||||||
if (hs->pos == 0) {
|
if (hs->pos == 0) {
|
||||||
@ -231,9 +197,7 @@ static int h5_state_end_tag_open(h5_state_t* hs)
|
|||||||
} else if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) {
|
} else if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) {
|
||||||
return h5_state_tag_name(hs);
|
return h5_state_tag_name(hs);
|
||||||
}
|
}
|
||||||
|
return h5_state_data(hs);
|
||||||
hs->is_close = 0;
|
|
||||||
return h5_state_bogus_comment(hs);
|
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
*
|
*
|
||||||
@ -267,12 +231,7 @@ static int h5_state_tag_name(h5_state_t* hs)
|
|||||||
pos = hs->pos;
|
pos = hs->pos;
|
||||||
while (pos < hs->len) {
|
while (pos < hs->len) {
|
||||||
ch = hs->s[pos];
|
ch = hs->s[pos];
|
||||||
if (ch == 0) {
|
if (h5_is_white(ch)) {
|
||||||
/* 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_start = hs->s + hs->pos;
|
||||||
hs->token_len = pos - hs->pos;
|
hs->token_len = pos - hs->pos;
|
||||||
hs->token_type = TAG_NAME_OPEN;
|
hs->token_type = TAG_NAME_OPEN;
|
||||||
@ -349,7 +308,7 @@ static int h5_state_attribute_name(h5_state_t* hs)
|
|||||||
size_t pos;
|
size_t pos;
|
||||||
|
|
||||||
TRACE();
|
TRACE();
|
||||||
pos = hs->pos + 1;
|
pos = hs->pos;
|
||||||
while (pos < hs->len) {
|
while (pos < hs->len) {
|
||||||
ch = hs->s[pos];
|
ch = hs->s[pos];
|
||||||
if (h5_is_white(ch)) {
|
if (h5_is_white(ch)) {
|
||||||
@ -399,19 +358,21 @@ 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_after_attribute_name(h5_state_t* hs)
|
||||||
{
|
{
|
||||||
int c;
|
int c;
|
||||||
|
size_t pos;
|
||||||
|
|
||||||
TRACE();
|
TRACE();
|
||||||
|
pos = hs->pos;
|
||||||
c = h5_skip_white(hs);
|
c = h5_skip_white(hs);
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case CHAR_EOF: {
|
case CHAR_EOF: {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
case CHAR_SLASH: {
|
case CHAR_SLASH: {
|
||||||
hs->pos += 1;
|
hs->pos = pos + 1;
|
||||||
return h5_state_self_closing_start_tag(hs);
|
return h5_state_self_closing_start_tag(hs);
|
||||||
}
|
}
|
||||||
case CHAR_EQUALS: {
|
case CHAR_EQUALS: {
|
||||||
hs->pos += 1;
|
hs->pos = pos + 1;
|
||||||
return h5_state_before_attribute_value(hs);
|
return h5_state_before_attribute_value(hs);
|
||||||
}
|
}
|
||||||
case CHAR_GT: {
|
case CHAR_GT: {
|
||||||
@ -442,9 +403,6 @@ static int h5_state_before_attribute_value(h5_state_t* hs)
|
|||||||
return h5_state_attribute_value_double_quote(hs);
|
return h5_state_attribute_value_double_quote(hs);
|
||||||
} else if (c == CHAR_SINGLE) {
|
} else if (c == CHAR_SINGLE) {
|
||||||
return h5_state_attribute_value_single_quote(hs);
|
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 {
|
} else {
|
||||||
return h5_state_attribute_value_no_quote(hs);
|
return h5_state_attribute_value_no_quote(hs);
|
||||||
}
|
}
|
||||||
@ -457,16 +415,8 @@ static int h5_state_attribute_value_quote(h5_state_t* hs, char qchar)
|
|||||||
|
|
||||||
TRACE();
|
TRACE();
|
||||||
|
|
||||||
/* skip initial quote in normal case.
|
/* skip quote */
|
||||||
* don't do this "if (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;
|
hs->pos += 1;
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
idx = (const char*) memchr(hs->s + hs->pos, qchar, hs->len - hs->pos);
|
idx = (const char*) memchr(hs->s + hs->pos, qchar, hs->len - hs->pos);
|
||||||
if (idx == NULL) {
|
if (idx == NULL) {
|
||||||
hs->token_start = hs->s + hs->pos;
|
hs->token_start = hs->s + hs->pos;
|
||||||
@ -497,13 +447,6 @@ int h5_state_attribute_value_single_quote(h5_state_t* hs)
|
|||||||
return h5_state_attribute_value_quote(hs, CHAR_SINGLE);
|
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)
|
static int h5_state_attribute_value_no_quote(h5_state_t* hs)
|
||||||
{
|
{
|
||||||
char ch;
|
char ch;
|
||||||
@ -713,13 +656,10 @@ static int h5_state_comment(h5_state_t* hs)
|
|||||||
char ch;
|
char ch;
|
||||||
const char* idx;
|
const char* idx;
|
||||||
size_t pos;
|
size_t pos;
|
||||||
size_t offset;
|
|
||||||
const char* end = hs->s + hs->len;
|
|
||||||
|
|
||||||
TRACE();
|
TRACE();
|
||||||
pos = hs->pos;
|
pos = hs->pos;
|
||||||
while (1) {
|
while (1) {
|
||||||
|
|
||||||
idx = (const char*) memchr(hs->s + pos, CHAR_DASH, hs->len - pos);
|
idx = (const char*) memchr(hs->s + pos, CHAR_DASH, hs->len - pos);
|
||||||
|
|
||||||
/* did not find anything or has less than 3 chars left */
|
/* did not find anything or has less than 3 chars left */
|
||||||
@ -730,62 +670,21 @@ static int h5_state_comment(h5_state_t* hs)
|
|||||||
hs->token_type = TAG_COMMENT;
|
hs->token_type = TAG_COMMENT;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
offset = 1;
|
ch = *(idx + 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) {
|
if (ch != CHAR_DASH && ch != CHAR_BANG) {
|
||||||
pos = (size_t)(idx - hs->s) + 1;
|
pos = (size_t)(idx - hs->s) + 1;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
ch = *(idx + 2);
|
||||||
/* 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) {
|
if (ch != CHAR_GT) {
|
||||||
pos = (size_t)(idx - hs->s) + 1;
|
pos = (size_t)(idx - hs->s) + 1;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
offset += 1;
|
|
||||||
|
|
||||||
/* ends in --> or -!> */
|
/* ends in --> or -!> */
|
||||||
hs->token_start = hs->s + hs->pos;
|
hs->token_start = hs->s + hs->pos;
|
||||||
hs->token_len = (size_t)(idx - hs->s) - hs->pos;
|
hs->token_len = (size_t)(idx - hs->s) - hs->pos;
|
||||||
hs->pos = (size_t)(idx + offset - hs->s);
|
hs->pos = (size_t)(idx - hs->s) + 3;
|
||||||
hs->state = h5_state_data;
|
hs->state = h5_state_data;
|
||||||
hs->token_type = TAG_COMMENT;
|
hs->token_type = TAG_COMMENT;
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* Copyright 2012,2016 Nick Galbreath
|
* Copyright 2012,2013 Nick Galbreath
|
||||||
* nickg@client9.com
|
* nickg@client9.com
|
||||||
* BSD License -- see COPYING.txt for details
|
* BSD License -- see COPYING.txt for details
|
||||||
*
|
*
|
||||||
@ -112,12 +112,16 @@ memchr2(const char *haystack, size_t haystack_len, char c0, char c1)
|
|||||||
}
|
}
|
||||||
|
|
||||||
while (cur < last) {
|
while (cur < last) {
|
||||||
/* safe since cur < len - 1 always */
|
if (cur[0] == c0) {
|
||||||
if (cur[0] == c0 && cur[1] == c1) {
|
if (cur[1] == c1) {
|
||||||
return cur;
|
return cur;
|
||||||
|
} else {
|
||||||
|
cur += 2; /* (c0 == c1) ? 1 : 2; */
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
cur += 1;
|
cur += 1;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@ -187,11 +191,11 @@ static int char_is_white(char ch) {
|
|||||||
/* ' ' space is 0x32
|
/* ' ' space is 0x32
|
||||||
'\t 0x09 \011 horizontal tab
|
'\t 0x09 \011 horizontal tab
|
||||||
'\n' 0x0a \012 new line
|
'\n' 0x0a \012 new line
|
||||||
'\v' 0x0b \013 vertical tab
|
'\v' 0x0b \013 verical tab
|
||||||
'\f' 0x0c \014 new page
|
'\f' 0x0c \014 new page
|
||||||
'\r' 0x0d \015 carriage return
|
'\r' 0x0d \015 carriage return
|
||||||
0x00 \000 null (oracle)
|
0x00 \000 null (oracle)
|
||||||
0xa0 \240 is Latin-1
|
0xa0 \240 is latin1
|
||||||
*/
|
*/
|
||||||
return strchr(" \t\n\v\f\r\240\000", ch) != NULL;
|
return strchr(" \t\n\v\f\r\240\000", ch) != NULL;
|
||||||
}
|
}
|
||||||
@ -290,7 +294,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,
|
static void st_assign_char(stoken_t * st, const char stype, size_t pos, size_t len,
|
||||||
const char value)
|
const char value)
|
||||||
{
|
{
|
||||||
/* done to eliminate unused warning */
|
/* done to elimiate unused warning */
|
||||||
(void)len;
|
(void)len;
|
||||||
st->type = (char) stype;
|
st->type = (char) stype;
|
||||||
st->pos = pos;
|
st->pos = pos;
|
||||||
@ -398,7 +402,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 '--'
|
* In MYSQL mode, it's a EOL comment like '--'
|
||||||
*/
|
*/
|
||||||
static size_t parse_hash(struct libinjection_sqli_state * sf)
|
static size_t parse_hash(struct libinjection_sqli_state * sf)
|
||||||
@ -838,7 +842,7 @@ static size_t parse_bstring(struct libinjection_sqli_state *sf)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* hex literal string
|
* hex literal string
|
||||||
* re: [xX]'[0123456789abcdefABCDEF]*'
|
* re: [XX]'[0123456789abcdefABCDEF]*'
|
||||||
* mysql has requirement of having EVEN number of chars,
|
* mysql has requirement of having EVEN number of chars,
|
||||||
* but pgsql does not
|
* but pgsql does not
|
||||||
*/
|
*/
|
||||||
@ -1068,7 +1072,7 @@ static size_t parse_money(struct libinjection_sqli_state *sf)
|
|||||||
/* we have $foobar$ ... find it again */
|
/* we have $foobar$ ... find it again */
|
||||||
strend = my_memmem(cs+xlen+2, slen - (pos+xlen+2), cs + pos, xlen+2);
|
strend = my_memmem(cs+xlen+2, slen - (pos+xlen+2), cs + pos, xlen+2);
|
||||||
|
|
||||||
if (strend == NULL || ((size_t)(strend - cs) < (pos+xlen+2))) {
|
if (strend == NULL) {
|
||||||
/* fell off edge */
|
/* fell off edge */
|
||||||
st_assign(sf->current, TYPE_STRING, pos+xlen+2, slen - pos - xlen - 2, cs+pos+xlen+2);
|
st_assign(sf->current, TYPE_STRING, pos+xlen+2, slen - pos - xlen - 2, cs+pos+xlen+2);
|
||||||
sf->current->str_open = '$';
|
sf->current->str_open = '$';
|
||||||
@ -1100,6 +1104,7 @@ static size_t parse_number(struct libinjection_sqli_state * sf)
|
|||||||
const char *cs = sf->s;
|
const char *cs = sf->s;
|
||||||
const size_t slen = sf->slen;
|
const size_t slen = sf->slen;
|
||||||
size_t pos = sf->pos;
|
size_t pos = sf->pos;
|
||||||
|
int have_dot = 0;
|
||||||
int have_e = 0;
|
int have_e = 0;
|
||||||
int have_exp = 0;
|
int have_exp = 0;
|
||||||
|
|
||||||
@ -1131,6 +1136,7 @@ static size_t parse_number(struct libinjection_sqli_state * sf)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (pos < slen && cs[pos] == '.') {
|
if (pos < slen && cs[pos] == '.') {
|
||||||
|
have_dot = 1;
|
||||||
pos += 1;
|
pos += 1;
|
||||||
while (pos < slen && ISDIGIT(cs[pos])) {
|
while (pos < slen && ISDIGIT(cs[pos])) {
|
||||||
pos += 1;
|
pos += 1;
|
||||||
@ -1179,7 +1185,7 @@ static size_t parse_number(struct libinjection_sqli_state * sf)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (have_e == 1 && have_exp == 0) {
|
if (have_dot == 1 && have_e == 1 && have_exp == 0) {
|
||||||
/* very special form of
|
/* very special form of
|
||||||
* "1234.e"
|
* "1234.e"
|
||||||
* "10.10E"
|
* "10.10E"
|
||||||
@ -1235,6 +1241,22 @@ int libinjection_sqli_tokenize(struct libinjection_sqli_state * sf)
|
|||||||
*/
|
*/
|
||||||
const unsigned char ch = (unsigned char) (s[*pos]);
|
const unsigned char ch = (unsigned char) (s[*pos]);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* if not ascii, then continue...
|
||||||
|
* actually probably need to just assuming
|
||||||
|
* it's a string
|
||||||
|
*/
|
||||||
|
if (ch > 127) {
|
||||||
|
|
||||||
|
/* 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
|
* look up the parser, and call it
|
||||||
*
|
*
|
||||||
@ -1242,7 +1264,7 @@ int libinjection_sqli_tokenize(struct libinjection_sqli_state * sf)
|
|||||||
* charparsers[ch]()
|
* charparsers[ch]()
|
||||||
*/
|
*/
|
||||||
fnptr = char_parse_map[ch];
|
fnptr = char_parse_map[ch];
|
||||||
|
}
|
||||||
*pos = (*fnptr) (sf);
|
*pos = (*fnptr) (sf);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1327,22 +1349,16 @@ static int syntax_merge_words(struct libinjection_sqli_state * sf,stoken_t * a,
|
|||||||
a->type == TYPE_UNION ||
|
a->type == TYPE_UNION ||
|
||||||
a->type == TYPE_FUNCTION ||
|
a->type == TYPE_FUNCTION ||
|
||||||
a->type == TYPE_EXPRESSION ||
|
a->type == TYPE_EXPRESSION ||
|
||||||
a->type == TYPE_TSQL ||
|
|
||||||
a->type == TYPE_SQLTYPE)) {
|
a->type == TYPE_SQLTYPE)) {
|
||||||
return FALSE;
|
return CHAR_NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!
|
if (b->type != TYPE_KEYWORD && b->type != TYPE_BAREWORD &&
|
||||||
(b->type == TYPE_KEYWORD ||
|
b->type != TYPE_OPERATOR && b->type != TYPE_SQLTYPE &&
|
||||||
b->type == TYPE_BAREWORD ||
|
b->type != TYPE_LOGIC_OPERATOR &&
|
||||||
b->type == TYPE_OPERATOR ||
|
b->type != TYPE_FUNCTION &&
|
||||||
b->type == TYPE_UNION ||
|
b->type != TYPE_UNION && b->type != TYPE_EXPRESSION) {
|
||||||
b->type == TYPE_FUNCTION ||
|
return CHAR_NULL;
|
||||||
b->type == TYPE_EXPRESSION ||
|
|
||||||
b->type == TYPE_TSQL ||
|
|
||||||
b->type == TYPE_SQLTYPE ||
|
|
||||||
b->type == TYPE_LOGIC_OPERATOR)) {
|
|
||||||
return FALSE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sz1 = a->len;
|
sz1 = a->len;
|
||||||
@ -1358,6 +1374,7 @@ static int syntax_merge_words(struct libinjection_sqli_state * sf,stoken_t * a,
|
|||||||
tmp[sz1] = ' ';
|
tmp[sz1] = ' ';
|
||||||
memcpy(tmp + sz1 + 1, b->val, sz2);
|
memcpy(tmp + sz1 + 1, b->val, sz2);
|
||||||
tmp[sz3] = CHAR_NULL;
|
tmp[sz3] = CHAR_NULL;
|
||||||
|
|
||||||
ch = sf->lookup(sf, LOOKUP_WORD, tmp, sz3);
|
ch = sf->lookup(sf, LOOKUP_WORD, tmp, sz3);
|
||||||
|
|
||||||
if (ch != CHAR_NULL) {
|
if (ch != CHAR_NULL) {
|
||||||
@ -1433,13 +1450,6 @@ int libinjection_sqli_fold(struct libinjection_sqli_state * sf)
|
|||||||
sf->tokenvec[2].type == TYPE_COMMA &&
|
sf->tokenvec[2].type == TYPE_COMMA &&
|
||||||
sf->tokenvec[3].type == TYPE_LEFTPARENS &&
|
sf->tokenvec[3].type == TYPE_LEFTPARENS &&
|
||||||
sf->tokenvec[4].type == TYPE_NUMBER
|
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
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
@ -1531,7 +1541,7 @@ int libinjection_sqli_fold(struct libinjection_sqli_state * sf)
|
|||||||
continue;
|
continue;
|
||||||
} else if ((sf->tokenvec[left].type == TYPE_BAREWORD || sf->tokenvec[left].type == TYPE_VARIABLE) &&
|
} else if ((sf->tokenvec[left].type == TYPE_BAREWORD || sf->tokenvec[left].type == TYPE_VARIABLE) &&
|
||||||
sf->tokenvec[left+1].type == TYPE_LEFTPARENS && (
|
sf->tokenvec[left+1].type == TYPE_LEFTPARENS && (
|
||||||
/* TSQL functions but common enough to be column names */
|
/* TSQL functions but common enough to be collumn names */
|
||||||
cstrcasecmp("USER_ID", sf->tokenvec[left].val, sf->tokenvec[left].len) == 0 ||
|
cstrcasecmp("USER_ID", sf->tokenvec[left].val, sf->tokenvec[left].len) == 0 ||
|
||||||
cstrcasecmp("USER_NAME", sf->tokenvec[left].val, sf->tokenvec[left].len) == 0 ||
|
cstrcasecmp("USER_NAME", sf->tokenvec[left].val, sf->tokenvec[left].len) == 0 ||
|
||||||
|
|
||||||
@ -1554,7 +1564,7 @@ int libinjection_sqli_fold(struct libinjection_sqli_state * sf)
|
|||||||
|
|
||||||
/* pos is the same
|
/* pos is the same
|
||||||
* other conversions need to go here... for instance
|
* other conversions need to go here... for instance
|
||||||
* password CAN be a function, coalesce CAN be a function
|
* password CAN be a function, coalese CAN be a function
|
||||||
*/
|
*/
|
||||||
sf->tokenvec[left].type = TYPE_FUNCTION;
|
sf->tokenvec[left].type = TYPE_FUNCTION;
|
||||||
continue;
|
continue;
|
||||||
@ -1818,7 +1828,7 @@ int libinjection_sqli_fold(struct libinjection_sqli_state * sf)
|
|||||||
* 1,-sin(1) --> 1 (1)
|
* 1,-sin(1) --> 1 (1)
|
||||||
* Here, just do
|
* Here, just do
|
||||||
* 1,-sin(1) --> 1,sin(1)
|
* 1,-sin(1) --> 1,sin(1)
|
||||||
* just remove unary operator
|
* just remove unary opartor
|
||||||
*/
|
*/
|
||||||
st_copy(&sf->tokenvec[left+1], &sf->tokenvec[left+2]);
|
st_copy(&sf->tokenvec[left+1], &sf->tokenvec[left+2]);
|
||||||
pos -= 1;
|
pos -= 1;
|
||||||
@ -1842,21 +1852,9 @@ int libinjection_sqli_fold(struct libinjection_sqli_state * sf)
|
|||||||
pos -= 1;
|
pos -= 1;
|
||||||
left = 0;
|
left = 0;
|
||||||
continue;
|
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
|
/* no folding -- assume left-most token is
|
||||||
is good, now use the existing 2 tokens --
|
is good, now use the existing 2 tokens --
|
||||||
do not get another
|
do not get another
|
||||||
@ -2021,7 +2019,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)
|
int libinjection_sqli_not_whitelist(struct libinjection_sqli_state* sql_state)
|
||||||
{
|
{
|
||||||
@ -2035,10 +2033,10 @@ int libinjection_sqli_not_whitelist(struct libinjection_sqli_state* sql_state)
|
|||||||
|
|
||||||
if (tlen > 1 && sql_state->fingerprint[tlen-1] == TYPE_COMMENT) {
|
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
|
* MS Audit log apparently ignores anything with
|
||||||
* 'sp_password' in it. Unable to find primary reference to
|
* 'sp_password' in it. Unable to find primary refernece to
|
||||||
* this "feature" of SQL Server but seems to be known SQLi
|
* this "feature" of SQL Server but seems to be known sqli
|
||||||
* technique
|
* technique
|
||||||
*/
|
*/
|
||||||
if (my_memmem(sql_state->s, sql_state->slen,
|
if (my_memmem(sql_state->s, sql_state->slen,
|
||||||
@ -2057,7 +2055,7 @@ int libinjection_sqli_not_whitelist(struct libinjection_sqli_state* sql_state)
|
|||||||
|
|
||||||
if (sql_state->fingerprint[1] == TYPE_UNION) {
|
if (sql_state->fingerprint[1] == TYPE_UNION) {
|
||||||
if (sql_state->stats_tokens == 2) {
|
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.
|
* likely part of parameter splitting/etc.
|
||||||
* lots of reasons why "1 union" might be normal
|
* lots of reasons why "1 union" might be normal
|
||||||
* input, so beep only if other SQLi things are present
|
* input, so beep only if other SQLi things are present
|
||||||
@ -2082,7 +2080,7 @@ int libinjection_sqli_not_whitelist(struct libinjection_sqli_state* sql_state)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* for fingerprint like 'nc', only comments of /x are treated
|
* 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 &&
|
if (sql_state->tokenvec[0].type == TYPE_BAREWORD &&
|
||||||
sql_state->tokenvec[1].type == TYPE_COMMENT &&
|
sql_state->tokenvec[1].type == TYPE_COMMENT &&
|
||||||
@ -2092,7 +2090,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 &&
|
if (sql_state->tokenvec[0].type == TYPE_NUMBER &&
|
||||||
sql_state->tokenvec[1].type == TYPE_COMMENT &&
|
sql_state->tokenvec[1].type == TYPE_COMMENT &&
|
||||||
@ -2115,13 +2113,13 @@ int libinjection_sqli_not_whitelist(struct libinjection_sqli_state* sql_state)
|
|||||||
if (sql_state->tokenvec[0].type == TYPE_NUMBER &&
|
if (sql_state->tokenvec[0].type == TYPE_NUMBER &&
|
||||||
sql_state->tokenvec[1].type == TYPE_COMMENT) {
|
sql_state->tokenvec[1].type == TYPE_COMMENT) {
|
||||||
if (sql_state->stats_tokens > 2) {
|
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__;
|
sql_state->reason = __LINE__;
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
* we check that next character after the number is either whitespace,
|
* 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];
|
ch = sql_state->s[sql_state->tokenvec[0].len];
|
||||||
if ( ch <= 32 ) {
|
if ( ch <= 32 ) {
|
||||||
@ -2143,7 +2141,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
|
* so only detect if input ends with '--', e.g. 1-- but not 1-- foo
|
||||||
*/
|
*/
|
||||||
if ((sql_state->tokenvec[1].len > 2)
|
if ((sql_state->tokenvec[1].len > 2)
|
||||||
@ -2179,7 +2177,7 @@ int libinjection_sqli_not_whitelist(struct libinjection_sqli_state* sql_state)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* not SQLi
|
* not sqli
|
||||||
*/
|
*/
|
||||||
sql_state->reason = __LINE__;
|
sql_state->reason = __LINE__;
|
||||||
return FALSE;
|
return FALSE;
|
||||||
@ -2188,8 +2186,8 @@ int libinjection_sqli_not_whitelist(struct libinjection_sqli_state* sql_state)
|
|||||||
streq(sql_state->fingerprint, "1&1") ||
|
streq(sql_state->fingerprint, "1&1") ||
|
||||||
streq(sql_state->fingerprint, "1&v") ||
|
streq(sql_state->fingerprint, "1&v") ||
|
||||||
streq(sql_state->fingerprint, "1&s")) {
|
streq(sql_state->fingerprint, "1&s")) {
|
||||||
/* 'sexy and 17' not SQLi
|
/* 'sexy and 17' not sqli
|
||||||
* 'sexy and 17<18' SQLi
|
* 'sexy and 17<18' sqli
|
||||||
*/
|
*/
|
||||||
if (sql_state->stats_tokens == 3) {
|
if (sql_state->stats_tokens == 3) {
|
||||||
sql_state->reason = __LINE__;
|
sql_state->reason = __LINE__;
|
||||||
@ -2245,7 +2243,7 @@ int libinjection_is_sqli(struct libinjection_sqli_state * sql_state)
|
|||||||
size_t slen = sql_state->slen;
|
size_t slen = sql_state->slen;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* no input? not SQLi
|
* no input? not sqli
|
||||||
*/
|
*/
|
||||||
if (slen == 0) {
|
if (slen == 0) {
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
@ -40,10 +40,6 @@ struct libinjection_sqli_token {
|
|||||||
#ifdef SWIG
|
#ifdef SWIG
|
||||||
%immutable;
|
%immutable;
|
||||||
#endif
|
#endif
|
||||||
char type;
|
|
||||||
char str_open;
|
|
||||||
char str_close;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* position and length of token
|
* position and length of token
|
||||||
* in original string
|
* in original string
|
||||||
@ -57,6 +53,9 @@ struct libinjection_sqli_token {
|
|||||||
*/
|
*/
|
||||||
int count;
|
int count;
|
||||||
|
|
||||||
|
char type;
|
||||||
|
char str_open;
|
||||||
|
char str_close;
|
||||||
char val[32];
|
char val[32];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -445,6 +445,8 @@ static const keyword_t sql_keywords[] = {
|
|||||||
{"0&VUEN", 'F'},
|
{"0&VUEN", 'F'},
|
||||||
{"0&VUES", 'F'},
|
{"0&VUES", 'F'},
|
||||||
{"0&VUEV", 'F'},
|
{"0&VUEV", 'F'},
|
||||||
|
{"0)&(EK", 'F'},
|
||||||
|
{"0)&(EN", 'F'},
|
||||||
{"0)UE(1", 'F'},
|
{"0)UE(1", 'F'},
|
||||||
{"0)UE(F", 'F'},
|
{"0)UE(F", 'F'},
|
||||||
{"0)UE(N", 'F'},
|
{"0)UE(N", 'F'},
|
||||||
@ -585,6 +587,7 @@ static const keyword_t sql_keywords[] = {
|
|||||||
{"01&K(S", 'F'},
|
{"01&K(S", 'F'},
|
||||||
{"01&K(V", 'F'},
|
{"01&K(V", 'F'},
|
||||||
{"01&K1O", 'F'},
|
{"01&K1O", 'F'},
|
||||||
|
{"01&KC", 'F'},
|
||||||
{"01&KF(", 'F'},
|
{"01&KF(", 'F'},
|
||||||
{"01&KNK", 'F'},
|
{"01&KNK", 'F'},
|
||||||
{"01&KO(", 'F'},
|
{"01&KO(", 'F'},
|
||||||
@ -876,6 +879,7 @@ static const keyword_t sql_keywords[] = {
|
|||||||
{"01)KN&", 'F'},
|
{"01)KN&", 'F'},
|
||||||
{"01)KN;", 'F'},
|
{"01)KN;", 'F'},
|
||||||
{"01)KNB", 'F'},
|
{"01)KNB", 'F'},
|
||||||
|
{"01)KNC", 'F'},
|
||||||
{"01)KNE", 'F'},
|
{"01)KNE", 'F'},
|
||||||
{"01)KNK", 'F'},
|
{"01)KNK", 'F'},
|
||||||
{"01)KNU", 'F'},
|
{"01)KNU", 'F'},
|
||||||
@ -1002,6 +1006,7 @@ static const keyword_t sql_keywords[] = {
|
|||||||
{"01;EVT", 'F'},
|
{"01;EVT", 'F'},
|
||||||
{"01;N:T", 'F'},
|
{"01;N:T", 'F'},
|
||||||
{"01;T(1", 'F'},
|
{"01;T(1", 'F'},
|
||||||
|
{"01;T(C", 'F'},
|
||||||
{"01;T(E", 'F'},
|
{"01;T(E", 'F'},
|
||||||
{"01;T(F", 'F'},
|
{"01;T(F", 'F'},
|
||||||
{"01;T(N", 'F'},
|
{"01;T(N", 'F'},
|
||||||
@ -2105,6 +2110,8 @@ static const keyword_t sql_keywords[] = {
|
|||||||
{"01VUE;", 'F'},
|
{"01VUE;", 'F'},
|
||||||
{"01VUEC", 'F'},
|
{"01VUEC", 'F'},
|
||||||
{"01VUEK", 'F'},
|
{"01VUEK", 'F'},
|
||||||
|
{"0;T(EF", 'F'},
|
||||||
|
{"0;T(EK", 'F'},
|
||||||
{"0;TKNC", 'F'},
|
{"0;TKNC", 'F'},
|
||||||
{"0E(1&(", 'F'},
|
{"0E(1&(", 'F'},
|
||||||
{"0E(1&1", 'F'},
|
{"0E(1&1", 'F'},
|
||||||
@ -3691,6 +3698,7 @@ static const keyword_t sql_keywords[] = {
|
|||||||
{"0N&K(S", 'F'},
|
{"0N&K(S", 'F'},
|
||||||
{"0N&K(V", 'F'},
|
{"0N&K(V", 'F'},
|
||||||
{"0N&K1O", 'F'},
|
{"0N&K1O", 'F'},
|
||||||
|
{"0N&KC", 'F'},
|
||||||
{"0N&KF(", 'F'},
|
{"0N&KF(", 'F'},
|
||||||
{"0N&KNK", 'F'},
|
{"0N&KNK", 'F'},
|
||||||
{"0N&KO(", 'F'},
|
{"0N&KO(", 'F'},
|
||||||
@ -3968,6 +3976,7 @@ static const keyword_t sql_keywords[] = {
|
|||||||
{"0N)KN&", 'F'},
|
{"0N)KN&", 'F'},
|
||||||
{"0N)KN;", 'F'},
|
{"0N)KN;", 'F'},
|
||||||
{"0N)KNB", 'F'},
|
{"0N)KNB", 'F'},
|
||||||
|
{"0N)KNC", 'F'},
|
||||||
{"0N)KNE", 'F'},
|
{"0N)KNE", 'F'},
|
||||||
{"0N)KNK", 'F'},
|
{"0N)KNK", 'F'},
|
||||||
{"0N)KNU", 'F'},
|
{"0N)KNU", 'F'},
|
||||||
@ -4121,6 +4130,7 @@ static const keyword_t sql_keywords[] = {
|
|||||||
{"0N;EVT", 'F'},
|
{"0N;EVT", 'F'},
|
||||||
{"0N;N:T", 'F'},
|
{"0N;N:T", 'F'},
|
||||||
{"0N;T(1", 'F'},
|
{"0N;T(1", 'F'},
|
||||||
|
{"0N;T(C", 'F'},
|
||||||
{"0N;T(E", 'F'},
|
{"0N;T(E", 'F'},
|
||||||
{"0N;T(F", 'F'},
|
{"0N;T(F", 'F'},
|
||||||
{"0N;T(N", 'F'},
|
{"0N;T(N", 'F'},
|
||||||
@ -4206,12 +4216,16 @@ static const keyword_t sql_keywords[] = {
|
|||||||
{"0NAVOF", 'F'},
|
{"0NAVOF", 'F'},
|
||||||
{"0NAVOS", 'F'},
|
{"0NAVOS", 'F'},
|
||||||
{"0NAVUE", 'F'},
|
{"0NAVUE", 'F'},
|
||||||
|
{"0NB(1&", 'F'},
|
||||||
{"0NB(1)", 'F'},
|
{"0NB(1)", 'F'},
|
||||||
{"0NB(1O", 'F'},
|
{"0NB(1O", 'F'},
|
||||||
{"0NB(F(", 'F'},
|
{"0NB(F(", 'F'},
|
||||||
|
{"0NB(N&", 'F'},
|
||||||
{"0NB(NO", 'F'},
|
{"0NB(NO", 'F'},
|
||||||
|
{"0NB(S&", 'F'},
|
||||||
{"0NB(S)", 'F'},
|
{"0NB(S)", 'F'},
|
||||||
{"0NB(SO", 'F'},
|
{"0NB(SO", 'F'},
|
||||||
|
{"0NB(V&", 'F'},
|
||||||
{"0NB(V)", 'F'},
|
{"0NB(V)", 'F'},
|
||||||
{"0NB(VO", 'F'},
|
{"0NB(VO", 'F'},
|
||||||
{"0NB1", 'F'},
|
{"0NB1", 'F'},
|
||||||
@ -5215,6 +5229,7 @@ static const keyword_t sql_keywords[] = {
|
|||||||
{"0S&K(S", 'F'},
|
{"0S&K(S", 'F'},
|
||||||
{"0S&K(V", 'F'},
|
{"0S&K(V", 'F'},
|
||||||
{"0S&K1O", 'F'},
|
{"0S&K1O", 'F'},
|
||||||
|
{"0S&KC", 'F'},
|
||||||
{"0S&KF(", 'F'},
|
{"0S&KF(", 'F'},
|
||||||
{"0S&KNK", 'F'},
|
{"0S&KNK", 'F'},
|
||||||
{"0S&KO(", 'F'},
|
{"0S&KO(", 'F'},
|
||||||
@ -5507,6 +5522,7 @@ static const keyword_t sql_keywords[] = {
|
|||||||
{"0S)KN&", 'F'},
|
{"0S)KN&", 'F'},
|
||||||
{"0S)KN;", 'F'},
|
{"0S)KN;", 'F'},
|
||||||
{"0S)KNB", 'F'},
|
{"0S)KNB", 'F'},
|
||||||
|
{"0S)KNC", 'F'},
|
||||||
{"0S)KNE", 'F'},
|
{"0S)KNE", 'F'},
|
||||||
{"0S)KNK", 'F'},
|
{"0S)KNK", 'F'},
|
||||||
{"0S)KNU", 'F'},
|
{"0S)KNU", 'F'},
|
||||||
@ -5654,6 +5670,7 @@ static const keyword_t sql_keywords[] = {
|
|||||||
{"0S;EVT", 'F'},
|
{"0S;EVT", 'F'},
|
||||||
{"0S;N:T", 'F'},
|
{"0S;N:T", 'F'},
|
||||||
{"0S;T(1", 'F'},
|
{"0S;T(1", 'F'},
|
||||||
|
{"0S;T(C", 'F'},
|
||||||
{"0S;T(E", 'F'},
|
{"0S;T(E", 'F'},
|
||||||
{"0S;T(F", 'F'},
|
{"0S;T(F", 'F'},
|
||||||
{"0S;T(N", 'F'},
|
{"0S;T(N", 'F'},
|
||||||
@ -7303,6 +7320,7 @@ static const keyword_t sql_keywords[] = {
|
|||||||
{"0V&K(S", 'F'},
|
{"0V&K(S", 'F'},
|
||||||
{"0V&K(V", 'F'},
|
{"0V&K(V", 'F'},
|
||||||
{"0V&K1O", 'F'},
|
{"0V&K1O", 'F'},
|
||||||
|
{"0V&KC", 'F'},
|
||||||
{"0V&KF(", 'F'},
|
{"0V&KF(", 'F'},
|
||||||
{"0V&KNK", 'F'},
|
{"0V&KNK", 'F'},
|
||||||
{"0V&KO(", 'F'},
|
{"0V&KO(", 'F'},
|
||||||
@ -7595,6 +7613,7 @@ static const keyword_t sql_keywords[] = {
|
|||||||
{"0V)KN&", 'F'},
|
{"0V)KN&", 'F'},
|
||||||
{"0V)KN;", 'F'},
|
{"0V)KN;", 'F'},
|
||||||
{"0V)KNB", 'F'},
|
{"0V)KNB", 'F'},
|
||||||
|
{"0V)KNC", 'F'},
|
||||||
{"0V)KNE", 'F'},
|
{"0V)KNE", 'F'},
|
||||||
{"0V)KNK", 'F'},
|
{"0V)KNK", 'F'},
|
||||||
{"0V)KNU", 'F'},
|
{"0V)KNU", 'F'},
|
||||||
@ -7722,6 +7741,7 @@ static const keyword_t sql_keywords[] = {
|
|||||||
{"0V;EVT", 'F'},
|
{"0V;EVT", 'F'},
|
||||||
{"0V;N:T", 'F'},
|
{"0V;N:T", 'F'},
|
||||||
{"0V;T(1", 'F'},
|
{"0V;T(1", 'F'},
|
||||||
|
{"0V;T(C", 'F'},
|
||||||
{"0V;T(E", 'F'},
|
{"0V;T(E", 'F'},
|
||||||
{"0V;T(F", 'F'},
|
{"0V;T(F", 'F'},
|
||||||
{"0V;T(N", 'F'},
|
{"0V;T(N", 'F'},
|
||||||
@ -8871,6 +8891,7 @@ static const keyword_t sql_keywords[] = {
|
|||||||
{"DAY_SECOND", 'k'},
|
{"DAY_SECOND", 'k'},
|
||||||
{"DBMS_LOCK.SLEEP", 'f'},
|
{"DBMS_LOCK.SLEEP", 'f'},
|
||||||
{"DBMS_PIPE.RECEIVE_MESSAGE", 'f'},
|
{"DBMS_PIPE.RECEIVE_MESSAGE", 'f'},
|
||||||
|
{"DBMS_UTILITY.SQLID_TO_SQLHASH", 'f'},
|
||||||
{"DB_ID", 'f'},
|
{"DB_ID", 'f'},
|
||||||
{"DB_NAME", 'f'},
|
{"DB_NAME", 'f'},
|
||||||
{"DCOUNT", 'f'},
|
{"DCOUNT", 'f'},
|
||||||
@ -9018,7 +9039,7 @@ static const keyword_t sql_keywords[] = {
|
|||||||
{"IDENT_SEED", 'f'},
|
{"IDENT_SEED", 'f'},
|
||||||
{"IF", 'f'},
|
{"IF", 'f'},
|
||||||
{"IF EXISTS", 'f'},
|
{"IF EXISTS", 'f'},
|
||||||
{"IF NOT", 'n'},
|
{"IF NOT", 'f'},
|
||||||
{"IF NOT EXISTS", 'f'},
|
{"IF NOT EXISTS", 'f'},
|
||||||
{"IFF", 'f'},
|
{"IFF", 'f'},
|
||||||
{"IFNULL", 'f'},
|
{"IFNULL", 'f'},
|
||||||
@ -9395,6 +9416,7 @@ static const keyword_t sql_keywords[] = {
|
|||||||
{"SPLIT_PART", 'f'},
|
{"SPLIT_PART", 'f'},
|
||||||
{"SQL", 'k'},
|
{"SQL", 'k'},
|
||||||
{"SQLEXCEPTION", 'k'},
|
{"SQLEXCEPTION", 'k'},
|
||||||
|
{"SQLITE_VERSION", 'f'},
|
||||||
{"SQLSTATE", 'k'},
|
{"SQLSTATE", 'k'},
|
||||||
{"SQLWARNING", 'k'},
|
{"SQLWARNING", 'k'},
|
||||||
{"SQL_BIG_RESULT", 'k'},
|
{"SQL_BIG_RESULT", 'k'},
|
||||||
@ -9626,5 +9648,5 @@ static const keyword_t sql_keywords[] = {
|
|||||||
{"||", '&'},
|
{"||", '&'},
|
||||||
{"~*", 'o'},
|
{"~*", 'o'},
|
||||||
};
|
};
|
||||||
static const size_t sql_keywords_sz = 9330;
|
static const size_t sql_keywords_sz = 9352;
|
||||||
#endif
|
#endif
|
||||||
|
@ -1,11 +1,15 @@
|
|||||||
|
|
||||||
#include "libinjection.h"
|
|
||||||
#include "libinjection_xss.h"
|
#include "libinjection_xss.h"
|
||||||
#include "libinjection_html5.h"
|
#include "libinjection_html5.h"
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* HEY THIS ISN'T DONE
|
||||||
|
* AND MISSING A KEY INGREDIENT!!
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
typedef enum attribute {
|
typedef enum attribute {
|
||||||
TYPE_NONE
|
TYPE_NONE
|
||||||
, TYPE_BLACK /* ban always */
|
, TYPE_BLACK /* ban always */
|
||||||
@ -14,128 +18,11 @@ typedef enum attribute {
|
|||||||
, TYPE_ATTR_INDIRECT /* attribute *name* is given in *value* */
|
, TYPE_ATTR_INDIRECT /* attribute *name* is given in *value* */
|
||||||
} attribute_t;
|
} 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 {
|
typedef struct stringtype {
|
||||||
const char* name;
|
const char* name;
|
||||||
attribute_t atype;
|
attribute_t atype;
|
||||||
} stringtype_t;
|
} 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:
|
* view-source:
|
||||||
* data:
|
* data:
|
||||||
@ -150,7 +37,7 @@ static stringtype_t BLACKATTR[] = {
|
|||||||
, { "DATASRC", TYPE_BLACK } /* IE */
|
, { "DATASRC", TYPE_BLACK } /* IE */
|
||||||
, { "DYNSRC", TYPE_ATTR_URL } /* Obsolete img attribute */
|
, { "DYNSRC", TYPE_ATTR_URL } /* Obsolete img attribute */
|
||||||
, { "FILTER", TYPE_STYLE } /* Opera, SVG inline style */
|
, { "FILTER", TYPE_STYLE } /* Opera, SVG inline style */
|
||||||
, { "FORMACTION", TYPE_ATTR_URL } /* HTML 5 */
|
, { "FORMACTION", TYPE_ATTR_URL } /* HTML5 */
|
||||||
, { "FOLDER", TYPE_ATTR_URL } /* Only on A tags, IE-only */
|
, { "FOLDER", TYPE_ATTR_URL } /* Only on A tags, IE-only */
|
||||||
, { "FROM", TYPE_ATTR_URL } /* SVG */
|
, { "FROM", TYPE_ATTR_URL } /* SVG */
|
||||||
, { "HANDLER", TYPE_ATTR_URL } /* SVG Tiny, Opera */
|
, { "HANDLER", TYPE_ATTR_URL } /* SVG Tiny, Opera */
|
||||||
@ -166,10 +53,10 @@ static stringtype_t BLACKATTR[] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/* xmlns */
|
/* xmlns */
|
||||||
/* `xml-stylesheet` > <eval>, <if expr=> */
|
/* xml-stylesheet > <eval>, <if expr=> */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
static const char* BLACKATTR[] = {
|
static const char* BLACKATTR[] = {
|
||||||
"ATTRIBUTENAME",
|
"ATTRIBUTENAME",
|
||||||
"BACKGROUND",
|
"BACKGROUND",
|
||||||
"DATAFORMATAS",
|
"DATAFORMATAS",
|
||||||
@ -179,14 +66,13 @@ static stringtype_t BLACKATTR[] = {
|
|||||||
"STYLE",
|
"STYLE",
|
||||||
"SRCDOC",
|
"SRCDOC",
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static const char* BLACKTAG[] = {
|
static const char* BLACKTAG[] = {
|
||||||
"APPLET"
|
"APPLET"
|
||||||
/* , "AUDIO" */
|
/* , "AUDIO" */
|
||||||
, "BASE"
|
, "BASE"
|
||||||
, "COMMENT" /* IE http://html5sec.org/#38 */
|
|
||||||
, "EMBED"
|
, "EMBED"
|
||||||
/* , "FORM" */
|
/* , "FORM" */
|
||||||
, "FRAME"
|
, "FRAME"
|
||||||
@ -206,94 +92,33 @@ static const char* BLACKTAG[] = {
|
|||||||
/* , "VIDEO" */
|
/* , "VIDEO" */
|
||||||
, "VMLFRAME"
|
, "VMLFRAME"
|
||||||
, "XML"
|
, "XML"
|
||||||
, "XSS"
|
|
||||||
, NULL
|
, NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static int is_black_tag(const char* s, size_t len);
|
||||||
|
static attribute_t is_black_attr(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 cstrcasecmp_with_null(const char *a, const char *b, size_t n)
|
static int cstrcasecmp_with_null(const char *a, const char *b, size_t n)
|
||||||
{
|
{
|
||||||
char ca;
|
|
||||||
char cb;
|
char cb;
|
||||||
/* printf("Comparing to %s %.*s\n", a, (int)n, b); */
|
|
||||||
while (n-- > 0) {
|
for (; n > 0; a++, b++, n--) {
|
||||||
cb = *b++;
|
cb = *b;
|
||||||
if (cb == '\0') continue;
|
if (cb == '\0') continue;
|
||||||
|
|
||||||
ca = *a++;
|
|
||||||
|
|
||||||
if (cb >= 'a' && cb <= 'z') {
|
if (cb >= 'a' && cb <= 'z') {
|
||||||
cb -= 0x20;
|
cb -= 0x20;
|
||||||
}
|
}
|
||||||
/* printf("Comparing %c vs %c with %d left\n", ca, cb, (int)n); */
|
if (*a != cb) {
|
||||||
if (ca != cb) {
|
return *a - cb;
|
||||||
return 1;
|
} else if (*a == '\0') {
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (*a == 0) {
|
return (*a == 0) ? 0 : 1;
|
||||||
/* printf(" MATCH \n"); */
|
|
||||||
return 0;
|
|
||||||
} else {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Does an HTML encoded binary string (const char*, length) start with
|
|
||||||
* a all uppercase c-string (null terminated), case insensitive!
|
|
||||||
*
|
|
||||||
* 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 vertical tab 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)
|
static int is_black_tag(const char* s, size_t len)
|
||||||
@ -307,7 +132,6 @@ static int is_black_tag(const char* s, size_t len)
|
|||||||
black = BLACKTAG;
|
black = BLACKTAG;
|
||||||
while (*black != NULL) {
|
while (*black != NULL) {
|
||||||
if (cstrcasecmp_with_null(*black, s, len) == 0) {
|
if (cstrcasecmp_with_null(*black, s, len) == 0) {
|
||||||
/* printf("Got black tag %s\n", *black); */
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
black += 1;
|
black += 1;
|
||||||
@ -317,7 +141,6 @@ static int is_black_tag(const char* s, size_t len)
|
|||||||
if ((s[0] == 's' || s[0] == 'S') &&
|
if ((s[0] == 's' || s[0] == 'S') &&
|
||||||
(s[1] == 'v' || s[1] == 'V') &&
|
(s[1] == 'v' || s[1] == 'V') &&
|
||||||
(s[2] == 'g' || s[2] == 'G')) {
|
(s[2] == 'g' || s[2] == 'G')) {
|
||||||
/* printf("Got SVG tag \n"); */
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -325,7 +148,6 @@ static int is_black_tag(const char* s, size_t len)
|
|||||||
if ((s[0] == 'x' || s[0] == 'X') &&
|
if ((s[0] == 'x' || s[0] == 'X') &&
|
||||||
(s[1] == 's' || s[1] == 'S') &&
|
(s[1] == 's' || s[1] == 'S') &&
|
||||||
(s[2] == 'l' || s[2] == 'L')) {
|
(s[2] == 'l' || s[2] == 'L')) {
|
||||||
/* printf("Got XSL tag\n"); */
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -340,9 +162,8 @@ static attribute_t is_black_attr(const char* s, size_t len)
|
|||||||
return TYPE_NONE;
|
return TYPE_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* JavaScript on.* */
|
/* javascript on.* */
|
||||||
if ((s[0] == 'o' || s[0] == 'O') && (s[1] == 'n' || s[1] == 'N')) {
|
if ((s[0] == 'o' || s[0] == 'O') && (s[1] == 'n' || s[1] == 'N')) {
|
||||||
/* printf("Got JavaScript on- attribute name\n"); */
|
|
||||||
return TYPE_BLACK;
|
return TYPE_BLACK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -350,7 +171,6 @@ static attribute_t is_black_attr(const char* s, size_t len)
|
|||||||
if (len >= 5) {
|
if (len >= 5) {
|
||||||
/* XMLNS can be used to create arbitrary tags */
|
/* XMLNS can be used to create arbitrary tags */
|
||||||
if (cstrcasecmp_with_null("XMLNS", s, 5) == 0 || cstrcasecmp_with_null("XLINK", s, 5) == 0) {
|
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;
|
return TYPE_BLACK;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -358,7 +178,6 @@ static attribute_t is_black_attr(const char* s, size_t len)
|
|||||||
black = BLACKATTR;
|
black = BLACKATTR;
|
||||||
while (black->name != NULL) {
|
while (black->name != NULL) {
|
||||||
if (cstrcasecmp_with_null(black->name, s, len) == 0) {
|
if (cstrcasecmp_with_null(black->name, s, len) == 0) {
|
||||||
/* printf("Got banned attribute name %s\n", black->name); */
|
|
||||||
return black->atype;
|
return black->atype;
|
||||||
}
|
}
|
||||||
black += 1;
|
black += 1;
|
||||||
@ -379,43 +198,49 @@ static int is_black_url(const char* s, size_t len)
|
|||||||
/* covers JAVA, JAVASCRIPT, + colon */
|
/* covers JAVA, JAVASCRIPT, + colon */
|
||||||
static const char* javascript_url = "JAVA";
|
static const char* javascript_url = "JAVA";
|
||||||
|
|
||||||
|
size_t tokenlen;
|
||||||
|
|
||||||
/* skip whitespace */
|
/* skip whitespace */
|
||||||
while (len > 0 && (*s <= 32 || *s >= 127)) {
|
while (len > 0) {
|
||||||
/*
|
/*
|
||||||
* HEY: this is a signed character.
|
* HEY: this is a signed character.
|
||||||
* We are intentionally skipping high-bit characters too
|
* We are intentionally skipping high-bit characters too
|
||||||
* since they are not ASCII, and Opera sometimes uses UTF-8 whitespace.
|
* since they are not ascii, and Opera sometimes uses UTF8 whitespace
|
||||||
*
|
|
||||||
* Also in EUC-JP some of the high bytes are just ignored.
|
|
||||||
*/
|
*/
|
||||||
|
if (*s <= 32) {
|
||||||
++s;
|
++s;
|
||||||
--len;
|
--len;
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (htmlencode_startswith(data_url, s, len)) {
|
tokenlen = strlen(data_url);
|
||||||
|
if (len > tokenlen && cstrcasecmp_with_null(data_url, s, tokenlen) == 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
tokenlen = strlen(viewsource_url);
|
||||||
|
if (len > tokenlen && cstrcasecmp_with_null(viewsource_url, s, tokenlen) == 0) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (htmlencode_startswith(viewsource_url, s, len)) {
|
tokenlen = strlen(javascript_url);
|
||||||
|
if (len > tokenlen && cstrcasecmp_with_null(javascript_url, s, tokenlen) == 0) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (htmlencode_startswith(javascript_url, s, len)) {
|
tokenlen = strlen(vbscript_url);
|
||||||
return 1;
|
if (len > tokenlen && cstrcasecmp_with_null(vbscript_url, s, tokenlen) == 0) {
|
||||||
}
|
|
||||||
|
|
||||||
if (htmlencode_startswith(vbscript_url, s, len)) {
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int libinjection_is_xss(const char* s, size_t len, int flags)
|
int libinjection_is_xss(const char* s, size_t len)
|
||||||
{
|
{
|
||||||
h5_state_t h5;
|
h5_state_t h5;
|
||||||
attribute_t attr = TYPE_NONE;
|
attribute_t attr = TYPE_NONE;
|
||||||
|
|
||||||
libinjection_h5_init(&h5, s, len, (enum html5_flags) flags);
|
libinjection_h5_init(&h5, s, len, 0);
|
||||||
while (libinjection_h5_next(&h5)) {
|
while (libinjection_h5_next(&h5)) {
|
||||||
if (h5.token_type != ATTR_VALUE) {
|
if (h5.token_type != ATTR_VALUE) {
|
||||||
attr = TYPE_NONE;
|
attr = TYPE_NONE;
|
||||||
@ -433,7 +258,7 @@ int libinjection_is_xss(const char* s, size_t len, int flags)
|
|||||||
/*
|
/*
|
||||||
* IE6,7,8 parsing works a bit differently so
|
* IE6,7,8 parsing works a bit differently so
|
||||||
* a whole <script> or other black tag might be hiding
|
* a whole <script> or other black tag might be hiding
|
||||||
* inside an attribute value under HTML 5 parsing
|
* inside an attribute value under HTML5 parsing
|
||||||
* See http://html5sec.org/#102
|
* See http://html5sec.org/#102
|
||||||
* to avoid doing a full reparse of the value, just
|
* to avoid doing a full reparse of the value, just
|
||||||
* look for "<". This probably need adjusting to
|
* look for "<". This probably need adjusting to
|
||||||
@ -482,7 +307,7 @@ int libinjection_is_xss(const char* s, size_t len, int flags)
|
|||||||
(h5.token_start[2] == 'f' || h5.token_start[2] == 'F')) {
|
(h5.token_start[2] == 'f' || h5.token_start[2] == 'F')) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
if ((h5.token_start[0] == 'x' || h5.token_start[0] == 'X') &&
|
if ((h5.token_start[0] == 'x' || h5.token_start[1] == 'X') &&
|
||||||
(h5.token_start[1] == 'm' || h5.token_start[1] == 'M') &&
|
(h5.token_start[1] == 'm' || h5.token_start[1] == 'M') &&
|
||||||
(h5.token_start[2] == 'l' || h5.token_start[2] == 'L')) {
|
(h5.token_start[2] == 'l' || h5.token_start[2] == 'L')) {
|
||||||
return 1;
|
return 1;
|
||||||
@ -504,28 +329,3 @@ int libinjection_is_xss(const char* s, size_t len, int flags)
|
|||||||
}
|
}
|
||||||
return 0;
|
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;
|
|
||||||
}
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user