From: Victor Julien Date: Mon, 18 Apr 2022 15:49:58 +0000 (+0200) Subject: smb2: track max read/write size and enforce its values X-Git-Tag: suricata-5.0.9~27 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=271a2347bcc8eab03e1b569aee60ffbf7368116c;p=thirdparty%2Fsuricata.git smb2: track max read/write size and enforce its values (cherry picked from commit f28888513ac3be18715f522db253c5753adb94ed) --- diff --git a/rust/src/smb/events.rs b/rust/src/smb/events.rs index a2540fdbcf..8bd7fe185e 100644 --- a/rust/src/smb/events.rs +++ b/rust/src/smb/events.rs @@ -30,6 +30,12 @@ pub enum SMBEvent { FileOverlap = 7, RequestToClient = 8, ResponseToServer = 9, + /// READ request asking for more than `max_read_size` + ReadRequestTooLarge = 10, + /// READ response bigger than `max_read_size` + ReadResponseTooLarge = 11, + /// WRITE request for more than `max_write_size` + WriteRequestTooLarge = 12, } impl SMBEvent { @@ -45,6 +51,9 @@ impl SMBEvent { 7 => Some(SMBEvent::FileOverlap), 8 => Some(SMBEvent::RequestToClient), 9 => Some(SMBEvent::ResponseToServer), + 10 => Some(SMBEvent::ReadRequestTooLarge), + 11 => Some(SMBEvent::ReadResponseTooLarge), + 12 => Some(SMBEvent::WriteRequestTooLarge), _ => None, } } @@ -63,6 +72,9 @@ pub fn smb_str_to_event(instr: &str) -> i32 { "file_overlap" => SMBEvent::FileOverlap as i32, "request_to_client" => SMBEvent::RequestToClient as i32, "response_to_server" => SMBEvent::ResponseToServer as i32, + "read_request_too_large" => SMBEvent::ReadRequestTooLarge as i32, + "read_response_too_large" => SMBEvent::ReadResponseTooLarge as i32, + "write_request_too_large" => SMBEvent::WriteRequestTooLarge as i32, _ => -1, } } diff --git a/rust/src/smb/smb.rs b/rust/src/smb/smb.rs index 2a1f7ef517..73bd3655a0 100644 --- a/rust/src/smb/smb.rs +++ b/rust/src/smb/smb.rs @@ -803,6 +803,9 @@ pub struct SMBState<> { /// them while inspecting DCERPC REQUEST txs pub dcerpc_ifaces: Option>, + pub max_read_size: u32, + pub max_write_size: u32, + /// Timestamp in seconds of last update. This is packet time, /// potentially coming from pcaps. ts: u64, @@ -840,6 +843,8 @@ impl SMBState { dialect_vec: None, dcerpc_ifaces: None, ts: 0, + max_read_size: 0, + max_write_size: 0, } } @@ -2219,6 +2224,9 @@ pub extern "C" fn rs_smb_state_get_event_info_by_id(event_id: std::os::raw::c_in SMBEvent::FileOverlap => { "file_overlap\0" }, SMBEvent::RequestToClient => { "request_to_client\0" }, SMBEvent::ResponseToServer => { "response_to_server\0" }, + SMBEvent::ReadRequestTooLarge => { "read_request_too_large\0" }, + SMBEvent::ReadResponseTooLarge => { "read_response_too_large\0" }, + SMBEvent::WriteRequestTooLarge => { "write_request_too_large\0" }, }; unsafe{ *event_name = estr.as_ptr() as *const std::os::raw::c_char; diff --git a/rust/src/smb/smb2.rs b/rust/src/smb/smb2.rs index 3f304d7693..cdf9a44954 100644 --- a/rust/src/smb/smb2.rs +++ b/rust/src/smb/smb2.rs @@ -128,6 +128,12 @@ pub fn smb2_read_response_record<'b>(state: &mut SMBState, r: &Smb2Record<'b>) return; } + if state.max_read_size > 0 && rd.len > state.max_read_size { + state.set_event(SMBEvent::ReadResponseTooLarge); + state.set_skip(STREAM_TOCLIENT, rd.len, rd.data.len() as u32); + return; + } + SCLogDebug!("SMBv2: read response => {:?}", rd); // get the request info. If we don't have it, there is nothing @@ -247,6 +253,12 @@ pub fn smb2_write_request_record<'b>(state: &mut SMBState, r: &Smb2Record<'b>) } match parse_smb2_request_write(r.data) { Ok((_, wr)) => { + if state.max_read_size != 0 && wr.wr_len > state.max_write_size { + state.set_event(SMBEvent::WriteRequestTooLarge); + state.set_skip(STREAM_TOSERVER, wr.wr_len, wr.data.len() as u32); + return; + } + /* update key-guid map */ let guid_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_GUID); state.ssn2vec_map.insert(guid_key, wr.guid.to_vec()); @@ -459,13 +471,17 @@ pub fn smb2_request_record<'b>(state: &mut SMBState, r: &Smb2Record<'b>) SMB2_COMMAND_READ => { match parse_smb2_request_read(r.data) { Ok((_, rd)) => { - SCLogDebug!("SMBv2 READ: GUID {:?} requesting {} bytes at offset {}", - rd.guid, rd.rd_len, rd.rd_offset); + if state.max_read_size != 0 && rd.rd_len > state.max_read_size { + events.push(SMBEvent::ReadRequestTooLarge); + } else { + SCLogDebug!("SMBv2 READ: GUID {:?} requesting {} bytes at offset {}", + rd.guid, rd.rd_len, rd.rd_offset); - // store read guid,offset in map - let guid_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_OFFSET); - let guidoff = SMBFileGUIDOffset::new(rd.guid.to_vec(), rd.rd_offset); - state.ssn2vecoffset_map.insert(guid_key, guidoff); + // store read guid,offset in map + let guid_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_OFFSET); + let guidoff = SMBFileGUIDOffset::new(rd.guid.to_vec(), rd.rd_offset); + state.ssn2vecoffset_map.insert(guid_key, guidoff); + } }, _ => { events.push(SMBEvent::MalformedData); @@ -746,6 +762,9 @@ pub fn smb2_response_record<'b>(state: &mut SMBState, r: &Smb2Record<'b>) SCLogDebug!("SERVER dialect => {}", &smb2_dialect_string(rd.dialect)); state.dialect = rd.dialect; + state.max_read_size = rd.max_read_size; + state.max_write_size = rd.max_write_size; + let found2 = match state.get_negotiate_tx(2) { Some(tx) => { if let Some(SMBTransactionTypeData::NEGOTIATE(ref mut tdn)) = tx.type_data { diff --git a/rust/src/smb/smb2_records.rs b/rust/src/smb/smb2_records.rs index 9410e79af6..92fa056075 100644 --- a/rust/src/smb/smb2_records.rs +++ b/rust/src/smb/smb2_records.rs @@ -152,6 +152,9 @@ named!(pub parse_smb2_request_negotiate_protocol { pub dialect: u16, pub server_guid: &'a[u8], + pub max_trans_size: u32, + pub max_read_size: u32, + pub max_write_size: u32, } named!(pub parse_smb2_response_negotiate_protocol, @@ -161,9 +164,16 @@ named!(pub parse_smb2_response_negotiate_protocol> dialect: le_u16 >> _ctx_cnt: le_u16 >> server_guid: take!(16) + >> _capabilities: le_u32 + >> max_trans_size: le_u32 + >> max_read_size: le_u32 + >> max_write_size: le_u32 >> (Smb2NegotiateProtocolResponseRecord { dialect, - server_guid + server_guid, + max_trans_size, + max_read_size, + max_write_size }) )); @@ -174,10 +184,12 @@ named!(pub parse_smb2_response_negotiate_protocol_error> (Smb2NegotiateProtocolResponseRecord { dialect: 0, server_guid: &[], + max_trans_size: 0, + max_read_size: 0, + max_write_size: 0 }) )); - #[derive(Debug,PartialEq)] pub struct Smb2SessionSetupRequestRecord<'a> { pub data: &'a[u8],