file extract: improve multipart parsing and set events on some error conditions.

remotes/origin/HEAD
Victor Julien 13 years ago
parent bfb3f1b7cf
commit e21d8cdf01

@ -23,10 +23,17 @@ alert http any any -> any any (msg:"SURICATA HTTP invalid authority port"; flow:
alert http any any -> any any (msg:"SURICATA HTTP request header invalid"; flow:established,to_server; app-layer-event:http.request_header_invalid; flowint:http.anomaly.count,+,1; classtype:protocol-command-decode; sid:2221013; rev:1;)
alert http any any -> any any (msg:"SURICATA HTTP response header invalid"; flow:established,to_client; app-layer-event:http.response_header_invalid; flowint:http.anomaly.count,+,1; classtype:protocol-command-decode; sid:2221021; rev:1;)
alert http any any -> any any (msg:"SURICATA HTTP missing Host header"; flow:established,to_server; app-layer-event:http.missing_host_header; flowint:http.anomaly.count,+,1; classtype:protocol-command-decode; sid:2221014; rev:1;)
alert http any any -> any any (msg:"SURICATA HTTP Host header ambiguous"; flow:established,to_server; app-layer-event:http.host_header_ambiguous; flowint:http.anomaly.count,+,1; classtype:protocol-command-decode; sid:2221015; rev:1;)
# If hostname is both part of URL and Host header. Not very useful as this matches on HTTP Proxy traffic.
#alert http any any -> any any (msg:"SURICATA HTTP Host header ambiguous"; flow:established,to_server; app-layer-event:http.host_header_ambiguous; flowint:http.anomaly.count,+,1; classtype:protocol-command-decode; sid:2221015; rev:1;)
alert http any any -> any any (msg:"SURICATA HTTP invalid request field folding"; flow:established,to_server; app-layer-event:http.invalid_request_field_folding; flowint:http.anomaly.count,+,1; classtype:protocol-command-decode; sid:2221016; rev:1;)
alert http any any -> any any (msg:"SURICATA HTTP invalid response field folding"; flow:established,to_client; app-layer-event:http.invalid_response_field_folding; flowint:http.anomaly.count,+,1; classtype:protocol-command-decode; sid:2221017; rev:1;)
alert http any any -> any any (msg:"SURICATA HTTP request field too long"; flow:established,to_server; app-layer-event:http.request_field_too_long; flowint:http.anomaly.count,+,1; classtype:protocol-command-decode; sid:2221018; rev:1;)
alert http any any -> any any (msg:"SURICATA HTTP response field too long"; flow:established,to_client; app-layer-event:http.response_field_too_long; flowint:http.anomaly.count,+,1; classtype:protocol-command-decode; sid:2221019; rev:1;)
# next sid 2221022
# Multipart parser detected generic error.
alert http any any -> any any (msg:"SURICATA HTTP multipart generic error"; flow:established,to_server; app-layer-event:http.multipart_generic_error; flowint:http.anomaly.count,+,1; classtype:protocol-command-decode; sid:2221022; rev:1;)
# Multipart header claiming a file to present, but no actual filedata available.
alert http any any -> any any (msg:"SURICATA HTTP multipart no filedata"; flow:established,to_server; app-layer-event:http.multipart_no_filedata; flowint:http.anomaly.count,+,1; classtype:protocol-command-decode; sid:2221023; rev:1;)
# Multipart header invalid.
alert http any any -> any any (msg:"SURICATA HTTP multipart invalid header"; flow:established,to_server; app-layer-event:http.multipart_invalid_header; flowint:http.anomaly.count,+,1; classtype:protocol-command-decode; sid:2221024; rev:1;)
# next sid 2221025

@ -1010,6 +1010,175 @@ end:
return result;
}
static int HTPFileParserTest08(void) {
int result = 0;
Flow *f = NULL;
uint8_t httpbuf1[] = "POST /upload.cgi HTTP/1.1\r\n"
"Host: www.server.lan\r\n"
"Content-Type: multipart/form-data; boundary=---------------------------277531038314945\r\n"
"Content-Length: 215\r\n"
"\r\n"
"-----------------------------277531038314945\r\n"
"Content-Disposition: form-data; name=\"uploadfile_0\"; filename=\"somepicture1.jpg\"\r\n"
"Content-Type: image/jpeg\r\n";
uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
uint8_t httpbuf2[] = "filecontent\r\n\r\n"
"-----------------------------277531038314945--";
uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
TcpSession ssn;
HtpState *http_state = NULL;
memset(&ssn, 0, sizeof(ssn));
f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
if (f == NULL)
goto end;
f->protoctx = &ssn;
StreamTcpInitConfig(TRUE);
SCLogDebug("\n>>>> processing chunk 1 <<<<\n");
int r = AppLayerParse(NULL, f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_START, httpbuf1, httplen1);
if (r != 0) {
printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
result = 0;
goto end;
}
SCLogDebug("\n>>>> processing chunk 2 size %u <<<<\n", httplen2);
r = AppLayerParse(NULL, f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_EOF, httpbuf2, httplen2);
if (r != 0) {
printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
result = 0;
goto end;
}
http_state = f->alstate;
if (http_state == NULL) {
printf("no http state: ");
result = 0;
goto end;
}
AppLayerDecoderEvents *decoder_events = AppLayerGetDecoderEventsForFlow(f);
if (decoder_events == NULL) {
printf("no app events: ");
goto end;
}
if (decoder_events->cnt != 2) {
printf("expected 2 events: ");
goto end;
}
result = 1;
end:
StreamTcpFreeConfig(TRUE);
if (http_state != NULL)
HTPStateFree(http_state);
UTHFreeFlow(f);
return result;
}
/** \test invalid header: Somereallylongheaderstr: has no value */
static int HTPFileParserTest09(void) {
int result = 0;
Flow *f = NULL;
uint8_t httpbuf1[] = "POST /upload.cgi HTTP/1.1\r\n"
"Host: www.server.lan\r\n"
"Content-Type: multipart/form-data; boundary=---------------------------277531038314945\r\n"
"Content-Length: 337\r\n"
"\r\n";
uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
uint8_t httpbuf2[] = "-----------------------------277531038314945\r\n"
"Content-Disposition: form-data; name=\"email\"\r\n"
"\r\n"
"someaddress@somedomain.lan\r\n";
uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
uint8_t httpbuf3[] = "-----------------------------277531038314945\r\n"
"Content-Disposition: form-data; name=\"uploadfile_0\"; filename=\"somepicture1.jpg\"\r\n"
"Somereallylongheaderstr:\r\n"
"\r\n";
uint32_t httplen3 = sizeof(httpbuf3) - 1; /* minus the \0 */
uint8_t httpbuf4[] = "filecontent\r\n"
"-----------------------------277531038314945--";
uint32_t httplen4 = sizeof(httpbuf4) - 1; /* minus the \0 */
TcpSession ssn;
HtpState *http_state = NULL;
memset(&ssn, 0, sizeof(ssn));
f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
if (f == NULL)
goto end;
f->protoctx = &ssn;
StreamTcpInitConfig(TRUE);
SCLogDebug("\n>>>> processing chunk 1 <<<<\n");
int r = AppLayerParse(NULL, f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_START, httpbuf1, httplen1);
if (r != 0) {
printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
result = 0;
goto end;
}
SCLogDebug("\n>>>> processing chunk 2 size %u <<<<\n", httplen2);
r = AppLayerParse(NULL, f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_EOF, httpbuf2, httplen2);
if (r != 0) {
printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
result = 0;
goto end;
}
SCLogDebug("\n>>>> processing chunk 3 size %u <<<<\n", httplen3);
r = AppLayerParse(NULL, f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_EOF, httpbuf3, httplen3);
if (r != 0) {
printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
result = 0;
goto end;
}
SCLogDebug("\n>>>> processing chunk 4 size %u <<<<\n", httplen4);
r = AppLayerParse(NULL, f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_EOF, httpbuf4, httplen4);
if (r != 0) {
printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r);
result = 0;
goto end;
}
http_state = f->alstate;
if (http_state == NULL) {
printf("no http state: ");
result = 0;
goto end;
}
AppLayerDecoderEvents *decoder_events = AppLayerGetDecoderEventsForFlow(f);
if (decoder_events == NULL) {
printf("no app events: ");
goto end;
}
if (decoder_events->cnt != 1) {
printf("expected 1 event: ");
goto end;
}
result = 1;
end:
StreamTcpFreeConfig(TRUE);
if (http_state != NULL)
HTPStateFree(http_state);
UTHFreeFlow(f);
return result;
}
#endif /* UNITTESTS */
void HTPFileParserRegisterTests(void) {
@ -1021,5 +1190,7 @@ void HTPFileParserRegisterTests(void) {
UtRegisterTest("HTPFileParserTest05", HTPFileParserTest05, 1);
UtRegisterTest("HTPFileParserTest06", HTPFileParserTest06, 1);
UtRegisterTest("HTPFileParserTest07", HTPFileParserTest07, 1);
UtRegisterTest("HTPFileParserTest08", HTPFileParserTest08, 1);
UtRegisterTest("HTPFileParserTest09", HTPFileParserTest09, 1);
#endif /* UNITTESTS */
}

@ -150,6 +150,15 @@ SCEnumCharMap http_decoder_event_table[ ] = {
HTTP_DECODER_EVENT_REQUEST_FIELD_TOO_LONG},
{ "RESPONSE_FIELD_TOO_LONG",
HTTP_DECODER_EVENT_RESPONSE_FIELD_TOO_LONG},
/* suricata warnings/errors */
{ "MULTIPART_GENERIC_ERROR",
HTTP_DECODER_EVENT_MULTIPART_GENERIC_ERROR},
{ "MULTIPART_NO_FILEDATA",
HTTP_DECODER_EVENT_MULTIPART_NO_FILEDATA},
{ "MULTIPART_INVALID_HEADER",
HTTP_DECODER_EVENT_MULTIPART_INVALID_HEADER},
{ NULL, -1 },
};
@ -1098,7 +1107,8 @@ error:
#define C_T_HDR "content-type:"
#define C_T_HDR_LEN 13
static void HtpRequestBodyMultipartParseHeader(uint8_t *header, uint32_t header_len,
static void HtpRequestBodyMultipartParseHeader(HtpState *hstate,
uint8_t *header, uint32_t header_len,
uint8_t **filename, uint16_t *filename_len,
uint8_t **filetype, uint16_t *filetype_len)
{
@ -1124,6 +1134,18 @@ static void HtpRequestBodyMultipartParseHeader(uint8_t *header, uint32_t header_
line_len = next_line - header;
}
uint8_t *sc = (uint8_t *)memchr(line, ':', line_len);
if (sc == NULL) {
AppLayerDecoderEventsSetEvent(hstate->f,
HTTP_DECODER_EVENT_MULTIPART_INVALID_HEADER);
} else {
/* if the : we found is the final char, it means we have
* no value */
if (line_len > 0 && sc == &line[line_len - 1])
AppLayerDecoderEventsSetEvent(hstate->f,
HTTP_DECODER_EVENT_MULTIPART_INVALID_HEADER);
}
#ifdef PRINT
printf("LINE START: \n");
PrintRawDataFp(stdout, line, line_len);
@ -1274,7 +1296,11 @@ int HtpRequestBodyHandleMultipart(HtpState *hstate, HtpTxUserData *htud,
flags = FILE_TRUNCATED;
}
BUG_ON(filedata_len > chunks_buffer_len);
if (filedata_len > chunks_buffer_len) {
AppLayerDecoderEventsSetEvent(hstate->f,
HTTP_DECODER_EVENT_MULTIPART_GENERIC_ERROR);
goto end;
}
#ifdef PRINT
printf("FILEDATA (final chunk) START: \n");
PrintRawDataFp(stdout, filedata, filedata_len);
@ -1343,8 +1369,8 @@ int HtpRequestBodyHandleMultipart(HtpState *hstate, HtpTxUserData *htud,
header = header_start + (expected_boundary_len + 2); // + for 0d 0a
}
HtpRequestBodyMultipartParseHeader(header, header_len, &filename,
&filename_len, &filetype, &filetype_len);
HtpRequestBodyMultipartParseHeader(hstate, header, header_len,
&filename, &filename_len, &filetype, &filetype_len);
if (filename != NULL) {
uint8_t *filedata = NULL;
@ -1361,7 +1387,18 @@ int HtpRequestBodyHandleMultipart(HtpState *hstate, HtpTxUserData *htud,
/* everything until the final boundary is the file */
if (form_end != NULL) {
filedata = header_end + 4;
if (form_end == filedata) {
AppLayerDecoderEventsSetEvent(hstate->f,
HTTP_DECODER_EVENT_MULTIPART_NO_FILEDATA);
goto end;
} else if (form_end < filedata) {
AppLayerDecoderEventsSetEvent(hstate->f,
HTTP_DECODER_EVENT_MULTIPART_GENERIC_ERROR);
goto end;
}
filedata_len = form_end - (header_end + 4 + 2);
SCLogDebug("filedata_len %"PRIuMAX, (uintmax_t)filedata_len);
/* or is it? */
uint8_t *header_next = Bs2bmSearch(filedata, filedata_len,
@ -1370,8 +1407,12 @@ int HtpRequestBodyHandleMultipart(HtpState *hstate, HtpTxUserData *htud,
filedata_len -= (form_end - header_next);
}
if (filedata_len > chunks_buffer_len) {
AppLayerDecoderEventsSetEvent(hstate->f,
HTTP_DECODER_EVENT_MULTIPART_GENERIC_ERROR);
goto end;
}
SCLogDebug("filedata_len %"PRIuMAX, (uintmax_t)filedata_len);
#ifdef PRINT
printf("FILEDATA START: \n");
PrintRawDataFp(stdout, filedata, filedata_len);
@ -1400,6 +1441,12 @@ int HtpRequestBodyHandleMultipart(HtpState *hstate, HtpTxUserData *htud,
filedata_len = chunks_buffer_len - (filedata - chunks_buffer);
SCLogDebug("filedata_len %u (chunks_buffer_len %u)", filedata_len, chunks_buffer_len);
if (filedata_len > chunks_buffer_len) {
AppLayerDecoderEventsSetEvent(hstate->f,
HTTP_DECODER_EVENT_MULTIPART_GENERIC_ERROR);
goto end;
}
#ifdef PRINT
printf("FILEDATA START: \n");
PrintRawDataFp(stdout, filedata, filedata_len);

@ -83,6 +83,7 @@ enum {
};
enum {
/* libhtp errors/warnings */
HTTP_DECODER_EVENT_UNKNOWN_ERROR,
HTTP_DECODER_EVENT_GZIP_DECOMPRESSION_FAILED,
HTTP_DECODER_EVENT_REQUEST_FIELD_MISSING_COLON,
@ -105,6 +106,11 @@ enum {
HTTP_DECODER_EVENT_INVALID_RESPONSE_FIELD_FOLDING,
HTTP_DECODER_EVENT_REQUEST_FIELD_TOO_LONG,
HTTP_DECODER_EVENT_RESPONSE_FIELD_TOO_LONG,
/* suricata errors/warnings */
HTTP_DECODER_EVENT_MULTIPART_GENERIC_ERROR,
HTTP_DECODER_EVENT_MULTIPART_NO_FILEDATA,
HTTP_DECODER_EVENT_MULTIPART_INVALID_HEADER,
};
#define HTP_PCRE_NONE 0x00 /**< No pcre executed yet */

Loading…
Cancel
Save