diff --git a/Cargo.toml b/Cargo.toml
index 8352e15..9508504 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -21,22 +21,22 @@ authors = ["_Frky <3105926+Frky@users.noreply.github.com>"]
edition = "2018"
[dependencies]
+bitflags = "1.2.1"
+byteorder = "1.4.3"
+chrono = "0.4.19"
+clap = "3.0.5"
+dns-parser = "0.8.0"
+flate2 = "1.0"
+itertools = "0.10.3"
+lazy_static = "1.4.0"
+log = "0.4.11"
+netdevice = "0.1.1"
pcap = "0.9.1"
pcap-file = "1.1.1"
pnet = "0.29.0"
-clap = "3.0.5"
-log = "0.4.11"
-stderrlog = "0.5.0"
-itertools = "0.10.3"
rand = "0.8.4"
-dns-parser = "0.8.0"
-netdevice = "0.1.1"
-bitflags = "1.2.1"
-lazy_static = "1.4.0"
siphasher = "0.3"
-chrono = "0.4.19"
-byteorder = "1.4.3"
-flate2 = "1.0"
+stderrlog = "0.5.0"
[[bin]]
name = "masscanned"
diff --git a/src/proto/mod.rs b/src/proto/mod.rs
index 7d425bb..2c780d1 100644
--- a/src/proto/mod.rs
+++ b/src/proto/mod.rs
@@ -39,12 +39,17 @@ use ghost::GHOST_PATTERN_SIGNATURE;
mod rpc;
use rpc::{RPC_CALL_TCP, RPC_CALL_UDP};
+mod smb;
+use smb::{SMB1_PATTERN_MAGIC, SMB2_PATTERN_MAGIC};
+
const PROTO_HTTP: usize = 1;
const PROTO_STUN: usize = 2;
const PROTO_SSH: usize = 3;
const PROTO_GHOST: usize = 4;
const PROTO_RPC_TCP: usize = 5;
const PROTO_RPC_UDP: usize = 6;
+const PROTO_SMB1: usize = 7;
+const PROTO_SMB2: usize = 8;
struct TCPControlBlock {
proto_state: usize,
@@ -100,6 +105,16 @@ fn proto_init() -> Smack {
PROTO_RPC_UDP,
SmackFlags::ANCHOR_BEGIN | SmackFlags::WILDCARDS,
);
+ smack.add_pattern(
+ SMB1_PATTERN_MAGIC,
+ PROTO_SMB1,
+ SmackFlags::ANCHOR_BEGIN | SmackFlags::WILDCARDS,
+ );
+ smack.add_pattern(
+ SMB2_PATTERN_MAGIC,
+ PROTO_SMB2,
+ SmackFlags::ANCHOR_BEGIN | SmackFlags::WILDCARDS,
+ );
smack.compile();
smack
}
@@ -129,13 +144,13 @@ pub fn repl<'a>(
let mut i = 0;
let mut tcb = ct.get_mut(&cookie).unwrap();
let mut state = tcb.proto_state;
- id = PROTO_SMACK.search_next(&mut state, &data.to_vec(), &mut i);
+ id = PROTO_SMACK.search_next(&mut state, data, &mut i);
tcb.proto_state = state;
} else {
/* proto over else (e.g., UDP) */
let mut i = 0;
let mut state = BASE_STATE;
- id = PROTO_SMACK.search_next(&mut state, &data.to_vec(), &mut i);
+ id = PROTO_SMACK.search_next(&mut state, data, &mut i);
/* because we are not over TCP, we can afford to assume end of pattern */
if id == NO_MATCH {
id = PROTO_SMACK.search_next_end(&mut state);
@@ -149,6 +164,8 @@ pub fn repl<'a>(
PROTO_GHOST => ghost::repl(data, masscanned, &mut client_info),
PROTO_RPC_TCP => rpc::repl_tcp(data, masscanned, &mut client_info),
PROTO_RPC_UDP => rpc::repl_udp(data, masscanned, &mut client_info),
+ PROTO_SMB1 => smb::repl_smb1(data, masscanned, &mut client_info),
+ PROTO_SMB2 => smb::repl_smb2(data, masscanned, &mut client_info),
_ => {
debug!("id: {}", id);
None
diff --git a/src/proto/smb.rs b/src/proto/smb.rs
new file mode 100644
index 0000000..0e1ad4b
--- /dev/null
+++ b/src/proto/smb.rs
@@ -0,0 +1,1157 @@
+// This file is part of masscanned.
+// Copyright 2021 - The IVRE project
+//
+// Masscanned is free software: you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Masscanned 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
+// along with Masscanned. If not, see .
+
+use log::*;
+use std::collections::HashSet;
+use std::convert::TryInto;
+use std::time::SystemTime;
+
+use crate::client::ClientInfo;
+use crate::logger::MetaLogger;
+use crate::Masscanned;
+
+// NBTSession + SMB Header
+// netbios type (1 byte) + reserved (1 byte) + length (2 bytes) + SMB MAGIC (4 bytes)
+//
+pub const SMB1_PATTERN_MAGIC: &[u8; 8] = b"\x00\x00**\xffSMB";
+pub const SMB2_PATTERN_MAGIC: &[u8; 8] = b"\x00\x00**\xfeSMB";
+
+// Build/Dissect secblob with Scapy using: GSSAPI_BLOB(b"`\x82.....")
+const SECURITY_BLOB: &[u8; 320] = b"`\x82\x01<\x06\x06+\x06\x01\x05\x05\x02\xa0\x82\x0100\x82\x01,\xa0\x1a0\x18\x06\n+\x06\x01\x04\x01\x827\x02\x02\x1e\x06\n+\x06\x01\x04\x01\x827\x02\x02\n\xa2\x82\x01\x0c\x04\x82\x01\x08NEGOEXTS\x01\x00\x00\x00\x00\x00\x00\x00`\x00\x00\x00p\x00\x00\x001<*:\xc7+<\xa9m\xac8t\xa7\xdd\x1d[\xf4Rk\x17\x03\x8aK\x91\xc2\t}\x9a\x8f\xe6,\x96\\Q$/\x90MG\xc7\xad\x8f\x87k\"\x02\xbf\xc6\x00\x00\x00\x00\x00\x00\x00\x00`\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\\3S\r\xea\xf9\rM\xb2\xecJ\xe3xn\xc3\x08NEGOEXTS\x03\x00\x00\x00\x01\x00\x00\x00@\x00\x00\x00\x98\x00\x00\x001<*:\xc7+<\xa9m\xac8t\xa7\xdd\x1d[\\3S\r\xea\xf9\rM\xb2\xecJ\xe3xn\xc3\x08@\x00\x00\x00X\x00\x00\x000V\xa0T0R0'\x80%0#1!0\x1f\x06\x03U\x04\x03\x13\x18Token Signing Public Key0'\x80%0#1!0\x1f\x06\x03U\x04\x03\x13\x18Token Signing Public Key";
+
+////////////
+// Common //
+////////////
+
+/// ### PacketDissector
+/// A util class used to dissect fields.
+#[derive(Debug, Clone)]
+struct PacketDissector {
+ i: usize,
+ state: T,
+}
+impl PacketDissector {
+ fn new(initial_state: T) -> PacketDissector {
+ return PacketDissector {
+ i: 0,
+ state: initial_state,
+ };
+ }
+ fn next_state(&mut self, state: T) {
+ self.state = state;
+ self.i = 0;
+ }
+ fn next_state_when_i_reaches(&mut self, state: T, i: usize) {
+ if self.i == i {
+ self.next_state(state);
+ }
+ }
+ fn _read_usize(&mut self, byte: &u8, value: usize, next_state: T, size: usize) -> usize {
+ self.i += 1;
+ self.next_state_when_i_reaches(next_state, size);
+ (value << 8) + *byte as usize
+ }
+ fn _read_ulesize(&mut self, byte: &u8, value: usize, next_state: T, size: usize) -> usize {
+ let ret = value + ((*byte as usize) << (8 * self.i));
+ self.i += 1;
+ self.next_state_when_i_reaches(next_state, size);
+ ret
+ }
+ fn read_u16(&mut self, byte: &u8, value: u16, next_state: T) -> u16 {
+ self._read_usize(byte, value as usize, next_state, 2) as u16
+ }
+ fn read_ule16(&mut self, byte: &u8, value: u16, next_state: T) -> u16 {
+ self._read_ulesize(byte, value as usize, next_state, 2) as u16
+ }
+ fn read_ule32(&mut self, byte: &u8, value: u32, next_state: T) -> u32 {
+ self._read_ulesize(byte, value as usize, next_state, 4) as u32
+ }
+ fn read_ule64(&mut self, byte: &u8, value: u64, next_state: T) -> u64 {
+ self._read_ulesize(byte, value as usize, next_state, 8) as u64
+ }
+}
+
+pub trait MPacket {
+ fn new() -> Self;
+ fn repl(&self) -> Option>;
+ fn parse(&mut self, byte: &u8);
+
+ fn parse_all(&mut self, bytes: &[u8]) {
+ for byte in bytes {
+ self.parse(byte);
+ }
+ }
+}
+
+/////////////
+// Netbios //
+/////////////
+
+#[derive(Debug, Clone, Copy)]
+enum NBTSessionState {
+ NBType,
+ Reserved,
+ Length,
+ End,
+}
+
+#[derive(Debug, Clone)]
+struct NBTSession {
+ // DISSECTION
+ d: PacketDissector,
+ // STRUCT
+ nb_type: u8,
+ length: u16,
+ payload: Option,
+}
+
+impl MPacket for NBTSession {
+ fn new() -> NBTSession {
+ Self {
+ d: PacketDissector::new(NBTSessionState::NBType),
+ nb_type: 0,
+ length: 0,
+ payload: None,
+ }
+ }
+
+ fn parse(&mut self, byte: &u8) {
+ match self.d.state {
+ NBTSessionState::NBType => {
+ self.nb_type = *byte;
+ self.d.next_state(NBTSessionState::Reserved);
+ }
+ NBTSessionState::Reserved => {
+ self.d.next_state(NBTSessionState::Length);
+ }
+ NBTSessionState::Length => {
+ self.length = self.d.read_u16(byte, self.length, NBTSessionState::End)
+ }
+ NBTSessionState::End => match self.get_payload() {
+ Some(pay) => pay.parse(byte),
+ None => return,
+ },
+ }
+ }
+
+ fn repl(&self) -> Option> {
+ let payload_resp = self.payload.as_ref()?.repl()?;
+ let mut resp: Vec = Vec::new();
+ let size = payload_resp.len() & 0x1ffff; // 7 first bits are 0
+ resp.push(0x0);
+ // 7 bits reserved + 17 bits length
+ resp.push(((size as u32 >> 16) & 0xff).try_into().unwrap());
+ resp.extend_from_slice(&((size & 0xffff) as u16).to_be_bytes());
+ resp.extend(payload_resp);
+ Some(resp)
+ }
+}
+
+impl NBTSession {
+ fn get_payload(&mut self) -> Option<&mut T> {
+ if self.payload.is_some() {
+ return self.payload.as_mut();
+ }
+ self.payload = Some(T::new());
+ self.payload.as_mut()
+ }
+}
+
+//////////
+// SMB1 //
+//////////
+
+#[derive(Debug, Clone, Copy)]
+enum SMB1HeaderState {
+ Start,
+ Command,
+ Status,
+ Flags,
+ Flags2,
+ PIDHigh,
+ SecuritySignature,
+ Reserved,
+ TID,
+ PIDLow,
+ UID,
+ MID,
+ End,
+}
+
+#[derive(Debug, Clone)]
+struct SMB1Header {
+ // DISSECTION
+ d: PacketDissector,
+ // STRUCT
+ start: [u8; 4],
+ command: u8,
+ status: u32,
+ flags: u8,
+ flags2: u16,
+ pid_high: u16,
+ security_signature: [u8; 8],
+ tid: u16,
+ pid_low: u16,
+ uid: u16,
+ mid: u16,
+ payload: Option,
+}
+
+impl MPacket for SMB1Header {
+ fn new() -> SMB1Header {
+ Self {
+ d: PacketDissector::new(SMB1HeaderState::Start),
+ start: [0; 4],
+ command: 0,
+ status: 0,
+ flags: 0,
+ flags2: 0,
+ pid_high: 0,
+ security_signature: [0; 8],
+ tid: 0,
+ pid_low: 0,
+ uid: 0,
+ mid: 0,
+ payload: None,
+ }
+ }
+
+ fn parse(&mut self, byte: &u8) {
+ match self.d.state {
+ SMB1HeaderState::Start => {
+ self.start[self.d.i] = *byte;
+ self.d.i += 1;
+ self.d
+ .next_state_when_i_reaches(SMB1HeaderState::Command, 4);
+ }
+ SMB1HeaderState::Command => {
+ self.command = *byte;
+ self.d.next_state(SMB1HeaderState::Status);
+ }
+ SMB1HeaderState::Status => {
+ self.status = self.d.read_ule32(byte, self.status, SMB1HeaderState::Flags);
+ }
+ SMB1HeaderState::Flags => {
+ self.flags = *byte;
+ self.d.next_state(SMB1HeaderState::Flags2);
+ }
+ SMB1HeaderState::Flags2 => {
+ self.flags2 = self
+ .d
+ .read_ule16(byte, self.flags2, SMB1HeaderState::PIDHigh);
+ }
+ SMB1HeaderState::PIDHigh => {
+ self.pid_high =
+ self.d
+ .read_ule16(byte, self.pid_high, SMB1HeaderState::SecuritySignature);
+ }
+ SMB1HeaderState::SecuritySignature => {
+ self.security_signature[self.d.i] = *byte;
+ self.d.i += 1;
+ self.d
+ .next_state_when_i_reaches(SMB1HeaderState::Reserved, 8);
+ }
+ SMB1HeaderState::Reserved => {
+ self.d.i += 1;
+ self.d.next_state_when_i_reaches(SMB1HeaderState::TID, 2);
+ }
+ SMB1HeaderState::TID => {
+ self.tid = self.d.read_ule16(byte, self.tid, SMB1HeaderState::PIDLow);
+ }
+ SMB1HeaderState::PIDLow => {
+ self.pid_low = self.d.read_ule16(byte, self.pid_low, SMB1HeaderState::UID);
+ }
+ SMB1HeaderState::UID => {
+ self.uid = self.d.read_ule16(byte, self.uid, SMB1HeaderState::MID);
+ }
+ SMB1HeaderState::MID => {
+ self.mid = self.d.read_ule16(byte, self.mid, SMB1HeaderState::End);
+ }
+ SMB1HeaderState::End => match self.get_payload() {
+ Some(pay) => pay.parse(byte),
+ None => return,
+ },
+ }
+ }
+
+ fn repl(&self) -> Option> {
+ let payload_resp = self.payload.as_ref()?.repl()?;
+ let mut resp: Vec = Vec::new();
+ resp.extend_from_slice(b"\xffSMB"); // Start
+ resp.push(self.command); // Command
+ resp.extend_from_slice(&0_u32.to_le_bytes()); // Status
+ resp.push(0x98); // Flags = CASE_INSENSITIVE+CANONICALIZED_PATHS+REPLY
+ resp.extend_from_slice(&0xc807_u16.to_le_bytes()); // Flags2 = LONG_NAMES+EAS+SMB_SECURITY_SIGNATURE+EXTENDED_SECURITY+NT_STATUS+UNICODE
+ resp.extend_from_slice(&self.pid_high.to_le_bytes()); // PIDHigh
+ resp.extend_from_slice(&[0; 8]); // SecuritySignature
+ resp.extend_from_slice(&[0; 2]); // Reserved
+ resp.extend_from_slice(&self.tid.to_le_bytes()); // TID
+ resp.extend_from_slice(&self.pid_low.to_le_bytes()); // PIDLOW
+ resp.extend_from_slice(&self.uid.to_le_bytes()); // UID
+ resp.extend_from_slice(&self.mid.to_le_bytes()); // MID
+ resp.extend(payload_resp);
+ Some(resp)
+ }
+}
+
+impl SMB1Header {
+ fn get_payload(&mut self) -> Option<&mut SMB1Payload> {
+ if self.payload.is_some() {
+ return self.payload.as_mut();
+ }
+ if self.flags & 0x80 == 0x80 {
+ // Response
+ return None;
+ }
+ self.payload = Some(match self.command {
+ 0x72 => {
+ // Negotiate
+ SMB1Payload::NegotiateRequest(SMB1NegotiateRequest::new())
+ }
+ // 0x73 => {
+ // // Setup
+ // SMB1Payload::SetupRequest(SMB2SetupRequest::new())
+ // }
+ _ => None?,
+ });
+ self.payload.as_mut()
+ }
+}
+
+#[derive(Debug, Clone, PartialEq)]
+struct SMB1Dialect {
+ buffer_format: u8,
+ dialect_string: String,
+}
+
+#[derive(Debug, Clone, Copy)]
+enum SMB1NegotiateRequestState {
+ WordCount,
+ ByteCount,
+ Dialects,
+ End,
+}
+
+#[derive(Debug, Clone)]
+struct SMB1NegotiateRequest {
+ // DISSECTION
+ d: PacketDissector,
+ _tmp_dialect: Option,
+ // STRUCT
+ word_count: u8,
+ byte_count: u16,
+ dialects: Vec,
+}
+
+impl MPacket for SMB1NegotiateRequest {
+ fn new() -> SMB1NegotiateRequest {
+ Self {
+ d: PacketDissector::new(SMB1NegotiateRequestState::WordCount),
+ _tmp_dialect: None,
+ word_count: 0,
+ byte_count: 0,
+ dialects: Vec::new(),
+ }
+ }
+
+ fn parse(&mut self, byte: &u8) {
+ match self.d.state {
+ SMB1NegotiateRequestState::WordCount => {
+ self.word_count = *byte;
+ self.d.next_state(SMB1NegotiateRequestState::ByteCount);
+ }
+ SMB1NegotiateRequestState::ByteCount => {
+ self.byte_count =
+ self.d
+ .read_ule16(byte, self.byte_count, SMB1NegotiateRequestState::Dialects);
+ }
+ SMB1NegotiateRequestState::Dialects => {
+ self.d.i += 1;
+ match self._tmp_dialect.as_mut() {
+ Some(dial) => {
+ if *byte == 0 {
+ // Final nul byte: dialect is finished
+ self.dialects.push(dial.clone());
+ self._tmp_dialect = None;
+ self.d.next_state_when_i_reaches(
+ SMB1NegotiateRequestState::End,
+ self.byte_count as usize,
+ );
+ } else {
+ dial.dialect_string.push(*byte as char);
+ }
+ }
+ None => {
+ self._tmp_dialect = Some(SMB1Dialect {
+ buffer_format: *byte,
+ dialect_string: String::new(),
+ });
+ }
+ }
+ }
+ SMB1NegotiateRequestState::End => {}
+ }
+ }
+
+ fn repl(&self) -> Option> {
+ if !matches!(self.d.state, SMB1NegotiateRequestState::End) {
+ return None;
+ }
+ let mut resp: Vec = Vec::new();
+ let time: u64 = (EPOCH_1601
+ + SystemTime::now()
+ .duration_since(SystemTime::UNIX_EPOCH)
+ .unwrap()
+ .as_secs())
+ * (1e7 as u64);
+ let mut dialect_index: u16 = 0;
+ let mut dialect_name = "Unknown";
+ for dialect in ["NT LM 0.12", "SMB 2.???", "SMB 2.002"] {
+ dialect_index = match self
+ .dialects
+ .iter()
+ .position(|x| x.dialect_string.eq(dialect))
+ {
+ Some(x) => {
+ dialect_name = dialect;
+ x as u16
+ }
+ None => continue,
+ };
+ break;
+ }
+ resp.push(17); // WordCount
+ resp.extend_from_slice(&dialect_index.to_le_bytes()); // DialectIndex
+ resp.push(3); // SecurityMode
+ resp.extend_from_slice(&50_u16.to_le_bytes()); // MaxMPXCount
+ resp.extend_from_slice(&50_u16.to_le_bytes()); // MaxNumberVC
+ resp.extend_from_slice(&0x10000_u32.to_le_bytes()); // MaxBufferSize
+ resp.extend_from_slice(&0x10000_u32.to_le_bytes()); // MaxRawSize
+ resp.extend_from_slice(&0x0_u32.to_le_bytes()); // SessionKey
+ resp.extend_from_slice(&0x8001e3fc_u32.to_le_bytes()); // ServerCapabilities = UNICODE+LARGE_FILES+NT_SMBS+RPC_REMOTE_APIS+STATUS32+LEVEL_II_OPLOCKS+LOCK_AND_READ+NT_FIND+INFOLEVEL_PASSTHRU+LARGE_READX+LARGE_WRITEX+LWIO+EXTENDED_SECURITY
+ resp.extend_from_slice(&time.to_le_bytes()); // ServerTime
+ resp.extend_from_slice(&0x3c_u16.to_le_bytes()); // ServerTimeZone
+ resp.push(0); // ChallengeLength
+ resp.extend_from_slice(&((SECURITY_BLOB.len() + 16) as u16).to_le_bytes()); // ByteCount
+ // Challenge: Empty
+ resp.extend_from_slice(&[0_u8; 16]); // GUID
+ resp.extend_from_slice(SECURITY_BLOB); // SecurityBlob
+ warn!("SMB1 Negotiate-Protocol-Reply ({})", dialect_name);
+ Some(resp)
+ }
+}
+
+// #[derive(Debug, Clone)]
+// struct SMB1SetupRequest {
+//
+// }
+
+// impl SMB1SetupRequest {
+// // TODO
+// fn new(data: &[u8]) -> Option {
+// if data.len() < 38 {
+// return None;
+// }
+// None
+// }
+
+// fn repl(&self) -> Option> {
+// None
+// }
+// }
+
+#[derive(Debug, Clone)]
+enum SMB1Payload {
+ NegotiateRequest(SMB1NegotiateRequest),
+ // SetupRequest(SMB1SetupRequest),
+}
+
+impl SMB1Payload {
+ fn repl(&self) -> Option> {
+ match self {
+ SMB1Payload::NegotiateRequest(x) => x.repl(),
+ // SMB1Payload::SetupRequest(x) => x.repl(),
+ }
+ }
+ fn parse(&mut self, byte: &u8) {
+ match self {
+ SMB1Payload::NegotiateRequest(x) => x.parse(byte),
+ // SMB1Payload::SetupRequest(x) => x.repl(),
+ }
+ }
+}
+
+//////////
+// SMB2 //
+//////////
+
+#[derive(Debug, Clone, Copy)]
+enum SMB2HeaderState {
+ Start,
+ StructureSize,
+ CreditsCharge,
+ Status,
+ Command,
+ CreditsRequested,
+ Flags,
+ NextCommand,
+ MessageId,
+ AsyncId,
+ SessionId,
+ SecuritySignature,
+ End,
+}
+
+#[derive(Debug, Clone)]
+struct SMB2Header {
+ // DISSECTION
+ d: PacketDissector,
+ // STRUCT
+ start: [u8; 4],
+ structure_size: u16,
+ credit_charge: u16,
+ status: u32,
+ command: u16,
+ credits_requested: u16,
+ flags: u32,
+ next_command: u32,
+ message_id: u64,
+ async_id: u64,
+ session_id: u64,
+ security_signature: [u8; 16],
+ // Payload
+ payload: Option,
+}
+
+impl MPacket for SMB2Header {
+ fn new() -> SMB2Header {
+ SMB2Header {
+ d: PacketDissector::new(SMB2HeaderState::Start),
+ start: [0; 4],
+ structure_size: 0,
+ credit_charge: 0,
+ status: 0,
+ command: 0,
+ credits_requested: 0,
+ flags: 0,
+ next_command: 0,
+ message_id: 0,
+ async_id: 0,
+ session_id: 0,
+ security_signature: [0; 16],
+ payload: None,
+ }
+ }
+
+ fn parse(&mut self, byte: &u8) {
+ match self.d.state {
+ SMB2HeaderState::Start => {
+ self.start[self.d.i] = *byte;
+ self.d.i += 1;
+ self.d
+ .next_state_when_i_reaches(SMB2HeaderState::StructureSize, 4);
+ }
+ SMB2HeaderState::StructureSize => {
+ self.structure_size =
+ self.d
+ .read_ule16(byte, self.structure_size, SMB2HeaderState::CreditsCharge)
+ }
+ SMB2HeaderState::CreditsCharge => {
+ self.credit_charge =
+ self.d
+ .read_ule16(byte, self.credit_charge, SMB2HeaderState::Status)
+ }
+ SMB2HeaderState::Status => {
+ self.status = self
+ .d
+ .read_ule32(byte, self.status, SMB2HeaderState::Command)
+ }
+ SMB2HeaderState::Command => {
+ self.command =
+ self.d
+ .read_ule16(byte, self.command, SMB2HeaderState::CreditsRequested)
+ }
+ SMB2HeaderState::CreditsRequested => {
+ self.credits_requested =
+ self.d
+ .read_ule16(byte, self.credits_requested, SMB2HeaderState::Flags)
+ }
+ SMB2HeaderState::Flags => {
+ self.flags = self
+ .d
+ .read_ule32(byte, self.flags, SMB2HeaderState::NextCommand)
+ }
+ SMB2HeaderState::NextCommand => {
+ self.next_command =
+ self.d
+ .read_ule32(byte, self.next_command, SMB2HeaderState::MessageId)
+ }
+ SMB2HeaderState::MessageId => {
+ self.message_id = self
+ .d
+ .read_ule64(byte, self.message_id, SMB2HeaderState::AsyncId)
+ }
+ SMB2HeaderState::AsyncId => {
+ self.async_id = self
+ .d
+ .read_ule64(byte, self.async_id, SMB2HeaderState::SessionId)
+ }
+ SMB2HeaderState::SessionId => {
+ self.session_id =
+ self.d
+ .read_ule64(byte, self.session_id, SMB2HeaderState::SecuritySignature)
+ }
+ SMB2HeaderState::SecuritySignature => {
+ self.security_signature[self.d.i] = *byte;
+ self.d.i += 1;
+ self.d.next_state_when_i_reaches(SMB2HeaderState::End, 16);
+ }
+ SMB2HeaderState::End => match self.get_payload() {
+ Some(pay) => pay.parse(byte),
+ None => return,
+ },
+ }
+ }
+
+ fn repl(&self) -> Option> {
+ let payload_resp = self.payload.as_ref()?.repl()?;
+ let mut resp: Vec = Vec::new();
+ resp.extend_from_slice(b"\xfeSMB"); // Start
+ resp.extend_from_slice(&64_u16.to_le_bytes()); // StructureSize
+ resp.extend_from_slice(&0_u16.to_le_bytes()); // CreditCharge
+ resp.extend_from_slice(&0_u32.to_le_bytes()); // Status
+ resp.extend_from_slice(&self.command.to_le_bytes()); // Command
+ resp.extend_from_slice(&1_u16.to_le_bytes()); // CreditsRequested
+ resp.extend_from_slice(&1_u32.to_le_bytes()); // Flags = Response
+ resp.extend_from_slice(&0_u32.to_le_bytes()); // NextCommand
+ resp.extend_from_slice(&self.message_id.to_le_bytes()); // MessageId
+ resp.extend_from_slice(&self.async_id.to_le_bytes()); // AsyncId
+ resp.extend_from_slice(&self.session_id.to_le_bytes()); // SessionId
+ resp.extend_from_slice(&[0; 16]); // SecuritySignature
+ // Payload
+ resp.extend(payload_resp);
+ Some(resp)
+ }
+}
+
+impl SMB2Header {
+ fn get_payload(&mut self) -> Option<&mut SMB2Payload> {
+ if let Some(_) = &self.payload {
+ return self.payload.as_mut();
+ }
+ if self.flags & 1 == 1 {
+ // Response
+ return None;
+ }
+ self.payload = Some(match self.command {
+ 0x0000 => {
+ // Negotiate
+ SMB2Payload::NegotiateRequest(SMB2NegotiateRequest::new())
+ }
+ 0x0001 => {
+ // Setup
+ SMB2Payload::SetupRequest(SMB2SetupRequest::new())
+ }
+ _ => None?,
+ });
+ self.payload.as_mut()
+ }
+}
+
+#[derive(Debug, Clone, Copy)]
+enum SMB2NegotiateRequestState {
+ StructureSize,
+ DialectCount,
+ SecurityMode,
+ Reserved,
+ Capabilities,
+ ClientGUID,
+ NegotiateAndReserved2,
+ Dialects,
+ End,
+}
+
+#[derive(Debug, Clone)]
+struct SMB2NegotiateRequest {
+ // DISSECTION
+ d: PacketDissector,
+ _tmp_dialect: u16,
+ // STRUCT
+ structure_size: u16,
+ dialect_count: u16,
+ security_mode: u16,
+ capabilities: u32,
+ client_guid: [u8; 16],
+ dialects: HashSet,
+}
+const EPOCH_1601: u64 = 11644473600;
+
+impl MPacket for SMB2NegotiateRequest {
+ fn new() -> Self {
+ SMB2NegotiateRequest {
+ d: PacketDissector::new(SMB2NegotiateRequestState::StructureSize),
+ _tmp_dialect: 0,
+ structure_size: 0,
+ dialect_count: 0,
+ security_mode: 0,
+ capabilities: 0,
+ client_guid: [0; 16],
+ dialects: HashSet::new(),
+ }
+ }
+
+ fn parse(&mut self, byte: &u8) {
+ match self.d.state {
+ SMB2NegotiateRequestState::StructureSize => {
+ self.structure_size = self.d.read_ule16(
+ byte,
+ self.structure_size,
+ SMB2NegotiateRequestState::DialectCount,
+ );
+ }
+ SMB2NegotiateRequestState::DialectCount => {
+ self.dialect_count = self.d.read_ule16(
+ byte,
+ self.dialect_count,
+ SMB2NegotiateRequestState::SecurityMode,
+ );
+ }
+ SMB2NegotiateRequestState::SecurityMode => {
+ self.security_mode = self.d.read_ule16(
+ byte,
+ self.security_mode,
+ SMB2NegotiateRequestState::Reserved,
+ );
+ }
+ SMB2NegotiateRequestState::Reserved => {
+ self.d.i += 1;
+ self.d
+ .next_state_when_i_reaches(SMB2NegotiateRequestState::Capabilities, 2);
+ }
+ SMB2NegotiateRequestState::Capabilities => {
+ self.capabilities = self.d.read_ule32(
+ byte,
+ self.capabilities,
+ SMB2NegotiateRequestState::ClientGUID,
+ );
+ }
+ SMB2NegotiateRequestState::ClientGUID => {
+ self.client_guid[self.d.i] = *byte;
+ self.d.i += 1;
+ self.d.next_state_when_i_reaches(
+ SMB2NegotiateRequestState::NegotiateAndReserved2,
+ 16,
+ );
+ }
+ SMB2NegotiateRequestState::NegotiateAndReserved2 => {
+ self.d.i += 1;
+ self.d
+ .next_state_when_i_reaches(SMB2NegotiateRequestState::Dialects, 8);
+ }
+ SMB2NegotiateRequestState::Dialects => {
+ self._tmp_dialect =
+ self.d
+ .read_ule16(byte, self._tmp_dialect, SMB2NegotiateRequestState::Dialects);
+ if self.d.i == 0 {
+ // Add to dialects list when finished
+ self.dialects.insert(self._tmp_dialect);
+ self._tmp_dialect = 0;
+ // Check if dialects list is finished
+ if self.dialects.len() == self.dialect_count as usize {
+ self.d.state = SMB2NegotiateRequestState::End;
+ }
+ }
+ }
+ SMB2NegotiateRequestState::End => {
+ return;
+ }
+ }
+ }
+ fn repl(&self) -> Option> {
+ if !matches!(self.d.state, SMB2NegotiateRequestState::End) {
+ return None;
+ }
+ let mut resp: Vec = Vec::new();
+ let time: u64 = (EPOCH_1601
+ + SystemTime::now()
+ .duration_since(SystemTime::UNIX_EPOCH)
+ .unwrap()
+ .as_secs())
+ * (1e7 as u64);
+ // Chose dialect
+ let smb2_versions = [
+ (0x0202, "SMB 2.002"),
+ (0x0210, "SMB 2.1"),
+ (0x02ff, "SMB 2.???"),
+ (0x0300, "SMB 3.0"),
+ (0x0302, "SMB 2.0.2"),
+ (0x0310, "SMB 3.1.0"),
+ (0x0311, "SMB 3.1.1"),
+ ];
+ let mut dialect = None;
+ let mut dialect_name = "Unknown";
+ if let Some(smb_ver) = smb2_versions
+ .iter()
+ .find(|(d, _)| self.dialects.contains(d))
+ {
+ dialect = Some(smb_ver.0);
+ dialect_name = smb_ver.1;
+ }
+ resp.extend_from_slice(&0x41_u16.to_le_bytes()); // StructureSize
+ resp.extend_from_slice(&0x1_u16.to_le_bytes()); // SecurityMode
+ resp.extend_from_slice(&dialect?.to_le_bytes()); // DialectRevision
+ resp.extend_from_slice(&0x1_u16.to_le_bytes()); // NegotiateCount
+ resp.extend_from_slice(&self.client_guid); // GUID
+ resp.extend_from_slice(&0x1_u32.to_le_bytes()); // Capabilities
+ resp.extend_from_slice(&0x10000_u32.to_le_bytes()); // MaxTransactionSize
+ resp.extend_from_slice(&0x10000_u32.to_le_bytes()); // MaxReadSize
+ resp.extend_from_slice(&0x10000_u32.to_le_bytes()); // MaxWriteSize
+ resp.extend_from_slice(&time.to_le_bytes()); // ServerTime
+ resp.extend_from_slice(&time.to_le_bytes()); // ServerStartTime
+ resp.extend_from_slice(&0x80_u16.to_le_bytes()); // SecurityBloboffset
+ resp.extend_from_slice(&(SECURITY_BLOB.len() as u16).to_le_bytes()); // SecurityBlobLength
+ resp.extend_from_slice(&0x0_u32.to_le_bytes()); // NegotiateContextOffset
+ resp.extend_from_slice(SECURITY_BLOB); // SecurityBlobw
+ warn!("SMB2 Negotiate-Protocol-Reply ({})", dialect_name);
+ Some(resp)
+ }
+}
+
+#[derive(Debug, Clone, Copy)]
+enum SMB2SetupRequestState {
+ StructureSize,
+ Flags,
+ SecurityMode,
+ Capabilities,
+ Channel,
+ SecurityBufferOffset,
+ SecurityLen,
+ PreviousSessionId,
+ SecurityBlob,
+ End,
+}
+
+#[derive(Debug, Clone)]
+struct SMB2SetupRequest {
+ // DISSECTION
+ d: PacketDissector,
+ // STRUCT
+ structure_size: u16,
+ flags: u8,
+ security_mode: u8,
+ capabilities: u32,
+ channel: u32,
+ security_buffer_offset: u16,
+ security_len: u16,
+ previous_session_id: u64,
+}
+impl MPacket for SMB2SetupRequest {
+ fn new() -> Self {
+ SMB2SetupRequest {
+ d: PacketDissector::new(SMB2SetupRequestState::StructureSize),
+ structure_size: 0,
+ flags: 0,
+ security_mode: 0,
+ capabilities: 0,
+ channel: 0,
+ security_buffer_offset: 0,
+ security_len: 0,
+ previous_session_id: 0,
+ }
+ }
+
+ fn parse(&mut self, byte: &u8) {
+ match self.d.state {
+ SMB2SetupRequestState::StructureSize => {
+ self.structure_size =
+ self.d
+ .read_ule16(byte, self.structure_size, SMB2SetupRequestState::Flags);
+ }
+ SMB2SetupRequestState::Flags => {
+ self.flags = *byte;
+ self.d.next_state(SMB2SetupRequestState::SecurityMode);
+ }
+ SMB2SetupRequestState::SecurityMode => {
+ self.security_mode = *byte;
+ self.d.next_state(SMB2SetupRequestState::Capabilities);
+ }
+ SMB2SetupRequestState::Capabilities => {
+ self.capabilities =
+ self.d
+ .read_ule32(byte, self.capabilities, SMB2SetupRequestState::Channel);
+ }
+ SMB2SetupRequestState::Channel => {
+ self.channel = self.d.read_ule32(
+ byte,
+ self.channel,
+ SMB2SetupRequestState::SecurityBufferOffset,
+ );
+ }
+ SMB2SetupRequestState::SecurityBufferOffset => {
+ self.security_buffer_offset = self.d.read_ule16(
+ byte,
+ self.security_buffer_offset,
+ SMB2SetupRequestState::SecurityLen,
+ );
+ }
+ SMB2SetupRequestState::SecurityLen => {
+ self.security_len = self.d.read_ule16(
+ byte,
+ self.security_len,
+ SMB2SetupRequestState::PreviousSessionId,
+ );
+ }
+ SMB2SetupRequestState::PreviousSessionId => {
+ self.previous_session_id = self.d.read_ule64(
+ byte,
+ self.previous_session_id,
+ SMB2SetupRequestState::SecurityBlob,
+ );
+ }
+ SMB2SetupRequestState::SecurityBlob => {
+ // TODO ? Not super useful TBH, also this is ASN.1 :///
+ self.d.next_state(SMB2SetupRequestState::End);
+ }
+ SMB2SetupRequestState::End => {}
+ }
+ }
+
+ fn repl(&self) -> Option> {
+ None
+ }
+}
+
+#[derive(Debug, Clone)]
+enum SMB2Payload {
+ NegotiateRequest(SMB2NegotiateRequest),
+ SetupRequest(SMB2SetupRequest),
+}
+
+impl SMB2Payload {
+ fn repl(&self) -> Option> {
+ match self {
+ SMB2Payload::NegotiateRequest(x) => x.repl(),
+ SMB2Payload::SetupRequest(x) => x.repl(),
+ }
+ }
+ fn parse(&mut self, byte: &u8) {
+ match self {
+ SMB2Payload::NegotiateRequest(x) => x.parse(byte),
+ SMB2Payload::SetupRequest(x) => x.parse(byte),
+ }
+ }
+}
+
+//////////////
+// Handlers //
+//////////////
+
+pub fn repl_smb1<'a>(
+ data: &'a [u8],
+ _masscanned: &Masscanned,
+ _client_info: &ClientInfo,
+) -> Option> {
+ let mut nbtsession: NBTSession = NBTSession::new();
+ for byte in data {
+ nbtsession.parse(byte);
+ }
+ nbtsession.repl()
+}
+
+pub fn repl_smb2<'a>(
+ data: &'a [u8],
+ _masscanned: &Masscanned,
+ _client_info: &ClientInfo,
+) -> Option> {
+ let mut nbtsession: NBTSession = NBTSession::new();
+ for byte in data {
+ nbtsession.parse(byte);
+ }
+ nbtsession.repl()
+}
+
+///////////
+// Tests //
+///////////
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use itertools::assert_equal;
+ use pnet::util::MacAddr;
+ use std::str::FromStr;
+
+ // Sent by `smbclient -U "" -N -L 10.1.1.1 -d10 --option='client min protocol=NT1'`
+ const SMB1_REQ_PAYLOAD: &[u8] = b"\x00\x00\x00T\xffSMBr\x00\x00\x00\x00\x18C\xc8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xfe\xff\x00\x00\x00\x00\x001\x00\x02NT LANMAN 1.0\x00\x02NT LM 0.12\x00\x02SMB 2.002\x00\x02SMB 2.???\x00";
+ // Sent by `smbclient -U "" -N -L 10.1.1.1 -d10`
+ const SMB2_REQ_PAYLOAD: &[u8] = b"\x00\x00\x00\xd0\xfeSMB@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00$\x00\x08\x00\x01\x00\x00\x00\x7f\x00\x00\x00\rr3\x97\"c\x8fA\x9f\xe0\xbawQ\x87rbx\x00\x00\x00\x03\x00\x00\x00\x02\x02\x10\x02\"\x02$\x02\x00\x03\x02\x03\x10\x03\x11\x03\x00\x00\x00\x00\x01\x00&\x00\x00\x00\x00\x00\x01\x00 \x00\x01\x00\xd5Z\x89\x87>\x80\xcd\x02\xc2\xab\x08\xa3\xf4\x94\xb6A\x05\x11V\xeeE\x19p\x19\xed\x17v\xda\x9b\x08\x99V\x00\x00\x02\x00\x06\x00\x00\x00\x00\x00\x02\x00\x02\x00\x01\x00\x00\x00\x05\x00\x10\x00\x00\x00\x00\x001\x000\x00.\x001\x00.\x001\x00.\x001\x00";
+ // You can dissect any of those payloads with Scapy using NBTSession(b"...")
+
+ #[test]
+ fn test_smb1_protocol_nego_parsing() {
+ let mut nbtsession: NBTSession = NBTSession::new();
+ nbtsession.parse_all(SMB1_REQ_PAYLOAD);
+ assert_eq!(nbtsession.nb_type, 0);
+ assert_eq!(nbtsession.length, 0x54);
+ let smb1 = nbtsession.payload.expect("Error while unpacking SMB");
+ assert_eq!(&smb1.start, b"\xffSMB");
+ assert_eq!(smb1.command, 0x72);
+ assert_eq!(smb1.status, 0);
+ assert_eq!(smb1.flags, 24);
+ assert_eq!(smb1.flags2, 51267);
+ assert_eq!(smb1.pid_high, 0);
+ assert_eq!(smb1.security_signature, [0; 8]);
+ assert_eq!(smb1.tid, 0);
+ assert_eq!(smb1.pid_low, 65534);
+ assert_eq!(smb1.uid, 0);
+ assert_eq!(smb1.mid, 0);
+ let neg_request = match smb1.payload.expect("Error while reading payload") {
+ SMB1Payload::NegotiateRequest(x) => x,
+ };
+ assert_eq!(neg_request.word_count, 0);
+ assert_eq!(neg_request.byte_count, 49);
+ assert_equal(
+ neg_request.dialects,
+ Vec::from([
+ SMB1Dialect {
+ buffer_format: 2,
+ dialect_string: "NT LANMAN 1.0".to_string(),
+ },
+ SMB1Dialect {
+ buffer_format: 2,
+ dialect_string: "NT LM 0.12".to_string(),
+ },
+ SMB1Dialect {
+ buffer_format: 2,
+ dialect_string: "SMB 2.002".to_string(),
+ },
+ SMB1Dialect {
+ buffer_format: 2,
+ dialect_string: "SMB 2.???".to_string(),
+ },
+ ]),
+ );
+ }
+ #[test]
+ fn test_smb1_protocol_nego_reply() {
+ let masscanned = Masscanned {
+ synack_key: [0, 0],
+ mac: MacAddr::from_str("00:00:00:00:00:00").expect("error parsing default MAC address"),
+ iface: None,
+ ip_addresses: None,
+ log: MetaLogger::new(),
+ };
+ let client_info = ClientInfo::new();
+ let answer =
+ repl_smb1(SMB1_REQ_PAYLOAD, &masscanned, &client_info).expect("Error: no answer");
+ let expected = [
+ 0, 0, 1, 149, 255, 83, 77, 66, 114, 0, 0, 0, 0, 152, 7, 200, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 254, 255, 0, 0, 0, 0, 17, 1, 0, 3, 50, 0, 50, 0, 0, 0, 1, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 252, 227, 1, 128, 0, 250, 218, 34, 238, 28, 216, 1, 60, 0, 0, 80, 1, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 130, 1, 60, 6, 6, 43, 6, 1, 5, 5, 2, 160,
+ 130, 1, 48, 48, 130, 1, 44, 160, 26, 48, 24, 6, 10, 43, 6, 1, 4, 1, 130, 55, 2, 2, 30,
+ 6, 10, 43, 6, 1, 4, 1, 130, 55, 2, 2, 10, 162, 130, 1, 12, 4, 130, 1, 8, 78, 69, 71,
+ 79, 69, 88, 84, 83, 1, 0, 0, 0, 0, 0, 0, 0, 96, 0, 0, 0, 112, 0, 0, 0, 49, 60, 42, 58,
+ 199, 43, 60, 169, 109, 172, 56, 116, 167, 221, 29, 91, 244, 82, 107, 23, 3, 138, 75,
+ 145, 194, 9, 125, 154, 143, 230, 44, 150, 92, 81, 36, 47, 144, 77, 71, 199, 173, 143,
+ 135, 107, 34, 2, 191, 198, 0, 0, 0, 0, 0, 0, 0, 0, 96, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 92, 51, 83, 13, 234, 249, 13, 77, 178, 236, 74, 227, 120, 110, 195, 8, 78,
+ 69, 71, 79, 69, 88, 84, 83, 3, 0, 0, 0, 1, 0, 0, 0, 64, 0, 0, 0, 152, 0, 0, 0, 49, 60,
+ 42, 58, 199, 43, 60, 169, 109, 172, 56, 116, 167, 221, 29, 91, 92, 51, 83, 13, 234,
+ 249, 13, 77, 178, 236, 74, 227, 120, 110, 195, 8, 64, 0, 0, 0, 88, 0, 0, 0, 48, 86,
+ 160, 84, 48, 82, 48, 39, 128, 37, 48, 35, 49, 33, 48, 31, 6, 3, 85, 4, 3, 19, 24, 84,
+ 111, 107, 101, 110, 32, 83, 105, 103, 110, 105, 110, 103, 32, 80, 117, 98, 108, 105,
+ 99, 32, 75, 101, 121, 48, 39, 128, 37, 48, 35, 49, 33, 48, 31, 6, 3, 85, 4, 3, 19, 24,
+ 84, 111, 107, 101, 110, 32, 83, 105, 103, 110, 105, 110, 103, 32, 80, 117, 98, 108,
+ 105, 99, 32, 75, 101, 121,
+ ];
+ assert_eq!(answer[..0x3c], expected[..0x3c]); // Test equality except "ServerTime" field
+ assert_eq!(answer[0x3c + 8..], expected[0x3c + 8..]);
+ }
+ #[test]
+ fn test_smb2_protocol_nego_parsing() {
+ let mut nbtsession: NBTSession = NBTSession::new();
+ nbtsession.parse_all(SMB2_REQ_PAYLOAD);
+ assert_eq!(nbtsession.nb_type, 0);
+ assert_eq!(nbtsession.length, 0xd0);
+ let smb2 = nbtsession.payload.expect("No SMB2 payload found !");
+ assert_eq!(&smb2.start, b"\xfeSMB");
+ assert_eq!(smb2.structure_size, 64);
+ assert_eq!(smb2.credit_charge, 0);
+ assert_eq!(smb2.status, 0);
+ assert_eq!(smb2.command, 0);
+ assert_eq!(smb2.credits_requested, 31);
+ assert_eq!(smb2.flags, 0);
+ assert_eq!(smb2.next_command, 0);
+ assert_eq!(smb2.message_id, 0);
+ assert_eq!(smb2.async_id, 0);
+ assert_eq!(smb2.session_id, 0);
+ assert_eq!(smb2.security_signature, [0; 16]);
+ let neg_request = match smb2.payload.expect("Error while reading payload") {
+ SMB2Payload::NegotiateRequest(x) => x,
+ _ => panic!("Invalid payload type"),
+ };
+ assert_eq!(neg_request.structure_size, 36);
+ assert_eq!(neg_request.dialect_count, 8);
+ assert_eq!(neg_request.security_mode, 1);
+ assert_eq!(neg_request.capabilities, 127);
+ assert_eq!(
+ neg_request.client_guid,
+ [13, 114, 51, 151, 34, 99, 143, 65, 159, 224, 186, 119, 81, 135, 114, 98]
+ );
+ assert_eq!(
+ neg_request.dialects,
+ HashSet::from([514, 528, 546, 548, 768, 770, 784, 785])
+ );
+ }
+ #[test]
+ fn test_smb2_protocol_nego_reply() {
+ let masscanned = Masscanned {
+ synack_key: [0, 0],
+ mac: MacAddr::from_str("00:00:00:00:00:00").expect("error parsing default MAC address"),
+ iface: None,
+ ip_addresses: None,
+ log: MetaLogger::new(),
+ };
+ let client_info = ClientInfo::new();
+ let answer =
+ repl_smb2(SMB2_REQ_PAYLOAD, &masscanned, &client_info).expect("Error: no answer");
+ let expected = [
+ 0, 0, 1, 192, 254, 83, 77, 66, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65, 0, 1, 0, 2, 2, 1, 0, 13, 114, 51, 151, 34,
+ 99, 143, 65, 159, 224, 186, 119, 81, 135, 114, 98, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 103, 222, 3, 242, 28, 216, 1, 0, 103, 222, 3, 242, 28, 216, 1, 128, 0,
+ 64, 1, 0, 0, 0, 0, 96, 130, 1, 60, 6, 6, 43, 6, 1, 5, 5, 2, 160, 130, 1, 48, 48, 130,
+ 1, 44, 160, 26, 48, 24, 6, 10, 43, 6, 1, 4, 1, 130, 55, 2, 2, 30, 6, 10, 43, 6, 1, 4,
+ 1, 130, 55, 2, 2, 10, 162, 130, 1, 12, 4, 130, 1, 8, 78, 69, 71, 79, 69, 88, 84, 83, 1,
+ 0, 0, 0, 0, 0, 0, 0, 96, 0, 0, 0, 112, 0, 0, 0, 49, 60, 42, 58, 199, 43, 60, 169, 109,
+ 172, 56, 116, 167, 221, 29, 91, 244, 82, 107, 23, 3, 138, 75, 145, 194, 9, 125, 154,
+ 143, 230, 44, 150, 92, 81, 36, 47, 144, 77, 71, 199, 173, 143, 135, 107, 34, 2, 191,
+ 198, 0, 0, 0, 0, 0, 0, 0, 0, 96, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 92, 51,
+ 83, 13, 234, 249, 13, 77, 178, 236, 74, 227, 120, 110, 195, 8, 78, 69, 71, 79, 69, 88,
+ 84, 83, 3, 0, 0, 0, 1, 0, 0, 0, 64, 0, 0, 0, 152, 0, 0, 0, 49, 60, 42, 58, 199, 43, 60,
+ 169, 109, 172, 56, 116, 167, 221, 29, 91, 92, 51, 83, 13, 234, 249, 13, 77, 178, 236,
+ 74, 227, 120, 110, 195, 8, 64, 0, 0, 0, 88, 0, 0, 0, 48, 86, 160, 84, 48, 82, 48, 39,
+ 128, 37, 48, 35, 49, 33, 48, 31, 6, 3, 85, 4, 3, 19, 24, 84, 111, 107, 101, 110, 32,
+ 83, 105, 103, 110, 105, 110, 103, 32, 80, 117, 98, 108, 105, 99, 32, 75, 101, 121, 48,
+ 39, 128, 37, 48, 35, 49, 33, 48, 31, 6, 3, 85, 4, 3, 19, 24, 84, 111, 107, 101, 110,
+ 32, 83, 105, 103, 110, 105, 110, 103, 32, 80, 117, 98, 108, 105, 99, 32, 75, 101, 121,
+ ];
+ assert_eq!(answer[..0x6c], expected[..0x6c]); // Test equality except the 2 "ServerTime" fields
+ assert_eq!(answer[0x6c + 16..], expected[0x6c + 16..]);
+ }
+}