]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
smb2/dcerpc: probe if response data is dcerpc
authorVictor Julien <victor@inliniac.net>
Wed, 3 Oct 2018 17:55:46 +0000 (19:55 +0200)
committerVictor Julien <victor@inliniac.net>
Sun, 7 Oct 2018 08:08:51 +0000 (10:08 +0200)
If we missed the tree connect we can't know for sure if we're
reading from a (DCERPC) PIPE or not. In this case probe the data
to see if it looks like DCERPC.

If the detection succeeds, use a special 'suricata::dcerpc' service
in the TX.

Simplify handling of DCERPC records that cross records

Update logging for the response only TXs.

rust/src/smb/dcerpc.rs
rust/src/smb/log.rs
rust/src/smb/smb.rs
rust/src/smb/smb1.rs
rust/src/smb/smb2.rs

index 9a3303f6e2a5dfa39a3588660bae25b275c6b5b2..89ef3bbb5a921235568c927f11f2b65f09a1bb14 100644 (file)
@@ -149,6 +149,7 @@ pub fn dcerpc_uuid_to_string(i: &DCERPCIface) -> String {
 pub struct SMBTransactionDCERPC {
     pub opnum: u16,
     pub req_cmd: u8,
+    pub req_set: bool,
     pub res_cmd: u8,
     pub res_set: bool,
     pub call_id: u32,
@@ -159,10 +160,25 @@ pub struct SMBTransactionDCERPC {
 }
 
 impl SMBTransactionDCERPC {
-    pub fn new(req: u8, call_id: u32) -> SMBTransactionDCERPC {
+    fn new_request(req: u8, call_id: u32) -> SMBTransactionDCERPC {
         return SMBTransactionDCERPC {
             opnum: 0,
             req_cmd: req,
+            req_set: true,
+            res_cmd: 0,
+            res_set: false,
+            call_id: call_id,
+            frag_cnt_ts: 0,
+            frag_cnt_tc: 0,
+            stub_data_ts:Vec::new(),
+            stub_data_tc:Vec::new(),
+        }
+    }
+    fn new_response(call_id: u32) -> SMBTransactionDCERPC {
+        return SMBTransactionDCERPC {
+            opnum: 0,
+            req_cmd: 0,
+            req_set: false,
             res_cmd: 0,
             res_set: false,
             call_id: call_id,
@@ -179,14 +195,14 @@ impl SMBTransactionDCERPC {
 }
 
 impl SMBState {
-    pub fn new_dcerpc_tx(&mut self, hdr: SMBCommonHdr, vercmd: SMBVerCmdStat, cmd: u8, call_id: u32)
+    fn new_dcerpc_tx(&mut self, hdr: SMBCommonHdr, vercmd: SMBVerCmdStat, cmd: u8, call_id: u32)
         -> (&mut SMBTransaction)
     {
         let mut tx = self.new_tx();
         tx.hdr = hdr;
         tx.vercmd = vercmd;
         tx.type_data = Some(SMBTransactionTypeData::DCERPC(
-                    SMBTransactionDCERPC::new(cmd, call_id)));
+                    SMBTransactionDCERPC::new_request(cmd, call_id)));
 
         SCLogDebug!("SMB: TX DCERPC created: ID {} hdr {:?}", tx.id, tx.hdr);
         self.transactions.push(tx);
@@ -194,7 +210,22 @@ impl SMBState {
         return tx_ref.unwrap();
     }
 
-    pub fn get_dcerpc_tx(&mut self, hdr: &SMBCommonHdr, vercmd: &SMBVerCmdStat, call_id: u32)
+    fn new_dcerpc_tx_for_response(&mut self, hdr: SMBCommonHdr, vercmd: SMBVerCmdStat, call_id: u32)
+        -> (&mut SMBTransaction)
+    {
+        let mut tx = self.new_tx();
+        tx.hdr = hdr;
+        tx.vercmd = vercmd;
+        tx.type_data = Some(SMBTransactionTypeData::DCERPC(
+                    SMBTransactionDCERPC::new_response(call_id)));
+
+        SCLogDebug!("SMB: TX DCERPC created: ID {} hdr {:?}", tx.id, tx.hdr);
+        self.transactions.push(tx);
+        let tx_ref = self.transactions.last_mut();
+        return tx_ref.unwrap();
+    }
+
+    fn get_dcerpc_tx(&mut self, hdr: &SMBCommonHdr, vercmd: &SMBVerCmdStat, call_id: u32)
         -> Option<&mut SMBTransaction>
     {
         let dce_hdr = hdr.to_dcerpc(vercmd);
@@ -422,6 +453,51 @@ fn smb_read_dcerpc_record_error(state: &mut SMBState,
     return found;
 }
 
+fn dcerpc_response_handle<'b>(tx: &mut SMBTransaction,
+        vercmd: SMBVerCmdStat,
+        dcer: &DceRpcRecord)
+{
+    let (_, ntstatus) = vercmd.get_ntstatus();
+    match dcer.packet_type {
+        DCERPC_TYPE_RESPONSE => {
+            match parse_dcerpc_response_record(dcer.data, dcer.frag_len) {
+                IResult::Done(_, respr) => {
+                    SCLogDebug!("SMBv1 READ RESPONSE {:?}", respr);
+                    if let Some(SMBTransactionTypeData::DCERPC(ref mut tdn)) = tx.type_data {
+                        SCLogDebug!("CMD 11 found at tx {}", tx.id);
+                        tdn.set_result(DCERPC_TYPE_RESPONSE);
+                        tdn.stub_data_tc.extend_from_slice(&respr.data);
+                        tdn.frag_cnt_tc += 1;
+                    }
+                    tx.vercmd.set_ntstatus(ntstatus);
+                    tx.response_done = dcer.last_frag;
+                },
+                _ => {
+                    tx.set_event(SMBEvent::MalformedData);
+                },
+            }
+        },
+        DCERPC_TYPE_BINDACK => {
+            // handled elsewhere
+        },
+        21...255 => {
+            if let Some(SMBTransactionTypeData::DCERPC(ref mut tdn)) = tx.type_data {
+                tdn.set_result(dcer.packet_type);
+            }
+            tx.vercmd.set_ntstatus(ntstatus);
+            tx.response_done = true;
+            tx.set_event(SMBEvent::MalformedData);
+        }
+        _ => { // valid type w/o special processing
+            if let Some(SMBTransactionTypeData::DCERPC(ref mut tdn)) = tx.type_data {
+                tdn.set_result(dcer.packet_type);
+            }
+            tx.vercmd.set_ntstatus(ntstatus);
+            tx.response_done = true;
+        },
+    }
+}
+
 /// Handle DCERPC reply record. Called for READ, TRANS, IOCTL
 ///
 pub fn smb_read_dcerpc_record<'b>(state: &mut SMBState,
@@ -432,19 +508,17 @@ pub fn smb_read_dcerpc_record<'b>(state: &mut SMBState,
 {
     let (_, ntstatus) = vercmd.get_ntstatus();
 
-    if ntstatus != SMB_NTSTATUS_SUCCESS {
+    if ntstatus != SMB_NTSTATUS_SUCCESS && ntstatus != SMB_NTSTATUS_BUFFER_OVERFLOW {
         return smb_read_dcerpc_record_error(state, hdr, vercmd, ntstatus);
     }
 
     SCLogDebug!("lets first see if we have prior data");
     // msg_id 0 as this data crosses cmd/reply pairs
-    let ehdr = SMBCommonHdr::new(SMBHDR_TYPE_TRANS_FRAG,
-            hdr.ssn_id as u64, hdr.tree_id as u32, 0 as u64);
-    let ekey = SMBHashKeyHdrGuid::new(ehdr, guid.to_vec());
-    SCLogDebug!("ekey {:?}", ekey);
-    let mut prevdata = match state.ssnguid2vec_map.remove(&ekey) {
+    let ehdr = SMBHashKeyHdrGuid::new(SMBCommonHdr::new(SMBHDR_TYPE_TRANS_FRAG,
+            hdr.ssn_id as u64, hdr.tree_id as u32, 0 as u64), guid.to_vec());
+    let mut prevdata = match state.ssnguid2vec_map.remove(&ehdr) {
         Some(s) => s,
-            None => Vec::new(),
+        None => Vec::new(),
     };
     SCLogDebug!("indata {} prevdata {}", indata.len(), prevdata.len());
     prevdata.extend_from_slice(&indata);
@@ -464,31 +538,10 @@ pub fn smb_read_dcerpc_record<'b>(state: &mut SMBState,
                         dcer.version_major, dcer.version_minor, dcer.data.len(), dcer);
 
                 if ntstatus == SMB_NTSTATUS_BUFFER_OVERFLOW && data.len() < dcer.frag_len as usize {
-                    SCLogDebug!("short record {} < {}: lets see if we expected this", data.len(), dcer.frag_len);
-                    let ehdr = SMBCommonHdr::new(SMBHDR_TYPE_MAX_SIZE,
-                            hdr.ssn_id as u64, hdr.tree_id as u32, hdr.msg_id as u64);
-                    let max_size = match state.ssn2maxsize_map.remove(&ehdr) {
-                        Some(s) => s,
-                        None => 0,
-                    };
-                    if max_size as usize == data.len() {
-                        SCLogDebug!("YES this is expected. We should now see a follow up READ for {} bytes",
-                                dcer.frag_len as usize - data.len());
-                        let store = match state.get_dcerpc_tx(&hdr, &vercmd, dcer.call_id) {
-                            Some(_) => true,
-                            None => {
-                                SCLogDebug!("no TX found");
-                                false
-                            },
-                        };
-                        if store {
-                            SCLogDebug!("storing partial data in state");
-                            let ehdr = SMBCommonHdr::new(SMBHDR_TYPE_MAX_SIZE,
-                                    hdr.ssn_id as u64, hdr.tree_id as u32, 0 as u64);
-                            state.ssn2vec_map.insert(ehdr, data.to_vec());
-                        }
-                        return true; // TODO review
-                    }
+                    SCLogDebug!("short record {} < {}: storing partial data in state",
+                            data.len(), dcer.frag_len);
+                    state.ssnguid2vec_map.insert(ehdr, data.to_vec());
+                    return true; // TODO review
                 }
 
                 if dcer.packet_type == DCERPC_TYPE_BINDACK {
@@ -496,50 +549,20 @@ pub fn smb_read_dcerpc_record<'b>(state: &mut SMBState,
                     return true;
                 }
 
-                let tx = match state.get_dcerpc_tx(&hdr, &vercmd, dcer.call_id) {
-                    Some(tx) => tx,
+                let found = match state.get_dcerpc_tx(&hdr, &vercmd, dcer.call_id) {
+                    Some(tx) => {
+                        dcerpc_response_handle(tx, vercmd.clone(), &dcer);
+                        true
+                    },
                     None => {
                         SCLogDebug!("no tx");
-                        return false; },
-                };
-
-                match dcer.packet_type {
-                    DCERPC_TYPE_RESPONSE => {
-                        match parse_dcerpc_response_record(dcer.data, dcer.frag_len) {
-                            IResult::Done(_, respr) => {
-                                SCLogDebug!("SMBv1 READ RESPONSE {:?}", respr);
-                                if let Some(SMBTransactionTypeData::DCERPC(ref mut tdn)) = tx.type_data {
-                                    SCLogDebug!("CMD 11 found at tx {}", tx.id);
-                                    tdn.set_result(DCERPC_TYPE_RESPONSE);
-                                    tdn.stub_data_tc.extend_from_slice(&respr.data);
-                                    tdn.frag_cnt_tc += 1;
-                                }
-                                tx.vercmd.set_ntstatus(ntstatus);
-                                tx.response_done = dcer.last_frag;
-                            },
-                            _ => {
-                                tx.set_event(SMBEvent::MalformedData);
-                            },
-                        }
-                    },
-                    DCERPC_TYPE_BINDACK => {
-                        // handled above
-                    },
-                    21...255 => {
-                        if let Some(SMBTransactionTypeData::DCERPC(ref mut tdn)) = tx.type_data {
-                            tdn.set_result(dcer.packet_type);
-                        }
-                        tx.vercmd.set_ntstatus(ntstatus);
-                        tx.response_done = true;
-                        tx.set_event(SMBEvent::MalformedData);
-                    }
-                    _ => { // valid type w/o special processing
-                        if let Some(SMBTransactionTypeData::DCERPC(ref mut tdn)) = tx.type_data {
-                            tdn.set_result(dcer.packet_type);
-                        }
-                        tx.vercmd.set_ntstatus(ntstatus);
-                        tx.response_done = true;
+                        false
                     },
+                };
+                if !found {
+                    // pick up DCERPC tx even if we missed the request
+                    let tx = state.new_dcerpc_tx_for_response(hdr, vercmd.clone(), dcer.call_id);
+                    dcerpc_response_handle(tx, vercmd, &dcer);
                 }
             },
             _ => {
index 48c531feb155d1f23fc88c45237de577a22edda2..2209c19cd531feb5b9c519f02a09a587100a40c0 100644 (file)
@@ -312,49 +312,63 @@ fn smb_common_header(state: &SMBState, tx: &SMBTransaction) -> Json
         },
         Some(SMBTransactionTypeData::DCERPC(ref x)) => {
             let jsd = Json::object();
-            jsd.set_string("request", &dcerpc_type_string(x.req_cmd));
+            if x.req_set {
+                jsd.set_string("request", &dcerpc_type_string(x.req_cmd));
+            } else {
+                jsd.set_string("request", "REQUEST_LOST");
+            }
             if x.res_set {
                 jsd.set_string("response", &dcerpc_type_string(x.res_cmd));
             } else {
                 jsd.set_string("response", "UNREPLIED");
             }
-            match x.req_cmd {
-                DCERPC_TYPE_REQUEST => {
-                    jsd.set_integer("opnum", x.opnum as u64);
-                    let req = Json::object();
-                    req.set_integer("frag_cnt", x.frag_cnt_ts as u64);
-                    req.set_integer("stub_data_size", x.stub_data_ts.len() as u64);
-                    jsd.set("req", req);
-                    let res = Json::object();
-                    res.set_integer("frag_cnt", x.frag_cnt_tc as u64);
-                    res.set_integer("stub_data_size", x.stub_data_tc.len() as u64);
-                    jsd.set("res", res);
-                },
-                DCERPC_TYPE_BIND => {
-                    match state.dcerpc_ifaces {
-                        Some(ref ifaces) => {
-                            let jsa = Json::array();
-                            for i in ifaces {
-                                let jso = Json::object();
-                                let ifstr = dcerpc_uuid_to_string(&i);
-                                jso.set_string("uuid", &ifstr);
-                                let vstr = format!("{}.{}", i.ver, i.ver_min);
-                                jso.set_string("version", &vstr);
-
-                                if i.acked {
-                                    jso.set_integer("ack_result", i.ack_result as u64);
-                                    jso.set_integer("ack_reason", i.ack_reason as u64);
+            if x.req_set {
+                match x.req_cmd {
+                    DCERPC_TYPE_REQUEST => {
+                        jsd.set_integer("opnum", x.opnum as u64);
+                        let req = Json::object();
+                        req.set_integer("frag_cnt", x.frag_cnt_ts as u64);
+                        req.set_integer("stub_data_size", x.stub_data_ts.len() as u64);
+                        jsd.set("req", req);
+                    },
+                    DCERPC_TYPE_BIND => {
+                        match state.dcerpc_ifaces {
+                            Some(ref ifaces) => {
+                                let jsa = Json::array();
+                                for i in ifaces {
+                                    let jso = Json::object();
+                                    let ifstr = dcerpc_uuid_to_string(&i);
+                                    jso.set_string("uuid", &ifstr);
+                                    let vstr = format!("{}.{}", i.ver, i.ver_min);
+                                    jso.set_string("version", &vstr);
+
+                                    if i.acked {
+                                        jso.set_integer("ack_result", i.ack_result as u64);
+                                        jso.set_integer("ack_reason", i.ack_reason as u64);
+                                    }
+
+                                    jsa.array_append(jso);
                                 }
 
-                                jsa.array_append(jso);
-                            }
-
-                            jsd.set("interfaces", jsa);
-                        },
-                        _ => {},
-                    }
-                },
-                _ => {},
+                                jsd.set("interfaces", jsa);
+                            },
+                            _ => {},
+                        }
+                    },
+                    _ => {},
+                }
+            }
+            if x.res_set {
+                match x.res_cmd {
+                    DCERPC_TYPE_RESPONSE => {
+                        let res = Json::object();
+                        res.set_integer("frag_cnt", x.frag_cnt_tc as u64);
+                        res.set_integer("stub_data_size", x.stub_data_tc.len() as u64);
+                        jsd.set("res", res);
+                    },
+                    // we don't handle BINDACK w/o BIND
+                    _ => {},
+                }
             }
             jsd.set_integer("call_id", x.call_id as u64);
             js.set("dcerpc", jsd);
index 84d0e1b3038555be48fa6c4d60c4d68b50475094..0e4ddc35545313a20c928935506b05634d9efa69 100644 (file)
@@ -1119,6 +1119,7 @@ impl SMBState {
                     Ok("lsarpc") => ("lsarpc", true),
                     Ok("samr") => ("samr", true),
                     Ok("spoolss") => ("spoolss", true),
+                    Ok("suricata::dcerpc") => ("unknown", true),
                     Err(_) => ("MALFORMED", false),
                     Ok(&_) => {
                         SCLogDebug!("don't know {}", String::from_utf8_lossy(&n));
index fad9dcaa1977005b57bea3c45eb6de6e5c92c33d..14e0b7b1ba06a86cca3bd91fa8d0b96a105b0cda 100644 (file)
@@ -869,9 +869,9 @@ pub fn smb1_trans_response_record<'b>(state: &mut SMBState, r: &SmbRecord<'b>)
             // if we get status 'BUFFER_OVERFLOW' this is only a part of
             // the data. Store it in the ssn/tree for later use.
             if r.nt_status == SMB_NTSTATUS_BUFFER_OVERFLOW {
-                state.ssnguid2vec_map.insert(SMBHashKeyHdrGuid::new(
-                            SMBCommonHdr::from1(r, SMBHDR_TYPE_TRANS_FRAG), fid),
-                        rd.data.to_vec());
+                let key = SMBHashKeyHdrGuid::new(SMBCommonHdr::from1(r, SMBHDR_TYPE_TRANS_FRAG), fid);
+                SCLogDebug!("SMBv1/TRANS: queueing data for len {} key {:?}", rd.data.len(), key);
+                state.ssnguid2vec_map.insert(key, rd.data.to_vec());
             } else if is_dcerpc {
                 SCLogDebug!("SMBv1 TRANS TO PIPE");
                 let hdr = SMBCommonHdr::from1(r, SMBHDR_TYPE_HEADER);
index 67d0d970daf28d8e63724d9a71ae0ca209c323a5..f7454bb7b8b598b1920d0566299925591b564e89 100644 (file)
@@ -19,6 +19,7 @@ use core::*;
 use log::*;
 use nom::IResult;
 
+use smb;
 use smb::smb::*;
 use smb::smb2_records::*;
 use smb::smb2_session::*;
@@ -118,7 +119,11 @@ pub fn smb2_read_response_record<'b>(state: &mut SMBState, r: &Smb2Record<'b>)
 
     match parse_smb2_response_read(r.data) {
         IResult::Done(_, rd) => {
-            if r.nt_status != SMB_NTSTATUS_SUCCESS {
+            if r.nt_status == SMB_NTSTATUS_BUFFER_OVERFLOW {
+                SCLogDebug!("SMBv2/READ: incomplete record, expecting a follow up");
+                // fall through
+
+            } else if r.nt_status != SMB_NTSTATUS_SUCCESS {
                 SCLogDebug!("SMBv2: read response error code received: skip record");
                 state.set_skip(STREAM_TOCLIENT, rd.len, rd.data.len() as u32);
                 return;
@@ -152,19 +157,48 @@ pub fn smb2_read_response_record<'b>(state: &mut SMBState, r: &Smb2Record<'b>)
                 },
                 None => { false },
             };
+            SCLogDebug!("existing file tx? {}", found);
             if !found {
                 let tree_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_SHARE);
-                let (share_name, is_pipe) = match state.ssn2tree_map.get(&tree_key) {
+                let (share_name, mut is_pipe) = match state.ssn2tree_map.get(&tree_key) {
                     Some(n) => (n.name.to_vec(), n.is_pipe),
                     _ => { (Vec::new(), false) },
                 };
-                let is_dcerpc = is_pipe && match state.get_service_for_guid(&file_guid) {
+                let mut is_dcerpc = is_pipe && match state.get_service_for_guid(&file_guid) {
                     (_, x) => x,
                 };
+                SCLogDebug!("SMBv2/READ: share_name {:?} is_pipe {} is_dcerpc {}", share_name, is_pipe, is_dcerpc);
+
+                if share_name.len() == 0 && !is_pipe {
+                    SCLogDebug!("SMBv2/READ: no tree connect seen, we don't know if we are a pipe");
+
+                    match smb::dcerpc_records::parse_dcerpc_record(rd.data) {
+                        IResult::Done(_, recr) => {
+                            SCLogDebug!("SMBv2/READ: could be DCERPC {:?}", recr);
+                            if recr.version_major == 5 && recr.version_minor < 3 &&
+                               recr.frag_len > 0 && recr.packet_type <= 20 {
+                                SCLogDebug!("SMBv2/READ: looks like dcerpc");
+                                // insert fake tree to assist in follow up lookups
+                                let tree = SMBTree::new(b"suricata::dcerpc".to_vec(), true);
+                                state.ssn2tree_map.insert(tree_key, tree);
+                                state.guid2name_map.insert(file_guid.to_vec(), b"suricata::dcerpc".to_vec());
+
+                                is_pipe = true;
+                                is_dcerpc = true;
+                            } else {
+                                SCLogDebug!("SMBv2/READ: not DCERPC");
+                            }
+                        },
+                        _ => {
+                            SCLogDebug!("SMBv2/READ: not DCERPC");
+                        },
+                    }
+                }
+
                 if is_pipe && is_dcerpc {
                     SCLogDebug!("SMBv2 DCERPC read");
                     let hdr = SMBCommonHdr::from2(r, SMBHDR_TYPE_HEADER);
-                    let vercmd = SMBVerCmdStat::new2(SMB2_COMMAND_READ);
+                    let vercmd = SMBVerCmdStat::new2_with_ntstatus(SMB2_COMMAND_READ, r.nt_status);
                     smb_read_dcerpc_record(state, vercmd, hdr, &file_guid, rd.data);
                 } else if is_pipe {
                     SCLogDebug!("non-DCERPC pipe");
@@ -191,6 +225,7 @@ pub fn smb2_read_response_record<'b>(state: &mut SMBState, r: &Smb2Record<'b>)
             state.set_file_left(STREAM_TOCLIENT, rd.len, rd.data.len() as u32, file_guid.to_vec());
         }
         _ => {
+            SCLogDebug!("SMBv2: failed to parse read response");
             state.set_event(SMBEvent::MalformedData);
         }
     }
@@ -198,6 +233,7 @@ pub fn smb2_read_response_record<'b>(state: &mut SMBState, r: &Smb2Record<'b>)
 
 pub fn smb2_write_request_record<'b>(state: &mut SMBState, r: &Smb2Record<'b>)
 {
+    SCLogDebug!("SMBv2/WRITE: request record");
     if smb2_create_new_tx(r.command) {
         let tx_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_GENERICTX);
         let tx = state.new_generic_tx(2, r.command, tx_key);
@@ -229,13 +265,47 @@ pub fn smb2_write_request_record<'b>(state: &mut SMBState, r: &Smb2Record<'b>)
             };
             if !found {
                 let tree_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_SHARE);
-                let is_pipe = match state.ssn2tree_map.get(&tree_key) {
-                    Some(n) => { n.is_pipe },
-                    _ => { false },
+                let (share_name, mut is_pipe) = match state.ssn2tree_map.get(&tree_key) {
+                    Some(n) => { (n.name.to_vec(), n.is_pipe) },
+                    _ => { (Vec::new(), false) },
                 };
-                let is_dcerpc = is_pipe && match state.get_service_for_guid(&wr.guid) {
-                    (_, x) => x,
+                let mut is_dcerpc = if is_pipe || (share_name.len() == 0 && !is_pipe) {
+                    match state.get_service_for_guid(&wr.guid) {
+                        (_, x) => x,
+                    }
+                } else {
+                    false
                 };
+                SCLogDebug!("share_name {:?} is_pipe {} is_dcerpc {}", share_name, is_pipe, is_dcerpc);
+
+                // if we missed the TREE connect we can't be sure if 'is_dcerpc' is correct
+                if share_name.len() == 0 && !is_pipe {
+                    SCLogDebug!("SMBv2/WRITE: no tree connect seen, we don't know if we are a pipe");
+
+                    match smb::dcerpc_records::parse_dcerpc_record(wr.data) {
+                        IResult::Done(_, recr) => {
+                            SCLogDebug!("SMBv2/WRITE: could be DCERPC {:?}", recr);
+                            if recr.version_major == 5 && recr.version_minor < 3 &&
+                               recr.frag_len > 0 && recr.packet_type <= 20 {
+                                SCLogDebug!("SMBv2/WRITE: looks like we have dcerpc");
+
+                                let tree = SMBTree::new(b"suricata::dcerpc".to_vec(), true);
+                                state.ssn2tree_map.insert(tree_key, tree);
+                                if !is_dcerpc {
+                                    state.guid2name_map.insert(file_guid.to_vec(),
+                                            b"suricata::dcerpc".to_vec());
+                                }
+                                is_pipe = true;
+                                is_dcerpc = true;
+                            } else {
+                                SCLogDebug!("SMBv2/WRITE: not DCERPC");
+                            }
+                        },
+                        _ => {
+                            SCLogDebug!("SMBv2/WRITE: not DCERPC");
+                        },
+                    }
+                }
                 if is_pipe && is_dcerpc {
                     SCLogDebug!("SMBv2 DCERPC write");
                     let hdr = SMBCommonHdr::from2(r, SMBHDR_TYPE_HEADER);
@@ -522,7 +592,8 @@ pub fn smb2_response_record<'b>(state: &mut SMBState, r: &Smb2Record<'b>)
             false // the request may have created a generic tx, so handle that here
         },
         SMB2_COMMAND_READ => {
-            if r.nt_status == SMB_NTSTATUS_SUCCESS {
+            if r.nt_status == SMB_NTSTATUS_SUCCESS ||
+               r.nt_status == SMB_NTSTATUS_BUFFER_OVERFLOW {
                 smb2_read_response_record(state, &r);
                 false