mirror of
https://github.com/owasp-modsecurity/ModSecurity.git
synced 2025-08-14 05:45:59 +03:00
Detect and prevent multipart evasion.
This commit is contained in:
parent
cb0cb93752
commit
c520886e10
@ -188,25 +188,38 @@ static int multipart_process_part_header(modsec_rec *msr, char **error_msg) {
|
||||
|
||||
/* Empty line. */
|
||||
|
||||
if (msr->mpd->buf[0] == '\n') {
|
||||
msr->mpd->flag_lf_line = 1;
|
||||
}
|
||||
|
||||
header_value = (char *)apr_table_get(msr->mpd->mpp->headers, "Content-Disposition");
|
||||
if (header_value == NULL) {
|
||||
*error_msg = apr_psprintf(msr->mp, "Multipart: Part is missing the Content-Disposition header");
|
||||
*error_msg = apr_psprintf(msr->mp, "Multipart: Part missing Content-Disposition header.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = multipart_parse_content_disposition(msr, header_value);
|
||||
if (rc < 0) {
|
||||
*error_msg = apr_psprintf(msr->mp, "Multipart: Invalid Content-Disposition header (%i): %s",
|
||||
*error_msg = apr_psprintf(msr->mp, "Multipart: Invalid Content-Disposition header (%i): %s.",
|
||||
rc, log_escape_nq(msr->mp, header_value));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (msr->mpd->mpp->name == NULL) {
|
||||
*error_msg = apr_psprintf(msr->mp, "Multipart: Part name missing");
|
||||
*error_msg = apr_psprintf(msr->mp, "Multipart: Content-Disposition header missing name field.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (msr->mpd->mpp->filename != NULL) {
|
||||
/* Some parsers use crude methods to extract the name and filename
|
||||
* values from the C-D header. We need to check for the case where they
|
||||
* don't understand a C-D we do.
|
||||
*/
|
||||
if (strstr(header_value, "filename=") == NULL) {
|
||||
*error_msg = apr_psprintf(msr->mp, "Multipart: Invalid Content-Disposition header (filename).");
|
||||
return -1;
|
||||
}
|
||||
|
||||
msr->mpd->mpp->type = MULTIPART_FILE;
|
||||
} else {
|
||||
msr->mpd->mpp->type = MULTIPART_FORMDATA;
|
||||
@ -220,14 +233,15 @@ static int multipart_process_part_header(modsec_rec *msr, char **error_msg) {
|
||||
char *header_value, *new_value, *data;
|
||||
|
||||
/* header folding, add data to the header we are building */
|
||||
msr->mpd->flag_header_folding = 1;
|
||||
|
||||
if (msr->mpd->mpp->last_header_name == NULL) {
|
||||
/* we are not building a header at this moment */
|
||||
*error_msg = apr_psprintf(msr->mp, "Multipart: invalid part header (invalid folding)");
|
||||
*error_msg = apr_psprintf(msr->mp, "Multipart: Invalid part header (folding error).");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* locate the beginning of the data */
|
||||
/* locate the beginning of data */
|
||||
data = msr->mpd->buf;
|
||||
while((*data == '\t')||(*data == ' ')) data++;
|
||||
|
||||
@ -244,7 +258,7 @@ static int multipart_process_part_header(modsec_rec *msr, char **error_msg) {
|
||||
log_escape(msr->mp, data));
|
||||
|
||||
if (strlen(new_value) > 4096) {
|
||||
*error_msg = apr_psprintf(msr->mp, "Multpart: invalid part header (too long)");
|
||||
*error_msg = apr_psprintf(msr->mp, "Multipart: Part header too long.");
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
@ -255,7 +269,7 @@ static int multipart_process_part_header(modsec_rec *msr, char **error_msg) {
|
||||
data = msr->mpd->buf;
|
||||
while((*data != ':')&&(*data != '\0')) data++;
|
||||
if (*data == '\0') {
|
||||
*error_msg = apr_psprintf(msr->mp, "Multipart: invalid part header (missing colon): %s",
|
||||
*error_msg = apr_psprintf(msr->mp, "Multipart: Invalid part header (colon missing): %s.",
|
||||
log_escape_nq(msr->mp, msr->mpd->buf));
|
||||
return -1;
|
||||
}
|
||||
@ -270,7 +284,7 @@ static int multipart_process_part_header(modsec_rec *msr, char **error_msg) {
|
||||
|
||||
/* error if the name already exists */
|
||||
if (apr_table_get(msr->mpd->mpp->headers, header_name) != NULL) {
|
||||
*error_msg = apr_psprintf(msr->mp, "Multipart: part header already exists: %s",
|
||||
*error_msg = apr_psprintf(msr->mp, "Multipart: Duplicate part header: %s.",
|
||||
log_escape_nq(msr->mp, header_name));
|
||||
return -1;
|
||||
}
|
||||
@ -538,15 +552,15 @@ int multipart_init(modsec_rec *msr, char **error_msg) {
|
||||
if ((len >= 2)&&(*b == '"')&&(*(b + len - 1) == '"')) {
|
||||
/* Quoted. */
|
||||
msr->mpd->boundary = apr_pstrndup(msr->mp, b + 1, len - 2);
|
||||
msr->mpd->boundary_quoted = 1;
|
||||
msr->mpd->flag_boundary_quoted = 1;
|
||||
} else {
|
||||
/* Not quoted. */
|
||||
msr->mpd->boundary = apr_pstrdup(msr->mp, b);
|
||||
msr->mpd->boundary_quoted = 0;
|
||||
msr->mpd->flag_boundary_quoted = 0;
|
||||
}
|
||||
|
||||
msr_log(msr, 9, "Multipart: Boundary%s: %s",
|
||||
(msr->mpd->boundary_quoted ? " (quoted)" : ""),
|
||||
(msr->mpd->flag_boundary_quoted ? " (quoted)" : ""),
|
||||
log_escape_nq(msr->mp, msr->mpd->boundary));
|
||||
|
||||
if (strlen(msr->mpd->boundary) == 0) {
|
||||
@ -599,6 +613,7 @@ int multipart_process_chunk(modsec_rec *msr, const char *buf,
|
||||
if (msr->mpd->seen_data == 0) msr->mpd->seen_data = 1;
|
||||
|
||||
if (msr->mpd->is_complete) {
|
||||
msr->mpd->flag_data_before = 1;
|
||||
msr_log(msr, 4, "Multipart: Ignoring data after last boundary (received %i bytes)", size);
|
||||
return 1;
|
||||
}
|
||||
@ -660,6 +675,10 @@ int multipart_process_chunk(modsec_rec *msr, const char *buf,
|
||||
|| ( (*boundary_end == '\n')
|
||||
&& (*(boundary_end + 1) == '\0') ) )
|
||||
{
|
||||
if (*boundary_end == '\n') {
|
||||
msr->mpd->flag_lf_line = 1;
|
||||
}
|
||||
|
||||
if (multipart_process_boundary(msr, (is_final ? 1 : 0), error_msg) < 0) {
|
||||
return -1;
|
||||
}
|
||||
@ -671,13 +690,14 @@ int multipart_process_chunk(modsec_rec *msr, const char *buf,
|
||||
else {
|
||||
/* error */
|
||||
*error_msg = apr_psprintf(msr->mp,
|
||||
"Multipart: Invalid boundary detected: %s",
|
||||
"Multipart: Invalid boundary: %s",
|
||||
log_escape_nq(msr->mp, msr->mpd->buf));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (msr->mpd->mpp == NULL) {
|
||||
msr->mpd->flag_data_before = 1;
|
||||
msr_log(msr, 4, "Multipart: Ignoring data before first boundary.");
|
||||
} else {
|
||||
if (msr->mpd->mpp_state == 0) {
|
||||
@ -712,6 +732,7 @@ int multipart_process_chunk(modsec_rec *msr, const char *buf,
|
||||
}
|
||||
|
||||
if ((msr->mpd->is_complete)&&(inleft != 0)) {
|
||||
msr->mpd->flag_data_after = 1;
|
||||
msr_log(msr, 4, "Multipart: Ignoring data after last boundary (%i bytes left)", inleft);
|
||||
return 1;
|
||||
}
|
||||
|
@ -64,7 +64,6 @@ struct multipart_data {
|
||||
* parts end and new begin
|
||||
*/
|
||||
char *boundary;
|
||||
int boundary_quoted;
|
||||
|
||||
/* internal buffer and other variables
|
||||
* used while parsing
|
||||
@ -98,6 +97,12 @@ struct multipart_data {
|
||||
|
||||
int seen_data;
|
||||
int is_complete;
|
||||
|
||||
int flag_data_before;
|
||||
int flag_data_after;
|
||||
int flag_header_folding;
|
||||
int flag_boundary_quoted;
|
||||
int flag_lf_line;
|
||||
};
|
||||
|
||||
|
||||
|
@ -1224,6 +1224,86 @@ static int var_modsec_build_generate(modsec_rec *msr, msre_var *var, msre_rule *
|
||||
return var_simple_generate(var, vartab, mptmp, modsec_build(mptmp));
|
||||
}
|
||||
|
||||
/* MULTIPART_BOUNDARY_QUOTED */
|
||||
|
||||
static int var_multipart_boundary_quoted_generate(modsec_rec *msr, msre_var *var, msre_rule *rule,
|
||||
apr_table_t *vartab, apr_pool_t *mptmp)
|
||||
{
|
||||
if ((msr->mpd != NULL)&&(msr->mpd->flag_boundary_quoted != 0)) {
|
||||
return var_simple_generate(var, vartab, mptmp, "1");
|
||||
} else {
|
||||
return var_simple_generate(var, vartab, mptmp, "0");
|
||||
}
|
||||
}
|
||||
|
||||
/* MULTIPART_DATA_AFTER */
|
||||
|
||||
static int var_multipart_data_after_generate(modsec_rec *msr, msre_var *var, msre_rule *rule,
|
||||
apr_table_t *vartab, apr_pool_t *mptmp)
|
||||
{
|
||||
if ((msr->mpd != NULL)&&(msr->mpd->flag_data_after != 0)) {
|
||||
return var_simple_generate(var, vartab, mptmp, "1");
|
||||
} else {
|
||||
return var_simple_generate(var, vartab, mptmp, "0");
|
||||
}
|
||||
}
|
||||
|
||||
/* MULTIPART_DATA_BEFORE */
|
||||
|
||||
static int var_multipart_data_before_generate(modsec_rec *msr, msre_var *var, msre_rule *rule,
|
||||
apr_table_t *vartab, apr_pool_t *mptmp)
|
||||
{
|
||||
if ((msr->mpd != NULL)&&(msr->mpd->flag_data_before != 0)) {
|
||||
return var_simple_generate(var, vartab, mptmp, "1");
|
||||
} else {
|
||||
return var_simple_generate(var, vartab, mptmp, "0");
|
||||
}
|
||||
}
|
||||
|
||||
/* MULTIPART_HEADER_FOLDING */
|
||||
|
||||
static int var_multipart_header_folding_generate(modsec_rec *msr, msre_var *var, msre_rule *rule,
|
||||
apr_table_t *vartab, apr_pool_t *mptmp)
|
||||
{
|
||||
if ((msr->mpd != NULL)&&(msr->mpd->flag_header_folding != 0)) {
|
||||
return var_simple_generate(var, vartab, mptmp, "1");
|
||||
} else {
|
||||
return var_simple_generate(var, vartab, mptmp, "0");
|
||||
}
|
||||
}
|
||||
|
||||
/* MULTIPART_LF_LINE */
|
||||
|
||||
static int var_multipart_lf_line_generate(modsec_rec *msr, msre_var *var, msre_rule *rule,
|
||||
apr_table_t *vartab, apr_pool_t *mptmp)
|
||||
{
|
||||
if ((msr->mpd != NULL)&&(msr->mpd->flag_lf_line != 0)) {
|
||||
return var_simple_generate(var, vartab, mptmp, "1");
|
||||
} else {
|
||||
return var_simple_generate(var, vartab, mptmp, "0");
|
||||
}
|
||||
}
|
||||
|
||||
/* MULTIPART_STRICT_ERROR */
|
||||
|
||||
static int var_multipart_strict_error_generate(modsec_rec *msr, msre_var *var, msre_rule *rule,
|
||||
apr_table_t *vartab, apr_pool_t *mptmp)
|
||||
{
|
||||
if (msr->mpd != NULL) {
|
||||
/* Respond positive if at least one of the multipart flags is raised. */
|
||||
if ( (msr->mpd->flag_boundary_quoted != 0)
|
||||
||(msr->mpd->flag_data_before != 0)
|
||||
||(msr->mpd->flag_data_after != 0)
|
||||
||(msr->mpd->flag_header_folding != 0)
|
||||
||(msr->mpd->flag_lf_line != 0)
|
||||
) {
|
||||
return var_simple_generate(var, vartab, mptmp, "1");
|
||||
}
|
||||
}
|
||||
|
||||
return var_simple_generate(var, vartab, mptmp, "0");
|
||||
}
|
||||
|
||||
/* TIME */
|
||||
|
||||
static int var_time_generate(modsec_rec *msr, msre_var *var, msre_rule *rule,
|
||||
@ -2145,6 +2225,72 @@ void msre_engine_register_default_variables(msre_engine *engine) {
|
||||
PHASE_REQUEST_HEADERS
|
||||
);
|
||||
|
||||
/* MULTIPART_BOUNDARY_QUOTED */
|
||||
msre_engine_variable_register(engine,
|
||||
"MULTIPART_BOUNDARY_QUOTED",
|
||||
VAR_SIMPLE,
|
||||
0, 0,
|
||||
NULL,
|
||||
var_multipart_boundary_quoted_generate,
|
||||
VAR_CACHE,
|
||||
PHASE_REQUEST_BODY
|
||||
);
|
||||
|
||||
/* MULTIPART_DATA_AFTER */
|
||||
msre_engine_variable_register(engine,
|
||||
"MULTIPART_DATA_AFTER",
|
||||
VAR_SIMPLE,
|
||||
0, 0,
|
||||
NULL,
|
||||
var_multipart_data_after_generate,
|
||||
VAR_CACHE,
|
||||
PHASE_REQUEST_BODY
|
||||
);
|
||||
|
||||
/* MULTIPART_DATA_BEFORE */
|
||||
msre_engine_variable_register(engine,
|
||||
"MULTIPART_DATA_BEFORE",
|
||||
VAR_SIMPLE,
|
||||
0, 0,
|
||||
NULL,
|
||||
var_multipart_data_before_generate,
|
||||
VAR_CACHE,
|
||||
PHASE_REQUEST_BODY
|
||||
);
|
||||
|
||||
/* MULTIPART_HEADER_FOLDING */
|
||||
msre_engine_variable_register(engine,
|
||||
"MULTIPART_HEADER_FOLDING",
|
||||
VAR_SIMPLE,
|
||||
0, 0,
|
||||
NULL,
|
||||
var_multipart_header_folding_generate,
|
||||
VAR_CACHE,
|
||||
PHASE_REQUEST_BODY
|
||||
);
|
||||
|
||||
/* MULTIPART_LF_LINE */
|
||||
msre_engine_variable_register(engine,
|
||||
"MULTIPART_LF_LINE",
|
||||
VAR_SIMPLE,
|
||||
0, 0,
|
||||
NULL,
|
||||
var_multipart_lf_line_generate,
|
||||
VAR_CACHE,
|
||||
PHASE_REQUEST_BODY
|
||||
);
|
||||
|
||||
/* MULTIPART_STRICT_ERROR */
|
||||
msre_engine_variable_register(engine,
|
||||
"MULTIPART_STRICT_ERROR",
|
||||
VAR_SIMPLE,
|
||||
0, 0,
|
||||
NULL,
|
||||
var_multipart_strict_error_generate,
|
||||
VAR_CACHE,
|
||||
PHASE_REQUEST_BODY
|
||||
);
|
||||
|
||||
/* PATH_INFO */
|
||||
msre_engine_variable_register(engine,
|
||||
"PATH_INFO",
|
||||
|
Loading…
x
Reference in New Issue
Block a user