pull/16027/merge
Shivani Bhardwaj 13 hours ago committed by GitHub
commit 1487d12aa4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -68,6 +68,21 @@ Example::
dcerpc.stub_data; content:"123456";
dcerpc.is_fragmented
--------------------
Match on whether a DCERPC PDU is fragmented. It takes a boolean value.
Syntax::
dcerpc.is_fragmented: true|false;
Example:
.. container:: example-rule
alert dcerpc any any -> any any (:example-rule-options:`dcerpc.is_fragmented: true;` sid: 1;)
Additional information
----------------------

@ -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,
@ -585,6 +587,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 +765,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 +773,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 +954,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 +987,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);

@ -17,12 +17,15 @@
use super::dcerpc::{
DCERPCState, DCERPCTransaction, ALPROTO_DCERPC, DCERPC_TYPE_REQUEST, DCERPC_TYPE_RESPONSE,
DCERPC_UUID_ENTRY_FLAG_FF,
DCERPC_UUID_ENTRY_FLAG_FF, PFCL1_FRAG, PFC_FIRST_FRAG, PFC_LAST_FRAG,
};
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_iface, smb_tx_match_dce_is_fragmented,
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 +396,103 @@ unsafe extern "C" fn dcerpc_opnum_free(_de: *mut DetectEngineCtx, ptr: *mut c_vo
}
}
#[derive(Debug)]
pub struct DCERPCIsFragmentedData {
pub is_fragmented: bool,
}
fn parse_is_fragmented(arg: &str) -> Option<bool> {
match arg.trim().to_ascii_lowercase().as_str() {
"true" => Some(true),
"false" => Some(false),
_ => None,
}
}
unsafe fn dcerpc_is_fragmented_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 parse_is_fragmented(arg) {
Some(is_fragmented) => {
Box::into_raw(Box::new(DCERPCIsFragmentedData { is_fragmented })) as *mut c_void
}
None => std::ptr::null_mut(),
}
}
unsafe fn dcerpc_tx_match_is_fragmented(flags: u8, tx: *mut c_void, ctx: *const SigMatchCtx) -> u8 {
let tx = cast_pointer!(tx, DCERPCTransaction);
let ctx = cast_pointer!(ctx, DCERPCIsFragmentedData);
let (frag_cnt, tx_flags) = if flags & STREAM_TOSERVER != 0 {
(tx.frag_cnt_ts, tx.req_flags)
} else {
(tx.frag_cnt_tc, tx.resp_flags)
};
// hack to tell if it's UDP
let fragmented = if !tx.activityuuid.is_empty() {
// For UDP, the flags1 fragment bit (0x04) explicitly marks a fragment
tx_flags & PFCL1_FRAG as u16 != 0
} else {
// For TCP, a request/response PDU is a single complete message only
// when it sets both PFC_FIRST_FRAG and PFC_LAST_FRAG; any PDU missing
// either flag (first, middle or last fragment) is fragmented. The
// frag_cnt guard keeps a direction that has seen no PDU from matching.
let both = (PFC_FIRST_FRAG | PFC_LAST_FRAG) as u16;
frag_cnt > 0 && (tx_flags & both) != both
};
if fragmented == ctx.is_fragmented {
return 1;
}
return 0;
}
unsafe extern "C" fn dcerpc_is_fragmented_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_is_fragmented(flags, tx, ctx) as c_int;
}
return smb_tx_match_dce_is_fragmented(flags, tx, ctx) as c_int;
}
unsafe extern "C" fn dcerpc_is_fragmented_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_is_fragmented_parse(raw);
if ctx.is_null() {
return -1;
}
if SCSigMatchAppendSMToList(
de,
s,
G_DCERPC_IS_FRAGMENTED_KW_ID,
ctx as *mut SigMatchCtx,
G_DCERPC_GENERIC_BUFFER_ID,
)
.is_null()
{
dcerpc_is_fragmented_free(std::ptr::null_mut(), ctx);
return -1;
}
return 0;
}
unsafe extern "C" fn dcerpc_is_fragmented_free(_de: *mut DetectEngineCtx, ptr: *mut c_void) {
if !ptr.is_null() {
std::mem::drop(Box::from_raw(ptr as *mut DCERPCIsFragmentedData));
}
}
unsafe extern "C" fn dcerpc_stub_data_setup(
de_ctx: *mut DetectEngineCtx, s: *mut Signature, _str: *const c_char,
) -> c_int {
@ -440,6 +540,7 @@ unsafe extern "C" fn dcerpc_tx_get_stub_data(
}
static mut G_DCERPC_OPNUM_KW_ID: u16 = 0;
static mut G_DCERPC_IS_FRAGMENTED_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;
@ -474,6 +575,17 @@ pub unsafe extern "C" fn SCDetectDcerpcRegister() {
b"dce_opnum\0".as_ptr() as *const libc::c_char,
);
let kw = SCSigTableAppLiteElmt {
name: b"dcerpc.is_fragmented\0".as_ptr() as *const libc::c_char,
desc: b"match if the DCERPC PDU is fragmented\0".as_ptr() as *const libc::c_char,
url: b"/rules/dcerpc-keywords.html#dcerpc-is-fragmented\0".as_ptr() as *const libc::c_char,
AppLayerTxMatch: Some(dcerpc_is_fragmented_match),
Setup: Some(dcerpc_is_fragmented_setup),
Free: Some(dcerpc_is_fragmented_free),
flags: 0,
};
G_DCERPC_IS_FRAGMENTED_KW_ID = SCDetectHelperKeywordRegister(&kw);
let kw = SCSigTableAppLiteElmt {
name: b"dcerpc.iface\0".as_ptr() as *const libc::c_char,
desc: b"match on the value of the interface UUID in a DCERPC header\0".as_ptr()

@ -94,6 +94,8 @@ pub struct SMBTransactionDCERPC {
pub res_cmd: u8,
pub res_set: bool,
pub call_id: u32,
pub req_is_fragmented: bool,
pub resp_is_fragmented: bool,
pub frag_cnt_ts: u16,
pub frag_cnt_tc: u16,
pub stub_data_ts: Vec<u8>,
@ -263,6 +265,7 @@ pub fn smb_write_dcerpc_record(
SCLogDebug!("first frag size {}", recr.data.len());
tdn.opnum = recr.opnum;
tdn.context_id = recr.context_id;
tdn.req_is_fragmented = !(dcer.first_frag && dcer.last_frag);
tdn.frag_cnt_ts = tdn.frag_cnt_ts.saturating_add(1);
let max_size = cfg_max_stub_size() as usize;
if tdn.stub_data_ts.len() + recr.data.len() < max_size {
@ -452,6 +455,7 @@ fn dcerpc_response_handle(tx: &mut SMBTransaction, vercmd: SMBVerCmdStat, dcer:
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.resp_is_fragmented = !(dcer.first_frag && dcer.last_frag);
let max_size = cfg_max_stub_size() as usize;
tdn.frag_cnt_tc = tdn.frag_cnt_tc.saturating_add(1);
if tdn.stub_data_tc.len() + respr.data.len() < max_size {

@ -18,7 +18,9 @@
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::dcerpc::detect::{
DCEIfaceData, DCEOpnumData, DCERPCIsFragmentedData, DETECT_DCE_OPNUM_RANGE_UNINITIALIZED,
};
use crate::detect::uint::detect_match_uint;
use crate::detect::{helper_keyword_register_sticky_buffer, SigTableElmtStickyBuffer};
use crate::direction::Direction;
@ -125,6 +127,26 @@ pub(crate) unsafe extern "C" fn smb_tx_match_dce_opnum(
return 0;
}
pub(crate) unsafe extern "C" fn smb_tx_match_dce_is_fragmented(
flags: u8, tx: *mut c_void, ctx: *const SigMatchCtx,
) -> u8 {
let tx = cast_pointer!(tx, SMBTransaction);
let ctx = cast_pointer!(ctx, DCERPCIsFragmentedData);
if let Some(SMBTransactionTypeData::DCERPC(ref x)) = tx.type_data {
let fragmented = if flags & STREAM_TOSERVER != 0 {
x.req_is_fragmented
} else {
x.resp_is_fragmented
};
if fragmented == ctx.is_fragmented {
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