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
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) {
UtRegisterTest("HTPFileParserTest05", HTPFileParserTest05, 1);
UtRegisterTest("HTPFileParserTest06", HTPFileParserTest06, 1);
UtRegisterTest("HTPFileParserTest07", HTPFileParserTest07, 1);
+ UtRegisterTest("HTPFileParserTest08", HTPFileParserTest08, 1);
+ UtRegisterTest("HTPFileParserTest09", HTPFileParserTest09, 1);
#endif /* UNITTESTS */
}
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 },
};
#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)
{
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);
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);
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;
/* 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,
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);
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);
};
enum {
+ /* libhtp errors/warnings */
HTTP_DECODER_EVENT_UNKNOWN_ERROR,
HTTP_DECODER_EVENT_GZIP_DECOMPRESSION_FAILED,
HTTP_DECODER_EVENT_REQUEST_FIELD_MISSING_COLON,
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 */