dcerpc: store persistently contexts

So that we can log and detect on interface uuids, even if
AlterContext packets change one of the contexts, but other contexts
are still used after it in request/responses

Ticket: 8378
(cherry picked from commit 05a11e2897)
pull/15132/head
Philippe Antoine 2 months ago committed by Philippe Antoine
parent a4aa865f52
commit 691114e95c

@ -264,6 +264,8 @@ pub struct DCERPCUuidEntry {
pub version: u16,
pub versionminor: u16,
pub flags: u16,
pub call_id: u32,
pub acked: bool,
}
impl DCERPCUuidEntry {
@ -327,8 +329,7 @@ pub struct DCERPCBindAck {
#[derive(Default, Debug)]
pub struct DCERPCState {
pub header: Option<DCERPCHdr>,
pub bind: Option<DCERPCBind>,
pub bindack: Option<DCERPCBindAck>,
pub interface_uuids: Vec<DCERPCUuidEntry>,
pub transactions: VecDeque<DCERPCTransaction>,
tx_index_completed: usize,
pub pad: u8,
@ -612,13 +613,14 @@ impl DCERPCState {
}
}
pub fn handle_bindctxitem(&mut self, input: &[u8], uuid_internal_id: u16) -> i32 {
pub fn handle_bindctxitem(&mut self, input: &[u8], uuid_internal_id: u16, call_id: u32) -> i32 {
let endianness = self.get_endianness();
match parser::parse_bindctx_item(input, endianness) {
Ok((leftover_bytes, ctxitem)) => {
let mut uuidentry = DCERPCUuidEntry::new();
uuidentry.uuid = ctxitem.uuid;
uuidentry.internal_id = uuid_internal_id;
uuidentry.call_id = call_id;
uuidentry.ctxid = ctxitem.ctxid;
uuidentry.version = ctxitem.version;
uuidentry.versionminor = ctxitem.versionminor;
@ -628,9 +630,15 @@ impl DCERPCState {
if pfcflags & PFC_FIRST_FRAG > 0 {
uuidentry.flags |= DCERPC_UUID_ENTRY_FLAG_FF;
}
if let Some(ref mut bind) = self.bind {
SCLogDebug!("DCERPC BIND CtxItem: Pushing uuid: {:?}", uuidentry);
bind.uuid_list.push(uuidentry);
for uuid in self.interface_uuids.iter_mut() {
if uuid.ctxid == uuidentry.ctxid {
*uuid = uuidentry;
return (input.len() - leftover_bytes.len()) as i32;
}
}
// arbitrary bound
if self.interface_uuids.len() < 64 {
self.interface_uuids.push(uuidentry);
}
(input.len() - leftover_bytes.len()) as i32
}
@ -653,16 +661,15 @@ impl DCERPCState {
match parser::parse_dcerpc_bind(input) {
Ok((leftover_bytes, header)) => {
let numctxitems = header.numctxitems;
self.bind = Some(header);
let call_id = self.get_hdr_call_id().unwrap_or(0);
for i in 0..numctxitems {
retval = self.handle_bindctxitem(&input[idx as usize..], i as u16);
retval = self.handle_bindctxitem(&input[idx as usize..], i as u16, call_id);
if retval == -1 {
SCLogDebug!("Error handling BindCtxItem");
return -1;
}
idx += retval;
}
let call_id = self.get_hdr_call_id().unwrap_or(0);
let mut tx = self.create_tx(call_id);
tx.req_cmd = self.get_hdr_type().unwrap_or(0);
tx.req_done = true;
@ -688,23 +695,19 @@ impl DCERPCState {
}
}
pub fn process_bindack_pdu(&mut self, input: &[u8]) -> i32 {
pub fn process_bindack_pdu(&mut self, input: &[u8], call_id: u32) -> i32 {
match parser::parse_dcerpc_bindack(input) {
Ok((leftover_bytes, mut back)) => {
if let Some(ref mut bind) = self.bind {
for (uuid_internal_id, r) in back.ctxitems.iter().enumerate() {
for uuid in bind.uuid_list.iter_mut() {
if uuid.internal_id == uuid_internal_id as u16 {
uuid.result = r.ack_result;
if uuid.result != 0 {
break;
}
back.accepted_uuid_list.push(uuid.clone());
Ok((leftover_bytes, back)) => {
for (uuid_internal_id, r) in back.ctxitems.iter().enumerate() {
for uuid in self.interface_uuids.iter_mut().rev() {
if uuid.internal_id == uuid_internal_id as u16 && uuid.call_id == call_id{
uuid.result = r.ack_result;
uuid.acked = true;
if uuid.result == 0 {
SCLogDebug!("DCERPC BINDACK accepted UUID: {:?}", uuid);
}
}
}
self.bindack = Some(back);
}
(input.len() - leftover_bytes.len()) as i32
}
@ -950,7 +953,7 @@ impl DCERPCState {
}
}
DCERPC_TYPE_BINDACK | DCERPC_TYPE_ALTER_CONTEXT_RESP => {
let retval = self.process_bindack_pdu(&cur_i[parsed as usize..]);
let retval = self.process_bindack_pdu(&cur_i[parsed as usize..], current_call_id);
if retval == -1 {
return AppLayerResult::err();
}
@ -1430,7 +1433,7 @@ mod tests {
];
let mut dcerpc_state = DCERPCState::new();
assert_eq!(16, dcerpc_state.process_header(header));
assert_eq!(44, dcerpc_state.handle_bindctxitem(bind, 0));
assert_eq!(44, dcerpc_state.handle_bindctxitem(bind, 0, 0));
}
#[test]
@ -1564,14 +1567,21 @@ mod tests {
let mut dcerpc_state = DCERPCState::new();
assert_eq!(16, dcerpc_state.process_header(bind));
assert_eq!(1068, dcerpc_state.process_bind_pdu(&bind[16..]));
assert_eq!(604, dcerpc_state.process_bindack_pdu(bindack));
if let Some(back) = dcerpc_state.bindack {
assert_eq!(1, back.accepted_uuid_list.len());
assert_eq!(
vec!(57, 25, 40, 106, 177, 12, 17, 208, 155, 168, 0, 192, 79, 217, 46, 245),
back.accepted_uuid_list[0].uuid
);
assert_eq!(11, back.accepted_uuid_list[0].internal_id);
let call_id = dcerpc_state.get_hdr_call_id().unwrap();
assert_eq!(604, dcerpc_state.process_bindack_pdu(bindack, call_id));
assert_eq!(24, dcerpc_state.interface_uuids.len());
for i in 0..24 {
assert!(dcerpc_state.interface_uuids[i].acked);
if i == 11 {
assert_eq!(
vec!(57, 25, 40, 106, 177, 12, 17, 208, 155, 168, 0, 192, 79, 217, 46, 245),
dcerpc_state.interface_uuids[11].uuid
);
assert_eq!(11, dcerpc_state.interface_uuids[11].internal_id);
assert_eq!(0, dcerpc_state.interface_uuids[11].result);
} else {
assert_ne!(0, dcerpc_state.interface_uuids[i].result);
}
}
}
@ -1804,19 +1814,17 @@ mod tests {
AppLayerResult::ok(),
dcerpc_state.handle_input_data(StreamSlice::from_slice(bindbuf, STREAM_TOSERVER, 0), Direction::ToServer)
);
if let Some(ref bind) = dcerpc_state.bind {
let bind_uuid = &bind.uuid_list[0].uuid;
assert_eq!(1, bind.uuid_list.len());
assert_eq!(
cmp::Ordering::Equal,
bind_uuid
.iter()
.zip(expected_uuid)
.map(|(x, y)| x.cmp(y))
.find(|&ord| ord != cmp::Ordering::Equal)
.unwrap_or_else(|| bind_uuid.len().cmp(&expected_uuid.len()))
);
}
assert_eq!(1, dcerpc_state.interface_uuids.len());
let bind_uuid = &dcerpc_state.interface_uuids[0].uuid;
assert_eq!(
cmp::Ordering::Equal,
bind_uuid
.iter()
.zip(expected_uuid)
.map(|(x, y)| x.cmp(y))
.find(|&ord| ord != cmp::Ordering::Equal)
.unwrap_or_else(|| bind_uuid.len().cmp(&expected_uuid.len()))
);
}
#[test]
@ -2072,11 +2080,9 @@ mod tests {
AppLayerResult::ok(),
dcerpc_state.handle_input_data(StreamSlice::from_slice(bind_ack1, STREAM_TOSERVER, 0), Direction::ToServer)
);
if let Some(ref back) = dcerpc_state.bindack {
assert_eq!(1, back.accepted_uuid_list.len());
assert_eq!(12, back.accepted_uuid_list[0].ctxid);
assert_eq!(expected_uuid1, back.accepted_uuid_list[0].uuid);
}
assert_eq!(13, dcerpc_state.interface_uuids.len());
assert_eq!(12, dcerpc_state.interface_uuids[12].ctxid);
assert_eq!(expected_uuid1, dcerpc_state.interface_uuids[12].uuid);
assert_eq!(
AppLayerResult::ok(),
dcerpc_state.handle_input_data(StreamSlice::from_slice(bind2, STREAM_TOSERVER, 0), Direction::ToServer)
@ -2085,11 +2091,9 @@ mod tests {
AppLayerResult::ok(),
dcerpc_state.handle_input_data(StreamSlice::from_slice(bind_ack2, STREAM_TOSERVER, 0), Direction::ToServer)
);
if let Some(ref back) = dcerpc_state.bindack {
assert_eq!(1, back.accepted_uuid_list.len());
assert_eq!(15, back.accepted_uuid_list[0].ctxid);
assert_eq!(expected_uuid2, back.accepted_uuid_list[0].uuid);
}
assert_eq!(16, dcerpc_state.interface_uuids.len());
assert_eq!(15, dcerpc_state.interface_uuids[15].ctxid);
assert_eq!(expected_uuid2, dcerpc_state.interface_uuids[15].uuid);
assert_eq!(
AppLayerResult::ok(),
dcerpc_state.handle_input_data(StreamSlice::from_slice(bind3, STREAM_TOSERVER, 0), Direction::ToServer)
@ -2098,11 +2102,9 @@ mod tests {
AppLayerResult::ok(),
dcerpc_state.handle_input_data(StreamSlice::from_slice(bind_ack3, STREAM_TOSERVER, 0), Direction::ToServer)
);
if let Some(ref back) = dcerpc_state.bindack {
assert_eq!(1, back.accepted_uuid_list.len());
assert_eq!(11, back.accepted_uuid_list[0].ctxid);
assert_eq!(expected_uuid3, back.accepted_uuid_list[0].uuid);
}
assert_eq!(16, dcerpc_state.interface_uuids.len());
assert_eq!(11, dcerpc_state.interface_uuids[11].ctxid);
assert_eq!(expected_uuid3, dcerpc_state.interface_uuids[11].uuid);
}
#[test]
@ -2154,11 +2156,9 @@ mod tests {
AppLayerResult::ok(),
dcerpc_state.handle_input_data(StreamSlice::from_slice(bindack, STREAM_TOSERVER, 0), Direction::ToServer)
);
if let Some(ref back) = dcerpc_state.bindack {
assert_eq!(1, back.accepted_uuid_list.len());
assert_eq!(0, back.accepted_uuid_list[0].ctxid);
assert_eq!(expected_uuid1, back.accepted_uuid_list[0].uuid);
}
assert_eq!(1, dcerpc_state.interface_uuids.len());
assert_eq!(0, dcerpc_state.interface_uuids[0].ctxid);
assert_eq!(expected_uuid1, dcerpc_state.interface_uuids[0].uuid);
assert_eq!(
AppLayerResult::ok(),
dcerpc_state.handle_input_data(StreamSlice::from_slice(alter_context, STREAM_TOSERVER, 0), Direction::ToServer)
@ -2167,10 +2167,8 @@ mod tests {
AppLayerResult::ok(),
dcerpc_state.handle_input_data(StreamSlice::from_slice(alter_context_resp, STREAM_TOSERVER, 0), Direction::ToServer)
);
if let Some(ref back) = dcerpc_state.bindack {
assert_eq!(1, back.accepted_uuid_list.len());
assert_eq!(1, back.accepted_uuid_list[0].ctxid);
assert_eq!(expected_uuid2, back.accepted_uuid_list[0].uuid);
}
assert_eq!(2, dcerpc_state.interface_uuids.len());
assert_eq!(1, dcerpc_state.interface_uuids[1].ctxid);
assert_eq!(expected_uuid2, dcerpc_state.interface_uuids[1].uuid);
}
}

@ -63,8 +63,8 @@ fn match_backuuid(
tx: &DCERPCTransaction, state: &mut DCERPCState, if_data: &mut DCEIfaceData,
) -> u8 {
let mut ret = 0;
if let Some(ref bindack) = state.bindack {
for uuidentry in bindack.accepted_uuid_list.iter() {
if !state.interface_uuids.is_empty() {
for uuidentry in &state.interface_uuids {
ret = 1;
// if any_frag is not enabled, we need to match only against the first fragment
if if_data.any_frag == 0 && (uuidentry.flags & DCERPC_UUID_ENTRY_FLAG_FF == 0) {
@ -72,7 +72,8 @@ fn match_backuuid(
continue;
}
// if the uuid has been rejected(uuidentry->result == 1), we skip to the next uuid
if uuidentry.result != 0 {
if !uuidentry.acked || uuidentry.result != 0 {
ret = 0;
SCLogDebug!("Skipping to next UUID");
continue;
}

@ -21,16 +21,19 @@ use crate::dcerpc::dcerpc_udp::*;
use crate::jsonbuilder::{JsonBuilder, JsonError};
fn log_bind_interfaces(jsb: &mut JsonBuilder, state: &DCERPCState) -> Result<(), JsonError> {
if let Some(bind) = &state.bind {
if !state.interface_uuids.is_empty() {
jsb.open_array("interfaces")?;
for uuid in &bind.uuid_list {
for uuid in &state.interface_uuids {
jsb.start_object()?;
let ifstr = Uuid::from_slice(uuid.uuid.as_slice());
let ifstr = ifstr.map(|uuid| uuid.to_hyphenated().to_string()).unwrap();
jsb.set_string("uuid", &ifstr)?;
let vstr = format!("{}.{}", uuid.version, uuid.versionminor);
jsb.set_string("version", &vstr)?;
jsb.set_uint("ack_result", uuid.result as u64)?;
// TODO? log only the interface for the right ctxid jsb.set_uint("ctxid", uuid.ctxid as u64)?;
if uuid.acked {
jsb.set_uint("ack_result", uuid.result as u64)?;
}
jsb.close()?;
}
jsb.close()?;

Loading…
Cancel
Save