Detect and prevent multipart evasion.

This commit is contained in:
ivanr 2007-08-09 10:50:53 +00:00
parent cb0cb93752
commit c520886e10
3 changed files with 187 additions and 15 deletions

View File

@ -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;
}

View File

@ -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;
};

View File

@ -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",