diff --git a/doc/userguide/rules/dcerpc-keywords.rst b/doc/userguide/rules/dcerpc-keywords.rst index a84d099847..6ae12a60e9 100644 --- a/doc/userguide/rules/dcerpc-keywords.rst +++ b/doc/userguide/rules/dcerpc-keywords.rst @@ -68,6 +68,18 @@ Example:: dcerpc.stub_data; content:"123456"; +dcerpc.hdr +---------- + +Match on the DCERPC header. It is a 'sticky buffer'. + +Multi-byte header fields appear in the byte order advertised in the header's +data representation. + +Example:: + + dcerpc.hdr; content:"|10|"; offset:4; depth:1; + Additional information ---------------------- diff --git a/rust/src/dcerpc/dcerpc.rs b/rust/src/dcerpc/dcerpc.rs index ca40e12703..002eeba553 100644 --- a/rust/src/dcerpc/dcerpc.rs +++ b/rust/src/dcerpc/dcerpc.rs @@ -205,6 +205,8 @@ pub struct DCERPCTransaction { pub resp_lost: bool, pub req_cmd: u8, pub resp_cmd: u8, + pub req_hdr: Option>, + pub resp_hdr: Option>, pub activityuuid: Vec, pub seqnum: u32, pub tx_data: AppLayerTxData, @@ -290,6 +292,27 @@ pub struct DCERPCHdr { pub call_id: u32, } +impl DCERPCHdr { + pub fn to_bytes(&self) -> [u8; DCERPC_HDR_LEN as usize] { + let mut b = [0u8; DCERPC_HDR_LEN as usize]; + b[0] = self.rpc_vers; + b[1] = self.rpc_vers_minor; + b[2] = self.hdrtype; + b[3] = self.pfc_flags; + b[4..8].copy_from_slice(&self.packed_drep); + if self.packed_drep[0] & 0x10 != 0 { + b[8..10].copy_from_slice(&self.frag_length.to_le_bytes()); + b[10..12].copy_from_slice(&self.auth_length.to_le_bytes()); + b[12..16].copy_from_slice(&self.call_id.to_le_bytes()); + } else { + b[8..10].copy_from_slice(&self.frag_length.to_be_bytes()); + b[10..12].copy_from_slice(&self.auth_length.to_be_bytes()); + b[12..16].copy_from_slice(&self.call_id.to_be_bytes()); + } + b + } +} + #[derive(Debug)] pub struct DCERPCBind { pub numctxitems: u8, @@ -585,6 +608,7 @@ impl DCERPCState { } let mut tx = self.create_tx(hdr); tx.req_cmd = hdr.hdrtype; + tx.req_hdr = Some(hdr.to_bytes().to_vec()); tx.req_done = true; if let Some(flow) = self.flow { sc_app_layer_parser_trigger_raw_stream_inspection( @@ -762,6 +786,7 @@ impl DCERPCState { match transaction { Some(ref mut tx) => { tx.req_cmd = hdr_type; + tx.req_hdr = Some(hdr.to_bytes().to_vec()); tx.ctxid = request.ctxid; tx.opnum = request.opnum; tx.first_request_seen = request.first_request_seen; @@ -769,6 +794,7 @@ impl DCERPCState { None => { let mut tx = self.create_tx(hdr); tx.req_cmd = hdr_type; + tx.req_hdr = Some(hdr.to_bytes().to_vec()); tx.ctxid = request.ctxid; tx.opnum = request.opnum; tx.first_request_seen = request.first_request_seen; @@ -949,10 +975,12 @@ impl DCERPCState { self.get_tx_by_call_id(current_call_id, Direction::ToClient, hdrtype) { tx.resp_cmd = hdrtype; + tx.resp_hdr = Some(hdr.to_bytes().to_vec()); tx } else { let mut tx = self.create_tx(&hdr); tx.resp_cmd = hdrtype; + tx.resp_hdr = Some(hdr.to_bytes().to_vec()); self.transactions.push_back(tx); self.transactions.back_mut().unwrap() }; @@ -980,10 +1008,12 @@ impl DCERPCState { match transaction { Some(tx) => { tx.resp_cmd = hdrtype; + tx.resp_hdr = Some(hdr.to_bytes().to_vec()); } None => { let mut tx = self.create_tx(&hdr); tx.resp_cmd = hdrtype; + tx.resp_hdr = Some(hdr.to_bytes().to_vec()); self.transactions.push_back(tx); } }; diff --git a/rust/src/dcerpc/dcerpc_udp.rs b/rust/src/dcerpc/dcerpc_udp.rs index 538ee3d3c6..6886e4eff9 100644 --- a/rust/src/dcerpc/dcerpc_udp.rs +++ b/rust/src/dcerpc/dcerpc_udp.rs @@ -162,7 +162,9 @@ impl DCERPCUDPState { }); } - pub fn handle_fragment_data(&mut self, hdr: &DCERPCHdrUdp, input: &[u8]) -> bool { + pub fn handle_fragment_data( + &mut self, hdr: &DCERPCHdrUdp, hdr_bytes: &[u8], input: &[u8], + ) -> bool { if hdr.pkt_type != DCERPC_TYPE_REQUEST && hdr.pkt_type != DCERPC_TYPE_RESPONSE { SCLogDebug!("Unrecognized packet type"); return false; @@ -186,10 +188,12 @@ impl DCERPCUDPState { tx.tx_data.0.updated_tc = true; tx.tx_data.0.updated_ts = true; let done = (hdr.flags1 & PFCL1_FRAG) == 0 || (hdr.flags1 & PFCL1_LASTFRAG) != 0; + let raw_hdr: Option> = Some(hdr_bytes.to_vec()); let max_size = cfg_max_stub_size() as usize; match hdr.pkt_type { DCERPC_TYPE_REQUEST => { + tx.req_hdr = raw_hdr; tx.frag_cnt_ts = tx.frag_cnt_ts.saturating_add(1); if input.len() + tx.stub_data_buffer_ts.len() < max_size { tx.stub_data_buffer_ts.extend_from_slice(input); @@ -203,6 +207,7 @@ impl DCERPCUDPState { return true; } DCERPC_TYPE_RESPONSE => { + tx.resp_hdr = raw_hdr; tx.frag_cnt_tc = tx.frag_cnt_tc.saturating_add(1); if input.len() + tx.stub_data_buffer_tc.len() < max_size { tx.stub_data_buffer_tc.extend_from_slice(input); @@ -244,7 +249,12 @@ impl DCERPCUDPState { ); return AppLayerResult::err(); } - if !self.handle_fragment_data(&header, &leftover_bytes[..header.fraglen as usize]) { + let hdr_len = DCERPC_UDP_HDR_LEN as usize; + if !self.handle_fragment_data( + &header, + &input[..hdr_len], + &leftover_bytes[..header.fraglen as usize], + ) { return AppLayerResult::err(); } } diff --git a/rust/src/dcerpc/detect.rs b/rust/src/dcerpc/detect.rs index 03241f7a87..367c248026 100644 --- a/rust/src/dcerpc/detect.rs +++ b/rust/src/dcerpc/detect.rs @@ -439,10 +439,56 @@ unsafe extern "C" fn dcerpc_tx_get_stub_data( return buffer; } +unsafe extern "C" fn dcerpc_hdr_setup( + de_ctx: *mut DetectEngineCtx, s: *mut Signature, _str: *const c_char, +) -> c_int { + if SCDetectSignatureSetAppProto(s, ALPROTO_DCERPC) < 0 { + return -1; + } + if SCDetectBufferSetActiveList(de_ctx, s, G_DCERPC_HDR_BUFFER_ID) < 0 { + return -1; + } + return 0; +} + +unsafe extern "C" fn dcerpc_tx_get_hdr( + det_ctx: *mut DetectEngineThreadCtx, transforms: *const DetectEngineTransforms, + _flow: *mut Flow, dir: u8, tx: *mut c_void, list_id: c_int, +) -> *mut InspectionBuffer { + let tx = cast_pointer!(tx, DCERPCTransaction); + let buffer = SCInspectionBufferGet(det_ctx, list_id); + if !(*buffer).initialized { + let hdr: Option<&[u8]> = if (dir & STREAM_TOSERVER) != 0 { + tx.req_hdr.as_deref() + } else { + tx.resp_hdr.as_deref() + }; + if let Some(hdr) = hdr { + if hdr[4] & 0x10 != 0 { + (*buffer).flags |= DETECT_CI_FLAGS_DCE_LE; + } else { + (*buffer).flags |= DETECT_CI_FLAGS_DCE_BE; + } + SCInspectionBufferSetupAndApplyTransforms( + det_ctx, + list_id, + buffer, + hdr.as_ptr(), + hdr.len() as u32, + transforms, + ); + } else { + return std::ptr::null_mut(); + } + } + return buffer; +} + static mut G_DCERPC_OPNUM_KW_ID: u16 = 0; static mut G_DCERPC_GENERIC_BUFFER_ID: c_int = 0; static mut G_DCERPC_IFACE_KW_ID: u16 = 0; static mut G_DCERPC_STUB_BUFFER_ID: c_int = 0; +static mut G_DCERPC_HDR_BUFFER_ID: c_int = 0; #[no_mangle] pub unsafe extern "C" fn SCDetectDcerpcRegister() { @@ -517,6 +563,22 @@ pub unsafe extern "C" fn SCDetectDcerpcRegister() { stub_kw_id, b"dce_stub_data\0".as_ptr() as *const libc::c_char, ); + + let kw_hdr = SigTableElmtStickyBuffer { + name: String::from("dcerpc.hdr"), + desc: String::from("match on the DCERPC header per direction"), + url: String::from("/rules/dcerpc-keywords.html#dcerpc-hdr"), + setup: dcerpc_hdr_setup, + }; + let _hdr_kw_id = helper_keyword_register_sticky_buffer(&kw_hdr); + G_DCERPC_HDR_BUFFER_ID = SCDetectRegisterMpmGeneric( + b"dcerpc.hdr\0".as_ptr() as *const libc::c_char, + b"dcerpc header\0".as_ptr() as *const libc::c_char, + ALPROTO_DCERPC, + STREAM_TOSERVER | STREAM_TOCLIENT, + Some(dcerpc_tx_get_hdr), + 0, + ); } #[cfg(test)]