mirror of https://github.com/OISF/suricata
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
422 lines
12 KiB
Rust
422 lines
12 KiB
Rust
/* Copyright (C) 2017 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 nom::{rest, le_u8, le_u16, le_u32, le_u64, AsBytes};
|
|
|
|
#[derive(Debug,PartialEq)]
|
|
pub struct Smb2SecBlobRecord<'a> {
|
|
pub data: &'a[u8],
|
|
}
|
|
|
|
named!(pub parse_smb2_sec_blob<Smb2SecBlobRecord>,
|
|
do_parse!(
|
|
data: rest
|
|
>> ( Smb2SecBlobRecord {
|
|
data: data,
|
|
})
|
|
));
|
|
|
|
#[derive(Debug,PartialEq)]
|
|
pub struct Smb2Record<'a> {
|
|
pub direction: u8, // 0 req, 1 res
|
|
pub nt_status: u32,
|
|
pub command: u16,
|
|
pub message_id: u64,
|
|
pub tree_id: u32,
|
|
pub session_id: u64,
|
|
pub data: &'a[u8],
|
|
}
|
|
|
|
named!(pub parse_smb2_request_record<Smb2Record>,
|
|
do_parse!(
|
|
server_component: tag!(b"\xfeSMB")
|
|
>> hlen: le_u16
|
|
>> credit_charge: le_u16
|
|
>> channel_seq: le_u16
|
|
>> reserved: take!(2)
|
|
>> command: le_u16
|
|
>> credits_requested: le_u16
|
|
>> flags: bits!(tuple!(
|
|
take_bits!(u8, 2), // reserved / unused
|
|
take_bits!(u8, 1), // replay op
|
|
take_bits!(u8, 1), // dfs op
|
|
take_bits!(u32, 24), // reserved / unused
|
|
take_bits!(u8, 1), // signing
|
|
take_bits!(u8, 1), // chained
|
|
take_bits!(u8, 1), // async
|
|
take_bits!(u8, 1) // response
|
|
))
|
|
>> chain_offset: le_u32
|
|
>> message_id: le_u64
|
|
>> process_id: le_u32
|
|
>> tree_id: le_u32
|
|
>> session_id: le_u64
|
|
>> signature: take!(16)
|
|
// there is probably a cleaner way to do this
|
|
>> data_c: cond!(chain_offset > hlen as u32, take!(chain_offset - hlen as u32))
|
|
>> data_r: cond!(chain_offset <= hlen as u32, rest)
|
|
>> (Smb2Record {
|
|
direction: flags.7,
|
|
nt_status: 0,
|
|
command:command,
|
|
message_id: message_id,
|
|
tree_id: tree_id,
|
|
session_id: session_id,
|
|
data: if data_c != None { data_c.unwrap() } else { data_r.unwrap() }
|
|
})
|
|
));
|
|
|
|
#[derive(Debug,PartialEq)]
|
|
pub struct Smb2NegotiateProtocolRequestRecord<> {
|
|
pub dialects_vec: Vec<u16>,
|
|
}
|
|
|
|
named!(pub parse_smb2_request_negotiate_protocol<Smb2NegotiateProtocolRequestRecord>,
|
|
do_parse!(
|
|
struct_size: take!(2)
|
|
>> dialects_count: le_u16
|
|
>> blob1: take!(32)
|
|
>> dia_vec: count!(le_u16, dialects_count as usize)
|
|
>> blob2: rest
|
|
>> (Smb2NegotiateProtocolRequestRecord {
|
|
dialects_vec: dia_vec,
|
|
})
|
|
));
|
|
|
|
#[derive(Debug,PartialEq)]
|
|
pub struct Smb2NegotiateProtocolResponseRecord<> {
|
|
pub dialect: u16,
|
|
}
|
|
|
|
named!(pub parse_smb2_response_negotiate_protocol<Smb2NegotiateProtocolResponseRecord>,
|
|
do_parse!(
|
|
struct_size: take!(2)
|
|
>> skip1: take!(2)
|
|
>> dialect: le_u16
|
|
>> (Smb2NegotiateProtocolResponseRecord {
|
|
dialect: dialect,
|
|
})
|
|
));
|
|
|
|
#[derive(Debug,PartialEq)]
|
|
pub struct Smb2SessionSetupRequestRecord<'a> {
|
|
pub data: &'a[u8],
|
|
}
|
|
|
|
named!(pub parse_smb2_request_session_setup<Smb2SessionSetupRequestRecord>,
|
|
do_parse!(
|
|
struct_size: take!(2)
|
|
>> flags: le_u8
|
|
>> security_mode: le_u8
|
|
>> capabilities: le_u32
|
|
>> channel: le_u32
|
|
>> sec_offset: le_u16
|
|
>> sec_len: le_u16
|
|
>> prev_ssn_id: take!(8)
|
|
>> data: rest
|
|
>> (Smb2SessionSetupRequestRecord {
|
|
data:data,
|
|
})
|
|
));
|
|
|
|
|
|
#[derive(Debug,PartialEq)]
|
|
pub struct Smb2TreeConnectRequestRecord<'a> {
|
|
pub share_name: &'a[u8],
|
|
}
|
|
|
|
named!(pub parse_smb2_request_tree_connect<Smb2TreeConnectRequestRecord>,
|
|
do_parse!(
|
|
struct_size: take!(2)
|
|
>> offset_length: take!(4)
|
|
>> data: rest
|
|
>> (Smb2TreeConnectRequestRecord {
|
|
share_name:data,
|
|
})
|
|
));
|
|
|
|
#[derive(Debug,PartialEq)]
|
|
pub struct Smb2TreeConnectResponseRecord<> {
|
|
pub share_type: u8,
|
|
}
|
|
|
|
named!(pub parse_smb2_response_tree_connect<Smb2TreeConnectResponseRecord>,
|
|
do_parse!(
|
|
struct_size: take!(2)
|
|
>> share_type: le_u8
|
|
>> share_flags: le_u32
|
|
>> share_caps: le_u32
|
|
>> access_mask: le_u32
|
|
>> (Smb2TreeConnectResponseRecord {
|
|
share_type:share_type,
|
|
})
|
|
));
|
|
|
|
|
|
#[derive(Debug,PartialEq)]
|
|
pub struct Smb2CreateRequestRecord<'a> {
|
|
pub disposition: u32,
|
|
pub create_options: u32,
|
|
pub data: &'a[u8],
|
|
}
|
|
|
|
named!(pub parse_smb2_request_create<Smb2CreateRequestRecord>,
|
|
do_parse!(
|
|
skip1: take!(36)
|
|
>> disposition: le_u32
|
|
>> create_options: le_u32
|
|
>> file_name_offset: le_u16
|
|
>> file_name_length: le_u16
|
|
>> skip2: take!(8)
|
|
>> data: take!(file_name_length)
|
|
>> skip3: rest
|
|
>> (Smb2CreateRequestRecord {
|
|
disposition: disposition,
|
|
create_options: create_options,
|
|
data:data,
|
|
})
|
|
));
|
|
|
|
#[derive(Debug,PartialEq)]
|
|
pub struct Smb2IOCtlRequestRecord<'a> {
|
|
pub is_pipe: bool,
|
|
pub guid: &'a[u8],
|
|
pub data: &'a[u8],
|
|
}
|
|
|
|
named!(pub parse_smb2_request_ioctl<Smb2IOCtlRequestRecord>,
|
|
do_parse!(
|
|
skip: take!(2) // structure size
|
|
>> take!(2) // reserved
|
|
>> func: le_u32
|
|
>> guid: take!(16)
|
|
>> indata_offset: le_u32
|
|
>> indata_len: le_u32
|
|
>> take!(4)
|
|
>> outdata_offset: le_u32
|
|
>> outdata_len: le_u32
|
|
>> take!(12)
|
|
>> data: take!(indata_len)
|
|
>> (Smb2IOCtlRequestRecord {
|
|
is_pipe: (func == 0x0011c017),
|
|
guid:guid,
|
|
data:data,
|
|
})
|
|
));
|
|
|
|
#[derive(Debug,PartialEq)]
|
|
pub struct Smb2IOCtlResponseRecord<'a> {
|
|
pub is_pipe: bool,
|
|
pub guid: &'a[u8],
|
|
pub data: &'a[u8],
|
|
pub indata_len: u32,
|
|
pub outdata_len: u32,
|
|
pub indata_offset: u32,
|
|
pub outdata_offset: u32,
|
|
}
|
|
|
|
named!(pub parse_smb2_response_ioctl<Smb2IOCtlResponseRecord>,
|
|
do_parse!(
|
|
skip: take!(2) // structure size
|
|
>> take!(2) // reserved
|
|
>> func: le_u32
|
|
>> guid: take!(16)
|
|
>> indata_offset: le_u32
|
|
>> indata_len: le_u32
|
|
>> outdata_offset: le_u32
|
|
>> outdata_len: le_u32
|
|
>> take!(8)
|
|
>> take!(indata_len)
|
|
>> data: take!(outdata_len)
|
|
>> (Smb2IOCtlResponseRecord {
|
|
is_pipe: (func == 0x0011c017),
|
|
guid:guid,
|
|
data:data,
|
|
indata_len:indata_len,
|
|
outdata_len:outdata_len,
|
|
indata_offset:indata_offset,
|
|
outdata_offset:outdata_offset,
|
|
})
|
|
));
|
|
|
|
#[derive(Debug,PartialEq)]
|
|
pub struct Smb2CloseRequestRecord<'a> {
|
|
pub guid: &'a[u8],
|
|
}
|
|
|
|
named!(pub parse_smb2_request_close<Smb2CloseRequestRecord>,
|
|
do_parse!(
|
|
skip: take!(8)
|
|
>> guid: take!(16)
|
|
>> (Smb2CloseRequestRecord {
|
|
guid:guid,
|
|
})
|
|
));
|
|
|
|
#[derive(Debug,PartialEq)]
|
|
pub struct Smb2WriteRequestRecord<'a> {
|
|
pub wr_len: u32,
|
|
pub wr_offset: u64,
|
|
pub guid: &'a[u8],
|
|
pub data: &'a[u8],
|
|
}
|
|
|
|
named!(pub parse_smb2_request_write<Smb2WriteRequestRecord>,
|
|
do_parse!(
|
|
skip1: take!(4)
|
|
>> wr_len: le_u32
|
|
>> wr_offset: le_u64
|
|
>> guid: take!(16)
|
|
>> channel: le_u32
|
|
>> remaining_bytes: le_u32
|
|
>> write_flags: le_u32
|
|
>> skip2: take!(4)
|
|
>> data: rest
|
|
>> (Smb2WriteRequestRecord {
|
|
wr_len:wr_len,
|
|
wr_offset:wr_offset,
|
|
guid:guid,
|
|
data:data,
|
|
})
|
|
));
|
|
|
|
#[derive(Debug,PartialEq)]
|
|
pub struct Smb2ReadRequestRecord<'a> {
|
|
pub rd_len: u32,
|
|
pub rd_offset: u64,
|
|
pub guid: &'a[u8],
|
|
}
|
|
|
|
named!(pub parse_smb2_request_read<Smb2ReadRequestRecord>,
|
|
do_parse!(
|
|
skip1: take!(4)
|
|
>> rd_len: le_u32
|
|
>> rd_offset: le_u64
|
|
>> guid: take!(16)
|
|
>> min_count: le_u32
|
|
>> channel: le_u32
|
|
>> remaining_bytes: le_u32
|
|
>> skip2: take!(4)
|
|
>> (Smb2ReadRequestRecord {
|
|
rd_len:rd_len,
|
|
rd_offset:rd_offset,
|
|
guid:guid,
|
|
})
|
|
));
|
|
|
|
#[derive(Debug,PartialEq)]
|
|
pub struct Smb2ReadResponseRecord<'a> {
|
|
pub len: u32,
|
|
pub data: &'a[u8],
|
|
}
|
|
|
|
named!(pub parse_smb2_response_read<Smb2ReadResponseRecord>,
|
|
do_parse!(
|
|
skip1: take!(4)
|
|
>> rd_len: le_u32
|
|
>> skip2: take!(8)
|
|
>> data: rest
|
|
>> (Smb2ReadResponseRecord {
|
|
len : rd_len,
|
|
data : data,
|
|
})
|
|
));
|
|
|
|
#[derive(Debug,PartialEq)]
|
|
pub struct Smb2CreateResponseRecord<'a> {
|
|
pub guid: &'a[u8],
|
|
}
|
|
|
|
named!(pub parse_smb2_response_create<Smb2CreateResponseRecord>,
|
|
do_parse!(
|
|
skip1: take!(64)
|
|
>> guid: take!(16)
|
|
>> skip2: take!(8)
|
|
>> (Smb2CreateResponseRecord {
|
|
guid : guid,
|
|
})
|
|
));
|
|
|
|
#[derive(Debug,PartialEq)]
|
|
pub struct Smb2WriteResponseRecord<> {
|
|
pub wr_cnt: u32,
|
|
}
|
|
|
|
named!(pub parse_smb2_response_write<Smb2WriteResponseRecord>,
|
|
do_parse!(
|
|
skip1: take!(4)
|
|
>> wr_cnt: le_u32
|
|
>> skip2: take!(6)
|
|
>> (Smb2WriteResponseRecord {
|
|
wr_cnt : wr_cnt,
|
|
})
|
|
));
|
|
|
|
named!(pub parse_smb2_response_record<Smb2Record>,
|
|
do_parse!(
|
|
server_component: tag!(b"\xfeSMB")
|
|
>> hlen: le_u16
|
|
>> credit_charge: le_u16
|
|
>> nt_status: le_u32
|
|
>> command: le_u16
|
|
>> credit_granted: le_u16
|
|
>> flags: bits!(tuple!(
|
|
take_bits!(u8, 2), // reserved / unused
|
|
take_bits!(u8, 1), // replay op
|
|
take_bits!(u8, 1), // dfs op
|
|
take_bits!(u32, 24), // reserved / unused
|
|
take_bits!(u8, 1), // signing
|
|
take_bits!(u8, 1), // chained
|
|
take_bits!(u8, 1), // async
|
|
take_bits!(u8, 1) // response
|
|
))
|
|
>> chain_offset: le_u32
|
|
>> message_id: le_u64
|
|
>> process_id: le_u32
|
|
>> tree_id: le_u32
|
|
>> session_id: le_u64
|
|
>> signature: take!(16)
|
|
// there is probably a cleaner way to do this
|
|
>> data_c: cond!(chain_offset > hlen as u32, take!(chain_offset - hlen as u32))
|
|
>> data_r: cond!(chain_offset <= hlen as u32, rest)
|
|
>> (Smb2Record {
|
|
direction: flags.7,
|
|
nt_status: nt_status,
|
|
message_id: message_id,
|
|
tree_id: tree_id,
|
|
session_id: session_id,
|
|
command:command,
|
|
data: if data_c != None { data_c.unwrap() } else { data_r.unwrap() }
|
|
})
|
|
));
|
|
|
|
#[derive(Debug,PartialEq)]
|
|
pub struct SmbRecordPostGap<'a> {
|
|
pub data: &'a[u8],
|
|
}
|
|
|
|
named!(pub search_smb_record<SmbRecordPostGap>,
|
|
do_parse!(
|
|
alt!(take_until!([0xfe, 0x53, 0x4d, 0x42].as_bytes())| // SMB2
|
|
take_until!([0xff, 0x53, 0x4d, 0x42].as_bytes())| // SMB1
|
|
take_until!([0xfd, 0x53, 0x4d, 0x42].as_bytes())) // SMB3 transform hdr
|
|
>> data : rest
|
|
>> ( SmbRecordPostGap {
|
|
data:data,
|
|
})
|
|
));
|