libinjection v3.0.0pre21 take 2

This commit is contained in:
Nick Galbreath
2013-06-23 13:58:22 +09:00
parent bebb45f3bb
commit c07b9a5362
4 changed files with 10438 additions and 6039 deletions

View File

@@ -3,25 +3,7 @@
* nickg@client9.com * nickg@client9.com
* BSD License -- see COPYING.txt for details * BSD License -- see COPYING.txt for details
* *
* * https://libinjection.client9.com/
* HOW TO USE:
*
* #include "libinjection.h"
*
* // Normalize query or postvar value
* // If it comes in urlencoded, then it's up to you
* // to urldecode it. If it's in correct form already
* // then nothing to do!
*
* sfilter s;
* int sqli = libinjection_is_sqli(&s, user_string, new_len,
* NULL, NULL);
*
* // 0 = not sqli
* // 1 = is sqli
*
* // That's it! sfilter s has some data on how it matched or not
* // details to come!
* *
*/ */
@@ -37,17 +19,38 @@ extern "C" {
* 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
*/ */
#define LIBINJECTION_VERSION "3.0.0-pre8" #define LIBINJECTION_VERSION "3.0.0-pre21"
#define ST_MAX_SIZE 32 /**
#define MAX_TOKENS 5 * 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
#define CHAR_NULL '\0' /**
#define CHAR_SINGLE '\'' * Number of tokens used to create a fingerprint
#define CHAR_DOUBLE '"' */
#ifndef LIBINJECTION_SQLI_MAX_TOKENS
#define LIBINJECTION_SQLI_MAX_TOKENS 5
#endif
#define COMMENTS_ANSI 0 enum lookup_type {
#define COMMENTS_MYSQL 1 FLAG_NONE = 0,
FLAG_QUOTE_NONE = 0,
FLAG_QUOTE_SINGLE = 1 << 1,
FLAG_QUOTE_DOUBLE = 1 << 2,
FLAG_SQL_ANSI = 1 << 3,
FLAG_SQL_MYSQL = 1 << 4,
LOOKUP_WORD,
LOOKUP_TYPE,
LOOKUP_OPERATOR,
LOOKUP_FINGERPRINT
};
typedef struct { typedef struct {
#ifdef SWIG #ifdef SWIG
@@ -56,35 +59,86 @@ typedef struct {
char type; char type;
char str_open; char str_open;
char str_close; char str_close;
char var_count;
char val[ST_MAX_SIZE];
} stoken_t;
typedef struct { /*
* 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[LIBINJECTION_SQLI_TOKEN_SIZE];
} 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);
typedef struct libinjection_sqli_state {
#ifdef SWIG #ifdef SWIG
%immutable; %immutable;
#endif #endif
/* input */ /*
* input, does not need to be null terminated.
* it is also not modified.
*/
const char *s; const char *s;
/*
* input length
*/
size_t slen; size_t slen;
/* current tokenize state */ /*
size_t pos; * How to lookup a word or fingerprint
int in_comment; */
ptr_lookup_fn lookup;
void* userdata;
/* final sqli data */ /*
*
*/
int flags;
/*
* pos is index in string we are at when tokenizing
*/
size_t pos;
/* MAX TOKENS + 1 since we use one extra token
* to determine the type of the previous token
*/
stoken_t tokenvec[LIBINJECTION_SQLI_MAX_TOKENS + 1];
/*
* Pointer to token position in tokenvec, above
*/
stoken_t *current; stoken_t *current;
/* MAX TOKENS + 1 since use one extra token to determine /*
the type of the previous token */ * fingerprint pattern c-string
stoken_t tokenvec[MAX_TOKENS + 1]; * +1 for ending null
*/
/* +1 for ending null */ char fingerprint[LIBINJECTION_SQLI_MAX_TOKENS + 1];
char pat[MAX_TOKENS + 1];
char delim;
char comment_style;
/*
* 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; int reason;
/* Number of ddw (dash-dash-white) comments /* Number of ddw (dash-dash-white) comments
@@ -107,16 +161,34 @@ typedef struct {
*/ */
int stats_comment_ddx; int stats_comment_ddx;
/*
* c-style comments found /x .. x/
*/
int stats_comment_c; int stats_comment_c;
/* '#' operators or mysql EOL comments found
*
*/
int stats_comment_hash;
/*
* number of tokens folded away
*/
int stats_folds; int stats_folds;
/*
* total tokens processed
*/
int stats_tokens;
} sfilter; } sfilter;
/** /**
* Pointer to function, takes cstr input, returns 1 for true, 0 for false *
*/ */
typedef int (*ptr_fingerprints_fn)(sfilter*, void* callbackarg); void libinjection_sqli_init(sfilter* sql_state,
const char* s, size_t slen,
int flags);
/** /**
* Main API: tests for SQLi in three possible contexts, no quotes, * Main API: tests for SQLi in three possible contexts, no quotes,
@@ -129,57 +201,54 @@ typedef int (*ptr_fingerprints_fn)(sfilter*, void* callbackarg);
* is a match or not. If NULL, then a hardwired list is * is a match or not. If NULL, then a hardwired list is
* used. Useful for loading fingerprints data from custom * used. Useful for loading fingerprints data from custom
* sources. * sources.
* \param callbackarg. For default case, use NULL
* *
* \return 1 (true) if SQLi, 0 (false) if benign * \return 1 (true) if SQLi, 0 (false) if benign
*/ */
int libinjection_is_sqli(sfilter * sql_state, int libinjection_is_sqli(sfilter * sql_state);
const char *s, size_t slen,
ptr_fingerprints_fn fn, void* callbackarg); /* FOR H@CKERS ONLY
*
*/
void libinjection_sqli_callback(sfilter* sql_state, ptr_lookup_fn fn, void* userdata);
/*
* Resets state, but keeps initial string and callbacks
*/
void libinjection_sqli_reset(sfilter* sql_state, int flags);
/**
*
*/
/** /**
* This detects SQLi in a single context, mostly useful for custom * This detects SQLi in a single context, mostly useful for custom
* logic and debugging. * logic and debugging.
* *
* \param sql_state * \param sql_state
* \param s
* \param slen
* \param delim must be char of
* CHAR_NULL (\0), raw context
* CHAR_SINGLE ('), single quote context
* CHAR_DOUBLE ("), double quote context
* Other values will likely be ignored.
* *
* \return pointer to sfilter.pat as convience. * \return pointer to sfilter.pat as convience.
* do not free! * do not free!
* *
*/ */
const char* libinjection_sqli_fingerprint(sfilter * sql_state, const char* libinjection_sqli_fingerprint(sfilter * sql_state, int flags);
const char *s, size_t slen,
char delim,
char comment_style);
/* FOR H@CKERS ONLY
*
*/
void libinjection_sqli_init(sfilter* sql_state, char libinjection_sqli_lookup_word(sfilter *sql_state, int lookup_type,
const char* s, size_t slen, const char* s, size_t slen);
char delim, char comment_style);
int libinjection_sqli_tokenize(sfilter * sql_state, stoken_t *ouput); int libinjection_sqli_tokenize(sfilter * sql_state);
/** The built-in default function to match fingerprints /** The built-in default function to match fingerprints
* and do false negative/positive analysis. This calls the following * and do false negative/positive analysis. This calls the following
* two functions. With this, you other-ride one part or the other. * two functions. With this, you other-ride one part or the other.
* *
* return libinjection_sqli_blacklist(sql_state, callbackarg) && * return libinjection_sqli_blacklist(sql_state) &&
* libinject_sqli_not_whitelist(sql_state, callbackarg); * libinject_sqli_not_whitelist(sql_state);
* *
* \param sql_state should be filled out after libinjection_sqli_fingerprint is called * \param sql_state should be filled out after libinjection_sqli_fingerprint is called
* \param callbackarg is unused but here to be used with API.
*/ */
int libinjection_sqli_check_fingerprint(sfilter *sql_state, void* callbackarg); int libinjection_sqli_check_fingerprint(sfilter *sql_state);
/* Given a pattern determine if it's a SQLi pattern. /* Given a pattern determine if it's a SQLi pattern.
* *

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -2137,28 +2137,29 @@ 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) {
sfilter sf;
int issqli = libinjection_is_sqli(&sf, var->value, var->value_len, NULL, NULL);
int capture = apr_table_get(rule->actionset->actions, "capture") ? 1 : 0;
if (error_msg == NULL) return -1; struct libinjection_sqli_state sqli_state;
*error_msg = NULL; int issqli;
int capture;
libinjecton_sqli_init(&sqli_state, var->value, var->value_len, 0);
issqli = libinjection_is_sqli(&sqli_state);
capture = apr_table_get(rule->actionset->actions, "capture") ? 1 : 0;
if (issqli) { if (issqli) {
set_match_to_tx(msr, capture, sf.pat, 0); set_match_to_tx(msr, capture, sqli_state.fingerprint, 0);
*error_msg = apr_psprintf(msr->mp, "detected SQLi using libinjection fingerprint '%s' at %s",
sf.pat, var->name);
*error_msg = apr_psprintf(msr->mp, "detected SQLi using libinjection with fingerprint '%s'",
sqli_state.fingerprint);
if (msr->txcfg->debuglog_level >= 9) { if (msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "detectSQLi: libinjection fingerprint '%s' matched input '%s'", msr_log(msr, 9, "ISSQL: libinjection fingerprint '%s' matched input '%s'",
sf.pat, sqli_state.fingerprint,
log_escape_ex(msr->mp, var->value, var->value_len)); log_escape_ex(msr->mp, var->value, var->value_len));
} }
} else { } else {
if (msr->txcfg->debuglog_level >= 9) { if (msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "detectSQLi: no sql, libinjection no match input '%s' at '%s'", msr_log(msr, 9, "ISSQL: not sqli, no libinjection sqli fingerprint matched input '%s'",
log_escape_ex(msr->mp, var->value, var->value_len), var->name); log_escape_ex(msr->mp, var->value, var->value_len));
} }
} }