pull/16045/merge
Shivani Bhardwaj 14 hours ago committed by GitHub
commit 69a0c467e0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -59,6 +59,22 @@ Examples::
dcerpc.opnum can since suricata 9 use an :ref:`unsigned 16-bits integer <rules-integer-keywords>`.
dcerpc.flags
------------
Match on the flags in a DCERPC header. The value is an
:ref:`unsigned 16-bit integer <rules-integer-keywords>`.
The 16-bit value carries the PFC flags of the transaction. Over TCP the low
byte holds ``pfc_flags``; over UDP the low byte holds ``flags1`` and the high
byte holds ``flags2``.
Examples::
dcerpc.flags:0x03;
dcerpc.flags:&0x03=0x03;
dcerpc.stub_data
----------------

@ -205,6 +205,8 @@ pub struct DCERPCTransaction {
pub resp_lost: bool,
pub req_cmd: u8,
pub resp_cmd: u8,
pub req_flags: u16,
pub resp_flags: u16,
pub activityuuid: Vec<u8>,
pub seqnum: u32,
pub tx_data: AppLayerTxData,
@ -539,8 +541,6 @@ impl DCERPCState {
uuidentry.version = ctxitem.version;
uuidentry.versionminor = ctxitem.versionminor;
let pfcflags = hdr.pfc_flags;
// Store the first frag flag in the uuid as pfc_flags will
// be overwritten by new packets
if pfcflags & PFC_FIRST_FRAG > 0 {
uuidentry.flags |= DCERPC_UUID_ENTRY_FLAG_FF;
}
@ -585,6 +585,7 @@ impl DCERPCState {
}
let mut tx = self.create_tx(hdr);
tx.req_cmd = hdr.hdrtype;
tx.req_flags |= hdr.pfc_flags as u16;
tx.req_done = true;
if let Some(flow) = self.flow {
sc_app_layer_parser_trigger_raw_stream_inspection(
@ -762,6 +763,7 @@ impl DCERPCState {
match transaction {
Some(ref mut tx) => {
tx.req_cmd = hdr_type;
tx.req_flags |= hdr.pfc_flags as u16;
tx.ctxid = request.ctxid;
tx.opnum = request.opnum;
tx.first_request_seen = request.first_request_seen;
@ -769,6 +771,7 @@ impl DCERPCState {
None => {
let mut tx = self.create_tx(hdr);
tx.req_cmd = hdr_type;
tx.req_flags |= hdr.pfc_flags as u16;
tx.ctxid = request.ctxid;
tx.opnum = request.opnum;
tx.first_request_seen = request.first_request_seen;
@ -949,10 +952,12 @@ impl DCERPCState {
self.get_tx_by_call_id(current_call_id, Direction::ToClient, hdrtype)
{
tx.resp_cmd = hdrtype;
tx.resp_flags |= hdr.pfc_flags as u16;
tx
} else {
let mut tx = self.create_tx(&hdr);
tx.resp_cmd = hdrtype;
tx.resp_flags |= hdr.pfc_flags as u16;
self.transactions.push_back(tx);
self.transactions.back_mut().unwrap()
};
@ -980,10 +985,12 @@ impl DCERPCState {
match transaction {
Some(tx) => {
tx.resp_cmd = hdrtype;
tx.resp_flags |= hdr.pfc_flags as u16;
}
None => {
let mut tx = self.create_tx(&hdr);
tx.resp_cmd = hdrtype;
tx.resp_flags |= hdr.pfc_flags as u16;
self.transactions.push_back(tx);
}
};

@ -190,6 +190,7 @@ impl DCERPCUDPState {
let max_size = cfg_max_stub_size() as usize;
match hdr.pkt_type {
DCERPC_TYPE_REQUEST => {
tx.req_flags |= (hdr.flags1 as u16) | ((hdr.flags2 as u16) << 8);
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 +204,7 @@ impl DCERPCUDPState {
return true;
}
DCERPC_TYPE_RESPONSE => {
tx.resp_flags |= (hdr.flags1 as u16) | ((hdr.flags2 as u16) << 8);
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);

@ -22,7 +22,9 @@ use super::dcerpc::{
use crate::core::{STREAM_TOCLIENT, STREAM_TOSERVER};
use crate::detect::uint::{detect_match_uint, detect_parse_uint, DetectUintData};
use crate::detect::{helper_keyword_register_sticky_buffer, SigTableElmtStickyBuffer};
use crate::smb::detect::{smb_tx_get_stub_data, smb_tx_match_dce_iface, smb_tx_match_dce_opnum};
use crate::smb::detect::{
smb_tx_get_stub_data, smb_tx_match_dce_flags, smb_tx_match_dce_iface, smb_tx_match_dce_opnum,
};
use crate::smb::smb::ALPROTO_SMB;
use std::ffi::CStr;
use std::os::raw::{c_char, c_int, c_void};
@ -393,6 +395,74 @@ unsafe extern "C" fn dcerpc_opnum_free(_de: *mut DetectEngineCtx, ptr: *mut c_vo
}
}
unsafe extern "C" fn dcerpc_tx_match_dce_flags(
tx: *mut c_void, ctx: *const SigMatchCtx, flags: u8,
) -> c_int {
let tx = cast_pointer!(tx, DCERPCTransaction);
let ctx = cast_pointer!(ctx, DetectUintData<u16>);
let value = if flags & STREAM_TOSERVER != 0 {
tx.req_flags
} else {
tx.resp_flags
};
return detect_match_uint(ctx, value) as i32;
}
unsafe fn dcerpc_flags_parse(carg: *const c_char) -> *mut c_void {
let arg = match CStr::from_ptr(carg).to_str() {
Ok(arg) => arg,
Err(_) => {
return std::ptr::null_mut();
}
};
match detect_parse_uint::<u16>(arg) {
Ok((_, ctx)) => Box::into_raw(Box::new(ctx)) as *mut c_void,
Err(_) => std::ptr::null_mut(),
}
}
unsafe extern "C" fn dcerpc_flags_match(
_de: *mut DetectEngineThreadCtx, f: *mut crate::flow::Flow, flags: u8, _state: *mut c_void,
tx: *mut c_void, _sig: *const Signature, ctx: *const SigMatchCtx,
) -> c_int {
if SCFlowGetAppProtocol(f) == ALPROTO_DCERPC {
return dcerpc_tx_match_dce_flags(tx, ctx, flags);
}
return smb_tx_match_dce_flags(tx, ctx, flags) as i32;
}
unsafe extern "C" fn dcerpc_flags_setup(
de: *mut DetectEngineCtx, s: *mut Signature, raw: *const libc::c_char,
) -> c_int {
if SCDetectSignatureSetAppProto(s, ALPROTO_DCERPC) != 0 {
return -1;
}
let ctx = dcerpc_flags_parse(raw);
if ctx.is_null() {
return -1;
}
if SCSigMatchAppendSMToList(
de,
s,
G_DCERPC_FLAGS_KW_ID,
ctx as *mut SigMatchCtx,
G_DCERPC_GENERIC_BUFFER_ID,
)
.is_null()
{
dcerpc_flags_free(std::ptr::null_mut(), ctx);
return -1;
}
return 0;
}
unsafe extern "C" fn dcerpc_flags_free(_de: *mut DetectEngineCtx, ptr: *mut c_void) {
if !ptr.is_null() {
std::mem::drop(Box::from_raw(ptr as *mut DetectUintData<u16>));
}
}
unsafe extern "C" fn dcerpc_stub_data_setup(
de_ctx: *mut DetectEngineCtx, s: *mut Signature, _str: *const c_char,
) -> c_int {
@ -440,6 +510,7 @@ unsafe extern "C" fn dcerpc_tx_get_stub_data(
}
static mut G_DCERPC_OPNUM_KW_ID: u16 = 0;
static mut G_DCERPC_FLAGS_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;
@ -457,6 +528,18 @@ pub unsafe extern "C" fn SCDetectDcerpcRegister() {
flags: 0,
};
G_DCERPC_OPNUM_KW_ID = SCDetectHelperKeywordRegister(&kw);
let kw = SCSigTableAppLiteElmt {
name: b"dcerpc.flags\0".as_ptr() as *const libc::c_char,
desc: b"match on the PFC flags in a DCERPC header\0".as_ptr() as *const libc::c_char,
url: b"/rules/dcerpc-keywords.html#dcerpc-flags\0".as_ptr() as *const libc::c_char,
AppLayerTxMatch: Some(dcerpc_flags_match),
Setup: Some(dcerpc_flags_setup),
Free: Some(dcerpc_flags_free),
flags: 0,
};
G_DCERPC_FLAGS_KW_ID = SCDetectHelperKeywordRegister(&kw);
G_DCERPC_GENERIC_BUFFER_ID = SCDetectHelperBufferProgressRegister(
b"dce_generic\0".as_ptr() as *const libc::c_char,
ALPROTO_DCERPC,

@ -94,6 +94,8 @@ pub struct SMBTransactionDCERPC {
pub res_cmd: u8,
pub res_set: bool,
pub call_id: u32,
pub req_flags: u8,
pub resp_flags: u8,
pub frag_cnt_ts: u16,
pub frag_cnt_tc: u16,
pub stub_data_ts: Vec<u8>,
@ -101,19 +103,21 @@ pub struct SMBTransactionDCERPC {
}
impl SMBTransactionDCERPC {
fn new_request(req: u8, call_id: u32) -> Self {
fn new_request(req: u8, call_id: u32, flags: u8) -> Self {
return Self {
opnum: 0,
context_id: 0,
req_cmd: req,
req_set: true,
req_flags: flags,
call_id,
..Default::default()
};
}
fn new_response(call_id: u32) -> Self {
fn new_response(call_id: u32, flags: u8) -> Self {
return Self {
call_id,
resp_flags: flags,
..Default::default()
};
}
@ -126,12 +130,13 @@ impl SMBTransactionDCERPC {
impl SMBState {
fn new_dcerpc_tx(
&mut self, hdr: SMBCommonHdr, vercmd: SMBVerCmdStat, cmd: u8, call_id: u32,
packet_flags: u8,
) -> Option<&mut SMBTransaction> {
let mut tx = self.new_tx()?;
tx.hdr = hdr;
tx.vercmd = vercmd;
tx.type_data = Some(SMBTransactionTypeData::DCERPC(
SMBTransactionDCERPC::new_request(cmd, call_id),
SMBTransactionDCERPC::new_request(cmd, call_id, packet_flags),
));
SCLogDebug!("SMB: TX DCERPC created: ID {} hdr {:?}", tx.id, tx.hdr);
@ -140,13 +145,13 @@ impl SMBState {
}
fn new_dcerpc_tx_for_response(
&mut self, hdr: SMBCommonHdr, vercmd: SMBVerCmdStat, call_id: u32,
&mut self, hdr: SMBCommonHdr, vercmd: SMBVerCmdStat, call_id: u32, packet_flags: u8,
) -> Option<&mut SMBTransaction> {
let mut tx = self.new_tx()?;
tx.hdr = hdr;
tx.vercmd = vercmd;
tx.type_data = Some(SMBTransactionTypeData::DCERPC(
SMBTransactionDCERPC::new_response(call_id),
SMBTransactionDCERPC::new_response(call_id, packet_flags),
));
SCLogDebug!("SMB: TX DCERPC created: ID {} hdr {:?}", tx.id, tx.hdr);
@ -213,6 +218,7 @@ pub fn smb_write_dcerpc_record(
if let Some(SMBTransactionTypeData::DCERPC(ref mut tdn)) =
tx.type_data
{
tdn.req_flags |= dcer.packet_flags;
tdn.frag_cnt_ts = tdn.frag_cnt_ts.saturating_add(1);
let max_size = cfg_max_stub_size() as usize;
if recr.data.len() + tdn.stub_data_ts.len() < max_size {
@ -249,7 +255,13 @@ pub fn smb_write_dcerpc_record(
}
}
let Some(tx) = state.new_dcerpc_tx(hdr, vercmd, dcer.packet_type, dcer.call_id) else {
let Some(tx) = state.new_dcerpc_tx(
hdr,
vercmd,
dcer.packet_type,
dcer.call_id,
dcer.packet_flags,
) else {
return false;
};
match dcer.packet_type {
@ -390,6 +402,7 @@ fn smb_dcerpc_response_bindack(
Some(tx) => {
if let Some(SMBTransactionTypeData::DCERPC(ref mut tdn)) = tx.type_data {
tdn.set_result(DCERPC_TYPE_BINDACK);
tdn.resp_flags |= dcer.packet_flags;
}
tx.vercmd.set_ntstatus(ntstatus);
tx.response_done = true;
@ -460,6 +473,7 @@ fn dcerpc_response_handle(tx: &mut SMBTransaction, vercmd: SMBVerCmdStat, dcer:
tdn.stub_data_tc
.extend_from_slice(&respr.data[..max_size - tdn.stub_data_tc.len()]);
}
tdn.resp_flags |= dcer.packet_flags;
}
tx.vercmd.set_ntstatus(ntstatus);
tx.response_done = dcer.last_frag;
@ -474,6 +488,7 @@ fn dcerpc_response_handle(tx: &mut SMBTransaction, vercmd: SMBVerCmdStat, dcer:
21..=255 => {
if let Some(SMBTransactionTypeData::DCERPC(ref mut tdn)) = tx.type_data {
tdn.set_result(dcer.packet_type);
tdn.resp_flags |= dcer.packet_flags;
}
tx.vercmd.set_ntstatus(ntstatus);
tx.response_done = true;
@ -483,6 +498,7 @@ fn dcerpc_response_handle(tx: &mut SMBTransaction, vercmd: SMBVerCmdStat, dcer:
// valid type w/o special processing
if let Some(SMBTransactionTypeData::DCERPC(ref mut tdn)) = tx.type_data {
tdn.set_result(dcer.packet_type);
tdn.resp_flags |= dcer.packet_flags;
}
tx.vercmd.set_ntstatus(ntstatus);
tx.response_done = true;
@ -556,9 +572,12 @@ pub fn smb_read_dcerpc_record(
};
if !found {
// pick up DCERPC tx even if we missed the request
let Some(tx) =
state.new_dcerpc_tx_for_response(hdr, vercmd.clone(), dcer.call_id)
else {
let Some(tx) = state.new_dcerpc_tx_for_response(
hdr,
vercmd.clone(),
dcer.call_id,
dcer.packet_flags,
) else {
return false;
};
dcerpc_response_handle(tx, vercmd, &dcer);

@ -212,6 +212,7 @@ pub struct DceRpcRecord<'a> {
pub little_endian: bool,
pub packet_type: u8,
pub packet_flags: u8,
pub call_id: u32,
pub data: &'a [u8],
@ -256,6 +257,7 @@ pub fn parse_dcerpc_record(i: &[u8]) -> IResult<&[u8], DceRpcRecord<'_>> {
version_major,
version_minor,
packet_type,
packet_flags: (packet_flags.0 << 2) | (packet_flags.1 << 1) | packet_flags.2,
first_frag: packet_flags.2 == 1,
last_frag: packet_flags.1 == 1,
frag_len,

@ -19,7 +19,7 @@ use super::smb::ALPROTO_SMB;
use crate::core::{STREAM_TOCLIENT, STREAM_TOSERVER};
use crate::dcerpc::dcerpc::DCERPC_TYPE_REQUEST;
use crate::dcerpc::detect::{DCEIfaceData, DCEOpnumData, DETECT_DCE_OPNUM_RANGE_UNINITIALIZED};
use crate::detect::uint::detect_match_uint;
use crate::detect::uint::{detect_match_uint, DetectUintData};
use crate::detect::{helper_keyword_register_sticky_buffer, SigTableElmtStickyBuffer};
use crate::direction::Direction;
use crate::smb::smb::*;
@ -125,6 +125,26 @@ pub(crate) unsafe extern "C" fn smb_tx_match_dce_opnum(
return 0;
}
pub(crate) unsafe extern "C" fn smb_tx_match_dce_flags(
tx: *mut c_void, ctx: *const SigMatchCtx, flags: u8,
) -> u8 {
let tx = cast_pointer!(tx, SMBTransaction);
let dce_data = cast_pointer!(ctx, DetectUintData<u16>);
if let Some(SMBTransactionTypeData::DCERPC(ref x)) = tx.type_data {
let pflags = if flags & STREAM_TOSERVER != 0 {
x.req_flags
} else {
x.resp_flags
};
if detect_match_uint(dce_data, pflags as u16) {
return 1;
}
}
return 0;
}
/* mimic logic that is/was in the C code:
* - match on REQUEST (so not on BIND/BINDACK (probably for mixing with
* dce_opnum and dce_stub_data)

Loading…
Cancel
Save