mirror of
https://github.com/owasp-modsecurity/ModSecurity.git
synced 2025-08-17 14:46:13 +03:00
libinjection sync
This commit is contained in:
parent
227de9fb8a
commit
74ec784005
@ -14,187 +14,10 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/**
|
|
||||||
* Libinjection's sqli module makes a "normalized"
|
|
||||||
* value of the token. This is the maximum size
|
|
||||||
* Token with values larger than this will be truncated
|
|
||||||
*/
|
|
||||||
#ifndef LIBINJECTION_SQLI_TOKEN_SIZE
|
|
||||||
#define LIBINJECTION_SQLI_TOKEN_SIZE 32
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Number of tokens used to create a fingerprint
|
|
||||||
*/
|
|
||||||
#ifndef LIBINJECTION_SQLI_MAX_TOKENS
|
|
||||||
#define LIBINJECTION_SQLI_MAX_TOKENS 5
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if LIBINJECTION_SQLI_MAX_TOKENS >= 8
|
|
||||||
#define LIBINJECTION_SQLI_BUFFER_SZ (LIBINJECTION_SQLI_MAX_TOKENS + 1)
|
|
||||||
#else
|
|
||||||
#define LIBINJECTION_SQLI_BUFFER_SZ 8
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
enum lookup_type {
|
|
||||||
FLAG_NONE = 0,
|
|
||||||
FLAG_QUOTE_NONE = 1 << 1,
|
|
||||||
FLAG_QUOTE_SINGLE = 1 << 2,
|
|
||||||
FLAG_QUOTE_DOUBLE = 1 << 3,
|
|
||||||
|
|
||||||
FLAG_SQL_ANSI = 1 << 4,
|
|
||||||
FLAG_SQL_MYSQL = 1 << 5,
|
|
||||||
|
|
||||||
LOOKUP_WORD,
|
|
||||||
LOOKUP_TYPE,
|
|
||||||
LOOKUP_OPERATOR,
|
|
||||||
LOOKUP_FINGERPRINT
|
|
||||||
};
|
|
||||||
|
|
||||||
struct libinjection_sqli_token {
|
|
||||||
#ifdef SWIG
|
|
||||||
%immutable;
|
|
||||||
#endif
|
|
||||||
char type;
|
|
||||||
char str_open;
|
|
||||||
char str_close;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* position and length of token
|
* Pull in size_t
|
||||||
* in original string
|
|
||||||
*/
|
*/
|
||||||
size_t pos;
|
#include <string.h>
|
||||||
size_t len;
|
|
||||||
|
|
||||||
/* count:
|
|
||||||
* in type 'v', used for number of opening '@'
|
|
||||||
* but maybe unsed in other contexts
|
|
||||||
*/
|
|
||||||
int count;
|
|
||||||
|
|
||||||
char val[LIBINJECTION_SQLI_TOKEN_SIZE];
|
|
||||||
};
|
|
||||||
|
|
||||||
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[LIBINJECTION_SQLI_BUFFER_SZ];
|
|
||||||
#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[LIBINJECTION_SQLI_BUFFER_SZ];
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
struct libinjection_sqli_token* libinjection_sqli_get_token(
|
|
||||||
struct libinjection_sqli_state* sqlistate, int i);
|
|
||||||
|
|
||||||
|
|
||||||
typedef struct libinjection_sqli_state sfilter;
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Version info.
|
* Version info.
|
||||||
@ -207,109 +30,18 @@ typedef struct libinjection_sqli_state sfilter;
|
|||||||
* See python's normalized version
|
* See python's normalized version
|
||||||
* http://www.python.org/dev/peps/pep-0386/#normalizedversion
|
* http://www.python.org/dev/peps/pep-0386/#normalizedversion
|
||||||
*/
|
*/
|
||||||
const char* libinjection_version();
|
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.
|
||||||
*/
|
*/
|
||||||
void libinjection_sqli_init(struct libinjection_sqli_state* sql_state,
|
int libinjection_sqli(const char* s, size_t slen, char fingerprint[]);
|
||||||
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
|
|
||||||
* \param s
|
|
||||||
* \param slen
|
|
||||||
* \param fn a pointer to a function that determines if a fingerprint
|
|
||||||
* is a match or not. If NULL, then a hardwired list is
|
|
||||||
* used. Useful for loading fingerprints data from custom
|
|
||||||
* sources.
|
|
||||||
*
|
|
||||||
* \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
|
|
||||||
*
|
|
||||||
* \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
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,14 @@
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
#define LIBINJECTION_VERSION "3.8.0"
|
#include "libinjection.h"
|
||||||
|
#include "libinjection_sqli.h"
|
||||||
|
#include "libinjection_sqli_data.h"
|
||||||
|
|
||||||
|
#define LIBINJECTION_VERSION "3.9.1"
|
||||||
|
|
||||||
|
#define LIBINJECTION_SQLI_TOKEN_SIZE sizeof(((stoken_t*)(0))->val)
|
||||||
|
#define LIBINJECTION_SQLI_MAX_TOKENS 5
|
||||||
|
|
||||||
#ifndef TRUE
|
#ifndef TRUE
|
||||||
#define TRUE 1
|
#define TRUE 1
|
||||||
@ -37,40 +44,38 @@
|
|||||||
#define FOLD_DEBUG
|
#define FOLD_DEBUG
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "libinjection_sqli_data.h"
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* not making public just yet
|
* not making public just yet
|
||||||
*/
|
*/
|
||||||
typedef enum {
|
typedef enum {
|
||||||
TYPE_NONE = 0,
|
TYPE_NONE = 0
|
||||||
TYPE_KEYWORD = (int)'k',
|
, TYPE_KEYWORD = (int)'k'
|
||||||
TYPE_UNION = (int)'U',
|
, TYPE_UNION = (int)'U'
|
||||||
TYPE_GROUP = (int)'B',
|
, TYPE_GROUP = (int)'B'
|
||||||
TYPE_EXPRESSION = (int)'E',
|
, TYPE_EXPRESSION = (int)'E'
|
||||||
TYPE_SQLTYPE = (int)'t',
|
, TYPE_SQLTYPE = (int)'t'
|
||||||
TYPE_FUNCTION = (int)'f',
|
, TYPE_FUNCTION = (int)'f'
|
||||||
TYPE_BAREWORD = (int)'n',
|
, TYPE_BAREWORD = (int)'n'
|
||||||
TYPE_NUMBER = (int)'1',
|
, TYPE_NUMBER = (int)'1'
|
||||||
TYPE_VARIABLE = (int)'v',
|
, TYPE_VARIABLE = (int)'v'
|
||||||
TYPE_STRING = (int)'s',
|
, TYPE_STRING = (int)'s'
|
||||||
TYPE_OPERATOR = (int)'o',
|
, TYPE_OPERATOR = (int)'o'
|
||||||
TYPE_LOGIC_OPERATOR = (int)'&',
|
, TYPE_LOGIC_OPERATOR = (int)'&'
|
||||||
TYPE_COMMENT = (int)'c',
|
, TYPE_COMMENT = (int)'c'
|
||||||
TYPE_COLLATE = (int)'A',
|
, TYPE_COLLATE = (int)'A'
|
||||||
TYPE_LEFTPARENS = (int)'(',
|
, TYPE_LEFTPARENS = (int)'('
|
||||||
TYPE_RIGHTPARENS = (int)')', /* not used? */
|
, TYPE_RIGHTPARENS = (int)')' /* not used? */
|
||||||
TYPE_LEFTBRACE = (int)'{',
|
, TYPE_LEFTBRACE = (int)'{'
|
||||||
TYPE_RIGHTBRACE = (int)'}',
|
, TYPE_RIGHTBRACE = (int)'}'
|
||||||
TYPE_DOT = (int)'.',
|
, TYPE_DOT = (int)'.'
|
||||||
TYPE_COMMA = (int)',',
|
, TYPE_COMMA = (int)','
|
||||||
TYPE_COLON = (int)':',
|
, TYPE_COLON = (int)':'
|
||||||
TYPE_SEMICOLON = (int)';',
|
, TYPE_SEMICOLON = (int)';'
|
||||||
TYPE_TSQL = (int)'T', /* TSQL start */
|
, TYPE_TSQL = (int)'T' /* TSQL start */
|
||||||
TYPE_UNKNOWN = (int)'?',
|
, TYPE_UNKNOWN = (int)'?'
|
||||||
TYPE_EVIL = (int)'X', /* unparsable, abort */
|
, TYPE_EVIL = (int)'X' /* unparsable, abort */
|
||||||
TYPE_FINGERPRINT = (int)'F', /* not really a token */
|
, TYPE_FINGERPRINT = (int)'F' /* not really a token */
|
||||||
TYPE_BACKSLASH = (int)'\\'
|
, TYPE_BACKSLASH = (int)'\\'
|
||||||
} sqli_token_types;
|
} sqli_token_types;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -392,8 +397,8 @@ static size_t parse_eol_comment(struct libinjection_sqli_state * sf)
|
|||||||
st_assign(sf->current, TYPE_COMMENT, pos, slen - pos, cs + pos);
|
st_assign(sf->current, TYPE_COMMENT, pos, slen - pos, cs + pos);
|
||||||
return slen;
|
return slen;
|
||||||
} else {
|
} else {
|
||||||
st_assign(sf->current, TYPE_COMMENT, pos, endpos - cs - pos, cs + pos);
|
st_assign(sf->current, TYPE_COMMENT, pos, (size_t)(endpos - cs) - pos, cs + pos);
|
||||||
return (endpos - cs) + 1;
|
return (size_t)((endpos - cs) + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -511,7 +516,7 @@ static size_t parse_slash(struct libinjection_sqli_state * sf)
|
|||||||
/* till end of line */
|
/* till end of line */
|
||||||
clen = slen - pos;
|
clen = slen - pos;
|
||||||
} else {
|
} else {
|
||||||
clen = (ptr + 2) - cur;
|
clen = (size_t)(ptr + 2 - cur);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -524,7 +529,7 @@ static size_t parse_slash(struct libinjection_sqli_state * sf)
|
|||||||
* are an automatic black ban!
|
* are an automatic black ban!
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (memchr2(cur + 2, ptr - (cur + 1), '/', '*') != NULL) {
|
if (memchr2(cur + 2, (size_t)(ptr - (cur + 1)), '/', '*') != NULL) {
|
||||||
ctype = TYPE_EVIL;
|
ctype = TYPE_EVIL;
|
||||||
} else if (is_mysql_comment(cs, slen, pos)) {
|
} else if (is_mysql_comment(cs, slen, pos)) {
|
||||||
ctype = TYPE_EVIL;
|
ctype = TYPE_EVIL;
|
||||||
@ -621,7 +626,7 @@ static size_t is_double_delim_escaped(const char* cur, const char* end)
|
|||||||
return ((cur + 1) < end) && *(cur+1) == *cur;
|
return ((cur + 1) < end) && *(cur+1) == *cur;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Look forward for doubling of deliminter
|
/* Look forward for doubling of delimiter
|
||||||
*
|
*
|
||||||
* case 'foo''bar' --> foo''bar
|
* case 'foo''bar' --> foo''bar
|
||||||
*
|
*
|
||||||
@ -667,20 +672,20 @@ static size_t parse_string_core(const char *cs, const size_t len, size_t pos,
|
|||||||
/* keep going, move ahead one character */
|
/* keep going, move ahead one character */
|
||||||
qpos =
|
qpos =
|
||||||
(const char *) memchr((const void *) (qpos + 1), delim,
|
(const char *) memchr((const void *) (qpos + 1), delim,
|
||||||
(cs + len) - (qpos + 1));
|
(size_t)((cs + len) - (qpos + 1)));
|
||||||
continue;
|
continue;
|
||||||
} else if (is_double_delim_escaped(qpos, cs + len)) {
|
} else if (is_double_delim_escaped(qpos, cs + len)) {
|
||||||
/* keep going, move ahead two characters */
|
/* keep going, move ahead two characters */
|
||||||
qpos =
|
qpos =
|
||||||
(const char *) memchr((const void *) (qpos + 2), delim,
|
(const char *) memchr((const void *) (qpos + 2), delim,
|
||||||
(cs + len) - (qpos + 2));
|
(size_t)((cs + len) - (qpos + 2)));
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
/* hey it's a normal string */
|
/* hey it's a normal string */
|
||||||
st_assign(st, TYPE_STRING, pos + offset,
|
st_assign(st, TYPE_STRING, pos + offset,
|
||||||
qpos - (cs + pos + offset), cs + pos + offset);
|
(size_t)(qpos - (cs + pos + offset)), cs + pos + offset);
|
||||||
st->str_close = delim;
|
st->str_close = delim;
|
||||||
return qpos - cs + 1;
|
return (size_t)(qpos - cs + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -736,7 +741,7 @@ static size_t parse_ustring(struct libinjection_sqli_state * sf)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t parse_qstring_core(struct libinjection_sqli_state * sf, int offset)
|
static size_t parse_qstring_core(struct libinjection_sqli_state * sf, size_t offset)
|
||||||
{
|
{
|
||||||
char ch;
|
char ch;
|
||||||
const char *strend;
|
const char *strend;
|
||||||
@ -779,10 +784,10 @@ static size_t parse_qstring_core(struct libinjection_sqli_state * sf, int offset
|
|||||||
sf->current->str_close = CHAR_NULL;
|
sf->current->str_close = CHAR_NULL;
|
||||||
return slen;
|
return slen;
|
||||||
} else {
|
} else {
|
||||||
st_assign(sf->current, TYPE_STRING, pos + 3, strend - cs - pos - 3, cs + pos + 3);
|
st_assign(sf->current, TYPE_STRING, pos + 3, (size_t)(strend - cs) - pos - 3, cs + pos + 3);
|
||||||
sf->current->str_open = 'q';
|
sf->current->str_open = 'q';
|
||||||
sf->current->str_close = 'q';
|
sf->current->str_close = 'q';
|
||||||
return (strend - cs) + 2;
|
return (size_t)(strend - cs + 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -878,8 +883,8 @@ static size_t parse_bword(struct libinjection_sqli_state * sf)
|
|||||||
st_assign(sf->current, TYPE_BAREWORD, pos, sf->slen - pos, cs + pos);
|
st_assign(sf->current, TYPE_BAREWORD, pos, sf->slen - pos, cs + pos);
|
||||||
return sf->slen;
|
return sf->slen;
|
||||||
} else {
|
} else {
|
||||||
st_assign(sf->current, TYPE_BAREWORD, pos, (endptr - cs) - pos + 1, cs + pos);
|
st_assign(sf->current, TYPE_BAREWORD, pos, (size_t)(endptr - cs) - pos + 1, cs + pos);
|
||||||
return (endptr - cs) + 1;
|
return (size_t)((endptr - cs) + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1042,10 +1047,11 @@ static size_t parse_money(struct libinjection_sqli_state *sf)
|
|||||||
sf->current->str_close = CHAR_NULL;
|
sf->current->str_close = CHAR_NULL;
|
||||||
return slen;
|
return slen;
|
||||||
} else {
|
} else {
|
||||||
st_assign(sf->current, TYPE_STRING, pos + 2, strend - (cs + pos + 2), cs + pos + 2);
|
st_assign(sf->current, TYPE_STRING, pos + 2,
|
||||||
|
(size_t)(strend - (cs + pos + 2)), cs + pos + 2);
|
||||||
sf->current->str_open = '$';
|
sf->current->str_open = '$';
|
||||||
sf->current->str_close = '$';
|
sf->current->str_close = '$';
|
||||||
return strend - cs + 2;
|
return (size_t)(strend - cs + 2);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/* ok it's not a number or '$$', but maybe it's pgsql "$ quoted strings" */
|
/* ok it's not a number or '$$', but maybe it's pgsql "$ quoted strings" */
|
||||||
@ -1074,10 +1080,11 @@ static size_t parse_money(struct libinjection_sqli_state *sf)
|
|||||||
return slen;
|
return slen;
|
||||||
} else {
|
} else {
|
||||||
/* got one */
|
/* got one */
|
||||||
st_assign(sf->current, TYPE_STRING, pos+xlen+2, strend - (cs + pos + xlen + 2), cs+pos+xlen+2);
|
st_assign(sf->current, TYPE_STRING, pos+xlen+2,
|
||||||
|
(size_t)(strend - (cs + pos + xlen + 2)), cs+pos+xlen+2);
|
||||||
sf->current->str_open = '$';
|
sf->current->str_open = '$';
|
||||||
sf->current->str_close = '$';
|
sf->current->str_close = '$';
|
||||||
return (strend + xlen + 2) - cs;
|
return (size_t)((strend + xlen + 2) - cs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (xlen == 1 && cs[pos + 1] == '.') {
|
} else if (xlen == 1 && cs[pos + 1] == '.') {
|
||||||
@ -1232,7 +1239,7 @@ int libinjection_sqli_tokenize(struct libinjection_sqli_state * sf)
|
|||||||
/*
|
/*
|
||||||
* get current character
|
* get current character
|
||||||
*/
|
*/
|
||||||
const unsigned char ch = (unsigned int) (s[*pos]);
|
const unsigned char ch = (unsigned char) (s[*pos]);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* if not ascii, then continue...
|
* if not ascii, then continue...
|
||||||
@ -1399,7 +1406,10 @@ int libinjection_sqli_fold(struct libinjection_sqli_state * sf)
|
|||||||
sf->current = &(sf->tokenvec[0]);
|
sf->current = &(sf->tokenvec[0]);
|
||||||
while (more) {
|
while (more) {
|
||||||
more = libinjection_sqli_tokenize(sf);
|
more = libinjection_sqli_tokenize(sf);
|
||||||
if ( ! (sf->current->type == TYPE_COMMENT || sf->current->type == TYPE_LEFTPARENS || st_is_unary_op(sf->current))) {
|
if ( ! (sf->current->type == TYPE_COMMENT ||
|
||||||
|
sf->current->type == TYPE_LEFTPARENS ||
|
||||||
|
sf->current->type == TYPE_SQLTYPE ||
|
||||||
|
st_is_unary_op(sf->current))) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1444,12 +1454,10 @@ int libinjection_sqli_fold(struct libinjection_sqli_state * sf)
|
|||||||
)
|
)
|
||||||
{
|
{
|
||||||
if (pos > LIBINJECTION_SQLI_MAX_TOKENS) {
|
if (pos > LIBINJECTION_SQLI_MAX_TOKENS) {
|
||||||
//printf("TRUNCATE pos = %lu vs. %lu\n", pos, LIBINJECTION_SQLI_MAX_TOKENS);
|
|
||||||
st_copy(&(sf->tokenvec[1]), &(sf->tokenvec[LIBINJECTION_SQLI_MAX_TOKENS]));
|
st_copy(&(sf->tokenvec[1]), &(sf->tokenvec[LIBINJECTION_SQLI_MAX_TOKENS]));
|
||||||
pos = 2;
|
pos = 2;
|
||||||
left = 0;
|
left = 0;
|
||||||
} else {
|
} else {
|
||||||
//printf("HEREIAM\n");
|
|
||||||
pos = 1;
|
pos = 1;
|
||||||
left = 0;
|
left = 0;
|
||||||
}
|
}
|
||||||
@ -1510,7 +1518,8 @@ int libinjection_sqli_fold(struct libinjection_sqli_state * sf)
|
|||||||
continue; /* reparse everything, but we probably can advance left, and pos */
|
continue; /* reparse everything, but we probably can advance left, and pos */
|
||||||
} else if ((sf->tokenvec[left].type == TYPE_OPERATOR ||
|
} else if ((sf->tokenvec[left].type == TYPE_OPERATOR ||
|
||||||
sf->tokenvec[left].type == TYPE_LOGIC_OPERATOR) &&
|
sf->tokenvec[left].type == TYPE_LOGIC_OPERATOR) &&
|
||||||
st_is_unary_op(&sf->tokenvec[left+1])) {
|
(st_is_unary_op(&sf->tokenvec[left+1]) ||
|
||||||
|
sf->tokenvec[left+1].type == TYPE_SQLTYPE)) {
|
||||||
pos -= 1;
|
pos -= 1;
|
||||||
sf->stats_folds += 1;
|
sf->stats_folds += 1;
|
||||||
left = 0;
|
left = 0;
|
||||||
@ -1587,9 +1596,20 @@ int libinjection_sqli_fold(struct libinjection_sqli_state * sf)
|
|||||||
* "foo" = LIKE(1,2)
|
* "foo" = LIKE(1,2)
|
||||||
*/
|
*/
|
||||||
continue;
|
continue;
|
||||||
|
} else if ((sf->tokenvec[left].type == TYPE_OPERATOR) && (
|
||||||
|
cstrcasecmp("LIKE", sf->tokenvec[left].val, sf->tokenvec[left].len) == 0 ||
|
||||||
|
cstrcasecmp("NOT LIKE", sf->tokenvec[left].val, sf->tokenvec[left].len) == 0)) {
|
||||||
|
if (sf->tokenvec[left+1].type == TYPE_LEFTPARENS) {
|
||||||
|
/* SELECT LIKE(...
|
||||||
|
* it's a function
|
||||||
|
*/
|
||||||
|
sf->tokenvec[left].type = TYPE_FUNCTION;
|
||||||
|
}
|
||||||
} else if (sf->tokenvec[left].type == TYPE_SQLTYPE &&
|
} else if (sf->tokenvec[left].type == TYPE_SQLTYPE &&
|
||||||
(sf->tokenvec[left+1].type == TYPE_BAREWORD ||
|
(sf->tokenvec[left+1].type == TYPE_BAREWORD ||
|
||||||
sf->tokenvec[left+1].type == TYPE_NUMBER ||
|
sf->tokenvec[left+1].type == TYPE_NUMBER ||
|
||||||
|
sf->tokenvec[left+1].type == TYPE_SQLTYPE ||
|
||||||
|
sf->tokenvec[left+1].type == TYPE_LEFTPARENS ||
|
||||||
sf->tokenvec[left+1].type == TYPE_FUNCTION ||
|
sf->tokenvec[left+1].type == TYPE_FUNCTION ||
|
||||||
sf->tokenvec[left+1].type == TYPE_VARIABLE ||
|
sf->tokenvec[left+1].type == TYPE_VARIABLE ||
|
||||||
sf->tokenvec[left+1].type == TYPE_STRING)) {
|
sf->tokenvec[left+1].type == TYPE_STRING)) {
|
||||||
@ -1606,6 +1626,7 @@ int libinjection_sqli_fold(struct libinjection_sqli_state * sf)
|
|||||||
*/
|
*/
|
||||||
if (strchr(sf->tokenvec[left+1].val, '_') != NULL) {
|
if (strchr(sf->tokenvec[left+1].val, '_') != NULL) {
|
||||||
sf->tokenvec[left+1].type = TYPE_SQLTYPE;
|
sf->tokenvec[left+1].type = TYPE_SQLTYPE;
|
||||||
|
left = 0;
|
||||||
}
|
}
|
||||||
} else if (sf->tokenvec[left].type == TYPE_BACKSLASH) {
|
} else if (sf->tokenvec[left].type == TYPE_BACKSLASH) {
|
||||||
if (st_is_arithmetic_op(&(sf->tokenvec[left+1]))) {
|
if (st_is_arithmetic_op(&(sf->tokenvec[left+1]))) {
|
||||||
@ -1622,31 +1643,52 @@ int libinjection_sqli_fold(struct libinjection_sqli_state * sf)
|
|||||||
} else if (sf->tokenvec[left].type == TYPE_LEFTPARENS &&
|
} else if (sf->tokenvec[left].type == TYPE_LEFTPARENS &&
|
||||||
sf->tokenvec[left+1].type == TYPE_LEFTPARENS) {
|
sf->tokenvec[left+1].type == TYPE_LEFTPARENS) {
|
||||||
pos -= 1;
|
pos -= 1;
|
||||||
|
left = 0;
|
||||||
sf->stats_folds += 1;
|
sf->stats_folds += 1;
|
||||||
continue;
|
continue;
|
||||||
} else if (sf->tokenvec[left].type == TYPE_RIGHTPARENS &&
|
} else if (sf->tokenvec[left].type == TYPE_RIGHTPARENS &&
|
||||||
sf->tokenvec[left+1].type == TYPE_RIGHTPARENS) {
|
sf->tokenvec[left+1].type == TYPE_RIGHTPARENS) {
|
||||||
pos -= 1;
|
pos -= 1;
|
||||||
sf->stats_folds += 1;
|
left = 0;
|
||||||
continue;
|
|
||||||
} else if (sf->tokenvec[left].type == TYPE_SQLTYPE &&
|
|
||||||
sf->tokenvec[left+1].type == TYPE_SQLTYPE) {
|
|
||||||
pos -= 1;
|
|
||||||
sf->stats_folds += 1;
|
sf->stats_folds += 1;
|
||||||
continue;
|
continue;
|
||||||
} else if (sf->tokenvec[left].type == TYPE_LEFTBRACE &&
|
} else if (sf->tokenvec[left].type == TYPE_LEFTBRACE &&
|
||||||
sf->tokenvec[left+1].type == TYPE_BAREWORD) {
|
sf->tokenvec[left+1].type == TYPE_BAREWORD) {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* MySQL Degenerate case --
|
||||||
|
*
|
||||||
|
* select { ``.``.id }; -- valid !!!
|
||||||
|
* select { ``.``.``.id }; -- invalid
|
||||||
|
* select ``.``.id; -- invalid
|
||||||
|
* select { ``.id }; -- invalid
|
||||||
|
*
|
||||||
|
* so it appears {``.``.id} is a magic case
|
||||||
|
* I suspect this is "current database, current table, field id"
|
||||||
|
*
|
||||||
|
* The folding code can't look at more than 3 tokens, and
|
||||||
|
* I don't want to make two passes.
|
||||||
|
*
|
||||||
|
* Since "{ ``" so rare, we are just going to blacklist it.
|
||||||
|
*
|
||||||
|
* Highly likely this will need revisiting!
|
||||||
|
*
|
||||||
|
* CREDIT @rsalgado 2013-11-25
|
||||||
|
*/
|
||||||
|
if (sf->tokenvec[left+1].len == 0) {
|
||||||
|
sf->tokenvec[left+1].type = TYPE_EVIL;
|
||||||
|
return (int)(left+2);
|
||||||
|
}
|
||||||
/* weird ODBC / MYSQL {foo expr} --> expr
|
/* weird ODBC / MYSQL {foo expr} --> expr
|
||||||
* but for this rule we just strip away the "{ foo" part
|
* but for this rule we just strip away the "{ foo" part
|
||||||
*/
|
*/
|
||||||
if (left > 0) {
|
left = 0;
|
||||||
left -= 1;
|
|
||||||
}
|
|
||||||
pos -= 2;
|
pos -= 2;
|
||||||
sf->stats_folds += 2;
|
sf->stats_folds += 2;
|
||||||
continue;
|
continue;
|
||||||
} else if (sf->tokenvec[left+1].type == TYPE_RIGHTBRACE) {
|
} else if (sf->tokenvec[left+1].type == TYPE_RIGHTBRACE) {
|
||||||
pos -= 1;
|
pos -= 1;
|
||||||
|
left = 0;
|
||||||
sf->stats_folds += 1;
|
sf->stats_folds += 1;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -1681,18 +1723,18 @@ int libinjection_sqli_fold(struct libinjection_sqli_state * sf)
|
|||||||
sf->tokenvec[left+1].type == TYPE_OPERATOR &&
|
sf->tokenvec[left+1].type == TYPE_OPERATOR &&
|
||||||
sf->tokenvec[left+2].type == TYPE_NUMBER) {
|
sf->tokenvec[left+2].type == TYPE_NUMBER) {
|
||||||
pos -= 2;
|
pos -= 2;
|
||||||
|
left = 0;
|
||||||
continue;
|
continue;
|
||||||
} else if (sf->tokenvec[left].type == TYPE_OPERATOR &&
|
} else if (sf->tokenvec[left].type == TYPE_OPERATOR &&
|
||||||
sf->tokenvec[left+1].type != TYPE_LEFTPARENS &&
|
sf->tokenvec[left+1].type != TYPE_LEFTPARENS &&
|
||||||
sf->tokenvec[left+2].type == TYPE_OPERATOR) {
|
sf->tokenvec[left+2].type == TYPE_OPERATOR) {
|
||||||
if (left > 0) {
|
left = 0;
|
||||||
left -= 1;
|
|
||||||
}
|
|
||||||
pos -= 2;
|
pos -= 2;
|
||||||
continue;
|
continue;
|
||||||
} else if (sf->tokenvec[left].type == TYPE_LOGIC_OPERATOR &&
|
} else if (sf->tokenvec[left].type == TYPE_LOGIC_OPERATOR &&
|
||||||
sf->tokenvec[left+2].type == TYPE_LOGIC_OPERATOR) {
|
sf->tokenvec[left+2].type == TYPE_LOGIC_OPERATOR) {
|
||||||
pos -= 2;
|
pos -= 2;
|
||||||
|
left = 0;
|
||||||
continue;
|
continue;
|
||||||
} else if (sf->tokenvec[left].type == TYPE_VARIABLE &&
|
} else if (sf->tokenvec[left].type == TYPE_VARIABLE &&
|
||||||
sf->tokenvec[left+1].type == TYPE_OPERATOR &&
|
sf->tokenvec[left+1].type == TYPE_OPERATOR &&
|
||||||
@ -1700,6 +1742,7 @@ int libinjection_sqli_fold(struct libinjection_sqli_state * sf)
|
|||||||
sf->tokenvec[left+2].type == TYPE_NUMBER ||
|
sf->tokenvec[left+2].type == TYPE_NUMBER ||
|
||||||
sf->tokenvec[left+2].type == TYPE_BAREWORD)) {
|
sf->tokenvec[left+2].type == TYPE_BAREWORD)) {
|
||||||
pos -= 2;
|
pos -= 2;
|
||||||
|
left = 0;
|
||||||
continue;
|
continue;
|
||||||
} else if ((sf->tokenvec[left].type == TYPE_BAREWORD ||
|
} else if ((sf->tokenvec[left].type == TYPE_BAREWORD ||
|
||||||
sf->tokenvec[left].type == TYPE_NUMBER ) &&
|
sf->tokenvec[left].type == TYPE_NUMBER ) &&
|
||||||
@ -1707,6 +1750,7 @@ int libinjection_sqli_fold(struct libinjection_sqli_state * sf)
|
|||||||
(sf->tokenvec[left+2].type == TYPE_NUMBER ||
|
(sf->tokenvec[left+2].type == TYPE_NUMBER ||
|
||||||
sf->tokenvec[left+2].type == TYPE_BAREWORD)) {
|
sf->tokenvec[left+2].type == TYPE_BAREWORD)) {
|
||||||
pos -= 2;
|
pos -= 2;
|
||||||
|
left = 0;
|
||||||
continue;
|
continue;
|
||||||
} else if ((sf->tokenvec[left].type == TYPE_BAREWORD ||
|
} else if ((sf->tokenvec[left].type == TYPE_BAREWORD ||
|
||||||
sf->tokenvec[left].type == TYPE_NUMBER ||
|
sf->tokenvec[left].type == TYPE_NUMBER ||
|
||||||
@ -1716,6 +1760,7 @@ int libinjection_sqli_fold(struct libinjection_sqli_state * sf)
|
|||||||
streq(sf->tokenvec[left+1].val, "::") &&
|
streq(sf->tokenvec[left+1].val, "::") &&
|
||||||
sf->tokenvec[left+2].type == TYPE_SQLTYPE) {
|
sf->tokenvec[left+2].type == TYPE_SQLTYPE) {
|
||||||
pos -= 2;
|
pos -= 2;
|
||||||
|
left = 0;
|
||||||
sf->stats_folds += 2;
|
sf->stats_folds += 2;
|
||||||
continue;
|
continue;
|
||||||
} else if ((sf->tokenvec[left].type == TYPE_BAREWORD ||
|
} else if ((sf->tokenvec[left].type == TYPE_BAREWORD ||
|
||||||
@ -1728,9 +1773,7 @@ int libinjection_sqli_fold(struct libinjection_sqli_state * sf)
|
|||||||
sf->tokenvec[left+2].type == TYPE_STRING ||
|
sf->tokenvec[left+2].type == TYPE_STRING ||
|
||||||
sf->tokenvec[left+2].type == TYPE_VARIABLE)) {
|
sf->tokenvec[left+2].type == TYPE_VARIABLE)) {
|
||||||
pos -= 2;
|
pos -= 2;
|
||||||
if (left > 0) {
|
left = 0;
|
||||||
left -= 1;
|
|
||||||
}
|
|
||||||
continue;
|
continue;
|
||||||
} else if ((sf->tokenvec[left].type == TYPE_EXPRESSION ||
|
} else if ((sf->tokenvec[left].type == TYPE_EXPRESSION ||
|
||||||
sf->tokenvec[left].type == TYPE_GROUP ||
|
sf->tokenvec[left].type == TYPE_GROUP ||
|
||||||
@ -1742,6 +1785,7 @@ int libinjection_sqli_fold(struct libinjection_sqli_state * sf)
|
|||||||
*/
|
*/
|
||||||
st_copy(&sf->tokenvec[left+1], &sf->tokenvec[left+2]);
|
st_copy(&sf->tokenvec[left+1], &sf->tokenvec[left+2]);
|
||||||
pos -= 1;
|
pos -= 1;
|
||||||
|
left = 0;
|
||||||
continue;
|
continue;
|
||||||
} else if ((sf->tokenvec[left].type == TYPE_KEYWORD ||
|
} else if ((sf->tokenvec[left].type == TYPE_KEYWORD ||
|
||||||
sf->tokenvec[left].type == TYPE_EXPRESSION ||
|
sf->tokenvec[left].type == TYPE_EXPRESSION ||
|
||||||
@ -1757,6 +1801,7 @@ int libinjection_sqli_fold(struct libinjection_sqli_state * sf)
|
|||||||
*/
|
*/
|
||||||
st_copy(&sf->tokenvec[left+1], &sf->tokenvec[left+2]);
|
st_copy(&sf->tokenvec[left+1], &sf->tokenvec[left+2]);
|
||||||
pos -= 1;
|
pos -= 1;
|
||||||
|
left = 0;
|
||||||
continue;
|
continue;
|
||||||
} else if (sf->tokenvec[left].type == TYPE_COMMA &&
|
} else if (sf->tokenvec[left].type == TYPE_COMMA &&
|
||||||
st_is_unary_op(&sf->tokenvec[left+1]) &&
|
st_is_unary_op(&sf->tokenvec[left+1]) &&
|
||||||
@ -1770,9 +1815,7 @@ int libinjection_sqli_fold(struct libinjection_sqli_state * sf)
|
|||||||
* "1,-1" --> "1"
|
* "1,-1" --> "1"
|
||||||
*/
|
*/
|
||||||
st_copy(&sf->tokenvec[left+1], &sf->tokenvec[left+2]);
|
st_copy(&sf->tokenvec[left+1], &sf->tokenvec[left+2]);
|
||||||
if (left > 0) {
|
left = 0;
|
||||||
left -= 1;
|
|
||||||
}
|
|
||||||
/* pos is >= 3 so this is safe */
|
/* pos is >= 3 so this is safe */
|
||||||
assert(pos >= 3);
|
assert(pos >= 3);
|
||||||
pos -= 3;
|
pos -= 3;
|
||||||
@ -1781,7 +1824,7 @@ int libinjection_sqli_fold(struct libinjection_sqli_state * sf)
|
|||||||
st_is_unary_op(&sf->tokenvec[left+1]) &&
|
st_is_unary_op(&sf->tokenvec[left+1]) &&
|
||||||
sf->tokenvec[left+2].type == TYPE_FUNCTION) {
|
sf->tokenvec[left+2].type == TYPE_FUNCTION) {
|
||||||
|
|
||||||
/* Seperate case from above since you end up with
|
/* Separate case from above since you end up with
|
||||||
* 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)
|
||||||
@ -1789,6 +1832,7 @@ int libinjection_sqli_fold(struct libinjection_sqli_state * sf)
|
|||||||
*/
|
*/
|
||||||
st_copy(&sf->tokenvec[left+1], &sf->tokenvec[left+2]);
|
st_copy(&sf->tokenvec[left+1], &sf->tokenvec[left+2]);
|
||||||
pos -= 1;
|
pos -= 1;
|
||||||
|
left = 0;
|
||||||
continue;
|
continue;
|
||||||
} else if ((sf->tokenvec[left].type == TYPE_BAREWORD) &&
|
} else if ((sf->tokenvec[left].type == TYPE_BAREWORD) &&
|
||||||
(sf->tokenvec[left+1].type == TYPE_DOT) &&
|
(sf->tokenvec[left+1].type == TYPE_DOT) &&
|
||||||
@ -1798,6 +1842,7 @@ int libinjection_sqli_fold(struct libinjection_sqli_state * sf)
|
|||||||
*/
|
*/
|
||||||
assert(pos >= 3);
|
assert(pos >= 3);
|
||||||
pos -= 2;
|
pos -= 2;
|
||||||
|
left = 0;
|
||||||
continue;
|
continue;
|
||||||
} else if ((sf->tokenvec[left].type == TYPE_EXPRESSION) &&
|
} else if ((sf->tokenvec[left].type == TYPE_EXPRESSION) &&
|
||||||
(sf->tokenvec[left+1].type == TYPE_DOT) &&
|
(sf->tokenvec[left+1].type == TYPE_DOT) &&
|
||||||
@ -1805,6 +1850,7 @@ int libinjection_sqli_fold(struct libinjection_sqli_state * sf)
|
|||||||
/* select . `foo` --> select `foo` */
|
/* select . `foo` --> select `foo` */
|
||||||
st_copy(&sf->tokenvec[left+1], &sf->tokenvec[left+2]);
|
st_copy(&sf->tokenvec[left+1], &sf->tokenvec[left+2]);
|
||||||
pos -= 1;
|
pos -= 1;
|
||||||
|
left = 0;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1906,12 +1952,6 @@ const char* libinjection_sqli_fingerprint(struct libinjection_sqli_state * sql_s
|
|||||||
return sql_state->fingerprint;
|
return sql_state->fingerprint;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
#define UNUSED(x) (void)(x)
|
|
||||||
|
|
||||||
int libinjection_sqli_check_fingerprint(struct libinjection_sqli_state* sql_state)
|
int libinjection_sqli_check_fingerprint(struct libinjection_sqli_state* sql_state)
|
||||||
{
|
{
|
||||||
return libinjection_sqli_blacklist(sql_state) &&
|
return libinjection_sqli_blacklist(sql_state) &&
|
||||||
@ -1934,7 +1974,7 @@ int libinjection_sqli_blacklist(struct libinjection_sqli_state* sql_state)
|
|||||||
* use minimum of 8 bytes to make sure gcc -fstack-protector
|
* use minimum of 8 bytes to make sure gcc -fstack-protector
|
||||||
* works correctly
|
* works correctly
|
||||||
*/
|
*/
|
||||||
char fp2[LIBINJECTION_SQLI_MAX_TOKENS + 2 < 8 ? 8 : LIBINJECTION_SQLI_MAX_TOKENS + 2];
|
char fp2[8];
|
||||||
char ch;
|
char ch;
|
||||||
size_t i;
|
size_t i;
|
||||||
size_t len = strlen(sql_state->fingerprint);
|
size_t len = strlen(sql_state->fingerprint);
|
||||||
@ -1994,7 +2034,7 @@ 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 appearantly ignores anything with
|
* MS Audit log apparently ignores anything with
|
||||||
* 'sp_password' in it. Unable to find primary refernece 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
|
||||||
@ -2191,7 +2231,7 @@ static int reparse_as_mysql(struct libinjection_sqli_state * sql_state)
|
|||||||
struct libinjection_sqli_token*
|
struct libinjection_sqli_token*
|
||||||
libinjection_sqli_get_token(struct libinjection_sqli_state * sql_state, int i)
|
libinjection_sqli_get_token(struct libinjection_sqli_state * sql_state, int i)
|
||||||
{
|
{
|
||||||
if (i < 0 || i > LIBINJECTION_SQLI_MAX_TOKENS) {
|
if (i < 0 || i > (int)LIBINJECTION_SQLI_MAX_TOKENS) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
return &(sql_state->tokenvec[i]);
|
return &(sql_state->tokenvec[i]);
|
||||||
@ -2263,3 +2303,18 @@ int libinjection_is_sqli(struct libinjection_sqli_state * sql_state)
|
|||||||
*/
|
*/
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int libinjection_sqli(const char* input, size_t slen, char fingerprint[])
|
||||||
|
{
|
||||||
|
int issqli;
|
||||||
|
struct libinjection_sqli_state state;
|
||||||
|
|
||||||
|
libinjection_sqli_init(&state, input, slen, 0);
|
||||||
|
issqli = libinjection_is_sqli(&state);
|
||||||
|
if (issqli) {
|
||||||
|
strcpy(fingerprint, state.fingerprint);
|
||||||
|
} else {
|
||||||
|
fingerprint[0] = '\0';
|
||||||
|
}
|
||||||
|
return issqli;
|
||||||
|
}
|
||||||
|
295
apache2/libinjection/libinjection_sqli.h
Normal file
295
apache2/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 */
|
File diff suppressed because it is too large
Load Diff
@ -2138,22 +2138,21 @@ static int msre_op_contains_execute(modsec_rec *msr, msre_rule *rule, msre_var *
|
|||||||
static int msre_op_detectSQLi_execute(modsec_rec *msr, msre_rule *rule, msre_var *var,
|
static int msre_op_detectSQLi_execute(modsec_rec *msr, msre_rule *rule, msre_var *var,
|
||||||
char **error_msg) {
|
char **error_msg) {
|
||||||
|
|
||||||
struct libinjection_sqli_state sqli_state;
|
char fingerprint[8];
|
||||||
int issqli;
|
int issqli;
|
||||||
int capture;
|
int capture;
|
||||||
|
|
||||||
libinjection_sqli_init(&sqli_state, var->value, var->value_len, 0);
|
issqli = libinjection_sqli(var->value, var->value_len, fingerprint);
|
||||||
issqli = libinjection_is_sqli(&sqli_state);
|
|
||||||
capture = apr_table_get(rule->actionset->actions, "capture") ? 1 : 0;
|
capture = apr_table_get(rule->actionset->actions, "capture") ? 1 : 0;
|
||||||
|
|
||||||
if (issqli) {
|
if (issqli) {
|
||||||
set_match_to_tx(msr, capture, sqli_state.fingerprint, 0);
|
set_match_to_tx(msr, capture, fingerprint, 0);
|
||||||
|
|
||||||
*error_msg = apr_psprintf(msr->mp, "detected SQLi using libinjection with fingerprint '%s'",
|
*error_msg = apr_psprintf(msr->mp, "detected SQLi using libinjection with fingerprint '%s'",
|
||||||
sqli_state.fingerprint);
|
fingerprint);
|
||||||
if (msr->txcfg->debuglog_level >= 9) {
|
if (msr->txcfg->debuglog_level >= 9) {
|
||||||
msr_log(msr, 9, "ISSQL: libinjection fingerprint '%s' matched input '%s'",
|
msr_log(msr, 9, "ISSQL: libinjection fingerprint '%s' matched input '%s'",
|
||||||
sqli_state.fingerprint,
|
fingerprint,
|
||||||
log_escape_ex(msr->mp, var->value, var->value_len));
|
log_escape_ex(msr->mp, var->value, var->value_len));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user