From: Jared Rittle (jrittle) Date: Thu, 20 Jun 2024 07:25:50 +0000 (+0000) Subject: Pull request #4334: Mms curse expansion X-Git-Tag: 3.3.1.0~14 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ccf3e361ad3c06505967823d76b3c0ad10de0ca1;p=thirdparty%2Fsnort3.git Pull request #4334: Mms curse expansion Merge in SNORT/snort3 from ~JRITTLE/snort3:mms_curse_expansion to master Squashed commit of the following: commit e5e80b73e05b19ecc9ef0b4b954f4504c70de766 Author: Jared Rittle <> Date: Tue May 28 14:24:39 2024 -0400 wizard: expand MMS curse --- diff --git a/src/service_inspectors/wizard/mms_curse.cc b/src/service_inspectors/wizard/mms_curse.cc index 389fbab83..e7d7554d2 100644 --- a/src/service_inspectors/wizard/mms_curse.cc +++ b/src/service_inspectors/wizard/mms_curse.cc @@ -27,214 +27,674 @@ #include -bool CurseBook::mms_curse(const uint8_t* data, unsigned len, CurseTracker* tracker) +enum PresCtx { - // peg the tracker to MMS - MmsTracker& mms = tracker->mms; + PRES_CTX_ACSE = 1, + PRES_CTX_MMS = 3, +}; - // if the state is set to MMS_STATE__SEARCH it means we most likely - // have a split pipelined message coming through and will need to - // reset the state - if ( mms.state == MMS_STATE__SEARCH ) +static bool verify_search_depth_idx( unsigned len, uint32_t idx, uint32_t max_depth_idx ) +{ + return idx + max_depth_idx < len; +} + +static uint32_t search_for_osi_session_spdu( MmsTracker& mms, const uint8_t* data, uint32_t idx ) +{ + switch ( data[idx] ) { - mms.state = mms.last_state; + // check for a known MMS message tag in the event Session/Pres/ACSE aren't used + case MMS_CONFIRMED_REQUEST_TAG: // fallthrough intentional + case MMS_CONFIRMED_RESPONSE_TAG: // fallthrough intentional + case MMS_CONFIRMED_ERROR_TAG: // fallthrough intentional + case MMS_UNCONFIRMED_TAG: // fallthrough intentional + case MMS_REJECT_TAG: // fallthrough intentional + case MMS_CANCEL_REQUEST_TAG: // fallthrough intentional + case MMS_CANCEL_RESPONSE_TAG: // fallthrough intentional + case MMS_CANCEL_ERROR_TAG: // fallthrough intentional + case MMS_INITIATE_REQUEST_TAG: // fallthrough intentional + case MMS_INITIATE_RESPONSE_TAG: // fallthrough intentional + case MMS_INITIATE_ERROR_TAG: // fallthrough intentional + case MMS_CONCLUDE_REQUEST_TAG: // fallthrough intentional + case MMS_CONCLUDE_RESPONSE_TAG: // fallthrough intentional + case MMS_CONCLUDE_ERROR_TAG: + { + // when it looks like MMS this early, drop the index back one and push processing down to the full MMS parsing + // taking this approach as there are other paths into the MMS determination + if ( idx > 0 ) + { + idx--; + mms.state = MMS_STATE__MMS; + break; + } + + // default to MMS not found + mms.state = MMS_STATE__NOT_FOUND; + break; } - // define all known MMS tags to check for + // if mms isn't found, search for a supported OSI Session layer + + // check for the Confirmed Request/Response path + case MMS_OSI_SESSION_SPDU_GT_DT: + { + mms.state = MMS_STATE__OSI_SESSION_SPDU_GT_LEN; + break; + } + + // check for the Initiate Request path + case MMS_OSI_SESSION_SPDU_CN: + { + mms.state = MMS_STATE__OSI_SESSION_SPDU_CN_LEN; + break; + } + + // check for the Initiate Response path + case MMS_OSI_SESSION_SPDU_AC: + { + mms.state = MMS_STATE__OSI_SESSION_SPDU_AC_LEN; + break; + } + + // if neither are found, it is most likely not MMS + default: + { + mms.state = MMS_STATE__NOT_FOUND; + break; + } + } + return idx; +} + +static uint32_t search_for_pres_ctx( MmsTracker& mms, const uint8_t* data, unsigned len, uint32_t idx ) +{ + // define some constants for search windows + constexpr uint32_t fully_encoded_data_window = 3; + constexpr uint32_t pres_ctx_window = 3; + constexpr uint32_t max_depth_idx = fully_encoded_data_window + pres_ctx_window; + + // try to determine if the ACSE layer exists + // len reduction is to allow space for the forward looking checks + bool ctx_likely = false; + for ( uint32_t init_byte = idx; ( init_byte < len - max_depth_idx ) && !ctx_likely; init_byte++) + { + // require the user-data fully encoded data tag ( | 61 | ) + if ( data[init_byte] != 0x61 ) + { + continue; + } + + // make sure there is still enough space left in the buffer for the forward search + // allow up to two bytes for the fully encoded data field + if ( !verify_search_depth_idx( len, init_byte, max_depth_idx ) ) + { + // not enough data to process + assert( !ctx_likely ); + break; + } + + for ( uint32_t encode_tag_shift = 1; ( encode_tag_shift < fully_encoded_data_window ) && !ctx_likely; encode_tag_shift++ ) + { + uint32_t encode_tag_byte = init_byte + encode_tag_shift; + // look for the ' fully encoded data ' user data tag ( | 30 | ); + if ( data[encode_tag_byte] != 0x30 ) + { + continue; + } + + for ( uint32_t pres_ctx_shift = 1; ( pres_ctx_shift < + pres_ctx_window ) && !ctx_likely; pres_ctx_shift++ ) + { + // look for the presentation context tag and length ( | 02 01 | ) + bool ctx_b1 = data[encode_tag_byte + pres_ctx_shift] == 0x02; + bool ctx_b2 = data[encode_tag_byte + pres_ctx_shift + 1] == 0x01; + if ( ctx_b1 && ctx_b2 ) + { + switch ( data[encode_tag_byte + pres_ctx_shift + 2] ) + { + // set the state accordingly when the OSI ACSE presentation context ( | 01 | ) has been found + case PresCtx::PRES_CTX_ACSE: + { + // place the index at the last byte of the presentation context + idx = init_byte + encode_tag_shift + pres_ctx_shift + 2; + mms.state = MMS_STATE__OSI_ACSE; + ctx_likely = true; + break; + } + // set the state accordingly when the MMS presentation context ( | 03 | ) has been found + case PresCtx::PRES_CTX_MMS: + { + // place the index at the last byte of the presentation context + idx = init_byte + encode_tag_shift + pres_ctx_shift + 2; + mms.state = MMS_STATE__MMS; + ctx_likely = true; + break; + } + // no default as we want to keep looking if an acceptable context is not found + } + } + } + } + } + return idx; +} + +static uint32_t search_for_osi_session_spdu_params( MmsTracker& mms, const uint8_t* data, unsigned len, uint32_t idx ) +{ enum { - MMS_CONFIRMED_REQUEST_TAG = 0xA0, - MMS_CONFIRMED_RESPONSE_TAG = 0xA1, - MMS_CONFIRMED_ERROR_TAG = 0xA2, - MMS_UNCONFIRMED_TAG = 0xA3, - MMS_REJECT_TAG = 0xA4, - MMS_CANCEL_REQUEST_TAG = 0x85, - MMS_CANCEL_RESPONSE_TAG = 0x86, - MMS_CANCEL_ERROR_TAG = 0xA7, - MMS_INITIATE_REQUEST_TAG = 0xA8, - MMS_INITIATE_RESPONSE_TAG = 0xA9, - MMS_INITIATE_ERROR_TAG = 0xAA, - MMS_CONCLUDE_REQUEST_TAG = 0x8B, - MMS_CONCLUDE_RESPONSE_TAG = 0x8C, - MMS_CONCLUDE_ERROR_TAG = 0xAD, + CN_SPDU_PARAM__CONNECT_ACCEPT_ITEM = 0x05, + CN_SPDU_PARAM__SESSION_REQUIREMENT = 0x14, + CN_SPDU_PARAM__SESSION_USER_DATA = 0xC1, }; - uint32_t idx = 0; - while ( idx < len ) + // len reduction is to allow space for the forward looking checks + while ( idx < len - 3 ) { - switch ( mms.state ) + // check for the possibility of a connect accept item + if ( data[idx] == CN_SPDU_PARAM__CONNECT_ACCEPT_ITEM ) { - case MMS_STATE__TPKT_VER: + // track that the item was found + mms.connect_accept_item_likely = true; + } + // check for the possibility of a session requirement + else if ( data[idx] == CN_SPDU_PARAM__SESSION_REQUIREMENT ) + { + // track that the item was found + mms.session_requirement_likely = true; + } + // check for the possibility of a session user data item + else if ( data[idx] == CN_SPDU_PARAM__SESSION_USER_DATA ) + { + // when this has been found and there is a good chance all of the other required items exist, move on to look for mms + if ( mms.connect_accept_item_likely && + mms.session_requirement_likely ) { - mms.state = MMS_STATE__TPKT_RES; + mms.state = MMS_STATE__OSI_SESSION_SPDU_USER_DATA_LEN; break; } - - case MMS_STATE__TPKT_RES: + // otherwise it is unlikely that this is mms + else { - mms.state = MMS_STATE__TPKT_LEN1; + mms.state = MMS_STATE__NOT_FOUND; break; } + } + // no else case as we want to allow for the possibility of non-standard items + + // increment the index to look at the item length field + idx++; + // increment the index to the end of the item data + idx += data[idx]; + // increment the index to the start of the next item + idx++; + + // if the index has gotten larger than the available buffer, bail + if ( idx >= len ) + { + mms.state = MMS_STATE__NOT_FOUND; + break; + } + } + return idx; +} + +static uint32_t search_for_osi_acse_type( MmsTracker& mms, const uint8_t* data, unsigned len, uint32_t idx ) +{ + enum + { + OSI_ACSE_AARQ = 0x60, + OSI_ACSE_AARE = 0x61, + }; - case MMS_STATE__TPKT_LEN1: + constexpr uint32_t max_search_depth_idx = 3; + if ( verify_search_depth_idx( len, idx, max_search_depth_idx ) ) + { + // when ACSE is likely found, do processing at that layer to determine if MMS is being transported + // length field could be 1-2 bytes depending on encoding + for ( uint32_t j = 0; j < max_search_depth_idx; j++ ) + { + // look for either the AARQ ( | 60 | ) or AARE ( | 61 | ) tag + if ( data[idx+j] == OSI_ACSE_AARQ || data[idx+j] == OSI_ACSE_AARE ) { - mms.state = MMS_STATE__TPKT_LEN2; + mms.state = MMS_STATE__OSI_ACSE_DATA; + idx += j; break; } + } + } + return idx; +} - case MMS_STATE__TPKT_LEN2: +static uint32_t search_for_osi_acse_data( MmsTracker& mms, const uint8_t* data, unsigned len, uint32_t idx ) +{ + // this will likely be a good distance from the current position + // minus three is to give space for the forward checks for the direct and indirect reference + for ( uint32_t k = 0; idx+k < len-3; k++ ) + { + // look for the MMS presentation context ( | 02 01 03 | ) + if ( data[idx+k] == 0x02 && data[idx+k+1] == 0x01 && data[idx+k+2] == 0x03 ) + { + mms.state = MMS_STATE__MMS; + // increment the index to the end of the mms context id reference + idx += k+2; + break; + } + } + return idx; +} + +static uint32_t search_for_mms( MmsTracker& mms, const uint8_t* data, unsigned len, uint32_t idx ) +{ + constexpr uint32_t max_search_depth_idx = 2; + if ( verify_search_depth_idx( len, idx, max_search_depth_idx ) ) + { + // search within the next two bytes to determine if mms is likely + for ( uint32_t j = 0; j < max_search_depth_idx; j++ ) + { + // for each remaining byte check to see if it is in the known tag map + switch ( data[idx+j] ) { - mms.state = MMS_STATE__COTP_LEN; + case MMS_CONFIRMED_REQUEST_TAG: + { + mms.state = MMS_STATE__MMS_CONFIRMED_REQUEST; + idx += j; break; } - - case MMS_STATE__COTP_LEN: + case MMS_CONFIRMED_RESPONSE_TAG: // fallthrough intentional + case MMS_CONFIRMED_ERROR_TAG: // fallthrough intentional + case MMS_UNCONFIRMED_TAG: // fallthrough intentional + case MMS_REJECT_TAG: // fallthrough intentional + case MMS_CANCEL_REQUEST_TAG: // fallthrough intentional + case MMS_CANCEL_RESPONSE_TAG: // fallthrough intentional + case MMS_CANCEL_ERROR_TAG: // fallthrough intentional + case MMS_INITIATE_REQUEST_TAG: // fallthrough intentional + case MMS_INITIATE_RESPONSE_TAG: // fallthrough intentional + case MMS_INITIATE_ERROR_TAG: // fallthrough intentional + case MMS_CONCLUDE_REQUEST_TAG: // fallthrough intentional + case MMS_CONCLUDE_RESPONSE_TAG: // fallthrough intentional + case MMS_CONCLUDE_ERROR_TAG: { - mms.state = MMS_STATE__COTP_PDU; + // if an MMS tag exists in the remaining data, + // hand off to the MMS service inspector + mms.state = MMS_STATE__FOUND; + idx += j; break; } + // no default as we want to keep searching if a result wasn't found in this loop + } - case MMS_STATE__COTP_PDU: + // move on to the next case when a state has been set + if ( mms.state != MMS_STATE__NOT_FOUND ) { - // 7 6 5 4 3 2 1 0 - // --------------- - // . . . . x x x x Destination Reference - // x x x x . . . . PDU Type - const uint32_t MMS_COTP_PDU_DT_DATA = 0x0F; + break; + } + } + } + return idx; +} - if ( data[idx] >> 0x04 != MMS_COTP_PDU_DT_DATA ) - { - mms.state = MMS_STATE__NOT_FOUND; - break; - } +static uint32_t search_for_mms_confirmed_request( MmsTracker& mms, const uint8_t* data, unsigned len, uint32_t idx ) +{ + // default to MMS not found + mms.state = MMS_STATE__NOT_FOUND; + + // confirmed service request types + enum + { + DEFINE_NAMED_TYPE_MESSAGE = 0xAE, + DEFINE_NAMED_VARIABLE_MESSAGE = 0xA7, + DEFINE_NAMED_VARIABLE_LIST_MESSAGE = 0xAB, + DEFINE_SCATTERED_ACCESS_MESSAGE = 0xA8, + DEFINE_SEMAPHORE_MESSAGE = 0xB5, + DELETE_NAMED_TYPE_MESSAGE = 0xB0, + DELETE_NAMED_VARIABLE_LIST_MESSAGE = 0xAD, + DELETE_SEMAPHORE_MESSAGE = 0xB6, + DELETE_VARIABLE_ACCESS_MESSAGE = 0xAA, + DOWNLOAD_SEGMENT_MESSAGE = 0x9B, + GET_NAME_LIST_MESSAGE = 0xA1, + GET_NAMED_TYPE_ATTRIBUTES_MESSAGE = 0xAF, + GET_NAMED_VARIABLE_LIST_ATTRIBUTES_MESSAGE = 0xAC, + GET_SCATTERED_ACCESS_ATTRIBUTES_MESSAGE = 0xA9, + GET_VARIABLE_ACCESS_ATTRIBUTES_MESSAGE = 0xA6, + IDENTIFY_MESSAGE = 0x82, + INITIATE_DOWNLOAD_SEQUENCE_MESSAGE = 0xBA, + INITIATE_UPLOAD_SEQUENCE_MESSAGE = 0x9D, + INPUT_MESSAGE = 0xB1, + OUTPUT_MESSAGE = 0xB2, + READ_MESSAGE = 0xA4, + RELINQUISH_CONTROL_MESSAGE = 0xB4, + RENAME_MESSAGE = 0xA3, + REPORT_POOL_SEMAPHORE_STATUS_MESSAGE = 0xB8, + REPORT_SEMAPHORE_ENTRY_STATUS_MESSAGE = 0xB9, + REPORT_SEMAPHORE_STATUS_MESSAGE = 0xB7, + STATUS_MESSAGE = 0x80, + TAKE_CONTROL_MESSAGE = 0xB3, + TERMINATE_DOWNLOAD_SEQUENCE_MESSAGE = 0xBC, + UPLOAD_SEGMENT_MESSAGE = 0x9E, + WRITE_MESSAGE = 0xA5, + EXPANSION_9F = 0x9F, + EXPANSION_BF = 0xBF, + }; - mms.state = MMS_STATE__COTP_TPDU_NUM; + // look for a known Confirmed Service Request 4-6 bytes away + constexpr uint32_t max_search_depth_idx = 6; + if ( verify_search_depth_idx( len, idx, max_search_depth_idx ) ) + { + for ( uint32_t j = 3; j <= max_search_depth_idx; j++ ) + { + // check for any of the single byte service tags + switch ( data[idx+j] ) + { + case DEFINE_NAMED_TYPE_MESSAGE: // fallthrough intentional + case DEFINE_NAMED_VARIABLE_MESSAGE: // fallthrough intentional + case DEFINE_NAMED_VARIABLE_LIST_MESSAGE: // fallthrough intentional + case DEFINE_SCATTERED_ACCESS_MESSAGE: // fallthrough intentional + case DEFINE_SEMAPHORE_MESSAGE: // fallthrough intentional + case DELETE_NAMED_TYPE_MESSAGE: // fallthrough intentional + case DELETE_NAMED_VARIABLE_LIST_MESSAGE: // fallthrough intentional + case DELETE_SEMAPHORE_MESSAGE: // fallthrough intentional + case DELETE_VARIABLE_ACCESS_MESSAGE: // fallthrough intentional + case DOWNLOAD_SEGMENT_MESSAGE: // fallthrough intentional + case GET_NAME_LIST_MESSAGE: // fallthrough intentional + case GET_NAMED_TYPE_ATTRIBUTES_MESSAGE: // fallthrough intentional + case GET_NAMED_VARIABLE_LIST_ATTRIBUTES_MESSAGE: // fallthrough intentional + case GET_SCATTERED_ACCESS_ATTRIBUTES_MESSAGE: // fallthrough intentional + case GET_VARIABLE_ACCESS_ATTRIBUTES_MESSAGE: // fallthrough intentional + case IDENTIFY_MESSAGE: // fallthrough intentional + case INITIATE_DOWNLOAD_SEQUENCE_MESSAGE: // fallthrough intentional + case INITIATE_UPLOAD_SEQUENCE_MESSAGE: // fallthrough intentional + case INPUT_MESSAGE: // fallthrough intentional + case OUTPUT_MESSAGE: // fallthrough intentional + case READ_MESSAGE: // fallthrough intentional + case RELINQUISH_CONTROL_MESSAGE: // fallthrough intentional + case RENAME_MESSAGE: // fallthrough intentional + case REPORT_POOL_SEMAPHORE_STATUS_MESSAGE: // fallthrough intentional + case REPORT_SEMAPHORE_ENTRY_STATUS_MESSAGE: // fallthrough intentional + case REPORT_SEMAPHORE_STATUS_MESSAGE: // fallthrough intentional + case STATUS_MESSAGE: // fallthrough intentional + case TAKE_CONTROL_MESSAGE: // fallthrough intentional + case TERMINATE_DOWNLOAD_SEQUENCE_MESSAGE: // fallthrough intentional + case UPLOAD_SEGMENT_MESSAGE: // fallthrough intentional + case WRITE_MESSAGE: // fallthrough intentional + case EXPANSION_9F: // fallthrough intentional + case EXPANSION_BF: + { + idx += j; + mms.state = MMS_STATE__FOUND; break; } + // no default as we want to keep searching if a result wasn't found in this loop + } - case MMS_STATE__COTP_TPDU_NUM: + // move on to the next case when a state has been set + if ( mms.state != MMS_STATE__NOT_FOUND ) { - mms.state = MMS_STATE__OSI_SESSION_SPDU; break; } + } + } - case MMS_STATE__OSI_SESSION_SPDU: + return idx; +} + +bool CurseBook::mms_curse( const uint8_t* data, unsigned len, CurseTracker* tracker ) +{ + // peg the tracker to MMS + MmsTracker& mms = tracker->mms; + + // if the state is set to MMS_STATE__SEARCH it means we most likely + // have a split pipelined message coming through and will need to + // reset the state + if ( mms.state == MMS_STATE__SEARCH ) + { + mms.state = mms.last_state; + } + + uint32_t idx = 0; + while ( idx < len ) + { + switch ( mms.state ) + { + case MMS_STATE__TPKT_VER: + { + // MMS is only known to run over version | 03 | + if ( data[idx] != 0x03 ) { - // define all known OSI Session layer SPDU tags to check - enum - { - MMS_OSI_SESSION_SPDU_GT_DT = 0x01, - MMS_OSI_SESSION_SPDU_CN = 0x0D, - MMS_OSI_SESSION_SPDU_AC = 0x0E, - }; + mms.state = MMS_STATE__NOT_FOUND; + break; + } - switch ( data[idx] ) - { - // check for a known MMS message tag in the event Session/Pres/ACSE aren't used - case MMS_CONFIRMED_REQUEST_TAG: // fallthrough intentional - case MMS_CONFIRMED_RESPONSE_TAG: // fallthrough intentional - case MMS_CONFIRMED_ERROR_TAG: // fallthrough intentional - case MMS_UNCONFIRMED_TAG: // fallthrough intentional - case MMS_REJECT_TAG: // fallthrough intentional - case MMS_CANCEL_REQUEST_TAG: // fallthrough intentional - case MMS_CANCEL_RESPONSE_TAG: // fallthrough intentional - case MMS_CANCEL_ERROR_TAG: // fallthrough intentional - case MMS_INITIATE_REQUEST_TAG: // fallthrough intentional - case MMS_INITIATE_RESPONSE_TAG: // fallthrough intentional - case MMS_INITIATE_ERROR_TAG: // fallthrough intentional - case MMS_CONCLUDE_REQUEST_TAG: // fallthrough intentional - case MMS_CONCLUDE_RESPONSE_TAG: // fallthrough intentional - case MMS_CONCLUDE_ERROR_TAG: - { - // if an MMS tag exists in the remaining data, - // hand off to the MMS service inspector - mms.state = MMS_STATE__FOUND; - break; - } + mms.state = MMS_STATE__TPKT_RES; + break; + } - // if mms isn't found, search for an OSI Session layer - case MMS_OSI_SESSION_SPDU_GT_DT: // fallthrough intentional - case MMS_OSI_SESSION_SPDU_CN: // fallthrough intentional - case MMS_OSI_SESSION_SPDU_AC: - { - mms.state = MMS_STATE__MMS; - break; - } + case MMS_STATE__TPKT_RES: + { + mms.state = MMS_STATE__TPKT_LEN1; + break; + } - // if neither are found, it is most likely not MMS - default: - { - mms.state = MMS_STATE__NOT_FOUND; - } - } + case MMS_STATE__TPKT_LEN1: + { + mms.state = MMS_STATE__TPKT_LEN2; + break; + } + case MMS_STATE__TPKT_LEN2: + { + mms.state = MMS_STATE__COTP_LEN; + break; + } + + case MMS_STATE__COTP_LEN: + { + mms.state = MMS_STATE__COTP_PDU; + break; + } + + case MMS_STATE__COTP_PDU: + { + // 7 6 5 4 3 2 1 0 + // --------------- + // . . . . x x x x Destination Reference + // x x x x . . . . PDU Type + constexpr uint32_t MMS_COTP_PDU_DT_DATA = 0x0F; + + if ( data[idx] >> 0x04 != MMS_COTP_PDU_DT_DATA ) + { + mms.state = MMS_STATE__NOT_FOUND; break; } - case MMS_STATE__MMS: - { - // loop through the remaining bytes in the buffer checking for known MMS tags - for ( uint32_t i=idx; i < len; i++ ) - { - // for each remaining byte check to see if it is in the known tag map - switch ( data[i] ) - { - case MMS_CONFIRMED_REQUEST_TAG: // fallthrough intentional - case MMS_CONFIRMED_RESPONSE_TAG: // fallthrough intentional - case MMS_CONFIRMED_ERROR_TAG: // fallthrough intentional - case MMS_UNCONFIRMED_TAG: // fallthrough intentional - case MMS_REJECT_TAG: // fallthrough intentional - case MMS_CANCEL_REQUEST_TAG: // fallthrough intentional - case MMS_CANCEL_RESPONSE_TAG: // fallthrough intentional - case MMS_CANCEL_ERROR_TAG: // fallthrough intentional - case MMS_INITIATE_REQUEST_TAG: // fallthrough intentional - case MMS_INITIATE_RESPONSE_TAG: // fallthrough intentional - case MMS_INITIATE_ERROR_TAG: // fallthrough intentional - case MMS_CONCLUDE_REQUEST_TAG: // fallthrough intentional - case MMS_CONCLUDE_RESPONSE_TAG: // fallthrough intentional - case MMS_CONCLUDE_ERROR_TAG: - { - // if an MMS tag exists in the remaining data, - // hand off to the MMS service inspector - mms.state = MMS_STATE__FOUND; - break; - } - // no default here as it we don't know when we would hit - // the first MMS tag without doing full parsing - } + mms.state = MMS_STATE__COTP_TPDU_NUM; + break; + } - // exit the loop when a state has been determined - if ( mms.state == MMS_STATE__NOT_FOUND - or mms.state == MMS_STATE__SEARCH - or mms.state == MMS_STATE__FOUND ) - { - break; - } - } + case MMS_STATE__COTP_TPDU_NUM: + { + mms.state = MMS_STATE__OSI_SESSION_SPDU; + break; + } + + case MMS_STATE__OSI_SESSION_SPDU: + { + idx = search_for_osi_session_spdu(mms, data, idx); + break; + } + + // + // State path for most MMS messages + // + + // check the length field of a GT SPDU + case MMS_STATE__OSI_SESSION_SPDU_GT_LEN: + { + // length should always be zero for MMS + if ( data[idx] != 0x00 ) + { + mms.state = MMS_STATE__NOT_FOUND; break; } - case MMS_STATE__FOUND: - { - mms.state = MMS_STATE__TPKT_VER; + mms.state = MMS_STATE__OSI_SESSION_SPDU_DT; + break; + } - return true; + // check for the tag of a DT SPDU + case MMS_STATE__OSI_SESSION_SPDU_DT: + { + // tag should always be | 01 | for DT + if ( data[idx] != MMS_OSI_SESSION_SPDU_GT_DT ) + { + mms.state = MMS_STATE__NOT_FOUND; + break; } - case MMS_STATE__NOT_FOUND: + mms.state = MMS_STATE__OSI_SESSION_SPDU_DT_LEN; + break; + } + + // check the length field of a DT SPDU + case MMS_STATE__OSI_SESSION_SPDU_DT_LEN: + { + // length should always be zero for MMS + if ( data[idx] != 0x00 ) { - mms.state = MMS_STATE__TPKT_VER; + mms.state = MMS_STATE__NOT_FOUND; + break; + } + + mms.state = MMS_STATE__OSI_PRES_USER_DATA; + break; + } + + // process the User Data Presentation type + case MMS_STATE__OSI_PRES_USER_DATA: + { + idx = search_for_pres_ctx(mms, data, len, idx); + break; + } + + + // + // State path for Initiate-Request and Initiate-Response + // + + // skip the CN SPDU length field + case MMS_STATE__OSI_SESSION_SPDU_CN_LEN: + { + mms.state = MMS_STATE__OSI_SESSION_SPDU_PARAMS; + break; + } + + // skip the AC SPDU length field + case MMS_STATE__OSI_SESSION_SPDU_AC_LEN: + { + mms.state = MMS_STATE__OSI_SESSION_SPDU_PARAMS; + break; + } + + // check the parameters + case MMS_STATE__OSI_SESSION_SPDU_PARAMS: + { + idx = search_for_osi_session_spdu_params(mms, data, len, idx); + break; + } - return false; + // jump over the length field + case MMS_STATE__OSI_SESSION_SPDU_USER_DATA_LEN: + { + mms.state = MMS_STATE__OSI_PRES_CP_CPA; + break; + } + + // process a CP or CPA presentation type + case MMS_STATE__OSI_PRES_CP_CPA: + { + // look for the ' CP ' or ' CPA ' presentation type tag ( | 31 | ); + if ( data[idx] != 0x31 ) + { + mms.state = MMS_STATE__NOT_FOUND; + break; } - default: + mms.state = MMS_STATE__OSI_PRES_CP_CPA_USER_DATA_ACSE_LOCATE; + break; + } + + // + case MMS_STATE__OSI_PRES_CP_CPA_USER_DATA_ACSE_LOCATE: + { + idx = search_for_pres_ctx(mms, data, len, idx); + break; + } + + // check for ACSE + case MMS_STATE__OSI_ACSE: + { + // look for the presentation data values single ASN1 type ( | A0 | ); + if ( data[idx] != 0xA0 ) { mms.state = MMS_STATE__NOT_FOUND; - assert(false); break; } + + mms.state = MMS_STATE__OSI_ACSE_TYPE; + break; + } + + // look for a supported ACSE type tag + case MMS_STATE__OSI_ACSE_TYPE: + { + idx = search_for_osi_acse_type(mms, data, len, idx); + break; + } + + // + case MMS_STATE__OSI_ACSE_DATA: + { + idx = search_for_osi_acse_data(mms, data, len, idx); + break; + } + + + // + // State path for MMS convergence + // + + // Look for a byte known to represent a MMS tag + case MMS_STATE__MMS: + { + idx = search_for_mms(mms, data, len, idx); + break; + } + + case MMS_STATE__MMS_CONFIRMED_REQUEST: + { + idx = search_for_mms_confirmed_request(mms, data, len, idx); + break; + } + + case MMS_STATE__FOUND: + { + mms.state = MMS_STATE__TPKT_VER; + + return true; + } + + case MMS_STATE__NOT_FOUND: + { + mms.state = MMS_STATE__TPKT_VER; + + return false; + } + + default: + { + mms.state = MMS_STATE__NOT_FOUND; + assert( false ); + break; + } } idx++; diff --git a/src/service_inspectors/wizard/mms_curse.h b/src/service_inspectors/wizard/mms_curse.h index 77ef3c06d..d3303db00 100644 --- a/src/service_inspectors/wizard/mms_curse.h +++ b/src/service_inspectors/wizard/mms_curse.h @@ -34,17 +34,60 @@ enum MMS_State MMS_STATE__COTP_PDU, MMS_STATE__COTP_TPDU_NUM, MMS_STATE__OSI_SESSION_SPDU, + MMS_STATE__OSI_SESSION_SPDU_GT_LEN, + MMS_STATE__OSI_SESSION_SPDU_DT, + MMS_STATE__OSI_SESSION_SPDU_DT_LEN, + MMS_STATE__OSI_SESSION_SPDU_CN_LEN, + MMS_STATE__OSI_SESSION_SPDU_AC_LEN, + MMS_STATE__OSI_SESSION_SPDU_PARAMS, + MMS_STATE__OSI_SESSION_SPDU_USER_DATA_LEN, + MMS_STATE__OSI_PRES_CP_CPA, + MMS_STATE__OSI_PRES_CP_CPA_USER_DATA_ACSE_LOCATE, + MMS_STATE__OSI_PRES_USER_DATA, + MMS_STATE__OSI_ACSE, + MMS_STATE__OSI_ACSE_TYPE, + MMS_STATE__OSI_ACSE_DATA, MMS_STATE__MMS, + MMS_STATE__MMS_CONFIRMED_REQUEST, MMS_STATE__FOUND, MMS_STATE__SEARCH, MMS_STATE__NOT_FOUND, }; +// define all known MMS tags to check for +enum +{ + MMS_CONFIRMED_REQUEST_TAG = 0xA0, + MMS_CONFIRMED_RESPONSE_TAG = 0xA1, + MMS_CONFIRMED_ERROR_TAG = 0xA2, + MMS_UNCONFIRMED_TAG = 0xA3, + MMS_REJECT_TAG = 0xA4, + MMS_CANCEL_REQUEST_TAG = 0x85, + MMS_CANCEL_RESPONSE_TAG = 0x86, + MMS_CANCEL_ERROR_TAG = 0xA7, + MMS_INITIATE_REQUEST_TAG = 0xA8, + MMS_INITIATE_RESPONSE_TAG = 0xA9, + MMS_INITIATE_ERROR_TAG = 0xAA, + MMS_CONCLUDE_REQUEST_TAG = 0x8B, + MMS_CONCLUDE_RESPONSE_TAG = 0x8C, + MMS_CONCLUDE_ERROR_TAG = 0xAD, +}; + +// define all applicable OSI Session layer SPDU tags to check +enum +{ + MMS_OSI_SESSION_SPDU_GT_DT = 0x01, + MMS_OSI_SESSION_SPDU_CN = 0x0D, + MMS_OSI_SESSION_SPDU_AC = 0x0E, +}; + class MmsTracker { public: MMS_State state = MMS_State::MMS_STATE__TPKT_VER; MMS_State last_state = MMS_State::MMS_STATE__TPKT_VER; + bool connect_accept_item_likely = false; + bool session_requirement_likely = false; }; #endif