mirror of
https://github.com/owasp-modsecurity/ModSecurity.git
synced 2025-09-29 19:24:29 +03:00
libinjection v3.0.0pre21 take 2
This commit is contained in:
@@ -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];
|
/*
|
||||||
|
* 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;
|
} stoken_t;
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
|
/**
|
||||||
|
* 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
@@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user