diff --git a/rust/src/dcerpc/dcerpc.rs b/rust/src/dcerpc/dcerpc.rs index 3187927e4d98..ff69522d3999 100644 --- a/rust/src/dcerpc/dcerpc.rs +++ b/rust/src/dcerpc/dcerpc.rs @@ -1,4 +1,4 @@ -/* Copyright (C) 2020-2024 Open Information Security Foundation +/* Copyright (C) 2020-2026 Open Information Security Foundation * * You can copy, redistribute or modify this Program under the terms of * the GNU General Public License version 2 as published by the Free @@ -558,6 +558,7 @@ impl DCERPCState { for i in 0..numctxitems { retval = self.handle_bindctxitem(&input[idx as usize..], i as u16, hdr); if retval == -1 { + SCLogDebug!("Error handling BindCtxItem"); return -1; } idx += retval; @@ -765,6 +766,7 @@ impl DCERPCState { let retval; let mut cur_i = stream_slice.as_slice(); let mut consumed = 0u32; + let mut rem = cur_i.len() as u32; // Skip the record since this means that its in the middle of a known length record if (self.ts_gap && direction == Direction::ToServer) || (self.tc_gap && direction == Direction::ToClient) { @@ -797,6 +799,7 @@ impl DCERPCState { }, } } + rem -= consumed; let mut flow = std::ptr::null_mut(); if let Some(f) = self.flow { @@ -835,82 +838,96 @@ impl DCERPCState { }; let parsed = DCERPC_HDR_LEN; let fraglen = hdr.frag_length; + match fraglen.cmp(&parsed) { + cmp::Ordering::Less => { + // fragment length should at least be header length + SCLogDebug!("Erroneous fragment length"); + return AppLayerResult::err(); + }, + cmp::Ordering::Equal => { + // input only consists of the header and that was consumed, so, return early + return AppLayerResult::ok(); + } + cmp::Ordering::Greater => {} + } - if cur_i.len() < fraglen as usize { + // rem == bytes consumed to move past gap; so those were not a part of the fragment + if rem < fraglen as u32 { SCLogDebug!("Possibly fragmented data, waiting for more.."); return AppLayerResult::incomplete(consumed, fraglen.into()); } let hdrtype = hdr.hdrtype; - let _hdr = Frame::new(flow, &stream_slice, cur_i, parsed as i64, DCERPCFrameType::Hdr as u8, None); - let _pdu = Frame::new(flow, &stream_slice, cur_i, fraglen as i64, DCERPCFrameType::Pdu as u8, None); - if fraglen >= DCERPC_HDR_LEN && cur_i.len() > DCERPC_HDR_LEN as usize { - let _data = Frame::new(flow, &stream_slice, &cur_i[DCERPC_HDR_LEN as usize..], (fraglen - DCERPC_HDR_LEN) as i64, DCERPCFrameType::Data as u8, None); + let _hdr = Frame::new(flow, &stream_slice, &cur_i[consumed as usize..], DCERPC_HDR_LEN as i64, DCERPCFrameType::Hdr as u8, None); + let _pdu = Frame::new(flow, &stream_slice, &cur_i[consumed as usize..], fraglen as i64, DCERPCFrameType::Pdu as u8, None); + if fraglen >= DCERPC_HDR_LEN && rem > DCERPC_HDR_LEN as u32 { + let _data = Frame::new(flow, &stream_slice, &cur_i[(consumed + DCERPC_HDR_LEN as u32) as usize..], (fraglen - DCERPC_HDR_LEN) as i64, DCERPCFrameType::Data as u8, None); } let current_call_id = hdr.call_id; + debug_validate_bug_on!(parsed > fraglen); match hdrtype { - DCERPC_TYPE_BIND | DCERPC_TYPE_ALTER_CONTEXT => { - retval = self.process_bind_pdu(&cur_i[parsed as usize..], &hdr); - if retval == -1 { - return AppLayerResult::err(); - } + DCERPC_TYPE_BIND | DCERPC_TYPE_ALTER_CONTEXT => { + retval = self.process_bind_pdu(&cur_i[parsed as usize..fraglen as usize], &hdr); + if retval == -1 { + return AppLayerResult::err(); } - DCERPC_TYPE_BINDACK | DCERPC_TYPE_ALTER_CONTEXT_RESP => { - retval = self.process_bindack_pdu(&cur_i[parsed as usize..]); - if retval == -1 { - return AppLayerResult::err(); - } - let tx = if let Some(tx) = self.get_tx_by_call_id(current_call_id, Direction::ToClient, hdrtype) { + } + DCERPC_TYPE_BINDACK | DCERPC_TYPE_ALTER_CONTEXT_RESP => { + retval = self.process_bindack_pdu(&cur_i[parsed as usize..fraglen as usize]); + if retval == -1 { + return AppLayerResult::err(); + } + let tx = if let Some(tx) = self.get_tx_by_call_id(current_call_id, Direction::ToClient, hdrtype) { + tx.resp_cmd = hdrtype; + tx + } else { + let mut tx = self.create_tx(&hdr); + tx.resp_cmd = hdrtype; + self.transactions.push_back(tx); + self.transactions.back_mut().unwrap() + }; + tx.resp_done = true; + tx.frag_cnt_tc = 1; + if let Some(flow) = self.flow { + sc_app_layer_parser_trigger_raw_stream_inspection(flow, Direction::ToClient as i32); + } + } + DCERPC_TYPE_REQUEST => { + retval = self.process_request_pdu(&cur_i[parsed as usize..fraglen as usize], &hdr); + if retval < 0 { + return AppLayerResult::err(); + } + // In case the response came first, the transaction would complete later when + // the corresponding request also comes through + } + DCERPC_TYPE_RESPONSE => { + let transaction = self.get_tx_by_call_id(current_call_id, Direction::ToClient, hdrtype); + match transaction { + Some(tx) => { tx.resp_cmd = hdrtype; - tx - } else { + } + None => { let mut tx = self.create_tx(&hdr); tx.resp_cmd = hdrtype; self.transactions.push_back(tx); - self.transactions.back_mut().unwrap() - }; - tx.resp_done = true; - tx.frag_cnt_tc = 1; - if let Some(flow) = self.flow { - sc_app_layer_parser_trigger_raw_stream_inspection(flow, Direction::ToClient as i32); - } - } - DCERPC_TYPE_REQUEST => { - retval = self.process_request_pdu(&cur_i[parsed as usize..], &hdr); - if retval < 0 { - return AppLayerResult::err(); } - // In case the response came first, the transaction would complete later when - // the corresponding request also comes through - } - DCERPC_TYPE_RESPONSE => { - let transaction = self.get_tx_by_call_id(current_call_id, Direction::ToClient, hdrtype); - match transaction { - Some(tx) => { - tx.resp_cmd = hdrtype; - } - None => { - let mut tx = self.create_tx(&hdr); - tx.resp_cmd = hdrtype; - self.transactions.push_back(tx); - } - }; - retval = self.handle_common_stub( - &cur_i[parsed as usize..], - 0, - Direction::ToClient, - &hdr, - ); - if retval < 0 { - return AppLayerResult::err(); - } - } - _ => { - SCLogDebug!("Unrecognized packet type: {:?}", hdrtype); + }; + retval = self.handle_common_stub( + &cur_i[parsed as usize..fraglen as usize], + 0, + Direction::ToClient, + &hdr, + ); + if retval < 0 { return AppLayerResult::err(); } + } + _ => { + SCLogDebug!("Unrecognized packet type: {:?}", hdrtype); + return AppLayerResult::err(); + } } self.post_gap_housekeeping(direction); @@ -1705,46 +1722,6 @@ mod tests { } } - #[test] - // Check for endless loop with bind PDUs (Imported from C code) - pub fn test_parse_bind_pdu_infinite_loop() { - let bindbuf: &[u8] = &[ - 0x05, 0x00, 0x0b, 0x03, 0x10, 0x00, 0x00, 0x00, 0x4A, 0x00, 0x00, 0x00, 0x7f, 0x00, - 0x00, 0x00, 0xd0, 0x16, 0xd0, 0x16, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x01, 0x00, 0xa0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, 0x00, 0x04, 0x5d, 0x88, 0x8a, - 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00, - 0x00, 0x00, 0x02, 0x00, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x01, 0x02, 0x03, 0x04, - 0x05, 0x06, 0x07, 0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x01, 0x02, - 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, - 0x01, 0x02, 0x03, 0x04, 0xFF, /* ka boom - endless loop */ - ]; - let mut dcerpc_state = DCERPCState::new(); - assert_eq!( - AppLayerResult::ok(), - dcerpc_state.handle_input_data(StreamSlice::from_slice(bindbuf, STREAM_TOSERVER, 0), Direction::ToServer) - ); - } - - #[test] - // Check for endless loop with bind_ack PDUs (Imported from C code) - pub fn test_parse_bindack_pdu_infinite_loop() { - let bind_ack: &[u8] = &[ - 0x05, 0x00, 0x0c, 0x03, 0x10, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x7f, 0x00, - 0x00, 0x00, 0xd0, 0x16, 0xd0, 0x16, 0xfd, 0x04, 0x01, 0x00, 0x04, 0x00, 0x31, 0x33, - 0x35, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x5d, - 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60, - 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c, - 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60, 0x01, 0x02, 0x03, 0x04, - 0xFF, - ]; - let mut dcerpc_state = DCERPCState::new(); - assert_eq!( - AppLayerResult::ok(), - dcerpc_state.handle_input_data(StreamSlice::from_slice(bind_ack, STREAM_TOSERVER, 0), Direction::ToServer) - ); - } - #[test] // Check for correct internal ids for bind_acks pub fn test_parse_bindack_internal_ids() {