]> 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>
Wed, 20 Apr 2022 18:46:03 +0000 (20:46 +0200)
(cherry picked from commit f28888513ac3be18715f522db253c5753adb94ed)

rust/src/smb/events.rs
rust/src/smb/smb.rs
rust/src/smb/smb2.rs
rust/src/smb/smb2_records.rs

index a2540fdbcff637933fe7a1ace3a97532f39a2fbb..8bd7fe185ef59368572134fc036a203e6b4ab1c8 100644 (file)
@@ -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,
     }
 }
index 2a1f7ef5170d640d39a3f296443f1aacb8e7219e..73bd3655a0b285c9f628b4140da594c6290ef80a 100644 (file)
@@ -803,6 +803,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,
@@ -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;
index 3f304d76938336a0cba886250e273b4ca8f0599c..cdf9a449542f867c8dda8da287be92aa516157f2 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(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 {
index 9410e79af6a3eeb39f55654a5c2053cb56e4aa3a..92fa05607586ac4eecd7aee381a3a6385a8e4b00 100644 (file)
@@ -152,6 +152,9 @@ named!(pub parse_smb2_request_negotiate_protocol<Smb2NegotiateProtocolRequestRec
 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,
 }
 
 named!(pub parse_smb2_response_negotiate_protocol<Smb2NegotiateProtocolResponseRecord>,
@@ -161,9 +164,16 @@ named!(pub parse_smb2_response_negotiate_protocol<Smb2NegotiateProtocolResponseR
         >>  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<Smb2NegotiateProtocolRes
         >>  (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],