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-7.0.0-beta1~725 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f28888513ac3be18715f522db253c5753adb94ed;p=thirdparty%2Fsuricata.git smb2: track max read/write size and enforce its values --- diff --git a/rust/src/smb/events.rs b/rust/src/smb/events.rs index b2a49ca1aa..dde00c6129 100644 --- a/rust/src/smb/events.rs +++ b/rust/src/smb/events.rs @@ -31,6 +31,12 @@ pub enum SMBEvent { RequestToClient, /// A response was seen in the to server direction, ResponseToServer, + /// READ request asking for more than `max_read_size` + ReadRequestTooLarge, + /// READ response bigger than `max_read_size` + ReadResponseTooLarge, + /// WRITE request for more than `max_write_size` + WriteRequestTooLarge, } impl SMBTransaction { diff --git a/rust/src/smb/smb.rs b/rust/src/smb/smb.rs index d50bbd9050..cdb07a9a7b 100644 --- a/rust/src/smb/smb.rs +++ b/rust/src/smb/smb.rs @@ -724,6 +724,7 @@ pub fn u32_as_bytes(i: u32) -> [u8;4] { return [o1, o2, o3, o4] } +#[derive(Default, Debug)] pub struct SMBState<> { /// map ssn/tree/msgid to vec (guid/name/share) pub ssn2vec_map: HashMap>, @@ -777,6 +778,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, @@ -818,6 +822,7 @@ impl SMBState { dialect_vec: None, dcerpc_ifaces: None, ts: 0, + ..Default::default() } } diff --git a/rust/src/smb/smb2.rs b/rust/src/smb/smb2.rs index f6ca66a974..00d3e68697 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(Direction::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 @@ -245,6 +251,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(Direction::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()); @@ -480,13 +492,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); @@ -767,6 +783,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 d069ef0486..f82b7ecf0e 100644 --- a/rust/src/smb/smb2_records.rs +++ b/rust/src/smb/smb2_records.rs @@ -157,6 +157,9 @@ pub fn parse_smb2_request_negotiate_protocol(i: &[u8]) -> IResult<&[u8], Smb2Neg pub struct Smb2NegotiateProtocolResponseRecord<'a> { pub dialect: u16, pub server_guid: &'a[u8], + pub max_trans_size: u32, + pub max_read_size: u32, + pub max_write_size: u32, } pub fn parse_smb2_response_negotiate_protocol(i: &[u8]) -> IResult<&[u8], Smb2NegotiateProtocolResponseRecord> { @@ -165,9 +168,16 @@ pub fn parse_smb2_response_negotiate_protocol(i: &[u8]) -> IResult<&[u8], Smb2Ne let (i, dialect) = le_u16(i)?; let (i, _ctx_cnt) = le_u16(i)?; let (i, server_guid) = take(16_usize)(i)?; + let (i, _capabilities) = le_u32(i)?; + let (i, max_trans_size) = le_u32(i)?; + let (i, max_read_size) = le_u32(i)?; + let (i, max_write_size) = le_u32(i)?; let record = Smb2NegotiateProtocolResponseRecord { dialect, - server_guid + server_guid, + max_trans_size, + max_read_size, + max_write_size }; Ok((i, record)) } @@ -178,6 +188,9 @@ pub fn parse_smb2_response_negotiate_protocol_error(i: &[u8]) -> IResult<&[u8], let record = Smb2NegotiateProtocolResponseRecord { dialect: 0, server_guid: &[], + max_trans_size: 0, + max_read_size: 0, + max_write_size: 0 }; Ok((i, record)) }