Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
136 changes: 67 additions & 69 deletions rust/src/dcerpc/dcerpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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;
Expand All @@ -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
}
Expand All @@ -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;
Expand All @@ -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
}
Expand Down Expand Up @@ -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();
}
Expand Down Expand Up @@ -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]
Expand Down Expand Up @@ -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);
}
}
}

Expand Down Expand Up @@ -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]
Expand Down Expand Up @@ -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)
Expand All @@ -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)
Expand All @@ -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]
Expand Down Expand Up @@ -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)
Expand All @@ -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);
}
}
7 changes: 4 additions & 3 deletions rust/src/dcerpc/detect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,16 +63,17 @@ 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) {
SCLogDebug!("any frag not enabled");
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;
}
Expand Down
73 changes: 52 additions & 21 deletions rust/src/dcerpc/log.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,24 +20,6 @@ use crate::dcerpc::dcerpc::*;
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 {
jsb.open_array("interfaces")?;
for uuid in &bind.uuid_list {
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)?;
jsb.close()?;
}
jsb.close()?;
}
return Ok(());
}

fn log_dcerpc_header_tcp(
jsb: &mut JsonBuilder, state: &DCERPCState, tx: &DCERPCTransaction,
) -> Result<(), JsonError> {
Expand All @@ -50,9 +32,56 @@ fn log_dcerpc_header_tcp(
jsb.set_uint("frag_cnt", tx.frag_cnt_ts as u64)?;
jsb.set_uint("stub_data_size", tx.stub_data_buffer_ts.len() as u64)?;
jsb.close()?;
log_bind_interfaces(jsb, state)?;

let mut found = false;
let mark = jsb.get_mark();
jsb.open_array("interfaces")?;
for uuid in &state.interface_uuids {
if tx.ctxid == uuid.ctxid {
found = true;
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)?;
if uuid.acked {
jsb.set_uint("ack_result", uuid.result as u64)?;
}
jsb.close()?;
}
}
if !found {
jsb.restore_mark(&mark)?;
} else {
jsb.close()?;
}
}
DCERPC_TYPE_BIND => {
let mut found = false;
let mark = jsb.get_mark();
jsb.open_array("interfaces")?;
for uuid in &state.interface_uuids {
if tx.call_id == uuid.call_id {
found = true;
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)?;
if uuid.acked {
jsb.set_uint("ack_result", uuid.result as u64)?;
}
jsb.close()?;
}
}
if !found {
jsb.restore_mark(&mark)?;
} else {
jsb.close()?;
}
}
DCERPC_TYPE_BIND => log_bind_interfaces(jsb, state)?,
_ => {}
}
} else {
Expand Down Expand Up @@ -118,7 +147,9 @@ fn log_dcerpc_header_udp(
jsb.set_string("response", "UNREPLIED")?;
}
let activityuuid = Uuid::from_slice(tx.activityuuid.as_slice());
let activityuuid = activityuuid.map(|uuid| uuid.to_hyphenated().to_string()).unwrap();
let activityuuid = activityuuid
.map(|uuid| uuid.to_hyphenated().to_string())
.unwrap();
jsb.set_string("activityuuid", &activityuuid)?;
jsb.set_uint("seqnum", tx.seqnum as u64)?;
jsb.set_string("rpc_version", "4.0")?;
Expand Down
Loading