]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
smb2: track max read/write size and enforce its values
authorVictor Julien <vjulien@oisf.net>
Mon, 18 Apr 2022 15:49:58 +0000 (17:49 +0200)
committerVictor Julien <vjulien@oisf.net>
Tue, 19 Apr 2022 11:14:23 +0000 (13:14 +0200)
rust/src/smb/events.rs
rust/src/smb/smb.rs
rust/src/smb/smb2.rs
rust/src/smb/smb2_records.rs

index b2a49ca1aabfb32d8733e59cd9e97b1cc3a5162e..dde00c612943bd08dfa9a78b16868e1520fdce8f 100644 (file)
@@ -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 {
index d50bbd905034f1ed9cb44ad1fef9c215d4a29ae4..cdb07a9a7b1885e12f3541d9be803fea43f66e31 100644 (file)
@@ -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<SMBCommonHdr, Vec<u8>>,
@@ -777,6 +778,9 @@ pub struct SMBState<> {
     /// them while inspecting DCERPC REQUEST txs
     pub dcerpc_ifaces: Option<Vec<DCERPCIface>>,
 
+    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()
         }
     }
 
index f6ca66a97433afff1da8a4782b53783ce43b9d81..00d3e686971bbac6fd3989178b728ee7b24035fd 100644 (file)
@@ -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 {
index d069ef0486c1b5dd22ec15f422b7211ef8a46d97..f82b7ecf0e585a546badbb76d89dabecf403da42 100644 (file)
@@ -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))
 }