From 08822e0dd43df88fcbe719c0092a2787cfc1e339 Mon Sep 17 00:00:00 2001 From: Jon Siwek Date: Fri, 22 May 2015 11:46:50 -0500 Subject: [PATCH] Allow '<' and '>' in MIME multipart boundaries. The spec doesn't actually seem to permit these, but Seth had a (private) pcap showing them used in the wild (and the HTTP/MIME analyzer failed to parse content as a result). --- src/analyzer/protocol/http/HTTP.cc | 7 +++- src/analyzer/protocol/mime/MIME.cc | 66 +++++++++++++++--------------- src/analyzer/protocol/mime/MIME.h | 7 ++-- 3 files changed, 42 insertions(+), 38 deletions(-) diff --git a/src/analyzer/protocol/http/HTTP.cc b/src/analyzer/protocol/http/HTTP.cc index f60d1372ac..ff72c6f350 100644 --- a/src/analyzer/protocol/http/HTTP.cc +++ b/src/analyzer/protocol/http/HTTP.cc @@ -1025,8 +1025,11 @@ void HTTP_Analyzer::DeliverStream(int len, const u_char* data, bool is_orig) } else { - ProtocolViolation("not a http reply line"); - reply_state = EXPECT_REPLY_NOTHING; + if ( line != end_of_line ) + { + ProtocolViolation("not a http reply line"); + reply_state = EXPECT_REPLY_NOTHING; + } } break; diff --git a/src/analyzer/protocol/mime/MIME.cc b/src/analyzer/protocol/mime/MIME.cc index a1759d97d0..17630f0501 100644 --- a/src/analyzer/protocol/mime/MIME.cc +++ b/src/analyzer/protocol/mime/MIME.cc @@ -246,11 +246,16 @@ int MIME_get_field_name(int len, const char* data, data_chunk_t* name) } // See RFC 2045, page 12. -int MIME_is_tspecial (char ch) +int MIME_is_tspecial (char ch, bool is_boundary = false) { - return ch == '(' || ch == ')' || ch == '<' || ch == '>' || ch == '@' || - ch == ',' || ch == ';' || ch == ':' || ch == '\\' || ch == '"' || - ch == '/' || ch == '[' || ch == ']' || ch == '?' || ch == '='; + if ( is_boundary ) + return ch == '(' || ch == ')' || ch == '@' || + ch == ',' || ch == ';' || ch == ':' || ch == '\\' || ch == '"' || + ch == '/' || ch == '[' || ch == ']' || ch == '?' || ch == '='; + else + return ch == '(' || ch == ')' || ch == '<' || ch == '>' || ch == '@' || + ch == ',' || ch == ';' || ch == ':' || ch == '\\' || ch == '"' || + ch == '/' || ch == '[' || ch == ']' || ch == '?' || ch == '='; } int MIME_is_field_name_char (char ch) @@ -258,26 +263,27 @@ int MIME_is_field_name_char (char ch) return ch >= 33 && ch <= 126 && ch != ':'; } -int MIME_is_token_char (char ch) +int MIME_is_token_char (char ch, bool is_boundary = false) { - return ch >= 33 && ch <= 126 && ! MIME_is_tspecial(ch); + return ch >= 33 && ch <= 126 && ! MIME_is_tspecial(ch, is_boundary); } // See RFC 2045, page 12. // A token is composed of characters that are not SPACE, CTLs or tspecials -int MIME_get_token(int len, const char* data, data_chunk_t* token) +int MIME_get_token(int len, const char* data, data_chunk_t* token, + bool is_boundary) { int i = MIME_skip_lws_comments(len, data); while ( i < len ) { int j; - if ( MIME_is_token_char(data[i]) ) + if ( MIME_is_token_char(data[i], is_boundary) ) { token->data = (data + i); for ( j = i; j < len; ++j ) { - if ( ! MIME_is_token_char(data[j]) ) + if ( ! MIME_is_token_char(data[j], is_boundary) ) break; } @@ -359,7 +365,7 @@ int MIME_get_quoted_string(int len, const char* data, data_chunk_t* str) return -1; } -int MIME_get_value(int len, const char* data, BroString*& buf) +int MIME_get_value(int len, const char* data, BroString*& buf, bool is_boundary) { int offset = MIME_skip_lws_comments(len, data); @@ -380,7 +386,7 @@ int MIME_get_value(int len, const char* data, BroString*& buf) else { data_chunk_t str; - int end = MIME_get_token(len, data, &str); + int end = MIME_get_token(len, data, &str, is_boundary); if ( end < 0 ) return -1; @@ -863,8 +869,22 @@ int MIME_Entity::ParseFieldParameters(int len, const char* data) len -= offset; BroString* val = 0; - // token or quoted-string - offset = MIME_get_value(len, data, val); + + if ( current_field_type == MIME_CONTENT_TYPE && + content_type == CONTENT_TYPE_MULTIPART && + strcasecmp_n(attr, "boundary") == 0 ) + { + // token or quoted-string (and some lenience for characters + // not explicitly allowed by the RFC, but encountered in the wild) + offset = MIME_get_value(len, data, val, true); + data_chunk_t vd = get_data_chunk(val); + multipart_boundary = new BroString((const u_char*)vd.data, + vd.length, 1); + } + else + // token or quoted-string + offset = MIME_get_value(len, data, val); + if ( offset < 0 ) { IllegalFormat("value not found in parameter specification"); @@ -874,8 +894,6 @@ int MIME_Entity::ParseFieldParameters(int len, const char* data) data += offset; len -= offset; - - ParseParameter(attr, get_data_chunk(val)); delete val; } @@ -920,24 +938,6 @@ void MIME_Entity::ParseContentEncoding(data_chunk_t encoding_mechanism) content_encoding = i; } -void MIME_Entity::ParseParameter(data_chunk_t attr, data_chunk_t val) - { - switch ( current_field_type ) { - case MIME_CONTENT_TYPE: - if ( content_type == CONTENT_TYPE_MULTIPART && - strcasecmp_n(attr, "boundary") == 0 ) - multipart_boundary = new BroString((const u_char*)val.data, val.length, 1); - break; - - case MIME_CONTENT_TRANSFER_ENCODING: - break; - - default: - break; - } - } - - int MIME_Entity::CheckBoundaryDelimiter(int len, const char* data) { if ( ! multipart_boundary ) diff --git a/src/analyzer/protocol/mime/MIME.h b/src/analyzer/protocol/mime/MIME.h index a3ee45d071..46c96658fb 100644 --- a/src/analyzer/protocol/mime/MIME.h +++ b/src/analyzer/protocol/mime/MIME.h @@ -117,7 +117,6 @@ protected: void ParseContentType(data_chunk_t type, data_chunk_t sub_type); void ParseContentEncoding(data_chunk_t encoding_mechanism); - void ParseParameter(data_chunk_t attr, data_chunk_t val); void BeginBody(); void NewDataLine(int len, const char* data, int trailing_CRLF); @@ -275,9 +274,11 @@ extern int MIME_count_leading_lws(int len, const char* data); extern int MIME_count_trailing_lws(int len, const char* data); extern int MIME_skip_comments(int len, const char* data); extern int MIME_skip_lws_comments(int len, const char* data); -extern int MIME_get_token(int len, const char* data, data_chunk_t* token); +extern int MIME_get_token(int len, const char* data, data_chunk_t* token, + bool is_boundary = false); extern int MIME_get_slash_token_pair(int len, const char* data, data_chunk_t* first, data_chunk_t* second); -extern int MIME_get_value(int len, const char* data, BroString*& buf); +extern int MIME_get_value(int len, const char* data, BroString*& buf, + bool is_boundary = false); extern int MIME_get_field_name(int len, const char* data, data_chunk_t* name); extern BroString* MIME_decode_quoted_pairs(data_chunk_t buf);