]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
smb: adds file overlap event against evasions
authorPhilippe Antoine <contact@catenacyber.fr>
Tue, 14 Apr 2020 08:30:33 +0000 (10:30 +0200)
committerVictor Julien <victor@inliniac.net>
Wed, 7 Oct 2020 16:41:09 +0000 (18:41 +0200)
Evasion scenario is
- a first dummy write of one byte at offset 0 is done
- the second full write of EICAR at offset 0 is then done
and does not trigger detection

The last write had the final value, and as we cannot "cancel"
the previous write, we set an event which is then transformed into
an app-layer decoder alert

rules/smb-events.rules
rust/src/filetracker.rs
rust/src/smb/events.rs
rust/src/smb/smb.rs
rust/src/smb/smb1.rs
rust/src/smb/smb2.rs

index 486323beb9684b31a6b45b0490a8e0292a862c8e..97fc675cb1741ef74a5187206bab8cfc235fa088 100644 (file)
@@ -14,3 +14,5 @@ alert smb any any -> any any (msg:"SURICATA SMB malformed response data"; flow:t
 alert smb any any -> any any (msg:"SURICATA SMB malformed NTLMSSP record"; flow:to_server; app-layer-event:smb.malformed_ntlmssp_request; classtype:protocol-command-decode; sid:2225004; rev:1;)
 
 alert smb any any -> any any (msg:"SURICATA SMB malformed request dialects"; flow:to_server; app-layer-event:smb.negotiate_malformed_dialects; classtype:protocol-command-decode; sid:2225005; rev:1;)
+
+alert smb any any -> any any (msg:"SURICATA SMB file overlap"; app-layer-event:smb.file_overlap; classtype:protocol-command-decode; sid:2225006; rev:1;)
index 233eefbedfcbd8a383a96aa656ae33a4740d6aa7..1981b54e006d3b3a41305e4b56ad59abc1960aa9 100644 (file)
@@ -52,7 +52,7 @@ impl FileChunk {
 #[derive(Debug)]
 pub struct FileTransferTracker {
     file_size: u64,
-    tracked: u64,
+    pub tracked: u64,
     cur_ooo: u64,   // how many bytes do we have queued from ooo chunks
     track_id: u32,
     chunk_left: u32,
index 3d3ec6adca81f37a5bf7e6f05ba4d717986b9223..3093d08e2fe4ac807d16af26cffb0655b2a90294 100644 (file)
@@ -28,6 +28,7 @@ pub enum SMBEvent {
     MalformedNtlmsspResponse = 4,
     DuplicateNegotiate = 5,
     NegotiateMalformedDialects = 6,
+    FileOverlap = 7,
 }
 
 impl SMBEvent {
@@ -40,6 +41,7 @@ impl SMBEvent {
             4 => Some(SMBEvent::MalformedNtlmsspResponse),
             5 => Some(SMBEvent::DuplicateNegotiate),
             6 => Some(SMBEvent::NegotiateMalformedDialects),
+            7 => Some(SMBEvent::FileOverlap),
             _ => None,
         }
     }
@@ -55,6 +57,7 @@ pub fn smb_str_to_event(instr: &str) -> i32 {
         "malformed_ntlmssp_response"    => SMBEvent::MalformedNtlmsspResponse as i32,
         "duplicate_negotiate"           => SMBEvent::DuplicateNegotiate as i32,
         "negotiate_malformed_dialects"  => SMBEvent::NegotiateMalformedDialects as i32,
+        "file_overlap"                  => SMBEvent::FileOverlap as i32,
         _ => -1,
     }
 }
index 0200bf0105527e8f3c58c054cb71088b8e15203c..def2b23b6718eb7faf463b92ece42add8b7d5ee8 100644 (file)
@@ -2164,6 +2164,7 @@ pub extern "C" fn rs_smb_state_get_event_info_by_id(event_id: std::os::raw::c_in
             SMBEvent::MalformedNtlmsspResponse => { "malformed_ntlmssp_response\0" },
             SMBEvent::DuplicateNegotiate => { "duplicate_negotiate\0" },
             SMBEvent::NegotiateMalformedDialects => { "netogiate_malformed_dialects\0" },
+            SMBEvent::FileOverlap => { "file_overlap\0" },
         };
         unsafe{
             *event_name = estr.as_ptr() as *const std::os::raw::c_char;
index b4262cf0ccd26cbe8c41b2a8524b0bd0274869b1..47e2d90dd4098b6db4605c0ab57cb3e1efddefc8 100644 (file)
@@ -907,10 +907,14 @@ pub fn smb1_write_request_record<'b>(state: &mut SMBState, r: &SmbRecord<'b>)
                 Some(n) => n.to_vec(),
                 None => b"<unknown>".to_vec(),
             };
+            let mut set_event_fileoverlap = false;
             let found = match state.get_file_tx_by_fuid(&file_fid, STREAM_TOSERVER) {
                 Some((tx, files, flags)) => {
                     let file_id : u32 = tx.id as u32;
                     if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data {
+                        if rd.offset < tdf.file_tracker.tracked {
+                            set_event_fileoverlap = true;
+                        }
                         filetracker_newchunk(&mut tdf.file_tracker, files, flags,
                                 &file_name, rd.data, rd.offset,
                                 rd.len, 0, false, &file_id);
@@ -936,6 +940,9 @@ pub fn smb1_write_request_record<'b>(state: &mut SMBState, r: &SmbRecord<'b>)
                     if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data {
                         let file_id : u32 = tx.id as u32;
                         SCLogDebug!("FID {:?} found at tx {}", file_fid, tx.id);
+                        if rd.offset < tdf.file_tracker.tracked {
+                            set_event_fileoverlap = true;
+                        }
                         filetracker_newchunk(&mut tdf.file_tracker, files, flags,
                                 &file_name, rd.data, rd.offset,
                                 rd.len, 0, false, &file_id);
@@ -944,6 +951,9 @@ pub fn smb1_write_request_record<'b>(state: &mut SMBState, r: &SmbRecord<'b>)
                     tx.vercmd.set_smb1_cmd(SMB1_COMMAND_WRITE_ANDX);
                 }
             }
+            if set_event_fileoverlap {
+                state.set_event(SMBEvent::FileOverlap);
+            }
 
             state.set_file_left(STREAM_TOSERVER, rd.len, rd.data.len() as u32, file_fid.to_vec());
 
@@ -990,11 +1000,15 @@ pub fn smb1_read_response_record<'b>(state: &mut SMBState, r: &SmbRecord<'b>)
                         Some(n) => n.to_vec(),
                         None => Vec::new(),
                     };
+                    let mut set_event_fileoverlap = false;
                     let found = match state.get_file_tx_by_fuid(&file_fid, STREAM_TOCLIENT) {
                         Some((tx, files, flags)) => {
                             if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data {
                                 let file_id : u32 = tx.id as u32;
                                 SCLogDebug!("FID {:?} found at tx {}", file_fid, tx.id);
+                                if offset < tdf.file_tracker.tracked {
+                                    set_event_fileoverlap = true;
+                                }
                                 filetracker_newchunk(&mut tdf.file_tracker, files, flags,
                                         &file_name, rd.data, offset,
                                         rd.len, 0, false, &file_id);
@@ -1008,6 +1022,9 @@ pub fn smb1_read_response_record<'b>(state: &mut SMBState, r: &SmbRecord<'b>)
                         if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data {
                             let file_id : u32 = tx.id as u32;
                             SCLogDebug!("FID {:?} found at tx {}", file_fid, tx.id);
+                            if offset < tdf.file_tracker.tracked {
+                                set_event_fileoverlap = true;
+                            }
                             filetracker_newchunk(&mut tdf.file_tracker, files, flags,
                                     &file_name, rd.data, offset,
                                     rd.len, 0, false, &file_id);
@@ -1015,6 +1032,9 @@ pub fn smb1_read_response_record<'b>(state: &mut SMBState, r: &SmbRecord<'b>)
                         }
                         tx.vercmd.set_smb1_cmd(SMB1_COMMAND_READ_ANDX);
                     }
+                    if set_event_fileoverlap {
+                        state.set_event(SMBEvent::FileOverlap);
+                    }
                 } else {
                     SCLogDebug!("SMBv1 READ response from PIPE");
                     let hdr = SMBCommonHdr::from1(r, SMBHDR_TYPE_HEADER);
index 2fd611482eb5318c7c47fe4315515a6bc85ebbc4..e5219dfebe00155fbed8689e6f045a4154c0bb97 100644 (file)
@@ -144,11 +144,15 @@ pub fn smb2_read_response_record<'b>(state: &mut SMBState, r: &Smb2Record<'b>)
             };
             SCLogDebug!("SMBv2 READ: GUID {:?} offset {}", file_guid, offset);
 
+            let mut set_event_fileoverlap = false;
             // look up existing tracker and if we have it update it
             let found = match state.get_file_tx_by_fuid(&file_guid, STREAM_TOCLIENT) {
                 Some((tx, files, flags)) => {
                     if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data {
                         let file_id : u32 = tx.id as u32;
+                        if offset < tdf.file_tracker.tracked {
+                            set_event_fileoverlap = true;
+                        }
                         filetracker_newchunk(&mut tdf.file_tracker, files, flags,
                                 &tdf.file_name, rd.data, offset,
                                 rd.len, 0, false, &file_id);
@@ -208,6 +212,9 @@ pub fn smb2_read_response_record<'b>(state: &mut SMBState, r: &Smb2Record<'b>)
                     let (tx, files, flags) = state.new_file_tx(&file_guid, &file_name, STREAM_TOCLIENT);
                     if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data {
                         let file_id : u32 = tx.id as u32;
+                        if offset < tdf.file_tracker.tracked {
+                            set_event_fileoverlap = true;
+                        }
                         filetracker_newchunk(&mut tdf.file_tracker, files, flags,
                                 &file_name, rd.data, offset,
                                 rd.len, 0, false, &file_id);
@@ -219,6 +226,9 @@ pub fn smb2_read_response_record<'b>(state: &mut SMBState, r: &Smb2Record<'b>)
                 }
             }
 
+            if set_event_fileoverlap {
+                state.set_event(SMBEvent::FileOverlap);
+            }
             state.set_file_left(STREAM_TOCLIENT, rd.len, rd.data.len() as u32, file_guid.to_vec());
         }
         _ => {
@@ -248,10 +258,14 @@ pub fn smb2_write_request_record<'b>(state: &mut SMBState, r: &Smb2Record<'b>)
                 None => Vec::new(),
             };
 
+            let mut set_event_fileoverlap = false;
             let found = match state.get_file_tx_by_fuid(&file_guid, STREAM_TOSERVER) {
                 Some((tx, files, flags)) => {
                     if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data {
                         let file_id : u32 = tx.id as u32;
+                        if wr.wr_offset < tdf.file_tracker.tracked {
+                            set_event_fileoverlap = true;
+                        }
                         filetracker_newchunk(&mut tdf.file_tracker, files, flags,
                                 &file_name, wr.data, wr.wr_offset,
                                 wr.wr_len, 0, false, &file_id);
@@ -307,6 +321,9 @@ pub fn smb2_write_request_record<'b>(state: &mut SMBState, r: &Smb2Record<'b>)
                     let (tx, files, flags) = state.new_file_tx(&file_guid, &file_name, STREAM_TOSERVER);
                     if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data {
                         let file_id : u32 = tx.id as u32;
+                        if wr.wr_offset < tdf.file_tracker.tracked {
+                            set_event_fileoverlap = true;
+                        }
                         filetracker_newchunk(&mut tdf.file_tracker, files, flags,
                                 &file_name, wr.data, wr.wr_offset,
                                 wr.wr_len, 0, false, &file_id);
@@ -316,6 +333,10 @@ pub fn smb2_write_request_record<'b>(state: &mut SMBState, r: &Smb2Record<'b>)
                             r.session_id, r.tree_id, 0); // TODO move into new_file_tx
                 }
             }
+
+            if set_event_fileoverlap {
+                state.set_event(SMBEvent::FileOverlap);
+            }
             state.set_file_left(STREAM_TOSERVER, wr.wr_len, wr.data.len() as u32, file_guid.to_vec());
         },
         _ => {