smb: session setup improvements

Improve ntlmssp version extraction and logging, make its data structures
optional. Extract native os/lm from smb1 ssn setup.

Move session setup handling into their own files.

Only log auth data for the session setup tx.
pull/3281/head
Victor Julien 8 years ago
parent 75d7c9d64a
commit 8bef120898

@ -306,15 +306,21 @@ fn parse_secblob_spnego_start(blob: &[u8]) -> IResult<&[u8], &[u8]>
IResult::Done(rem, d)
}
fn parse_secblob_spnego(state: &mut SMBState, blob: &[u8])
pub struct SpnegoRequest {
pub krb: Option<Kerberos5Ticket>,
pub ntlmssp: Option<NtlmsspData>,
}
fn parse_secblob_spnego(blob: &[u8]) -> Option<SpnegoRequest>
{
let mut ntlmssp = false;
let mut kerberos = false;
let mut have_ntlmssp = false;
let mut have_kerberos = false;
let mut kticket : Option<Kerberos5Ticket> = None;
let mut ntlmssp : Option<NtlmsspData> = None;
let o = match der_parser::parse_der_sequence(blob) {
IResult::Done(_, o) => o,
_ => { return; },
_ => { return None; },
};
for s in o {
SCLogDebug!("s {:?}", s);
@ -337,11 +343,11 @@ fn parse_secblob_spnego(state: &mut SMBState, blob: &[u8])
SCLogDebug!("OID {:?}", oid);
match oid.to_string().as_str() {
"1.2.840.48018.1.2.2" => { SCLogDebug!("Microsoft Kerberos 5"); },
"1.2.840.113554.1.2.2" => { SCLogDebug!("Kerberos 5"); kerberos = true; },
"1.2.840.113554.1.2.2" => { SCLogDebug!("Kerberos 5"); have_kerberos = true; },
"1.2.840.113554.1.2.2.1" => { SCLogDebug!("krb5-name"); },
"1.2.840.113554.1.2.2.2" => { SCLogDebug!("krb5-principal"); },
"1.2.840.113554.1.2.2.3" => { SCLogDebug!("krb5-user-to-user-mech"); },
"1.3.6.1.4.1.311.2.2.10" => { SCLogDebug!("NTLMSSP"); ntlmssp = true; },
"1.3.6.1.4.1.311.2.2.10" => { SCLogDebug!("NTLMSSP"); have_ntlmssp = true; },
"1.3.6.1.4.1.311.2.2.30" => { SCLogDebug!("NegoEx"); },
_ => { SCLogNotice!("unexpected OID {:?}", oid); },
}
@ -351,7 +357,7 @@ fn parse_secblob_spnego(state: &mut SMBState, blob: &[u8])
}
},
der_parser::DerObjectContent::OctetString(ref os) => {
if kerberos {
if have_kerberos {
match parse_kerberos5_request(os) {
IResult::Done(_, t) => {
kticket = Some(t)
@ -360,16 +366,20 @@ fn parse_secblob_spnego(state: &mut SMBState, blob: &[u8])
}
}
if ntlmssp && kticket == None {
if have_ntlmssp && kticket == None {
SCLogDebug!("parsing expected NTLMSSP");
parse_ntlmssp_blob(state, os);
ntlmssp = parse_ntlmssp_blob(os);
}
},
_ => {},
}
}
state.krb_ticket = kticket;
let s = SpnegoRequest {
krb: kticket,
ntlmssp: ntlmssp,
};
Some(s)
}
#[derive(Debug,PartialEq)]
@ -377,11 +387,14 @@ pub struct NtlmsspData {
pub host: Vec<u8>,
pub user: Vec<u8>,
pub domain: Vec<u8>,
pub version: Option<NTLMSSPVersion>,
}
/// take in blob, search for the header and parse it
fn parse_ntlmssp_blob(state: &mut SMBState, blob: &[u8])
fn parse_ntlmssp_blob(blob: &[u8]) -> Option<NtlmsspData>
{
let mut ntlmssp_data : Option<NtlmsspData> = None;
SCLogDebug!("NTLMSSP {:?}", blob);
match parse_ntlmssp(blob) {
IResult::Done(_, nd) => {
@ -405,8 +418,9 @@ fn parse_ntlmssp_blob(state: &mut SMBState, blob: &[u8])
host: host,
user: user,
domain: domain,
version: ad.version,
};
state.ntlmssp = Some(d);
ntlmssp_data = Some(d);
},
_ => {},
}
@ -416,24 +430,43 @@ fn parse_ntlmssp_blob(state: &mut SMBState, blob: &[u8])
},
_ => {},
}
return ntlmssp_data;
}
// if spnego parsing fails try to fall back to ntlmssp
pub fn parse_secblob(state: &mut SMBState, blob: &[u8])
pub fn parse_secblob(blob: &[u8]) -> Option<SpnegoRequest>
{
match parse_secblob_get_spnego(blob) {
IResult::Done(_, spnego) => {
match parse_secblob_spnego_start(spnego) {
IResult::Done(_, spnego_start) => {
parse_secblob_spnego(state, spnego_start);
parse_secblob_spnego(spnego_start)
},
_ => {
parse_ntlmssp_blob(state, blob);
match parse_ntlmssp_blob(blob) {
Some(n) => {
let s = SpnegoRequest {
krb: None,
ntlmssp: Some(n),
};
Some(s)
},
None => { None },
}
},
}
},
_ => {
parse_ntlmssp_blob(state, blob);
match parse_ntlmssp_blob(blob) {
Some(n) => {
let s = SpnegoRequest {
krb: None,
ntlmssp: Some(n),
};
Some(s)
},
None => { None },
}
},
}
}

@ -90,55 +90,89 @@ fn smb_common_header(state: &SMBState, tx: &SMBTransaction) -> Json
js.set_boolean("request_done", tx.request_done);
js.set_boolean("response_done", tx.request_done);
match tx.type_data {
Some(SMBTransactionTypeData::SESSIONSETUP(ref x)) => {
if let Some(ref ntlmssp) = x.ntlmssp {
let jsd = Json::object();
let domain = match str::from_utf8(&ntlmssp.domain) {
Ok(v) => v,
Err(_) => "UTF8_ERROR",
};
jsd.set_string("domain", &domain);
match state.ntlmssp {
Some(ref ntlmssp) => {
let jsd = Json::object();
let domain = match str::from_utf8(&ntlmssp.domain) {
Ok(v) => v,
Err(_) => "UTF8_ERROR",
};
jsd.set_string("domain", &domain);
let user = match str::from_utf8(&ntlmssp.user) {
Ok(v) => v,
Err(_) => "UTF8_ERROR",
};
jsd.set_string("user", &user);
let user = match str::from_utf8(&ntlmssp.user) {
Ok(v) => v,
Err(_) => "UTF8_ERROR",
};
jsd.set_string("user", &user);
let host = match str::from_utf8(&ntlmssp.host) {
Ok(v) => v,
Err(_) => "UTF8_ERROR",
};
jsd.set_string("host", &host);
let host = match str::from_utf8(&ntlmssp.host) {
Ok(v) => v,
Err(_) => "UTF8_ERROR",
};
jsd.set_string("host", &host);
js.set("ntlmssp", jsd);
}
None => {},
}
if let Some(ref v) = ntlmssp.version {
jsd.set_string("version", v.to_string().as_str());
}
match state.krb_ticket {
Some(ref ticket) => {
let jsd = Json::object();
let realm = match str::from_utf8(&ticket.realm) {
Ok(v) => v,
Err(_) => "UTF8_ERROR",
};
jsd.set_string("realm", &realm);
let jsa = Json::array();
for sname in &ticket.snames {
let name = match str::from_utf8(&sname) {
js.set("ntlmssp", jsd);
}
if let Some(ref ticket) = x.krb_ticket {
let jsd = Json::object();
let realm = match str::from_utf8(&ticket.realm) {
Ok(v) => v,
Err(_) => "UTF8_ERROR",
};
jsa.array_append_string(&name);
jsd.set_string("realm", &realm);
let jsa = Json::array();
for sname in &ticket.snames {
let name = match str::from_utf8(&sname) {
Ok(v) => v,
Err(_) => "UTF8_ERROR",
};
jsa.array_append_string(&name);
}
jsd.set("snames", jsa);
js.set("kerberos", jsd);
}
jsd.set("snames", jsa);
js.set("kerberos", jsd);
},
None => { },
}
match tx.type_data {
match x.request_host {
Some(ref r) => {
let jsd = Json::object();
let os = match str::from_utf8(&r.native_os) {
Ok(v) => v,
Err(_) => "UTF8_ERROR",
};
jsd.set_string("native_os", &os);
let lm = match str::from_utf8(&r.native_lm) {
Ok(v) => v,
Err(_) => "UTF8_ERROR",
};
jsd.set_string("native_lm", &lm);
js.set("request", jsd);
},
None => { },
}
match x.response_host {
Some(ref r) => {
let jsd = Json::object();
let os = match str::from_utf8(&r.native_os) {
Ok(v) => v,
Err(_) => "UTF8_ERROR",
};
jsd.set_string("native_os", &os);
let lm = match str::from_utf8(&r.native_lm) {
Ok(v) => v,
Err(_) => "UTF8_ERROR",
};
jsd.set_string("native_lm", &lm);
js.set("response", jsd);
},
None => { },
}
},
Some(SMBTransactionTypeData::CREATE(ref x)) => {
let mut name_raw = x.filename.to_vec();
name_raw.retain(|&i|i != 0x00);

@ -23,8 +23,11 @@ pub mod ntlmssp_records;
pub mod smb;
pub mod smb1;
pub mod smb1_session;
pub mod smb2;
pub mod smb2_session;
pub mod dcerpc;
pub mod session;
pub mod log;
pub mod detect;
pub mod debug;

@ -15,17 +15,49 @@
* 02110-1301, USA.
*/
use nom::{rest, le_u16, le_u32};
use nom::{rest, le_u8, le_u16, le_u32};
#[derive(Debug,PartialEq)]
pub struct NTLMSSPVersion {
pub ver_major: u8,
pub ver_minor: u8,
pub ver_build: u16,
pub ver_ntlm_rev: u8,
}
impl NTLMSSPVersion {
pub fn to_string(&self) -> String {
format!("{}.{} build {} rev {}",
self.ver_major, self.ver_minor,
self.ver_build, self.ver_ntlm_rev)
}
}
named!(parse_ntlm_auth_version<NTLMSSPVersion>,
do_parse!(
ver_major: le_u8
>> ver_minor: le_u8
>> ver_build: le_u16
>> take!(3)
>> ver_ntlm_rev: le_u8
>> ( NTLMSSPVersion {
ver_major: ver_major,
ver_minor: ver_minor,
ver_build: ver_build,
ver_ntlm_rev: ver_ntlm_rev,
})
));
#[derive(Debug,PartialEq)]
pub struct NTLMSSPAuthRecord<'a> {
pub domain: &'a[u8],
pub user: &'a[u8],
pub host: &'a[u8],
pub version: Option<NTLMSSPVersion>,
}
named!(pub parse_ntlm_auth_record<NTLMSSPAuthRecord>,
dbg_dmp!(do_parse!(
do_parse!(
lm_blob_len: le_u16
>> lm_blob_maxlen: le_u16
>> lm_blob_offset: le_u32
@ -50,12 +82,15 @@ named!(pub parse_ntlm_auth_record<NTLMSSPAuthRecord>,
>> ssnkey_blob_maxlen: le_u16
>> ssnkey_blob_offset: le_u32
>> nego_flags: bits!(tuple!(take_bits!(u8, 6),take_bits!(u8,1),take_bits!(u32,25)))
>> version: cond!(nego_flags.1==1, parse_ntlm_auth_version)
// subtrack 12 as idenfier (8) and type (4) are cut before we are called
// subtract 48 for the len/offset/maxlen fields above
>> take!(domain_blob_offset - (12 + 48))
// subtract 60 for the len/offset/maxlen fields above
>> cond!(nego_flags.1==1, take!(domain_blob_offset - (12 + 60)))
// or 52 if we have no version
>> cond!(nego_flags.1==0, take!(domain_blob_offset - (12 + 52)))
//>> lm_blob: take!(lm_blob_len)
//>> ntlmresp_blob: take!(ntlmresp_blob_len)
>> domain_blob: take!(domain_blob_len)
>> user_blob: take!(user_blob_len)
>> host_blob: take!(host_blob_len)
@ -64,8 +99,10 @@ named!(pub parse_ntlm_auth_record<NTLMSSPAuthRecord>,
domain: domain_blob,
user: user_blob,
host: host_blob,
version: version,
})
)));
));
#[derive(Debug,PartialEq)]
pub struct NTLMSSPRecord<'a> {

@ -0,0 +1,75 @@
/* Copyright (C) 2018 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 log::*;
use smb::smb::*;
use smb::smb1_session::*;
use smb::auth::*;
#[derive(Debug)]
pub struct SMBTransactionSessionSetup {
pub request_host: Option<SessionSetupRequest>,
pub response_host: Option<SessionSetupResponse>,
pub ntlmssp: Option<NtlmsspData>,
pub krb_ticket: Option<Kerberos5Ticket>,
}
impl SMBTransactionSessionSetup {
pub fn new() -> SMBTransactionSessionSetup {
return SMBTransactionSessionSetup {
request_host: None,
response_host: None,
ntlmssp: None,
krb_ticket: None,
}
}
}
impl SMBState {
pub fn new_sessionsetup_tx(&mut self, hdr: SMBCommonHdr)
-> (&mut SMBTransaction)
{
let mut tx = self.new_tx();
tx.hdr = hdr;
tx.type_data = Some(SMBTransactionTypeData::SESSIONSETUP(
SMBTransactionSessionSetup::new()));
tx.request_done = true;
tx.response_done = self.tc_trunc; // no response expected if tc is truncated
SCLogDebug!("SMB: TX SESSIONSETUP created: ID {}", tx.id);
self.transactions.push(tx);
let tx_ref = self.transactions.last_mut();
return tx_ref.unwrap();
}
pub fn get_sessionsetup_tx(&mut self, hdr: SMBCommonHdr)
-> Option<&mut SMBTransaction>
{
for tx in &mut self.transactions {
let hit = tx.hdr == hdr && match tx.type_data {
Some(SMBTransactionTypeData::SESSIONSETUP(_)) => { true },
_ => { false },
};
if hit {
return Some(tx);
}
}
return None;
}
}

@ -45,8 +45,8 @@ use smb::smb2_records::*;
use smb::smb1::*;
use smb::smb2::*;
use smb::dcerpc::*;
use smb::session::*;
use smb::events::*;
use smb::auth::*;
use smb::files::*;
pub static mut SURICATA_SMB_FILE_CONFIG: Option<&'static SuricataFileContext> = None;
@ -311,6 +311,7 @@ pub enum SMBTransactionTypeData {
NEGOTIATE(SMBTransactionNegotiate),
DCERPC(SMBTransactionDCERPC),
CREATE(SMBTransactionCreate),
SESSIONSETUP(SMBTransactionSessionSetup),
}
#[derive(Debug)]
@ -600,8 +601,8 @@ pub struct SMBState<> {
pub ts_gap: bool, // last TS update was gap
pub tc_gap: bool, // last TC update was gap
ts_trunc: bool, // no more data for TOSERVER
tc_trunc: bool, // no more data for TOCLIENT
pub ts_trunc: bool, // no more data for TOSERVER
pub tc_trunc: bool, // no more data for TOCLIENT
/// transactions list
pub transactions: Vec<SMBTransaction>,
@ -616,9 +617,6 @@ pub struct SMBState<> {
/// dcerpc interfaces, stored here to be able to match
/// them while inspecting DCERPC REQUEST txs
pub dcerpc_ifaces: Option<Vec<DCERPCIface>>,
pub ntlmssp: Option<NtlmsspData>,
pub krb_ticket: Option<Kerberos5Ticket>,
}
impl SMBState {
@ -652,8 +650,6 @@ impl SMBState {
dialect_vec: None,
dialects: None,
dcerpc_ifaces: None,
ntlmssp: None,
krb_ticket: None,
}
}

@ -27,13 +27,14 @@ use nom::{IResult};
use core::*;
use log::*;
use smb::smb1_records::*;
use smb::smb::*;
use smb::dcerpc::*;
use smb::events::*;
use smb::auth::*;
use smb::files::*;
use smb::smb1_records::*;
use smb::smb1_session::*;
// https://msdn.microsoft.com/en-us/library/ee441741.aspx
pub const SMB1_COMMAND_CREATE_DIRECTORY: u8 = 0x00;
pub const SMB1_COMMAND_DELETE_DIRECTORY: u8 = 0x01;
@ -129,10 +130,6 @@ pub fn smb1_create_new_tx(_cmd: u8) -> bool {
pub fn smb1_request_record<'b>(state: &mut SMBState, r: &SmbRecord<'b>) -> u32 {
SCLogDebug!("record: {:?} command {}", r.greeter, r.command);
// init default tx keys
let mut key_ssn_id = r.ssn_id;
let key_tree_id = r.tree_id;
let key_multiplex_id = r.multiplex_id;
let mut events : Vec<SMBEvent> = Vec::new();
let mut no_response_expected = false;
@ -229,21 +226,8 @@ pub fn smb1_request_record<'b>(state: &mut SMBState, r: &SmbRecord<'b>) -> u32 {
},
SMB1_COMMAND_SESSION_SETUP_ANDX => {
SCLogDebug!("SMB1_COMMAND_SESSION_SETUP_ANDX user_id {}", r.user_id);
match parse_smb_setup_andx_record(r.data) {
IResult::Done(_, setup) => {
parse_secblob(state, setup.sec_blob);
/*
_ => {
events.push(SMBEvent::MalformedNtlmsspRequest);
},
*/
},
_ => {
events.push(SMBEvent::MalformedData);
},
}
key_ssn_id = 0;
false
smb1_session_setup_request(state, r);
true
},
SMB1_COMMAND_TREE_CONNECT_ANDX => {
SCLogDebug!("SMB1_COMMAND_TREE_CONNECT_ANDX");
@ -353,8 +337,7 @@ pub fn smb1_request_record<'b>(state: &mut SMBState, r: &SmbRecord<'b>) -> u32 {
};
if !have_tx {
if smb1_create_new_tx(r.command) {
let tx_key = SMBCommonHdr::new(SMBHDR_TYPE_GENERICTX,
key_ssn_id as u64, key_tree_id as u32, key_multiplex_id as u64);
let tx_key = SMBCommonHdr::from1(r, SMBHDR_TYPE_GENERICTX);
let tx = state.new_generic_tx(1, r.command as u16, tx_key);
SCLogDebug!("tx {} created for {}/{}", tx.id, r.command, &smb1_command_string(r.command));
tx.set_events(events);
@ -510,8 +493,20 @@ pub fn smb1_response_record<'b>(state: &mut SMBState, r: &SmbRecord<'b>) -> u32
true
},
SMB1_COMMAND_SESSION_SETUP_ANDX => {
/*
SCLogDebug!("SMB1_COMMAND_SESSION_SETUP_ANDX user_id {}", r.user_id);
match parse_smb_response_setup_andx_record(r.data) {
IResult::Done(rem, _setup) => {
//parse_secblob(state, setup.sec_blob);
state.response_host = Some(smb1_session_setup_response_host_info(r, rem));
},
_ => {},
}
tx_sync = true;
false
*/
smb1_session_setup_response(state, r);
true
},
SMB1_COMMAND_LOGOFF_ANDX => {
tx_sync = true;

@ -400,12 +400,29 @@ named!(pub parse_smb_setup_andx_record<SmbRecordSetupAndX>,
>> skip2: take!(8)
>> bcc: le_u16
>> sec_blob: take!(sec_blob_len)
>> skip3: rest
//>> skip3: rest
>> (SmbRecordSetupAndX {
sec_blob:sec_blob,
}))
);
#[derive(Debug,PartialEq)]
pub struct SmbResponseRecordSetupAndX<'a> {
pub sec_blob: &'a[u8],
}
named!(pub parse_smb_response_setup_andx_record<SmbResponseRecordSetupAndX>,
do_parse!(
skip1: take!(7)
>> sec_blob_len: le_u16
>> bcc: le_u16
>> sec_blob: take!(sec_blob_len)
//>> skip3: rest
>> (SmbResponseRecordSetupAndX {
sec_blob:sec_blob,
}))
);
#[derive(Debug,PartialEq)]
pub struct SmbRequestReadAndXRecord<'a> {
pub fid: &'a[u8],

@ -0,0 +1,234 @@
/* Copyright (C) 2018 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::{IResult, ErrorKind};
use log::*;
use smb::smb1_records::*;
use smb::smb::*;
use smb::events::*;
use smb::auth::*;
#[derive(Debug)]
pub struct SessionSetupRequest {
pub native_os: Vec<u8>,
pub native_lm: Vec<u8>,
pub primary_domain: Vec<u8>,
}
#[derive(Debug)]
pub struct SessionSetupResponse {
pub native_os: Vec<u8>,
pub native_lm: Vec<u8>,
}
fn get_unicode_string(blob: &[u8]) -> IResult<&[u8], Vec<u8>>
{
SCLogDebug!("get_unicode_string: blob {} {:?}", blob.len(), blob);
let mut name : Vec<u8> = Vec::new();
let mut c = blob;
while c.len() >= 1 {
if c.len() == 1 && c[0] == 0 {
let rem = &c[1..];
SCLogDebug!("get_unicode_string: name {:?}", name);
return IResult::Done(rem, name)
} else if c.len() == 1 {
break;
} else if c[0] == 0 && c[1] == 0 {
let rem = &c[2..];
SCLogDebug!("get_unicode_string: name {:?}", name);
return IResult::Done(rem, name)
}
name.push(c[0]);
c = &c[2..];
//SCLogNotice!("get_unicode_string: c {:?}", c);
}
IResult::Error(error_code!(ErrorKind::Custom(130)))
}
named!(pub get_nullterm_string<Vec<u8>>,
do_parse!(
s: take_until_and_consume!("\x00")
>> ( s.to_vec() )
));
pub fn smb1_session_setup_request_host_info(r: &SmbRecord, blob: &[u8]) -> SessionSetupRequest
{
if blob.len() > 1 && r.flags2 & 0x8000_u16 != 0 {
let offset = r.data.len() - blob.len();
let blob = if offset % 2 == 1 { &blob[1..] } else { blob };
let (native_os, native_lm, primary_domain) = match get_unicode_string(blob) {
IResult::Done(rem, n1) => {
match get_unicode_string(rem) {
IResult::Done(rem, n2) => {
match get_unicode_string(rem) {
IResult::Done(_, n3) => { (n1, n2, n3) },
_ => { (n1, n2, Vec::new()) },
}
},
_ => { (n1, Vec::new(), Vec::new()) },
}
},
_ => { (Vec::new(), Vec::new(), Vec::new()) },
};
SCLogDebug!("name1 {:?} name2 {:?} name3 {:?}", native_os,native_lm,primary_domain);
SessionSetupRequest {
native_os:native_os,
native_lm:native_lm,
primary_domain:primary_domain,
}
} else {
let (native_os, native_lm, primary_domain) = match get_nullterm_string(blob) {
IResult::Done(rem, n1) => {
match get_nullterm_string(rem) {
IResult::Done(rem, n2) => {
match get_nullterm_string(rem) {
IResult::Done(_, n3) => { (n1, n2, n3) },
_ => { (n1, n2, Vec::new()) },
}
},
_ => { (n1, Vec::new(), Vec::new()) },
}
},
_ => { (Vec::new(), Vec::new(), Vec::new()) },
};
SCLogDebug!("session_setup_request_host_info: not unicode");
SessionSetupRequest {
native_os: native_os,
native_lm: native_lm,
primary_domain: primary_domain,
}
}
}
pub fn smb1_session_setup_response_host_info(r: &SmbRecord, blob: &[u8]) -> SessionSetupResponse
{
if blob.len() > 1 && r.flags2 & 0x8000_u16 != 0 {
let offset = r.data.len() - blob.len();
let blob = if offset % 2 == 1 { &blob[1..] } else { blob };
let (native_os, native_lm) = match get_unicode_string(blob) {
IResult::Done(rem, n1) => {
match get_unicode_string(rem) {
IResult::Done(_, n2) => {
(n1, n2)
},
_ => { (n1, Vec::new()) },
}
},
_ => { (Vec::new(), Vec::new()) },
};
SCLogDebug!("name1 {:?} name2 {:?}", native_os,native_lm);
SessionSetupResponse {
native_os:native_os,
native_lm:native_lm,
}
} else {
SCLogDebug!("session_setup_response_host_info: not unicode");
let (native_os, native_lm) = match get_nullterm_string(blob) {
IResult::Done(rem, n1) => {
match get_nullterm_string(rem) {
IResult::Done(_, n2) => {
(n1, n2)
},
_ => { (n1, Vec::new()) },
}
},
_ => { (Vec::new(), Vec::new()) },
};
SessionSetupResponse {
native_os: native_os,
native_lm: native_lm,
}
}
}
pub fn smb1_session_setup_request(state: &mut SMBState, r: &SmbRecord)
{
SCLogDebug!("SMB1_COMMAND_SESSION_SETUP_ANDX user_id {}", r.user_id);
match parse_smb_setup_andx_record(r.data) {
IResult::Done(rem, setup) => {
let hdr = SMBCommonHdr::from1(r, SMBHDR_TYPE_HEADER);
let tx = state.new_sessionsetup_tx(hdr);
tx.vercmd.set_smb1_cmd(r.command);
if let Some(SMBTransactionTypeData::SESSIONSETUP(ref mut td)) = tx.type_data {
match parse_secblob(setup.sec_blob) {
Some(s) => {
td.ntlmssp = s.ntlmssp;
td.krb_ticket = s.krb;
},
None => { },
}
td.request_host = Some(smb1_session_setup_request_host_info(r, rem));
}
},
_ => {
// events.push(SMBEvent::MalformedData);
},
}
}
fn smb1_session_setup_update_tx(tx: &mut SMBTransaction, r: &SmbRecord)
{
match parse_smb_response_setup_andx_record(r.data) {
IResult::Done(rem, _setup) => {
if let Some(SMBTransactionTypeData::SESSIONSETUP(ref mut td)) = tx.type_data {
td.response_host = Some(smb1_session_setup_response_host_info(r, rem));
}
},
_ => {
tx.set_event(SMBEvent::MalformedData);
},
}
// update tx even if we can't parse the response
tx.hdr = SMBCommonHdr::from1(r, SMBHDR_TYPE_HEADER); // to overwrite ssn_id 0
tx.set_status(r.nt_status, r.is_dos_error);
tx.response_done = true;
}
pub fn smb1_session_setup_response(state: &mut SMBState, r: &SmbRecord)
{
// try exact match with session id already set (e.g. NTLMSSP AUTH phase)
let found = r.ssn_id != 0 && match state.get_sessionsetup_tx(
SMBCommonHdr::from1(r, SMBHDR_TYPE_HEADER))
{
Some(tx) => {
smb1_session_setup_update_tx(tx, r);
SCLogDebug!("smb1_session_setup_response: tx {:?}", tx);
true
},
None => { false },
};
// otherwise try match with ssn id 0 (e.g. NTLMSSP_NEGOTIATE)
if !found {
match state.get_sessionsetup_tx(
SMBCommonHdr::new(SMBHDR_TYPE_HEADER, 0, 0, r.multiplex_id as u64))
{
Some(tx) => {
smb1_session_setup_update_tx(tx, r);
SCLogDebug!("smb1_session_setup_response: tx {:?}", tx);
},
None => {
SCLogNotice!("smb1_session_setup_response: tx not found for {:?}", r);
},
}
}
}

@ -21,9 +21,9 @@ use nom::IResult;
use smb::smb::*;
use smb::smb2_records::*;
use smb::smb2_session::*;
use smb::dcerpc::*;
use smb::events::*;
use smb::auth::*;
use smb::files::*;
pub const SMB2_COMMAND_NEGOTIATE_PROTOCOL: u16 = 0;
@ -324,47 +324,8 @@ pub fn smb2_request_record<'b>(state: &mut SMBState, r: &Smb2Record<'b>)
}
},
SMB2_COMMAND_SESSION_SETUP => {
SCLogDebug!("SMB2_COMMAND_SESSION_SETUP: r.data.len() {}", r.data.len());
match parse_smb2_request_session_setup(r.data) {
IResult::Done(_, setup) => {
SCLogDebug!("SMB2_COMMAND_SESSION_SETUP parsed. rd.data.len() {}", setup.data.len());
parse_secblob(state, setup.data);
/*
match parse_ntlmssp(rd.data) {
IResult::Done(_, nd) => {
SCLogDebug!("NTLMSSP TYPE {}/{} nd {:?}",
nd.msg_type, &ntlmssp_type_string(nd.msg_type), nd);
match nd.msg_type {
NTLMSSP_NEGOTIATE => {
key_session_id = 0;
},
NTLMSSP_AUTH => {
match parse_ntlm_auth_record(nd.data) {
IResult::Done(_, ad) => {
SCLogDebug!("auth data {:?}", ad);
state.ntlmssp_host = ad.host.to_vec();
state.ntlmssp_user = ad.user.to_vec();
state.ntlmssp_domain = ad.domain.to_vec();
},
_ => {
events.push(SMBEvent::MalformedData);
},
}
},
_ => { },
}
},
_ => {
SCLogNotice!("error parsing ntlmssp");
},
}
*/
},
_ => {
events.push(SMBEvent::MalformedData);
},
}
false
smb2_session_setup_request(state, r);
true
},
SMB2_COMMAND_TREE_CONNECT => {
match parse_smb2_request_tree_connect(r.data) {
@ -490,34 +451,6 @@ pub fn smb2_request_record<'b>(state: &mut SMBState, r: &Smb2Record<'b>)
}
}
fn smb2_response_record_session_setup<'b>(state: &mut SMBState, r: &Smb2Record<'b>)
{
// first try exact match
let found = match state.get_generic_tx(2, r.command, &SMBCommonHdr::from2(r, SMBHDR_TYPE_GENERICTX)) {
Some(tx) => {
if r.nt_status != SMB_NTSTATUS_PENDING {
tx.response_done = true;
}
tx.set_status(r.nt_status, false);
true
},
None => false,
};
// if not found try with ssn id 0.
if !found {
let tx_key = SMBCommonHdr::new(SMBHDR_TYPE_GENERICTX, 0, 0, r.message_id);
match state.get_generic_tx(2, r.command, &tx_key) {
Some(tx) => {
if r.nt_status != SMB_NTSTATUS_PENDING {
tx.response_done = true;
}
tx.set_status(r.nt_status, false);
},
None => { },
}
}
}
pub fn smb2_response_record<'b>(state: &mut SMBState, r: &Smb2Record<'b>)
{
SCLogDebug!("SMBv2 response record, command {} status {} tree {} session {} message {}",
@ -578,7 +511,7 @@ pub fn smb2_response_record<'b>(state: &mut SMBState, r: &Smb2Record<'b>)
have_ioctl_tx
},
SMB2_COMMAND_SESSION_SETUP => {
smb2_response_record_session_setup(state, r);
smb2_session_setup_response(state, r);
true
},
SMB2_COMMAND_WRITE => {

@ -0,0 +1,83 @@
/* Copyright (C) 2018 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::{IResult};
use log::*;
use smb::smb2_records::*;
use smb::smb::*;
//use smb::events::*;
use smb::auth::*;
pub fn smb2_session_setup_request(state: &mut SMBState, r: &Smb2Record)
{
SCLogDebug!("SMB2_COMMAND_SESSION_SETUP: r.data.len() {}", r.data.len());
match parse_smb2_request_session_setup(r.data) {
IResult::Done(_, setup) => {
let hdr = SMBCommonHdr::from2(r, SMBHDR_TYPE_HEADER);
let tx = state.new_sessionsetup_tx(hdr);
tx.vercmd.set_smb2_cmd(r.command);
if let Some(SMBTransactionTypeData::SESSIONSETUP(ref mut td)) = tx.type_data {
if let Some(s) = parse_secblob(setup.data) {
td.ntlmssp = s.ntlmssp;
td.krb_ticket = s.krb;
}
}
},
_ => {
// events.push(SMBEvent::MalformedData);
},
}
}
fn smb2_session_setup_update_tx(tx: &mut SMBTransaction, r: &Smb2Record)
{
tx.hdr = SMBCommonHdr::from2(r, SMBHDR_TYPE_HEADER); // to overwrite ssn_id 0
tx.set_status(r.nt_status, false);
tx.response_done = true;
}
pub fn smb2_session_setup_response(state: &mut SMBState, r: &Smb2Record)
{
// try exact match with session id already set (e.g. NTLMSSP AUTH phase)
let found = r.session_id != 0 && match state.get_sessionsetup_tx(
SMBCommonHdr::from2(r, SMBHDR_TYPE_HEADER))
{
Some(tx) => {
smb2_session_setup_update_tx(tx, r);
SCLogDebug!("smb2_session_setup_response: tx {:?}", tx);
true
},
None => { false },
};
// otherwise try match with ssn id 0 (e.g. NTLMSSP_NEGOTIATE)
if !found {
match state.get_sessionsetup_tx(
SMBCommonHdr::new(SMBHDR_TYPE_HEADER, 0, 0, r.message_id))
{
Some(tx) => {
smb2_session_setup_update_tx(tx, r);
SCLogDebug!("smb2_session_setup_response: tx {:?}", tx);
},
None => {
SCLogNotice!("smb2_session_setup_response: tx not found for {:?}", r);
},
}
}
}
Loading…
Cancel
Save