diff --git a/rust/src/dcerpc/dcerpc.rs b/rust/src/dcerpc/dcerpc.rs index 71ce8f6070..27cd7415bc 100644 --- a/rust/src/dcerpc/dcerpc.rs +++ b/rust/src/dcerpc/dcerpc.rs @@ -32,7 +32,9 @@ use std; use std::cmp; use std::ffi::CString; use std::collections::VecDeque; -use crate::conf::conf_get; +use crate::conf::{conf_get, get_memval}; + +pub static mut DCERPC_MAX_STUB_SIZE: u32 = 1048576; // Constant DCERPC UDP Header length pub const DCERPC_HDR_LEN: u16 = 16; @@ -177,6 +179,11 @@ pub fn get_req_type_for_resp(t: u8) -> u8 { _ => DCERPC_TYPE_UNKNOWN, } } +#[inline(always)] +pub fn cfg_max_stub_size() -> u32 { + unsafe { DCERPC_MAX_STUB_SIZE } +} + #[derive(Default, Debug)] pub struct DCERPCTransaction { @@ -1019,7 +1026,12 @@ fn evaluate_stub_params( } let input_slice = &input[..stub_len as usize]; - stub_data_buffer.extend_from_slice(input_slice); + let max_size = cfg_max_stub_size() as usize; + if (stub_data_buffer.len() + input_slice.len()) < max_size { + stub_data_buffer.extend_from_slice(input_slice); + } else if stub_data_buffer.len() < max_size { + stub_data_buffer.extend_from_slice(&input_slice[..max_size - stub_data_buffer.len()]); + } stub_len } @@ -1268,6 +1280,21 @@ pub unsafe extern "C" fn SCRegisterDcerpcParser() { } } SCLogDebug!("Rust DCERPC parser registered."); + let retval = conf_get("app-layer.protocols.dcerpc.max-stub-size"); + if let Some(val) = retval { + match get_memval(val) { + Ok(retval) => { + if retval > 0 { + DCERPC_MAX_STUB_SIZE = retval as u32; + } else { + SCLogError!("Invalid max-stub-size value"); + } + } + Err(_) => { + SCLogError!("Invalid max-stub-size value"); + } + } + } } else { SCLogDebug!("Protocol detector and parser disabled for DCERPC."); } diff --git a/rust/src/dcerpc/dcerpc_udp.rs b/rust/src/dcerpc/dcerpc_udp.rs index 64f5d365ff..0633d89898 100644 --- a/rust/src/dcerpc/dcerpc_udp.rs +++ b/rust/src/dcerpc/dcerpc_udp.rs @@ -1,4 +1,4 @@ -/* Copyright (C) 2020 Open Information Security Foundation +/* Copyright (C) 2020-2026 Open Information Security Foundation * * You can copy, redistribute or modify this Program under the terms of * the GNU General Public License version 2 as published by the Free @@ -19,7 +19,7 @@ use crate::core; use crate::applayer::{self, *}; use crate::dcerpc::dcerpc::{ DCERPCTransaction, DCERPC_MAX_TX, DCERPC_TYPE_REQUEST, DCERPC_TYPE_RESPONSE, PFCL1_FRAG, PFCL1_LASTFRAG, - get_alstate_progress, ALPROTO_DCERPC, PARSER_NAME, + get_alstate_progress, ALPROTO_DCERPC, PARSER_NAME, cfg_max_stub_size, }; use crate::direction::{Direction, DIR_BOTH}; use crate::flow::Flow; @@ -175,18 +175,27 @@ impl DCERPCUDPState { tx.tx_data.updated_ts = true; let done = (hdr.flags1 & PFCL1_FRAG) == 0 || (hdr.flags1 & PFCL1_LASTFRAG) != 0; + let max_size = cfg_max_stub_size() as usize; match hdr.pkt_type { DCERPC_TYPE_REQUEST => { - tx.stub_data_buffer_ts.extend_from_slice(input); tx.frag_cnt_ts += 1; + if input.len() + tx.stub_data_buffer_ts.len() < max_size { + tx.stub_data_buffer_ts.extend_from_slice(input); + } else if tx.stub_data_buffer_ts.len() < max_size { + tx.stub_data_buffer_ts.extend_from_slice(&input[..max_size - tx.stub_data_buffer_ts.len()]); + } if done { tx.req_done = true; } return true; } DCERPC_TYPE_RESPONSE => { - tx.stub_data_buffer_tc.extend_from_slice(input); tx.frag_cnt_tc += 1; + if input.len() + tx.stub_data_buffer_tc.len() < max_size { + tx.stub_data_buffer_tc.extend_from_slice(input); + } else if tx.stub_data_buffer_tc.len() < max_size { + tx.stub_data_buffer_tc.extend_from_slice(&input[..max_size - tx.stub_data_buffer_tc.len()]); + } if done { tx.resp_done = true; } @@ -399,7 +408,6 @@ pub unsafe extern "C" fn SCRegisterDcerpcUdpParser() { } } - #[cfg(test)] mod tests { use crate::applayer::AppLayerResult; diff --git a/rust/src/smb/dcerpc.rs b/rust/src/smb/dcerpc.rs index d528b372a1..1002d1f832 100644 --- a/rust/src/smb/dcerpc.rs +++ b/rust/src/smb/dcerpc.rs @@ -18,7 +18,7 @@ // written by Victor Julien use uuid; -use crate::smb::smb::*; +use crate::smb::smb::{cfg_max_stub_size, *}; use crate::smb::smb2::*; use crate::smb::dcerpc_records::*; use crate::smb::events::*; @@ -205,10 +205,15 @@ pub fn smb_write_dcerpc_record(state: &mut SMBState, SCLogDebug!("previous CMD {} found at tx {} => {:?}", dcer.packet_type, tx.id, tx); if let Some(SMBTransactionTypeData::DCERPC(ref mut tdn)) = tx.type_data { - SCLogDebug!("additional frag of size {}", recr.data.len()); - tdn.stub_data_ts.extend_from_slice(recr.data); tdn.frag_cnt_ts += 1; - SCLogDebug!("stub_data now {}", tdn.stub_data_ts.len()); + let max_size = cfg_max_stub_size() as usize; + if recr.data.len() + tdn.stub_data_ts.len() < max_size { + SCLogDebug!("additional frag of size {}", recr.data.len()); + tdn.stub_data_ts.extend_from_slice(recr.data); + SCLogDebug!("stub_data now {}", tdn.stub_data_ts.len()); + } else if tdn.stub_data_ts.len() < max_size { + tdn.stub_data_ts.extend_from_slice(&recr.data[..max_size - tdn.stub_data_ts.len()]); + } } if dcer.last_frag { SCLogDebug!("last frag set, so request side of DCERPC closed"); @@ -240,12 +245,17 @@ pub fn smb_write_dcerpc_record(state: &mut SMBState, SCLogDebug!("DCERPC: REQUEST {:?}", recr); if let Some(SMBTransactionTypeData::DCERPC(ref mut tdn)) = tx.type_data { SCLogDebug!("first frag size {}", recr.data.len()); - tdn.stub_data_ts.extend_from_slice(recr.data); tdn.opnum = recr.opnum; tdn.context_id = recr.context_id; tdn.frag_cnt_ts += 1; - SCLogDebug!("DCERPC: REQUEST opnum {} stub data len {}", - tdn.opnum, tdn.stub_data_ts.len()); + let max_size = cfg_max_stub_size() as usize; + if tdn.stub_data_ts.len() + recr.data.len() < max_size { + tdn.stub_data_ts.extend_from_slice(recr.data); + SCLogDebug!("DCERPC: REQUEST opnum {} stub data len {}", + tdn.opnum, tdn.stub_data_ts.len()); + } else if tdn.stub_data_ts.len() < max_size { + tdn.stub_data_ts.extend_from_slice(&recr.data[..max_size - tdn.stub_data_ts.len()]); + } } if dcer.last_frag { tx.request_done = true; @@ -407,8 +417,13 @@ fn dcerpc_response_handle(tx: &mut SMBTransaction, if let Some(SMBTransactionTypeData::DCERPC(ref mut tdn)) = tx.type_data { SCLogDebug!("CMD 11 found at tx {}", tx.id); tdn.set_result(DCERPC_TYPE_RESPONSE); - tdn.stub_data_tc.extend_from_slice(respr.data); + let max_size = cfg_max_stub_size() as usize; tdn.frag_cnt_tc += 1; + if tdn.stub_data_tc.len() + respr.data.len() < max_size { + tdn.stub_data_tc.extend_from_slice(respr.data); + } else if tdn.stub_data_tc.len() < max_size { + tdn.stub_data_tc.extend_from_slice(&respr.data[..max_size - tdn.stub_data_tc.len()]); + } } tx.vercmd.set_ntstatus(ntstatus); tx.response_done = dcer.last_frag; diff --git a/rust/src/smb/smb.rs b/rust/src/smb/smb.rs index caab1dd6d4..1784866574 100644 --- a/rust/src/smb/smb.rs +++ b/rust/src/smb/smb.rs @@ -101,6 +101,8 @@ pub static mut SMB_CFG_MAX_FRAG_CACHE_SIZE: usize = 128; /// For SMBState::ssn2vec_cache pub static mut SMB_CFG_MAX_SSN2VEC_CACHE_SIZE: usize = 512; +pub static mut SMB_DCERPC_MAX_STUB_SIZE: u32 = 1048576; + static mut ALPROTO_SMB: AppProto = ALPROTO_UNKNOWN; static mut SMB_MAX_TX: usize = 1024; @@ -2513,6 +2515,21 @@ pub unsafe extern "C" fn SCRegisterSmbParser() { SCLogError!("Invalid max-dcerpc-frag-cache-size value"); } } + let retval = conf_get("app-layer.protocols.smb.dcerpc.max-stub-size"); + if let Some(val) = retval { + match get_memval(val) { + Ok(retval) => { + if retval > 0 { + SMB_DCERPC_MAX_STUB_SIZE = retval as u32; + } else { + SCLogError!("Invalid max-stub-size value"); + } + } + Err(_) => { + SCLogError!("Invalid max-stub-size value"); + } + } + } let retval = conf_get("app-layer.protocols.smb.max-session-cache-size"); if let Some(val) = retval { if let Ok(v) = val.parse::() { @@ -2583,3 +2600,9 @@ fn cfg_max_write_queue_size() -> u32 { fn cfg_max_guid_cache_size() -> usize { unsafe { SMB_CFG_MAX_GUID_CACHE_SIZE } } + +#[inline(always)] +pub fn cfg_max_stub_size() -> u32 { + unsafe { SMB_DCERPC_MAX_STUB_SIZE } +} + diff --git a/suricata.yaml.in b/suricata.yaml.in index 53abe0f29d..7a108476af 100644 --- a/suricata.yaml.in +++ b/suricata.yaml.in @@ -979,6 +979,8 @@ app-layer: enabled: yes # Maximum number of live DCERPC transactions per flow # max-tx: 1024 + #max-stub-size: 1MiB + ftp: enabled: yes # memcap: 64 MiB @@ -1069,6 +1071,8 @@ app-layer: # Stream reassembly size for SMB streams. By default track it completely. #stream-depth: 0 + #dcerpc: + # max-stub-size: 1MiB nfs: enabled: yes