diff --git a/doc/userguide/rules/dcerpc-keywords.rst b/doc/userguide/rules/dcerpc-keywords.rst index 72c0b460ce..a84d099847 100644 --- a/doc/userguide/rules/dcerpc-keywords.rst +++ b/doc/userguide/rules/dcerpc-keywords.rst @@ -43,17 +43,21 @@ interface in a DCERPC header. The format of the keyword:: dcerpc.opnum:; + dcerpc.opnum:[>,<,!,=]; dcerpc.opnum:,,....; dcerpc.opnum:-; Examples:: dcerpc.opnum:15; + dcerpc.opnum:>10; dcerpc.opnum:12,24,62,61; dcerpc.opnum:12,18-24,5; dcerpc.opnum:12-14,12,121,62-78; -.. note:: earlier versions of the documentation incorrectly stated that greater than, less than, etc. notation was supported. This may be added later, see ticket `#8179 `_). +.. note:: earlier versions of the documentation incorrectly stated that greater than, less than, etc. notation was supported. This was added in suricata 9, see ticket `#8179 `_). + +dcerpc.opnum can since suricata 9 use an :ref:`unsigned 16-bits integer `. dcerpc.stub_data ---------------- diff --git a/rust/src/dcerpc/detect.rs b/rust/src/dcerpc/detect.rs index 47357600f0..df3da3d56b 100644 --- a/rust/src/dcerpc/detect.rs +++ b/rust/src/dcerpc/detect.rs @@ -64,10 +64,16 @@ impl DCEOpnumRange { } #[derive(Debug)] -pub struct DCEOpnumData { +pub(crate) struct DCEOpnumDataRanges { pub data: Vec, } +#[derive(Debug)] +pub(crate) enum DCEOpnumData { + Ranges(DCEOpnumDataRanges), + Num(DetectUintData), +} + fn match_backuuid( tx: &DCERPCTransaction, state: &mut DCERPCState, if_data: &mut DCEIfaceData, ) -> u8 { @@ -174,7 +180,7 @@ fn convert_str_to_u32(arg: &str) -> Result { } } -fn parse_opnum_data(arg: &str) -> Result { +fn parse_opnum_data_ranges(arg: &str) -> Result { let split_args: Vec<&str> = arg.split(',').collect(); let mut dce_opnum_data: Vec = Vec::new(); for range in split_args.iter() { @@ -207,9 +213,19 @@ fn parse_opnum_data(arg: &str) -> Result { dce_opnum_data.push(opnum_range); } - Ok(DCEOpnumData { + Ok(DCEOpnumData::Ranges(DCEOpnumDataRanges { data: dce_opnum_data, - }) + })) +} + +fn parse_opnum_data(arg: &str) -> Result { + if let Ok(r) = parse_opnum_data_ranges(arg) { + return Ok(r); + } + if let Ok((_, du16)) = detect_parse_uint::(arg) { + return Ok(DCEOpnumData::Num(du16)); + } + return Err(()); } #[no_mangle] @@ -262,13 +278,22 @@ unsafe extern "C" fn dcerpc_tx_match_dce_opnum(tx: *mut c_void, ctx: *const SigM return 0; } let opnum = tx.get_req_opnum(); - for range in opnum_data.data.iter() { - if range.range2 == DETECT_DCE_OPNUM_RANGE_UNINITIALIZED { - if range.range1 == opnum as u32 { + match opnum_data { + DCEOpnumData::Num(ref num_data) => { + if detect_match_uint(num_data, opnum) { return 1; } - } else if range.range1 <= opnum as u32 && range.range2 >= opnum as u32 { - return 1; + } + DCEOpnumData::Ranges(ref ranges_data) => { + for range in ranges_data.data.iter() { + if range.range2 == DETECT_DCE_OPNUM_RANGE_UNINITIALIZED { + if range.range1 == opnum as u32 { + return 1; + } + } else if range.range1 <= opnum as u32 && range.range2 >= opnum as u32 { + return 1; + } + } } } @@ -504,6 +529,9 @@ mod test { fn test_parse_opnum_data() { let arg = "12"; let opnum_data = parse_opnum_data(arg).unwrap(); + let DCEOpnumData::Ranges(opnum_data) = opnum_data else { + panic!("Result should have been ranges."); + }; assert_eq!(1, opnum_data.data.len()); assert_eq!(12, opnum_data.data[0].range1); assert_eq!( @@ -513,12 +541,18 @@ mod test { let arg = "12,24"; let opnum_data = parse_opnum_data(arg).unwrap(); + let DCEOpnumData::Ranges(opnum_data) = opnum_data else { + panic!("Result should have been ranges."); + }; assert_eq!(2, opnum_data.data.len()); assert_eq!(12, opnum_data.data[0].range1); assert_eq!(24, opnum_data.data[1].range1); let arg = "12,12-24"; let opnum_data = parse_opnum_data(arg).unwrap(); + let DCEOpnumData::Ranges(opnum_data) = opnum_data else { + panic!("Result should have been ranges."); + }; assert_eq!(2, opnum_data.data.len()); assert_eq!(12, opnum_data.data[0].range1); assert_eq!(12, opnum_data.data[1].range1); @@ -526,6 +560,9 @@ mod test { let arg = "12-14,12,121,62-78"; let opnum_data = parse_opnum_data(arg).unwrap(); + let DCEOpnumData::Ranges(opnum_data) = opnum_data else { + panic!("Result should have been ranges."); + }; assert_eq!(4, opnum_data.data.len()); assert_eq!(12, opnum_data.data[0].range1); assert_eq!(14, opnum_data.data[0].range2); @@ -534,6 +571,9 @@ mod test { let arg = "12,26,62,61,6513-6666"; let opnum_data = parse_opnum_data(arg).unwrap(); + let DCEOpnumData::Ranges(opnum_data) = opnum_data else { + panic!("Result should have been ranges."); + }; assert_eq!(5, opnum_data.data.len()); assert_eq!(61, opnum_data.data[3].range1); assert_eq!(6513, opnum_data.data[4].range1); diff --git a/rust/src/smb/detect.rs b/rust/src/smb/detect.rs index 4831295b2b..113a078e7a 100644 --- a/rust/src/smb/detect.rs +++ b/rust/src/smb/detect.rs @@ -101,13 +101,22 @@ pub(crate) unsafe extern "C" fn smb_tx_match_dce_opnum( SCLogDebug!("smb_tx_match_dce_opnum: start"); if let Some(SMBTransactionTypeData::DCERPC(ref x)) = tx.type_data { if x.req_cmd == DCERPC_TYPE_REQUEST { - for range in dce_data.data.iter() { - if range.range2 == DETECT_DCE_OPNUM_RANGE_UNINITIALIZED { - if range.range1 == x.opnum as u32 { + match dce_data { + DCEOpnumData::Num(ref num_data) => { + if detect_match_uint(num_data, x.opnum) { return 1; } - } else if range.range1 <= x.opnum as u32 && range.range2 >= x.opnum as u32 { - return 1; + } + DCEOpnumData::Ranges(ref ranges_data) => { + for range in ranges_data.data.iter() { + if range.range2 == DETECT_DCE_OPNUM_RANGE_UNINITIALIZED { + if range.range1 == x.opnum as u32 { + return 1; + } + } else if range.range1 <= x.opnum as u32 && range.range2 >= x.opnum as u32 { + return 1; + } + } } } }