rust: Add DCERPC parser

This parser rewrites the DCE/RPC protocol implementation of Suricata
in Rust. More tests have been added to improve the coverage and some
fixes have been made to the tests already written in C. Most of the
valid tests from C have been imported to Rust.

File anatomy

src/dcerpc.rs
This file contains the implementation of single transactions in DCE/RPC
over TCP. It takes care of REQUEST, RESPONSE, BIND and BINDACK business
logic before and after the data parsing. DCERPCState holds the state
corresponding to a particular transaction and handles all important
aspects. It also defines any common structures and constants required
for DCE/RPC parsing irrespective of the carrier protocol.

src/dcerpc_udp.rs
This file contains the implementation of single transactions in DCE/RPC
over UDP. It takes care of REQUEST and RESPONSE parsing. It borrows the
Request and Response structs from src/dcerpc.rs.

src/detect.rs
This file contains the implementation of dce_iface and opnum detect
keywords. Both the parsing and the matching is taken care of by
functions in this file. Tests have been rewritten with the test data
from C.

src/parser.rs
This file contains all the nom parsers written for DCERPCRequest,
DCERPCResponse, DCERPCBind, DCERPCBindAck, DCERPCHeader, DCERPCHdrUdp.
It also implements functions to assemble and convert UUIDs. All the
fields have their endianness defined unless its an 8bit field or an
unusable one, then it's little endian but it won't make any difference.

src/mod.rs
This file contains all the modules of dcerpc folder which should be
taken into account during compilation.

Function calls

This is a State-wise implementation of the protocol for single
transaction only i.e. a valid state object is required to parse any
record. Function calls start with the app layer parser in C which
detects the application layer protocol to be DCE/RPC and calls the
appropriate functions in C which in turn make a call to these functions
in Rust using FFI. All the necessary information is passed from C to the
parsers and handlers in Rust.

Implementation

When a batch of input comes in, there is an analysis of whether the
input header and the direction is appropriate. Next check is about the
size of fragment. If it is as defined by the header, process goes
through else the data is buffered and more data is awaited. After this,
type of record as indicated by the header is checked. A call to the
appropriate handler is made. After the handling, State is updated with
the latest information about whatever record came in.
AppLayerResult::ok() is returned in case all went well else
AppLayerResult::err() is returned indicating something went wrong.
pull/4958/head
Shivani Bhardwaj 6 years ago committed by Victor Julien
parent 6db1f19d62
commit 8036202c7b

File diff suppressed because it is too large Load Diff

@ -0,0 +1,512 @@
/* Copyright (C) 2020 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
* Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* version 2 along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
use std::mem::transmute;
use crate::applayer::AppLayerResult;
use crate::core;
use crate::dcerpc::dcerpc::{
DCERPCRequest, DCERPCResponse, DCERPCUuidEntry, DCERPC_TYPE_REQUEST, DCERPC_TYPE_RESPONSE,
PFC_FIRST_FRAG,
};
use crate::dcerpc::parser;
use crate::log::*;
use std::cmp;
// Constant DCERPC UDP Header length
pub const DCERPC_UDP_HDR_LEN: i32 = 80;
#[derive(Debug)]
pub struct DCERPCHdrUdp {
pub rpc_vers: u8,
pub pkt_type: u8,
pub flags1: u8,
pub flags2: u8,
pub drep: Vec<u8>,
pub serial_hi: u8,
pub objectuuid: Vec<u8>,
pub interfaceuuid: Vec<u8>,
pub activityuuid: Vec<u8>,
pub server_boot: u32,
pub if_vers: u32,
pub seqnum: u32,
pub opnum: u16,
pub ihint: u16,
pub ahint: u16,
pub fraglen: u16,
pub fragnum: u16,
pub auth_proto: u8,
pub serial_lo: u8,
}
#[derive(Debug)]
pub struct DCERPCUDPState {
pub header: Option<DCERPCHdrUdp>,
pub request: Option<DCERPCRequest>,
pub response: Option<DCERPCResponse>,
pub fraglenleft: u16,
pub uuid_entry: Option<DCERPCUuidEntry>,
pub uuid_list: Vec<DCERPCUuidEntry>,
pub de_state: Option<*mut core::DetectEngineState>,
}
impl DCERPCUDPState {
pub fn new() -> DCERPCUDPState {
return DCERPCUDPState {
header: None,
request: None,
response: None,
fraglenleft: 0,
uuid_entry: None,
uuid_list: Vec::new(),
de_state: None,
};
}
fn new_request(&mut self) {
let request = DCERPCRequest::new();
self.request = Some(request);
}
fn new_response(&mut self) {
let response = DCERPCResponse::new();
self.response = Some(response);
}
fn create_new_query(&mut self, pkt_type: u8) {
match pkt_type {
DCERPC_TYPE_REQUEST => {
self.new_request();
}
DCERPC_TYPE_RESPONSE => {
self.new_response();
}
_ => {
SCLogDebug!("Unrecognized packet type");
}
}
}
fn get_hdr_pkt_type(&self) -> Option<u8> {
debug_validate_bug_on!(self.header.is_none());
if let Some(ref hdr) = &self.header {
return Some(hdr.pkt_type);
}
// Shouldn't happen
None
}
fn get_hdr_flags1(&self) -> Option<u8> {
debug_validate_bug_on!(self.header.is_none());
if let Some(ref hdr) = &self.header {
return Some(hdr.flags1);
}
// Shouldn't happen
None
}
pub fn get_hdr_fraglen(&self) -> Option<u16> {
debug_validate_bug_on!(self.header.is_none());
if let Some(ref hdr) = &self.header {
return Some(hdr.fraglen);
}
// Shouldn't happen
None
}
pub fn handle_fragment_data(&mut self, input: &[u8], input_len: u16) -> u16 {
let mut retval: u16 = 0;
let hdrflags1 = self.get_hdr_flags1().unwrap_or(0);
let fraglenleft = self.fraglenleft;
// Update the stub params based on the packet type
match self.get_hdr_pkt_type().unwrap_or(0) {
DCERPC_TYPE_REQUEST => {
if let Some(ref mut req) = self.request {
retval = evaluate_stub_params(
input,
input_len,
hdrflags1,
fraglenleft,
&mut req.stub_data_buffer,
&mut req.stub_data_buffer_len,
);
}
}
DCERPC_TYPE_RESPONSE => {
if let Some(ref mut resp) = self.response {
retval = evaluate_stub_params(
input,
input_len,
hdrflags1,
fraglenleft,
&mut resp.stub_data_buffer,
&mut resp.stub_data_buffer_len,
);
}
}
_ => {
SCLogDebug!("Unrecognized packet type");
return 0;
}
}
// Update the remaining fragment length
self.fraglenleft -= retval;
retval
}
pub fn process_header(&mut self, input: &[u8]) -> i32 {
match parser::parse_dcerpc_udp_header(input) {
Ok((leftover_bytes, header)) => {
if header.rpc_vers != 4 {
SCLogDebug!("DCERPC UDP Header did not validate.");
return -1;
}
let mut uuidentry = DCERPCUuidEntry::new();
let auuid = header.activityuuid.to_vec();
uuidentry.uuid = auuid;
self.uuid_list.push(uuidentry);
self.header = Some(header);
(input.len() - leftover_bytes.len()) as i32
}
Err(nom::Err::Incomplete(_)) => {
// Insufficient data.
SCLogDebug!("Insufficient data while parsing DCERPC request");
-1
}
Err(_) => {
// Error, probably malformed data.
SCLogDebug!("An error occurred while parsing DCERPC request");
-1
}
}
}
pub fn handle_input_data(&mut self, input: &[u8]) -> AppLayerResult {
// Input length should at least be header length
if (input.len() as i32) < DCERPC_UDP_HDR_LEN {
return AppLayerResult::err();
}
// Call header parser first
let mut parsed = self.process_header(input);
if parsed == -1 {
return AppLayerResult::err();
}
let mut input_left = input.len() as i32 - parsed;
let pkt_type = self.get_hdr_pkt_type().unwrap_or(0);
let fraglen = self.get_hdr_fraglen().unwrap_or(0);
self.fraglenleft = fraglen;
self.create_new_query(pkt_type);
// Parse rest of the body
while parsed >= DCERPC_UDP_HDR_LEN && parsed < fraglen as i32 && input_left > 0 {
let retval = self.handle_fragment_data(&input[parsed as usize..], input_left as u16);
if retval > 0 && retval <= input_left as u16 {
parsed += retval as i32;
input_left -= retval as i32;
} else if input_left > 0 {
SCLogDebug!("Error parsing DCERPC UDP Fragment Data");
parsed -= input_left;
input_left = 0;
}
}
return AppLayerResult::ok();
}
}
fn evaluate_stub_params(
input: &[u8],
input_len: u16,
hdrflags: u8,
lenleft: u16,
stub_data_buffer: &mut Vec<u8>,
stub_data_buffer_len: &mut u16,
) -> u16 {
let stub_len: u16;
stub_len = cmp::min(lenleft, input_len);
if stub_len == 0 {
return 0;
}
// If the UDP frag is the the first frag irrespective of it being a part of
// a multi frag PDU or not, it indicates the previous PDU's stub would
// have been buffered and processed and we can use the buffer to hold
// frags from a fresh request/response
if hdrflags & PFC_FIRST_FRAG > 0 {
*stub_data_buffer_len = 0;
}
let input_slice = &input[..stub_len as usize];
stub_data_buffer.extend_from_slice(&input_slice);
*stub_data_buffer_len += stub_len;
stub_len
}
#[no_mangle]
pub extern "C" fn rs_dcerpc_udp_parse(
_flow: *mut core::Flow,
state: &mut DCERPCUDPState,
_pstate: *mut std::os::raw::c_void,
input: *const u8,
input_len: u32,
_data: *mut std::os::raw::c_void,
_flags: u8,
) -> AppLayerResult {
if input_len > 0 && input != std::ptr::null_mut() {
let buf = build_slice!(input, input_len as usize);
return state.handle_input_data(buf);
}
AppLayerResult::err()
}
#[no_mangle]
pub extern "C" fn rs_dcerpc_udp_state_free(state: *mut std::os::raw::c_void) {
let _drop: Box<DCERPCUDPState> = unsafe { transmute(state) };
}
#[no_mangle]
pub unsafe extern "C" fn rs_dcerpc_udp_state_new() -> *mut std::os::raw::c_void {
let state = DCERPCUDPState::new();
let boxed = Box::new(state);
transmute(boxed)
}
#[no_mangle]
pub extern "C" fn rs_dcerpc_udp_state_transaction_free(
_state: *mut std::os::raw::c_void,
_tx_id: u64,
) {
// do nothing
}
#[no_mangle]
pub extern "C" fn rs_dcerpc_udp_get_tx_detect_state(
vtx: *mut std::os::raw::c_void,
) -> *mut core::DetectEngineState {
let dce_state = cast_pointer!(vtx, DCERPCUDPState);
match dce_state.de_state {
Some(ds) => ds,
None => std::ptr::null_mut(),
}
}
#[no_mangle]
pub extern "C" fn rs_dcerpc_udp_set_tx_detect_state(
vtx: *mut std::os::raw::c_void,
de_state: *mut core::DetectEngineState,
) -> u8 {
let dce_state = cast_pointer!(vtx, DCERPCUDPState);
dce_state.de_state = Some(de_state);
0
}
#[no_mangle]
pub extern "C" fn rs_dcerpc_udp_get_tx(
state: *mut std::os::raw::c_void,
_tx_id: u64,
) -> *mut DCERPCUDPState {
let dce_state = cast_pointer!(state, DCERPCUDPState);
dce_state
}
#[no_mangle]
pub extern "C" fn rs_dcerpc_udp_get_tx_cnt(_state: *mut std::os::raw::c_void) -> u8 {
1
}
#[no_mangle]
pub extern "C" fn rs_dcerpc_udp_get_alstate_progress(
_tx: *mut std::os::raw::c_void,
_direction: u8,
) -> u8 {
0
}
#[no_mangle]
pub extern "C" fn rs_dcerpc_udp_get_alstate_progress_completion_status(_direction: u8) -> u8 {
1
}
#[cfg(test)]
mod tests {
use crate::applayer::AppLayerResult;
use crate::dcerpc::dcerpc_udp::DCERPCUDPState;
#[test]
fn test_process_header_udp_incomplete_hdr() {
let request: &[u8] = &[
0x04, 0x00, 0x08, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb8, 0x4a, 0x9f, 0x4d,
0x1c, 0x7d, 0xcf, 0x11,
];
let mut dcerpcudp_state = DCERPCUDPState::new();
assert_eq!(-1, dcerpcudp_state.process_header(request));
}
#[test]
fn test_process_header_udp_perfect_hdr() {
let request: &[u8] = &[
0x04, 0x00, 0x08, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb8, 0x4a, 0x9f, 0x4d,
0x1c, 0x7d, 0xcf, 0x11, 0x86, 0x1e, 0x00, 0x20, 0xaf, 0x6e, 0x7c, 0x57, 0x86, 0xc2,
0x37, 0x67, 0xf7, 0x1e, 0xd1, 0x11, 0xbc, 0xd9, 0x00, 0x60, 0x97, 0x92, 0xd2, 0x6c,
0x79, 0xbe, 0x01, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff, 0x68, 0x00, 0x00, 0x00, 0x0a, 0x00,
];
let mut dcerpcudp_state = DCERPCUDPState::new();
assert_eq!(80, dcerpcudp_state.process_header(request));
}
#[test]
fn test_handle_fragment_data_udp_no_body() {
let request: &[u8] = &[
0x04, 0x00, 0x08, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb8, 0x4a, 0x9f, 0x4d,
0x1c, 0x7d, 0xcf, 0x11, 0x86, 0x1e, 0x00, 0x20, 0xaf, 0x6e, 0x7c, 0x57, 0x86, 0xc2,
0x37, 0x67, 0xf7, 0x1e, 0xd1, 0x11, 0xbc, 0xd9, 0x00, 0x60, 0x97, 0x92, 0xd2, 0x6c,
0x79, 0xbe, 0x01, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff, 0x68, 0x00, 0x00, 0x00, 0x0a, 0x00,
];
let mut dcerpcudp_state = DCERPCUDPState::new();
assert_eq!(
0,
dcerpcudp_state.handle_fragment_data(request, request.len() as u16)
);
}
#[test]
fn test_handle_input_data_udp_full_body() {
let request: &[u8] = &[
0x04, 0x00, 0x2c, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x3f, 0x98,
0xf0, 0x5c, 0xd9, 0x63, 0xcc, 0x46, 0xc2, 0x74, 0x51, 0x6c, 0x8a, 0x53, 0x7d, 0x6f,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00,
0xff, 0xff, 0xff, 0xff, 0x70, 0x05, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x24, 0x58, 0xfd, 0xcc, 0x45,
0x64, 0x49, 0xb0, 0x70, 0xdd, 0xae, 0x74, 0x2c, 0x96, 0xd2, 0x60, 0x5e, 0x0d, 0x00,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x5e, 0x0d, 0x00, 0x02, 0x00,
0x00, 0x00, 0x7c, 0x5e, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
0x80, 0x96, 0xf1, 0xf1, 0x2a, 0x4d, 0xce, 0x11, 0xa6, 0x6a, 0x00, 0x20, 0xaf, 0x6e,
0x72, 0xf4, 0x0c, 0x00, 0x00, 0x00, 0x4d, 0x41, 0x52, 0x42, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x0d, 0xf0, 0xad, 0xba, 0x00, 0x00, 0x00, 0x00, 0xa8, 0xf4,
0x0b, 0x00, 0x10, 0x09, 0x00, 0x00, 0x10, 0x09, 0x00, 0x00, 0x4d, 0x45, 0x4f, 0x57,
0x04, 0x00, 0x00, 0x00, 0xa2, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x38, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x08,
0x00, 0x00, 0xd8, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x08, 0x00,
0xcc, 0xcc, 0xcc, 0xcc, 0xc8, 0x00, 0x00, 0x00, 0x4d, 0x45, 0x4f, 0x57, 0xd8, 0x08,
0x00, 0x00, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc4, 0x28, 0xcd, 0x00, 0x64, 0x29, 0xcd, 0x00,
0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0xb9, 0x01, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0xab, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0xa5, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46,
0xa6, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x46, 0xa4, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x46, 0xad, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0xaa, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x07, 0x00, 0x00, 0x00, 0x60, 0x00,
0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
0x20, 0x00, 0x00, 0x00, 0x28, 0x06, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x01, 0x00,
0x00, 0x00, 0x01, 0x10, 0x08, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0x50, 0x00, 0x00, 0x00,
0x4f, 0xb6, 0x88, 0x20, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x10, 0x08, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0x48, 0x00, 0x00, 0x00, 0x07, 0x00,
0x66, 0x00, 0x06, 0x09, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x46, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x19, 0x0c, 0x00,
0x58, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x70, 0xd8,
0x98, 0x93, 0x98, 0x4f, 0xd2, 0x11, 0xa9, 0x3d, 0xbe, 0x57, 0xb2, 0x00, 0x00, 0x00,
0x32, 0x00, 0x31, 0x00, 0x01, 0x10, 0x08, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0x80, 0x00,
0x00, 0x00, 0x0d, 0xf0, 0xad, 0xba, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x43, 0x14, 0x00, 0x00, 0x00,
0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x4d, 0x45, 0x4f, 0x57,
0x04, 0x00, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x3b, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00,
0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x81, 0xc5, 0x17, 0x03, 0x80, 0x0e, 0xe9, 0x4a,
0x99, 0x99, 0xf1, 0x8a, 0x50, 0x6f, 0x7a, 0x85, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x10, 0x08, 0x00, 0xcc, 0xcc,
0xcc, 0xcc, 0x30, 0x00, 0x00, 0x00, 0x78, 0x00, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00,
0xd8, 0xda, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x2f,
0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x46, 0x00, 0x58, 0x00, 0x00, 0x00,
0x00, 0x00, 0x01, 0x10, 0x08, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0x10, 0x00, 0x00, 0x00,
0x30, 0x00, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x08, 0x00, 0xcc, 0xcc, 0xcc, 0xcc,
0x68, 0x00, 0x00, 0x00, 0x0e, 0x00, 0xff, 0xff, 0x68, 0x8b, 0x0b, 0x00, 0x02, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x02, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xfe, 0x02, 0x00, 0x00, 0x5c, 0x00, 0x5c, 0x00, 0x31, 0x00,
0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00,
0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00,
0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x9d, 0x13, 0x00, 0x01, 0xcc, 0xe0, 0xfd, 0x7f,
0xcc, 0xe0, 0xfd, 0x7f, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
0x90, 0x90,
];
let mut dcerpcudp_state = DCERPCUDPState::new();
assert_eq!(
AppLayerResult::ok(),
dcerpcudp_state.handle_input_data(request)
);
assert_eq!(0, dcerpcudp_state.fraglenleft);
if let Some(req) = dcerpcudp_state.request {
assert_eq!(1392, req.stub_data_buffer_len);
}
}
}

@ -0,0 +1,504 @@
/* Copyright (C) 2020 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
* Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* version 2 along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
use super::dcerpc::{
DCERPCState, DCERPC_TYPE_REQUEST, DCERPC_TYPE_RESPONSE, DCERPC_UUID_ENTRY_FLAG_FF,
};
use crate::log::*;
use std::ffi::CStr;
use std::os::raw::{c_char, c_void};
use uuid::Uuid;
pub const DETECT_DCE_IFACE_OP_NONE: u8 = 0;
pub const DETECT_DCE_IFACE_OP_LT: u8 = 1;
pub const DETECT_DCE_IFACE_OP_GT: u8 = 2;
pub const DETECT_DCE_IFACE_OP_EQ: u8 = 3;
pub const DETECT_DCE_IFACE_OP_NE: u8 = 4;
pub const DETECT_DCE_OPNUM_RANGE_UNINITIALIZED: u32 = 100000;
#[derive(Debug)]
pub struct DCEIfaceData {
pub if_uuid: Vec<u8>,
pub op: u8,
pub version: u16,
pub any_frag: u8,
}
#[derive(Debug)]
pub struct DCEOpnumRange {
pub range1: u32,
pub range2: u32,
}
impl DCEOpnumRange {
pub fn new() -> DCEOpnumRange {
return DCEOpnumRange {
range1: DETECT_DCE_OPNUM_RANGE_UNINITIALIZED,
range2: DETECT_DCE_OPNUM_RANGE_UNINITIALIZED,
};
}
}
#[derive(Debug)]
pub struct DCEOpnumData {
pub data: Vec<DCEOpnumRange>,
}
fn extract_op_version(opver: &str) -> Result<(u8, u16), ()> {
let (op, version) = opver.split_at(1);
let opval: u8 = match op {
">" => DETECT_DCE_IFACE_OP_GT,
"<" => DETECT_DCE_IFACE_OP_LT,
"=" => DETECT_DCE_IFACE_OP_EQ,
"!" => DETECT_DCE_IFACE_OP_NE,
_ => DETECT_DCE_IFACE_OP_NONE,
};
let version: u16 = match version.parse::<u16>() {
Ok(res) => res,
_ => {
return Err(());
}
};
if opval == DETECT_DCE_IFACE_OP_NONE
|| (opval == DETECT_DCE_IFACE_OP_LT && version == std::u16::MIN)
|| (opval == DETECT_DCE_IFACE_OP_GT && version == std::u16::MAX)
{
return Err(());
}
Ok((opval, version))
}
fn match_iface_version(version: u16, if_data: &DCEIfaceData) -> bool {
match if_data.op {
DETECT_DCE_IFACE_OP_LT => version < if_data.version,
DETECT_DCE_IFACE_OP_GT => version > if_data.version,
DETECT_DCE_IFACE_OP_EQ => version == if_data.version,
DETECT_DCE_IFACE_OP_NE => version != if_data.version,
_ => {
return true;
}
}
}
fn match_backuuid(state: &mut DCERPCState, if_data: &mut DCEIfaceData) -> u8 {
let mut ret = 1;
if let Some(ref bindack) = state.bindack {
for uuidentry in bindack.accepted_uuid_list.iter() {
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 {
SCLogDebug!("Skipping to next UUID");
continue;
}
for i in 0..16 {
if if_data.if_uuid[i] != uuidentry.uuid[i] {
SCLogDebug!("Iface UUID and BINDACK Accepted UUID does not match");
ret = 0;
break;
}
}
let ctxid = state.get_req_ctxid().unwrap_or(0);
ret = ret & ((uuidentry.ctxid == ctxid) as u8);
if ret == 0 {
SCLogDebug!("CTX IDs/UUIDs do not match");
continue;
}
if if_data.op != DETECT_DCE_IFACE_OP_NONE
&& !match_iface_version(uuidentry.version, if_data)
{
SCLogDebug!("Interface version did not match");
ret &= 0;
}
if ret == 1 {
return 1;
}
}
}
return ret;
}
fn parse_iface_data(arg: &str) -> Result<DCEIfaceData, ()> {
let split_args: Vec<&str> = arg.split(',').collect();
let mut op_version = (0, 0);
let mut any_frag: u8 = 0;
let if_uuid = match Uuid::parse_str(split_args[0]) {
Ok(res) => res.as_bytes().to_vec(),
_ => {
return Err(());
}
};
match split_args.len() {
1 => {}
2 => match split_args[1] {
"any_frag" => {
any_frag = 1;
}
_ => {
op_version = match extract_op_version(split_args[1]) {
Ok((op, ver)) => (op, ver),
_ => {
return Err(());
}
};
}
},
3 => {
op_version = match extract_op_version(split_args[1]) {
Ok((op, ver)) => (op, ver),
_ => {
return Err(());
}
};
if split_args[2] != "any_frag" {
return Err(());
}
any_frag = 1;
}
_ => {
return Err(());
}
}
Ok(DCEIfaceData {
if_uuid: if_uuid,
op: op_version.0,
version: op_version.1,
any_frag: any_frag,
})
}
fn convert_str_to_u32(arg: &str) -> Result<u32, ()> {
match arg.parse::<u32>() {
Ok(res) => Ok(res),
_ => Err(()),
}
}
fn parse_opnum_data(arg: &str) -> Result<DCEOpnumData, ()> {
let split_args: Vec<&str> = arg.split(',').collect();
let mut dce_opnum_data: Vec<DCEOpnumRange> = Vec::new();
for range in split_args.iter() {
let mut opnum_range = DCEOpnumRange::new();
let split_range: Vec<&str> = range.split('-').collect();
let split_len = split_range.len();
if (split_len > 0 && convert_str_to_u32(split_range[0]).is_err())
|| (split_len > 1 && convert_str_to_u32(split_range[1]).is_err())
{
return Err(());
}
match split_len {
1 => {
opnum_range.range1 = convert_str_to_u32(split_range[0]).unwrap();
}
2 => {
let range1 = convert_str_to_u32(split_range[0]).unwrap();
let range2 = convert_str_to_u32(split_range[1]).unwrap();
if range2 < range1 {
return Err(());
}
opnum_range.range1 = range1;
opnum_range.range2 = range2;
}
_ => {
return Err(());
}
}
dce_opnum_data.push(opnum_range);
}
Ok(DCEOpnumData {
data: dce_opnum_data,
})
}
#[no_mangle]
pub extern "C" fn rs_dcerpc_iface_match(state: &mut DCERPCState, if_data: &mut DCEIfaceData) -> u8 {
let first_req_seen = state.get_first_req_seen().unwrap_or(0);
if first_req_seen == 0 {
return 0;
}
match state.get_hdr_type() {
Some(x) => match x {
DCERPC_TYPE_REQUEST | DCERPC_TYPE_RESPONSE => {
}
_ => {
return 0;
}
},
None => {
return 0;
}
};
return match_backuuid(state, if_data);
}
#[no_mangle]
pub unsafe extern "C" fn rs_dcerpc_iface_parse(carg: *const c_char) -> *mut c_void {
if carg.is_null() {
return std::ptr::null_mut();
}
let arg = match CStr::from_ptr(carg).to_str() {
Ok(arg) => arg,
_ => {
return std::ptr::null_mut();
}
};
match parse_iface_data(&arg) {
Ok(detect) => std::mem::transmute(Box::new(detect)),
Err(_) => std::ptr::null_mut(),
}
}
#[no_mangle]
pub unsafe extern "C" fn rs_dcerpc_iface_free(ptr: *mut c_void) {
if ptr != std::ptr::null_mut() {
let _: Box<DCEIfaceData> = std::mem::transmute(ptr);
}
}
#[no_mangle]
pub unsafe extern "C" fn rs_dcerpc_opnum_match(
state: &mut DCERPCState,
opnum_data: &mut DCEOpnumData,
) -> u8 {
let opnum = state.get_req_opnum().unwrap_or(0); // TODO is the default to 0 OK?
for range in opnum_data.data.iter() {
if range.range2 == DETECT_DCE_OPNUM_RANGE_UNINITIALIZED {
if range.range1 == opnum as u32 {
return 1;
}
} else if range.range1 <= opnum as u32 && range.range2 >= opnum as u32 {
return 1;
}
}
0
}
#[no_mangle]
pub unsafe extern "C" fn rs_dcerpc_opnum_parse(carg: *const c_char) -> *mut c_void {
if carg.is_null() {
return std::ptr::null_mut();
}
let arg = match CStr::from_ptr(carg).to_str() {
Ok(arg) => arg,
_ => {
return std::ptr::null_mut();
}
};
match parse_opnum_data(&arg) {
Ok(detect) => std::mem::transmute(Box::new(detect)),
Err(_) => std::ptr::null_mut(),
}
}
#[no_mangle]
pub unsafe extern "C" fn rs_dcerpc_opnum_free(ptr: *mut c_void) {
if ptr != std::ptr::null_mut() {
let _: Box<DCEOpnumData> = std::mem::transmute(ptr);
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_extract_op_version() {
let op_version = "<1";
assert_eq!(
Ok((DETECT_DCE_IFACE_OP_LT, 1)),
extract_op_version(op_version)
);
let op_version = ">10";
assert_eq!(
Ok((DETECT_DCE_IFACE_OP_GT, 10)),
extract_op_version(op_version)
);
let op_version = "=45";
assert_eq!(
Ok((DETECT_DCE_IFACE_OP_EQ, 45)),
extract_op_version(op_version)
);
let op_version = "!0";
assert_eq!(
Ok((DETECT_DCE_IFACE_OP_NE, 0)),
extract_op_version(op_version)
);
let op_version = "@1";
assert_eq!(true, extract_op_version(op_version).is_err());
}
#[test]
fn test_match_iface_version() {
let iface_data = DCEIfaceData {
if_uuid: Vec::new(),
op: 3,
version: 10,
any_frag: 0,
};
let version = 10;
assert_eq!(true, match_iface_version(version, &iface_data));
let version = 2;
assert_eq!(false, match_iface_version(version, &iface_data));
}
#[test]
fn test_parse_iface_data() {
let arg = "12345678-1234-1234-1234-123456789ABC";
let iface_data = parse_iface_data(arg).unwrap();
let expected_uuid = Ok(String::from("12345678-1234-1234-1234-123456789ABC").to_lowercase());
let uuid = Uuid::from_slice(iface_data.if_uuid.as_slice());
let uuid = uuid.map(|uuid| uuid.to_hyphenated().to_string());
assert_eq!(expected_uuid, uuid);
let arg = "12345678-1234-1234-1234-123456789ABC,>1";
let iface_data = parse_iface_data(arg).unwrap();
let expected_uuid = Ok(String::from("12345678-1234-1234-1234-123456789ABC").to_lowercase());
let uuid = Uuid::from_slice(iface_data.if_uuid.as_slice());
let uuid = uuid.map(|uuid| uuid.to_hyphenated().to_string());
assert_eq!(expected_uuid, uuid);
assert_eq!(DETECT_DCE_IFACE_OP_GT, iface_data.op);
assert_eq!(1, iface_data.version);
let arg = "12345678-1234-1234-1234-123456789ABC,any_frag";
let iface_data = parse_iface_data(arg).unwrap();
let expected_uuid = Ok(String::from("12345678-1234-1234-1234-123456789ABC").to_lowercase());
let uuid = Uuid::from_slice(iface_data.if_uuid.as_slice());
let uuid = uuid.map(|uuid| uuid.to_hyphenated().to_string());
assert_eq!(expected_uuid, uuid);
assert_eq!(DETECT_DCE_IFACE_OP_NONE, iface_data.op);
assert_eq!(1, iface_data.any_frag);
assert_eq!(0, iface_data.version);
let arg = "12345678-1234-1234-1234-123456789ABC,!10,any_frag";
let iface_data = parse_iface_data(arg).unwrap();
let expected_uuid = Ok(String::from("12345678-1234-1234-1234-123456789ABC").to_lowercase());
let uuid = Uuid::from_slice(iface_data.if_uuid.as_slice());
let uuid = uuid.map(|uuid| uuid.to_hyphenated().to_string());
assert_eq!(expected_uuid, uuid);
assert_eq!(DETECT_DCE_IFACE_OP_NE, iface_data.op);
assert_eq!(1, iface_data.any_frag);
assert_eq!(10, iface_data.version);
let arg = "12345678-1234-1234-1234-123456789ABC,>1,ay_frag";
let iface_data = parse_iface_data(arg);
assert_eq!(iface_data.is_err(), true);
let arg = "12345678-1234-1234-1234-12345679ABC,>1,any_frag";
let iface_data = parse_iface_data(arg);
assert_eq!(iface_data.is_err(), true);
let arg = "12345678-1234-1234-134-123456789ABC,>1,any_frag";
let iface_data = parse_iface_data(arg);
assert_eq!(iface_data.is_err(), true);
let arg = "12345678-123-124-1234-123456789ABC,>1,any_frag";
let iface_data = parse_iface_data(arg);
assert_eq!(iface_data.is_err(), true);
let arg = "1234568-1234-1234-1234-123456789ABC,>1,any_frag";
let iface_data = parse_iface_data(arg);
assert_eq!(iface_data.is_err(), true);
let arg = "12345678-1234-1234-1234-123456789ABC,>65536,any_frag";
let iface_data = parse_iface_data(arg);
assert_eq!(iface_data.is_err(), true);
let arg = "12345678-1234-1234-1234-123456789ABC,>=1,any_frag";
let iface_data = parse_iface_data(arg);
assert_eq!(iface_data.is_err(), true);
let arg = "12345678-1234-1234-1234-123456789ABC,<0,any_frag";
let iface_data = parse_iface_data(arg);
assert_eq!(iface_data.is_err(), true);
let arg = "12345678-1234-1234-1234-123456789ABC,>65535,any_frag";
let iface_data = parse_iface_data(arg);
assert_eq!(iface_data.is_err(), true);
}
#[test]
fn test_parse_opnum_data() {
let arg = "12";
let opnum_data = parse_opnum_data(arg).unwrap();
assert_eq!(1, opnum_data.data.len());
assert_eq!(12, opnum_data.data[0].range1);
assert_eq!(
DETECT_DCE_OPNUM_RANGE_UNINITIALIZED,
opnum_data.data[0].range2
);
let arg = "12,24";
let opnum_data = parse_opnum_data(arg).unwrap();
assert_eq!(2, opnum_data.data.len());
assert_eq!(12, opnum_data.data[0].range1);
assert_eq!(24, opnum_data.data[1].range1);
let arg = "12,12-24";
let opnum_data = parse_opnum_data(arg).unwrap();
assert_eq!(2, opnum_data.data.len());
assert_eq!(12, opnum_data.data[0].range1);
assert_eq!(12, opnum_data.data[1].range1);
assert_eq!(24, opnum_data.data[1].range2);
let arg = "12-14,12,121,62-78";
let opnum_data = parse_opnum_data(arg).unwrap();
assert_eq!(4, opnum_data.data.len());
assert_eq!(12, opnum_data.data[0].range1);
assert_eq!(14, opnum_data.data[0].range2);
assert_eq!(121, opnum_data.data[2].range1);
assert_eq!(78, opnum_data.data[3].range2);
let arg = "12,26,62,61,6513-6666";
let opnum_data = parse_opnum_data(arg).unwrap();
assert_eq!(5, opnum_data.data.len());
assert_eq!(61, opnum_data.data[3].range1);
assert_eq!(6513, opnum_data.data[4].range1);
let arg = "12,26,62,61,6513--";
let opnum_data = parse_opnum_data(arg);
assert_eq!(true, opnum_data.is_err());
let arg = "12-14,12,121,62-8";
let opnum_data = parse_opnum_data(arg);
assert_eq!(true, opnum_data.is_err());
}
}

@ -0,0 +1,21 @@
/* Copyright (C) 2020 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
* Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* version 2 along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
pub mod dcerpc;
pub mod dcerpc_udp;
pub mod parser;
pub mod detect;

@ -0,0 +1,371 @@
/* Copyright (C) 2020 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
* Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* version 2 along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
use crate::dcerpc::dcerpc::{
BindCtxItem, DCERPCBind, DCERPCBindAck, DCERPCBindAckResult, DCERPCHdr, DCERPCRequest, Uuid,
};
use crate::dcerpc::dcerpc_udp::DCERPCHdrUdp;
use crate::log::*;
use nom::number::complete::{le_u16, le_u32, le_u8};
use nom::number::Endianness;
fn uuid_to_vec(uuid: Uuid) -> Vec<u8> {
let mut uuidtmp = uuid;
let mut vect: Vec<u8> = Vec::new();
vect.append(&mut uuidtmp.time_low);
vect.append(&mut uuidtmp.time_mid);
vect.append(&mut uuidtmp.time_hi_and_version);
vect.push(uuidtmp.clock_seq_hi_and_reserved);
vect.push(uuidtmp.clock_seq_low);
vect.append(&mut uuidtmp.node);
vect
}
fn assemble_uuid(uuid: Uuid) -> Vec<u8> {
let mut uuidtmp = uuid;
let mut vect: Vec<u8> = Vec::new();
uuidtmp.time_low.reverse();
uuidtmp.time_mid.reverse();
uuidtmp.time_hi_and_version.reverse();
vect.append(&mut uuidtmp.time_low);
vect.append(&mut uuidtmp.time_mid);
vect.append(&mut uuidtmp.time_hi_and_version);
vect.push(uuidtmp.clock_seq_hi_and_reserved);
vect.push(uuidtmp.clock_seq_low);
vect.append(&mut uuidtmp.node);
vect
}
named!(pub parse_uuid<Uuid>,
do_parse!(
time_low: take!(4)
>> time_mid: take!(2)
>> time_hi_and_version: take!(2)
>> clock_seq_hi_and_reserved: le_u8
>> clock_seq_low: le_u8
>> node: take!(6)
>> (
Uuid {
time_low: time_low.to_vec(),
time_mid: time_mid.to_vec(),
time_hi_and_version: time_hi_and_version.to_vec(),
clock_seq_hi_and_reserved: clock_seq_hi_and_reserved,
clock_seq_low: clock_seq_low,
node: node.to_vec(),
}
)
)
);
named!(pub parse_dcerpc_udp_header<DCERPCHdrUdp>,
do_parse!(
rpc_vers: le_u8
>> pkt_type: le_u8
>> flags1: le_u8
>> flags2: le_u8
>> drep: take!(3)
>> endianness: value!(if drep[0] == 0 { Endianness::Big } else { Endianness::Little })
>> serial_hi: le_u8
>> objectuuid: take!(16)
>> interfaceuuid: take!(16)
>> activityuuid: take!(16)
>> server_boot: u32!(endianness)
>> if_vers: u32!(endianness)
>> seqnum: u32!(endianness)
>> opnum: u16!(endianness)
>> ihint: u16!(endianness)
>> ahint: u16!(endianness)
>> fraglen: u16!(endianness)
>> fragnum: u16!(endianness)
>> auth_proto: le_u8
>> serial_lo: le_u8
>> (
DCERPCHdrUdp {
rpc_vers: rpc_vers,
pkt_type: pkt_type,
flags1: flags1,
flags2: flags2,
drep: drep.to_vec(),
serial_hi: serial_hi,
objectuuid: match parse_uuid(objectuuid) {
Ok((_, vect)) => assemble_uuid(vect),
Err(e) => {
SCLogDebug!("{}", e);
vec![0]
},
},
interfaceuuid: match parse_uuid(interfaceuuid) {
Ok((_, vect)) => assemble_uuid(vect),
Err(e) => {
SCLogDebug!("{}", e);
vec![0]
},
},
activityuuid: match parse_uuid(activityuuid){
Ok((_, vect)) => assemble_uuid(vect),
Err(e) => {
SCLogDebug!("{}", e);
vec![0]
},
},
server_boot: server_boot,
if_vers: if_vers,
seqnum: seqnum,
opnum: opnum,
ihint: ihint,
ahint: ahint,
fraglen: fraglen,
fragnum: fragnum,
auth_proto: auth_proto,
serial_lo: serial_lo,
}
)
)
);
named!(pub parse_dcerpc_bindack_result<DCERPCBindAckResult>,
do_parse!(
ack_result: le_u16
>> ack_reason: le_u16
>> transfer_syntax: take!(16)
>> syntax_version: le_u32
>> (
DCERPCBindAckResult {
ack_result:ack_result,
ack_reason:ack_reason,
transfer_syntax:transfer_syntax.to_vec(),
syntax_version:syntax_version,
}
)
)
);
named!(pub parse_dcerpc_bindack<DCERPCBindAck>,
do_parse!(
_max_xmit_frag: le_u16
>> _max_recv_frag: le_u16
>> _assoc_group: take!(4)
>> sec_addr_len: le_u16
>> take!(sec_addr_len)
>> cond!((sec_addr_len + 2) % 4 != 0, take!(4 - (sec_addr_len + 2) % 4))
>> numctxitems: le_u8
>> take!(3) // Padding
>> ctxitems: count!(parse_dcerpc_bindack_result, numctxitems as usize)
>> (
DCERPCBindAck {
accepted_uuid_list: Vec::new(),
sec_addr_len: sec_addr_len,
numctxitems: numctxitems,
ctxitems: ctxitems,
}
)
)
);
named_args!(pub parse_bindctx_item(endianness: Endianness) <BindCtxItem>,
do_parse!(
ctxid: u16!(endianness)
>> _num_trans_items: le_u8
>> take!(1) // Reservid bit
>> uuid: take!(16)
>> version: u16!(endianness)
>> versionminor: u16!(endianness)
>> take!(20)
>> (
BindCtxItem {
ctxid: ctxid,
// UUID parsing for TCP seems to change as per endianness
uuid: match parse_uuid(uuid) {
Ok((_, vect)) => match endianness {
Endianness::Little => assemble_uuid(vect),
_ => uuid_to_vec(vect),
},
// Shouldn't happen
Err(_e) => {vec![0]},
},
version: version,
versionminor: versionminor,
}
)
)
);
named!(pub parse_dcerpc_bind<DCERPCBind>,
do_parse!(
_max_xmit_frag: le_u16
>> _max_recv_frag: le_u16
>> _assoc_group_id: le_u32
>> numctxitems: le_u8
>> take!(3)
>> (
DCERPCBind {
numctxitems: numctxitems,
uuid_list: Vec::new(),
}
)
)
);
named!(pub parse_dcerpc_header<DCERPCHdr>,
do_parse!(
rpc_vers: le_u8
>> rpc_vers_minor: le_u8
>> hdrtype: le_u8
>> pfc_flags: le_u8
>> packed_drep: take!(4)
>> endianness: value!(if packed_drep[0] & 0x10 == 0 { Endianness::Big } else { Endianness::Little })
>> frag_length: u16!(endianness)
>> auth_length: u16!(endianness)
>> call_id: u32!(endianness)
>> (
DCERPCHdr {
rpc_vers: rpc_vers,
rpc_vers_minor: rpc_vers_minor,
hdrtype: hdrtype,
pfc_flags: pfc_flags,
packed_drep: packed_drep.to_vec(),
frag_length: frag_length,
auth_length: auth_length,
call_id: call_id,
}
)
)
);
named_args!(pub parse_dcerpc_request(endianness: Endianness) <DCERPCRequest>,
do_parse!(
_pad: take!(4)
>> ctxid: u16!(endianness)
>> opnum: u16!(endianness)
>> (
DCERPCRequest {
ctxid: ctxid,
opnum: opnum,
first_request_seen: 1,
stub_data_buffer: Vec::new(),
stub_data_buffer_len: 0,
stub_data_buffer_reset: false,
cmd: 0,
}
)
)
);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_uuid() {
let uuid: &[u8] = &[
0xb8, 0x4a, 0x9f, 0x4d, 0x1c, 0x7d, 0xcf, 0x11, 0x86, 0x1e, 0x00, 0x20, 0xaf, 0x6e,
0x7c, 0x57,
];
let expected_uuid = Uuid {
time_low: vec![0xb8, 0x4a, 0x9f, 0x4d],
time_mid: vec![0x1c, 0x7d],
time_hi_and_version: vec![0xcf, 0x11],
clock_seq_hi_and_reserved: 0x86,
clock_seq_low: 0x1e,
node: vec![0x00, 0x20, 0xaf, 0x6e, 0x7c, 0x57],
};
let (_remainder, parsed_uuid) = parse_uuid(uuid).unwrap();
assert_eq!(expected_uuid, parsed_uuid);
}
#[test]
fn test_assemble_uuid() {
let uuid = Uuid {
time_low: vec![0xb8, 0x4a, 0x9f, 0x4d],
time_mid: vec![0x1c, 0x7d],
time_hi_and_version: vec![0xcf, 0x11],
clock_seq_hi_and_reserved: 0x86,
clock_seq_low: 0x1e,
node: vec![0x00, 0x20, 0xaf, 0x6e, 0x7c, 0x57],
};
let expected_val = vec![
0x4d, 0x9f, 0x4a, 0xb8, 0x7d, 0x1c, 0x11, 0xcf, 0x86, 0x1e, 0x00, 0x20, 0xaf, 0x6e,
0x7c, 0x57,
];
assert_eq!(expected_val, assemble_uuid(uuid));
}
#[test]
fn test_parse_dcerpc_udp_header() {
let dcerpcheader: &[u8] = &[
0x04, 0x00, 0x08, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb8, 0x4a, 0x9f, 0x4d,
0x1c, 0x7d, 0xcf, 0x11, 0x86, 0x1e, 0x00, 0x20, 0xaf, 0x6e, 0x7c, 0x57, 0x86, 0xc2,
0x37, 0x67, 0xf7, 0x1e, 0xd1, 0x11, 0xbc, 0xd9, 0x00, 0x60, 0x97, 0x92, 0xd2, 0x6c,
0x79, 0xbe, 0x01, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff, 0x68, 0x00, 0x00, 0x00, 0x0a, 0x00,
];
let (_remainder, header) = parse_dcerpc_udp_header(dcerpcheader).unwrap();
let expected_activityuuid = vec![
0x67, 0x37, 0xc2, 0x86, 0x1e, 0xf7, 0x11, 0xd1, 0xbc, 0xd9, 0x00, 0x60, 0x97, 0x92,
0xd2, 0x6c,
];
assert_eq!(0x04, header.rpc_vers);
assert_eq!(0x00, header.pkt_type);
assert_eq!(0x08, header.flags1);
assert_eq!(0x00, header.flags2);
assert_eq!(vec!(0x10, 0x00, 0x00), header.drep);
assert_eq!(0x00, header.serial_hi);
assert_eq!(expected_activityuuid, header.activityuuid);
assert_eq!(0x3401be79, header.server_boot);
assert_eq!(0x00000000, header.seqnum);
assert_eq!(0xffff, header.ihint);
assert_eq!(0x0068, header.fraglen);
assert_eq!(0x0a, header.auth_proto);
}
#[test]
fn test_parse_dcerpc_header() {
let dcerpcheader: &[u8] = &[
0x05, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00,
];
let (_remainder, header) = parse_dcerpc_header(dcerpcheader).unwrap();
assert_eq!(5, header.rpc_vers);
assert_eq!(0, header.rpc_vers_minor);
assert_eq!(0, header.hdrtype);
assert_eq!(1024, header.frag_length);
}
#[test]
fn test_parse_dcerpc_bind() {
let dcerpcbind: &[u8] = &[
0xd0, 0x16, 0xd0, 0x16, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
];
let (_remainder, bind) = parse_dcerpc_bind(dcerpcbind).unwrap();
assert_eq!(24, bind.numctxitems);
}
#[test]
fn test_parse_bindctx_item() {
let dcerpcbind: &[u8] = &[
0x00, 0x00, 0x01, 0x00, 0x2c, 0xd0, 0x28, 0xda, 0x76, 0x91, 0xf6, 0x6e, 0xcb, 0x0f,
0xbf, 0x85, 0xcd, 0x9b, 0xf6, 0x39, 0x01, 0x00, 0x03, 0x00, 0x04, 0x5d, 0x88, 0x8a,
0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00,
0x00, 0x00,
];
let (_remainder, ctxitem) = parse_bindctx_item(dcerpcbind, Endianness::Little).unwrap();
assert_eq!(0, ctxitem.ctxid);
assert_eq!(1, ctxitem.version);
assert_eq!(3, ctxitem.versionminor);
}
}
Loading…
Cancel
Save