pull/15925/merge
Shivani Bhardwaj 12 hours ago committed by GitHub
commit 5aac489bc4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -59,6 +59,53 @@ Examples::
dcerpc.opnum can since suricata 9 use an :ref:`unsigned 16-bits integer <rules-integer-keywords>`.
dcerpc.ptype
------------
Match on the PDU type of a DCERPC header. On a to_server match the request PDU
type is used, on a to_client match the response PDU type is used.
The format of the keyword::
dcerpc.ptype:<u8>;
dcerpc.ptype:[>,<,!,=]<u8>;
The PDU type values are:
===== =====================
Value PDU type
===== =====================
0 request
1 ping
2 response
3 fault
4 working
5 nocall
6 reject
7 ack
8 cl_cancel
9 fack
10 cancel_ack
11 bind
12 bind_ack
13 bind_nak
14 alter_context
15 alter_context_resp
16 auth3
17 shutdown
18 co_cancel
19 orphaned
20 rts
===== =====================
Examples::
dcerpc.ptype:11;
dcerpc.ptype:!0;
dcerpc.ptype:>10;
dcerpc.ptype uses an :ref:`unsigned 8-bits integer <rules-integer-keywords>`.
dcerpc.stub_data
----------------

@ -469,7 +469,12 @@
}
},
"request": {
"type": "string"
"type": "string",
"suricata": {
"keywords": [
"dcerpc.ptype"
]
}
},
"res": {
"type": "object",
@ -484,7 +489,12 @@
}
},
"response": {
"type": "string"
"type": "string",
"suricata": {
"keywords": [
"dcerpc.ptype"
]
}
},
"rpc_version": {
"type": "string"

@ -205,6 +205,8 @@ pub struct DCERPCTransaction {
pub resp_lost: bool,
pub req_cmd: u8,
pub resp_cmd: u8,
pub req_seen: bool,
pub resp_seen: bool,
pub activityuuid: Vec<u8>,
pub seqnum: u32,
pub tx_data: AppLayerTxData,
@ -221,9 +223,9 @@ impl DCERPCTransaction {
return Self {
stub_data_buffer_ts: Vec::new(),
stub_data_buffer_tc: Vec::new(),
req_cmd: DCERPC_TYPE_REQUEST,
resp_cmd: DCERPC_TYPE_RESPONSE,
activityuuid: Vec::new(),
req_cmd: DCERPC_TYPE_UNKNOWN,
resp_cmd: DCERPC_TYPE_UNKNOWN,
tx_data: AppLayerTxData::new(),
..Default::default()
};
@ -278,13 +280,13 @@ pub struct Uuid {
pub node: Vec<u8>,
}
#[derive(Debug)]
#[derive(Debug, Clone, Copy)]
pub struct DCERPCHdr {
pub rpc_vers: u8,
pub rpc_vers_minor: u8,
pub hdrtype: u8,
pub pfc_flags: u8,
pub packed_drep: Vec<u8>,
pub packed_drep: [u8; 4],
pub frag_length: u16,
pub auth_length: u16,
pub call_id: u32,
@ -445,8 +447,8 @@ impl DCERPCState {
if tx.req_done || tx.req_lost {
continue;
}
let resp_cmd = get_resp_type_for_req(cmd);
if resp_cmd != tx.resp_cmd {
/* do not match if the transaction's command does not match expected */
if tx.req_cmd != cmd && get_req_type_for_resp(tx.resp_cmd) != cmd {
continue;
}
}
@ -454,8 +456,8 @@ impl DCERPCState {
if tx.resp_done || tx.resp_lost {
continue;
}
let req_cmd = get_req_type_for_resp(cmd);
if req_cmd != tx.req_cmd {
/* do not match if the transaction's command does not match expected */
if tx.resp_cmd != cmd && get_resp_type_for_req(tx.req_cmd) != cmd {
continue;
}
}
@ -585,6 +587,7 @@ impl DCERPCState {
}
let mut tx = self.create_tx(hdr);
tx.req_cmd = hdr.hdrtype;
tx.req_seen = true;
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_seen = true;
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_seen = true;
tx.ctxid = request.ctxid;
tx.opnum = request.opnum;
tx.first_request_seen = request.first_request_seen;
@ -796,6 +801,24 @@ impl DCERPCState {
}
}
fn save_unhandled_ptypes(&mut self, hdr: &DCERPCHdr, direction: Direction) {
/* Create tx even on unhandled PDU types to be able to
* at least issue a match on header only fields */
let mut tx = self.create_tx(hdr);
if direction == Direction::ToServer {
tx.req_cmd = hdr.hdrtype;
tx.req_seen = true;
} else {
tx.resp_cmd = hdr.hdrtype;
tx.resp_seen = true;
}
tx.req_done = true;
tx.resp_done = true;
self.transactions.push_back(tx);
// recognized DCERPC PDU types that we do not process
SCLogDebug!("Unhandled packet type: {:?}", hdr.hdrtype);
}
pub fn handle_input_data(
&mut self, stream_slice: StreamSlice, direction: Direction,
) -> AppLayerResult {
@ -888,6 +911,15 @@ impl DCERPCState {
// input only consists of the header and that was consumed, so, return early
cur_i = &cur_i[fraglen as usize..];
consumed += fraglen as u32;
// save header-only PDU types too
if matches!(
hdr.hdrtype,
DCERPC_TYPE_SHUTDOWN | DCERPC_TYPE_CO_CANCEL | DCERPC_TYPE_ORPHANED
) {
self.save_unhandled_ptypes(&hdr, direction);
/* DCERPC_TYPE_CO_CANCEL and ORPHANED can optionally be more than header
* too so they're also handled later on the in the unrecognized branch */
}
continue;
}
cmp::Ordering::Greater => {}
@ -949,10 +981,12 @@ impl DCERPCState {
self.get_tx_by_call_id(current_call_id, Direction::ToClient, hdrtype)
{
tx.resp_cmd = hdrtype;
tx.resp_seen = true;
tx
} else {
let mut tx = self.create_tx(&hdr);
tx.resp_cmd = hdrtype;
tx.resp_seen = true;
self.transactions.push_back(tx);
self.transactions.back_mut().unwrap()
};
@ -980,10 +1014,12 @@ impl DCERPCState {
match transaction {
Some(tx) => {
tx.resp_cmd = hdrtype;
tx.resp_seen = true;
}
None => {
let mut tx = self.create_tx(&hdr);
tx.resp_cmd = hdrtype;
tx.resp_seen = true;
self.transactions.push_back(tx);
}
};
@ -997,9 +1033,24 @@ impl DCERPCState {
return AppLayerResult::err();
}
}
DCERPC_TYPE_PING
| DCERPC_TYPE_FAULT
| DCERPC_TYPE_WORKING
| DCERPC_TYPE_NOCALL
| DCERPC_TYPE_REJECT
| DCERPC_TYPE_ACK
| DCERPC_TYPE_CL_CANCEL
| DCERPC_TYPE_FACK
| DCERPC_TYPE_CANCEL_ACK
| DCERPC_TYPE_BINDNAK
| DCERPC_TYPE_AUTH3
| DCERPC_TYPE_CO_CANCEL
| DCERPC_TYPE_ORPHANED
| DCERPC_TYPE_RTS => {
self.save_unhandled_ptypes(&hdr, direction);
}
_ => {
SCLogDebug!("Unrecognized packet type: {:?}", hdrtype);
// skip unrecognized packet types such as AUTH3
}
}
consumed += fraglen as u32;

@ -19,7 +19,10 @@ use crate::applayer::{self, *};
use crate::core;
use crate::dcerpc::dcerpc::{
cfg_max_stub_size, get_alstate_progress, DCERPCTransaction, ALPROTO_DCERPC, DCERPC_MAX_TX,
DCERPC_TYPE_REQUEST, DCERPC_TYPE_RESPONSE, PARSER_NAME, PFCL1_FRAG, PFCL1_LASTFRAG,
DCERPC_TYPE_ACK, DCERPC_TYPE_CANCEL_ACK, DCERPC_TYPE_CL_CANCEL, DCERPC_TYPE_FACK,
DCERPC_TYPE_FAULT, DCERPC_TYPE_NOCALL, DCERPC_TYPE_PING, DCERPC_TYPE_REJECT,
DCERPC_TYPE_REQUEST, DCERPC_TYPE_RESPONSE, DCERPC_TYPE_WORKING, PARSER_NAME, PFCL1_FRAG,
PFCL1_LASTFRAG,
};
use crate::dcerpc::parser;
use crate::direction::{Direction, DIR_BOTH};
@ -163,8 +166,8 @@ impl DCERPCUDPState {
}
pub fn handle_fragment_data(&mut self, hdr: &DCERPCHdrUdp, input: &[u8]) -> bool {
if hdr.pkt_type != DCERPC_TYPE_REQUEST && hdr.pkt_type != DCERPC_TYPE_RESPONSE {
SCLogDebug!("Unrecognized packet type");
if hdr.pkt_type > DCERPC_TYPE_CANCEL_ACK {
SCLogDebug!("Unrecognized packet type: {}", hdr.pkt_type);
return false;
}
@ -190,6 +193,8 @@ impl DCERPCUDPState {
let max_size = cfg_max_stub_size() as usize;
match hdr.pkt_type {
DCERPC_TYPE_REQUEST => {
tx.req_cmd = hdr.pkt_type;
tx.req_seen = true;
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 +208,8 @@ impl DCERPCUDPState {
return true;
}
DCERPC_TYPE_RESPONSE => {
tx.resp_cmd = hdr.pkt_type;
tx.resp_seen = true;
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);
@ -215,8 +222,32 @@ impl DCERPCUDPState {
}
return true;
}
/* Unhandled connectionless PDU types are not otherwise
* processed; store the PDU type so dcerpc.ptype can match */
DCERPC_TYPE_PING | DCERPC_TYPE_ACK | DCERPC_TYPE_CL_CANCEL => {
// client-to-server PDU types
tx.req_cmd = hdr.pkt_type;
tx.req_seen = true;
tx.req_done = true;
tx.resp_done = true;
return true;
}
// server-to-client PDU types (fack is bidirectional in CL and
// is recorded here as a response)
DCERPC_TYPE_FAULT
| DCERPC_TYPE_WORKING
| DCERPC_TYPE_NOCALL
| DCERPC_TYPE_REJECT
| DCERPC_TYPE_FACK
| DCERPC_TYPE_CANCEL_ACK => {
tx.resp_cmd = hdr.pkt_type;
tx.resp_seen = true;
tx.req_done = true;
tx.resp_done = true;
return true;
}
_ => {
// unreachable
// unreachable: pkt_type is guarded to 0..=10 above
}
}
}

@ -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
@ -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_iface, smb_tx_match_dce_opnum, smb_tx_match_dce_ptype,
};
use crate::smb::smb::ALPROTO_SMB;
use std::ffi::CStr;
use std::os::raw::{c_char, c_int, c_void};
@ -439,9 +441,85 @@ unsafe extern "C" fn dcerpc_tx_get_stub_data(
return buffer;
}
fn parse_dcerpc_ptype(arg: &str) -> Option<DetectUintData<u8>> {
if let Ok((_, ctx)) = detect_parse_uint::<u8>(arg) {
return Some(ctx);
}
None
}
unsafe extern "C" fn dcerpc_ptype_parse(carg: *const c_char) -> *mut c_void {
if let Ok(arg) = CStr::from_ptr(carg).to_str() {
if let Some(detect) = parse_dcerpc_ptype(arg) {
return Box::into_raw(Box::new(detect)) as *mut _;
}
}
return std::ptr::null_mut();
}
unsafe extern "C" fn dcerpc_ptype_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_ptype_parse(raw);
if ctx.is_null() {
return -1;
}
if SCSigMatchAppendSMToList(
de,
s,
G_DCERPC_PTYPE_KW_ID,
ctx as *mut SigMatchCtx,
G_DCERPC_GENERIC_BUFFER_ID,
)
.is_null()
{
dcerpc_ptype_free(std::ptr::null_mut(), ctx);
return -1;
}
return 0;
}
unsafe fn dcerpc_tx_match_dce_ptype(tx: *mut c_void, ctx: *const SigMatchCtx, flags: u8) -> c_int {
let tx = cast_pointer!(tx, DCERPCTransaction);
let du8 = cast_pointer!(ctx, DetectUintData<u8>);
if flags & STREAM_TOSERVER != 0 {
if tx.req_seen && detect_match_uint(du8, tx.req_cmd) {
return 1;
}
} else if tx.resp_seen && detect_match_uint(du8, tx.resp_cmd) {
return 1;
}
return 0;
}
unsafe extern "C" fn dcerpc_ptype_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_ptype(tx, ctx, flags);
}
if smb_tx_match_dce_ptype(tx, ctx, flags) != 1 {
return 0;
}
return 1;
}
unsafe extern "C" fn dcerpc_ptype_free(_de: *mut DetectEngineCtx, ptr: *mut c_void) {
if !ptr.is_null() {
std::mem::drop(Box::from_raw(ptr as *mut DetectUintData<u8>));
}
}
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_PTYPE_KW_ID: u16 = 0;
static mut G_DCERPC_STUB_BUFFER_ID: c_int = 0;
#[no_mangle]
@ -490,6 +568,17 @@ pub unsafe extern "C" fn SCDetectDcerpcRegister() {
b"dce_iface\0".as_ptr() as *const libc::c_char,
);
let kw = SCSigTableAppLiteElmt {
name: b"dcerpc.ptype\0".as_ptr() as *const libc::c_char,
desc: b"match on the PDU type in a DCERPC header\0".as_ptr() as *const libc::c_char,
url: b"/rules/dcerpc-keywords.html#dcerpc-ptype\0".as_ptr() as *const libc::c_char,
AppLayerTxMatch: Some(dcerpc_ptype_match),
Setup: Some(dcerpc_ptype_setup),
Free: Some(dcerpc_ptype_free),
flags: 0,
};
G_DCERPC_PTYPE_KW_ID = SCDetectHelperKeywordRegister(&kw);
let kw_stub = SigTableElmtStickyBuffer {
name: String::from("dcerpc.stub_data"),
desc: String::from("match on the stub data in a DCERPC packet"),

@ -233,7 +233,12 @@ pub(super) fn parse_dcerpc_header(i: &[u8]) -> IResult<&[u8], DCERPCHdr> {
rpc_vers_minor,
hdrtype,
pfc_flags,
packed_drep: packed_drep.to_vec(),
packed_drep: [
packed_drep[0],
packed_drep[1],
packed_drep[2],
packed_drep[3],
],
frag_length,
auth_length,
call_id,

@ -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,25 @@ pub(crate) unsafe extern "C" fn smb_tx_match_dce_opnum(
return 0;
}
pub(crate) unsafe extern "C" fn smb_tx_match_dce_ptype(
tx: *mut c_void, ctx: *const SigMatchCtx, flags: u8,
) -> u8 {
let tx = cast_pointer!(tx, SMBTransaction);
let du8 = cast_pointer!(ctx, DetectUintData<u8>);
if let Some(SMBTransactionTypeData::DCERPC(ref x)) = tx.type_data {
if flags & STREAM_TOSERVER != 0 {
if x.req_set && detect_match_uint(du8, x.req_cmd) {
return 1;
}
} else if x.res_set && detect_match_uint(du8, x.res_cmd) {
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