From: Russ Combs (rucombs) Date: Wed, 17 Feb 2016 21:15:34 +0000 (-0500) Subject: Merge pull request #268 in SNORT/snort3 from dce to master X-Git-Tag: 3.0.0-233~599 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=64c58a4c54885dfa147d4307c8e0813460695f0c;p=thirdparty%2Fsnort3.git Merge pull request #268 in SNORT/snort3 from dce to master Squashed commit of the following: commit 69fbec7af0f882ff21c7e70efd25d6cf01170f12 Author: rrp Date: Mon Feb 15 14:11:56 2016 -0500 DCE - Add session creation/lookup and packet checks to TCP and SMB eval code. Tested with DCE over TCP and DCE over SMB traffic pcaps. Verified that the sessions get created/looked up correctly. Verified peg counters and profile stats for different processing sections --- diff --git a/src/main/snort_debug.h b/src/main/snort_debug.h index 4365c7df2..ba1f7f9e0 100644 --- a/src/main/snort_debug.h +++ b/src/main/snort_debug.h @@ -81,9 +81,10 @@ #define DEBUG_LOGGER 0x0040000000000000LL #define DEBUG_DCE_TCP 0x0080000000000000LL #define DEBUG_DCE_SMB 0x0100000000000000LL +#define DEBUG_DCE_COMMON 0x0200000000000000LL #ifdef PIGLET -#define DEBUG_PIGLET 0x0200000000000000LL +#define DEBUG_PIGLET 0x0400000000000000LL #endif diff --git a/src/service_inspectors/dce_rpc/CMakeLists.txt b/src/service_inspectors/dce_rpc/CMakeLists.txt index d64aad138..5fee68f9b 100644 --- a/src/service_inspectors/dce_rpc/CMakeLists.txt +++ b/src/service_inspectors/dce_rpc/CMakeLists.txt @@ -1,5 +1,6 @@ set( FILE_LIST + dce_co.cc dce_co.h dce_common.cc dce_common.h diff --git a/src/service_inspectors/dce_rpc/Makefile.am b/src/service_inspectors/dce_rpc/Makefile.am index 7db794e99..2126698b0 100644 --- a/src/service_inspectors/dce_rpc/Makefile.am +++ b/src/service_inspectors/dce_rpc/Makefile.am @@ -1,4 +1,5 @@ file_list = \ +dce_co.cc \ dce_co.h \ dce_common.cc \ dce_common.h \ diff --git a/src/service_inspectors/dce_rpc/dce_co.cc b/src/service_inspectors/dce_rpc/dce_co.cc new file mode 100644 index 000000000..46bd5fdc8 --- /dev/null +++ b/src/service_inspectors/dce_rpc/dce_co.cc @@ -0,0 +1,51 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2016-2016 Cisco and/or its affiliates. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License Version 2 as published +// by the Free Software Foundation. You may not use, modify or distribute +// this program under any other version of the GNU General Public License. +// +// 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 along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +//-------------------------------------------------------------------------- + +// dce_co.cc author Rashmi Pitre +// based on work by Todd Wease + +#include "dce_co.h" + +/******************************************************************** + * Function: DCE2_CoInitTracker() + * + * Initializes fields in the connection-oriented tracker to + * sentinels. Many decisions are made based on whether or not + * these fields have been set. + * + ********************************************************************/ +void DCE2_CoInitTracker(DCE2_CoTracker *cot) +{ + if (cot == NULL) + return; + + cot->max_xmit_frag = DCE2_SENTINEL; + cot->data_byte_order = DCE2_SENTINEL; + cot->ctx_id = DCE2_SENTINEL; + cot->opnum = DCE2_SENTINEL; + cot->call_id = DCE2_SENTINEL; + cot->stub_data = NULL; + cot->got_bind = 0; + + cot->frag_tracker.opnum = DCE2_SENTINEL; + cot->frag_tracker.ctx_id = DCE2_SENTINEL; + cot->frag_tracker.expected_call_id = DCE2_SENTINEL; + cot->frag_tracker.expected_opnum = DCE2_SENTINEL; + cot->frag_tracker.expected_ctx_id = DCE2_SENTINEL; +} + diff --git a/src/service_inspectors/dce_rpc/dce_co.h b/src/service_inspectors/dce_rpc/dce_co.h index d6fa8ee89..d97ce9d62 100644 --- a/src/service_inspectors/dce_rpc/dce_co.h +++ b/src/service_inspectors/dce_rpc/dce_co.h @@ -23,6 +23,7 @@ #define DCE_CO_H #include "dce_common.h" +#include "dce_list.h" #define DCE2_CO_BAD_MAJOR_VERSION 27 #define DCE2_CO_BAD_MINOR_VERSION 28 @@ -89,6 +90,56 @@ struct DceRpcCoHdr #pragma pack() +struct DCE2_CoFragTracker +{ + DCE2_Buffer* cli_stub_buf; + DCE2_Buffer* srv_stub_buf; + + int opnum; /* Opnum that is ultimatley used for request */ + int ctx_id; /* Context id that is ultimatley used for request */ + + /* These are set on a first fragment received */ + int expected_call_id; /* Expected call id for fragments */ + int expected_opnum; /* Expected call id for fragments */ + int expected_ctx_id; /* Expected call id for fragments */ +}; + +struct DCE2_CoSeg +{ + DCE2_Buffer* buf; + + /* If there is enough data in segmentation buffer for header, + * this will be set to the frag length in the header */ + uint16_t frag_len; +}; + +struct DCE2_CoTracker +{ + DCE2_List* ctx_ids; /* splayed list so most recently used goes to front of list */ + int got_bind; /* got an accepted bind */ + + /* Queue of pending client bind or alter context request context items + * Since the actual context id number doesn't have to occur sequentially + * in the context list in the client packet, need to keep track to match + * up server response since server doesn't reply with actual context id + * numbers, but in the order they were in the client packet */ + DCE2_Queue* pending_ctx_ids; + + /* Keeps track of fragmentation buffer and frag specfic data */ + DCE2_CoFragTracker frag_tracker; + + int max_xmit_frag; /* The maximum negotiated size of a client request */ + int data_byte_order; /* Depending on policy is from bind or request */ + int ctx_id; /* The current context id of the request */ + int opnum; /* The current opnum of the request */ + int call_id; /* The current call id of the request */ + const uint8_t* stub_data; /* Current pointer to stub data in the request */ + + /* For transport segmentation */ + DCE2_CoSeg cli_seg; + DCE2_CoSeg srv_seg; +}; + inline uint8_t DceRpcCoVersMaj(const DceRpcCoHdr* co) { return co->pversion.major; @@ -114,5 +165,7 @@ inline uint16_t DceRpcCoFragLen(const DceRpcCoHdr* co) return DceRpcNtohs(&co->frag_length, DceRpcCoByteOrder(co)); } +void DCE2_CoInitTracker(DCE2_CoTracker*); + #endif diff --git a/src/service_inspectors/dce_rpc/dce_common.cc b/src/service_inspectors/dce_rpc/dce_common.cc index 09d237336..9c96365d0 100644 --- a/src/service_inspectors/dce_rpc/dce_common.cc +++ b/src/service_inspectors/dce_rpc/dce_common.cc @@ -25,7 +25,7 @@ #include "log/messages.h" #include "main/snort_debug.h" -const char* dce2_get_policy_name(DCE2_POLICY policy) +const char* dce2_get_policy_name(DCE2_Policy policy) { const char* policyStr = nullptr; switch (policy) @@ -75,7 +75,7 @@ bool dce2_set_common_config(Value& v, dce2CommonProtoConf& common) common.max_frag_len = v.get_long(); else if ( v.is("policy") ) - common.policy = (DCE2_POLICY)v.get_long(); + common.policy = (DCE2_Policy)v.get_long(); else return false; return true; @@ -92,21 +92,26 @@ void print_dce2_common_config(dce2CommonProtoConf& common) dce2_get_policy_name(common.policy)); } -bool dce2_paf_abort(Flow* flow) +bool dce2_paf_abort(Flow* flow, DCE2_SsnData* sd) { if (flow->get_session_flags() & SSNFLAG_MIDSTREAM) { - DebugMessage(DEBUG_DCE_TCP, + DebugMessage(DEBUG_DCE_COMMON, "Aborting PAF because of midstream pickup.\n"); return true; } else if (!(flow->get_session_flags() & SSNFLAG_ESTABLISHED)) { - DebugMessage(DEBUG_DCE_TCP, + DebugMessage(DEBUG_DCE_COMMON, "Aborting PAF because of unestablished session.\n"); return true; } - // FIXIT-M add the remaining checks + + if ((sd != NULL) && DCE2_SsnNoInspect(sd)) + { + DebugMessage(DEBUG_DCE_COMMON, "Aborting PAF because of session data check.\n"); + return true; + } return false; } diff --git a/src/service_inspectors/dce_rpc/dce_common.h b/src/service_inspectors/dce_rpc/dce_common.h index 637162ca2..4250b656d 100644 --- a/src/service_inspectors/dce_rpc/dce_common.h +++ b/src/service_inspectors/dce_rpc/dce_common.h @@ -25,13 +25,14 @@ #include "main/snort_types.h" #include "framework/module.h" #include "framework/inspector.h" +#include "protocols/packet.h" extern const InspectApi dce2_smb_api; extern const InspectApi dce2_tcp_api; #define GID_DCE2 145 -enum DCE2_POLICY +enum DCE2_Policy { DCE2_POLICY__WIN2000 = 0, DCE2_POLICY__WINXP, @@ -60,7 +61,7 @@ struct dce2CommonProtoConf { bool disable_defrag; uint16_t max_frag_len; - DCE2_POLICY policy; + DCE2_Policy policy; }; #define DCE2_DEBUG__PAF_END_MSG "==========================================================" @@ -136,10 +137,19 @@ struct DCE2_Roptions const uint8_t* stub_data; /* Set to NULL if not applicable */ }; +enum DCE2_SsnFlag +{ + DCE2_SSN_FLAG__NONE = 0x0000, + DCE2_SSN_FLAG__AUTODETECTED = 0x0001, + DCE2_SSN_FLAG__NO_INSPECT = 0x0002, + DCE2_SSN_FLAG__ALL = 0xffff +}; + struct DCE2_SsnData { - DCE2_POLICY server_policy; - DCE2_POLICY client_policy; + DCE2_TransType trans; + DCE2_Policy server_policy; + DCE2_Policy client_policy; int flags; const Packet* wire_pkt; uint64_t alert_mask; @@ -186,9 +196,50 @@ inline uint16_t DceRpcNtohs(const uint16_t* ptr, const DceRpcBoFlag bo_flag) return ((value & 0xff00) >> 8) | ((value & 0x00ff) << 8); } +inline void DCE2_ResetRopts(DCE2_Roptions* ropts) +{ + ropts->first_frag = DCE2_SENTINEL; + ropts->opnum = DCE2_SENTINEL; + ropts->hdr_byte_order = DCE2_SENTINEL; + ropts->data_byte_order = DCE2_SENTINEL; + ropts->stub_data = NULL; +} + +inline void DCE2_SsnSetAutodetected(DCE2_SsnData* sd, Packet* p) +{ + sd->flags |= DCE2_SSN_FLAG__AUTODETECTED; + sd->autodetect_dir = p->packet_flags & (PKT_FROM_CLIENT | PKT_FROM_SERVER); +} + +inline int DCE2_SsnAutodetectDir(DCE2_SsnData* sd) +{ + return sd->autodetect_dir; +} + +inline int DCE2_SsnAutodetected(DCE2_SsnData* sd) +{ + return sd->flags & DCE2_SSN_FLAG__AUTODETECTED; +} + +inline void DCE2_SsnClearAutodetected(DCE2_SsnData* sd) +{ + sd->flags &= ~DCE2_SSN_FLAG__AUTODETECTED; + sd->autodetect_dir = 0; +} + +inline void DCE2_SsnSetNoInspect(DCE2_SsnData* sd) +{ + sd->flags |= DCE2_SSN_FLAG__NO_INSPECT; +} + +inline int DCE2_SsnNoInspect(DCE2_SsnData* sd) +{ + return sd->flags & DCE2_SSN_FLAG__NO_INSPECT; +} + bool dce2_set_common_config(Value&, dce2CommonProtoConf&); void print_dce2_common_config(dce2CommonProtoConf&); -bool dce2_paf_abort(Flow*); +bool dce2_paf_abort(Flow*, DCE2_SsnData*); #endif diff --git a/src/service_inspectors/dce_rpc/dce_list.h b/src/service_inspectors/dce_rpc/dce_list.h index 67fe9f7fb..18d6e679b 100644 --- a/src/service_inspectors/dce_rpc/dce_list.h +++ b/src/service_inspectors/dce_rpc/dce_list.h @@ -25,8 +25,8 @@ * ****************************************************************************/ -#ifndef _DCE_LIST_H_ -#define _DCE_LIST_H_ +#ifndef DCE_LIST_H +#define DCE_LIST_H #include "dce_utils.h" @@ -82,6 +82,26 @@ struct DCE2_List struct DCE2_ListNode* prev; }; +struct DCE2_QueueNode +{ + void* data; + DCE2_QueueNode* prev; + DCE2_QueueNode* next; +}; + +typedef DCE2_ListDataFree DCE2_QueueDataFree; + +struct DCE2_Queue +{ + uint32_t num_nodes; + DCE2_QueueDataFree data_free; + DCE2_QueueNode* current; + DCE2_QueueNode* head; + DCE2_QueueNode* tail; + DCE2_QueueNode* next; + DCE2_QueueNode* prev; +}; + /******************************************************************** * Public function prototypes ********************************************************************/ @@ -120,4 +140,5 @@ inline bool DCE2_ListIsEmpty(DCE2_List* list) return false; } -#endif +#endif + diff --git a/src/service_inspectors/dce_rpc/dce_smb.cc b/src/service_inspectors/dce_rpc/dce_smb.cc index 90fe7f531..efb931866 100644 --- a/src/service_inspectors/dce_rpc/dce_smb.cc +++ b/src/service_inspectors/dce_rpc/dce_smb.cc @@ -23,13 +23,13 @@ #include "dce_smb_module.h" #include "dce_list.h" #include "main/snort_debug.h" +#include "file_api/file_service.h" THREAD_LOCAL dce2SmbStats dce2_smb_stats; THREAD_LOCAL ProfileStats dce2_smb_pstat_main; THREAD_LOCAL ProfileStats dce2_smb_pstat_session; THREAD_LOCAL ProfileStats dce2_smb_pstat_new_session; -THREAD_LOCAL ProfileStats dce2_smb_pstat_session_state; THREAD_LOCAL ProfileStats dce2_smb_pstat_detect; THREAD_LOCAL ProfileStats dce2_smb_pstat_log; THREAD_LOCAL ProfileStats dce2_smb_pstat_co_seg; @@ -47,6 +47,10 @@ THREAD_LOCAL ProfileStats dce2_smb_pstat_smb_file_api; THREAD_LOCAL ProfileStats dce2_smb_pstat_smb_fingerprint; THREAD_LOCAL ProfileStats dce2_smb_pstat_smb_negotiate; +Dce2SmbFlowData::Dce2SmbFlowData() : FlowData(flow_id) +{ +} + unsigned Dce2SmbFlowData::flow_id = 0; DCE2_SmbSsnData* get_dce2_smb_session_data(Flow* flow) @@ -57,6 +61,91 @@ DCE2_SmbSsnData* get_dce2_smb_session_data(Flow* flow) return fd ? &fd->dce2_smb_session : nullptr; } +static DCE2_SmbSsnData* set_new_dce2_smb_session(Packet* p) +{ + Dce2SmbFlowData* fd = new Dce2SmbFlowData; + + p->flow->set_application_data(fd); + return(&fd->dce2_smb_session); +} + +static DCE2_SmbSsnData* dce2_create_new_smb_session(Packet* p, dce2SmbProtoConf config) +{ + DCE2_SmbSsnData* dce2_smb_sess = NULL; + Profile profile(dce2_smb_pstat_new_session); + + if (DCE2_SmbAutodetect(p)) + { + DebugMessage(DEBUG_DCE_SMB, "DCE over SMB packet detected\n"); + DebugMessage(DEBUG_DCE_SMB, "Creating new session\n"); + + dce2_smb_sess = set_new_dce2_smb_session(p); + if ( dce2_smb_sess ) + { + dce2_smb_sess->dialect_index = DCE2_SENTINEL; + dce2_smb_sess->max_outstanding_requests = 10; // Until Negotiate/SessionSetupAndX + dce2_smb_sess->cli_data_state = DCE2_SMB_DATA_STATE__NETBIOS_HEADER; + dce2_smb_sess->srv_data_state = DCE2_SMB_DATA_STATE__NETBIOS_HEADER; + dce2_smb_sess->pdu_state = DCE2_SMB_PDU_STATE__COMMAND; + dce2_smb_sess->uid = DCE2_SENTINEL; + dce2_smb_sess->tid = DCE2_SENTINEL; + dce2_smb_sess->ftracker.fid = DCE2_SENTINEL; + dce2_smb_sess->rtracker.mid = DCE2_SENTINEL; + dce2_smb_sess->max_file_depth = FileService::get_max_file_depth(); + + DCE2_ResetRopts(&dce2_smb_sess->sd.ropts); + + dce2_smb_stats.smb_sessions++; + DebugFormat(DEBUG_DCE_SMB,"Created (%p)\n", (void*)dce2_smb_sess); + + dce2_smb_sess->sd.trans = DCE2_TRANS_TYPE__SMB; + dce2_smb_sess->sd.server_policy = config.common.policy; + dce2_smb_sess->sd.client_policy = DCE2_POLICY__WINXP; + dce2_smb_sess->sd.wire_pkt = p; + + DCE2_SsnSetAutodetected(&dce2_smb_sess->sd, p); + } + } + + return dce2_smb_sess; +} + +DCE2_SmbSsnData* dce2_handle_smb_session(Packet* p, dce2SmbProtoConf& config) +{ + Profile profile(dce2_smb_pstat_session); + + DCE2_SmbSsnData* dce2_smb_sess = get_dce2_smb_session_data(p->flow); + + if (dce2_smb_sess == nullptr) + { + dce2_smb_sess = dce2_create_new_smb_session(p, config); + } + else + { + DCE2_SsnData* sd = (DCE2_SsnData*)dce2_smb_sess; + sd->wire_pkt = p; + + if (DCE2_SsnAutodetected(sd) && !(p->packet_flags & sd->autodetect_dir)) + { + /* Try to autodetect in opposite direction */ + if (!DCE2_SmbAutodetect(p)) + { + DebugMessage(DEBUG_DCE_SMB, "Bad autodetect.\n"); + DCE2_SsnNoInspect(sd); + dce2_smb_stats.sessions_aborted++; + dce2_smb_stats.bad_autodetects++; + return NULL; + } + DCE2_SsnClearAutodetected(sd); + } + } + DebugFormat(DEBUG_DCE_SMB, "Session pointer: %p\n", (void*)dce2_smb_sess); + + // FIXIT-M add remaining session handling logic + + return dce2_smb_sess; +} + //------------------------------------------------------------------------- // class stuff //------------------------------------------------------------------------- @@ -98,17 +187,25 @@ void Dce2Smb::show(SnortConfig*) void Dce2Smb::eval(Packet* p) { - DCE2_SmbSsnData* dce2_sess = get_dce2_smb_session_data(p->flow); + DCE2_SmbSsnData* dce2_smb_sess; + Profile profile(dce2_smb_pstat_main); + + assert(p->has_tcp_data()); + assert(p->flow); - if (dce2_sess == nullptr) + if (p->flow->get_session_flags() & SSNFLAG_MIDSTREAM) { - /*Check if it is a DCE2 over SMB packet*/ + DebugMessage(DEBUG_DCE_SMB, + "Midstream - not inspecting.\n"); + return; + } - if (DCE2_SmbAutodetect(p)) - { - DebugMessage(DEBUG_DCE_SMB, "DCE over SMB packet detected\n"); - } + dce2_smb_sess = dce2_handle_smb_session(p, config); + if (!dce2_smb_sess) + { + return; } + dce2_smb_stats.smb_pkts++; } //------------------------------------------------------------------------- @@ -125,6 +222,11 @@ static void mod_dtor(Module* m) delete m; } +static void dce2_smb_init() +{ + Dce2SmbFlowData::init(); +} + static Inspector* dce2_smb_ctor(Module* m) { Dce2SmbModule* mod = (Dce2SmbModule*)m; @@ -156,7 +258,7 @@ const InspectApi dce2_smb_api = (uint16_t)PktType::PDU, nullptr, // buffers "dce_smb", - nullptr, + dce2_smb_init, nullptr, // pterm nullptr, // tinit nullptr, // tterm diff --git a/src/service_inspectors/dce_rpc/dce_smb.h b/src/service_inspectors/dce_rpc/dce_smb.h index e6fa7bd1d..996c218df 100644 --- a/src/service_inspectors/dce_rpc/dce_smb.h +++ b/src/service_inspectors/dce_rpc/dce_smb.h @@ -23,6 +23,7 @@ #define DCE_SMB_H #include "dce_common.h" +#include "dce_co.h" #include "protocols/packet.h" #include "profiler/profiler.h" #include "framework/counts.h" @@ -183,7 +184,6 @@ extern THREAD_LOCAL dce2SmbStats dce2_smb_stats; extern THREAD_LOCAL ProfileStats dce2_smb_pstat_main; extern THREAD_LOCAL ProfileStats dce2_smb_pstat_session; extern THREAD_LOCAL ProfileStats dce2_smb_pstat_new_session; -extern THREAD_LOCAL ProfileStats dce2_smb_pstat_session_state; extern THREAD_LOCAL ProfileStats dce2_smb_pstat_detect; extern THREAD_LOCAL ProfileStats dce2_smb_pstat_log; extern THREAD_LOCAL ProfileStats dce2_smb_pstat_co_seg; @@ -221,7 +221,7 @@ struct NbssHdr uint8_t type; uint8_t flags; /* Treat flags as the upper byte to length */ uint16_t length; -} ; +}; struct SmbNtHdr { @@ -249,11 +249,200 @@ struct SmbNtHdr }; #pragma pack() +enum DCE2_SmbSsnState +{ + DCE2_SMB_SSN_STATE__START = 0x00, + DCE2_SMB_SSN_STATE__NEGOTIATED = 0x01, + DCE2_SMB_SSN_STATE__FP_CLIENT = 0x02, // Fingerprinted client + DCE2_SMB_SSN_STATE__FP_SERVER = 0x04 // Fingerprinted server +}; + +enum DCE2_SmbDataState +{ + DCE2_SMB_DATA_STATE__NETBIOS_HEADER, + DCE2_SMB_DATA_STATE__SMB_HEADER, + DCE2_SMB_DATA_STATE__NETBIOS_PDU +}; + +enum DCE2_SmbPduState +{ + DCE2_SMB_PDU_STATE__COMMAND, + DCE2_SMB_PDU_STATE__RAW_DATA +}; + +enum DCE2_SmbFileDirection +{ + DCE2_SMB_FILE_DIRECTION__UNKNOWN = 0, + DCE2_SMB_FILE_DIRECTION__UPLOAD, + DCE2_SMB_FILE_DIRECTION__DOWNLOAD +}; + +struct DCE2_SmbWriteAndXRaw +{ + int remaining; // A signed integer so it can be negative + DCE2_Buffer* buf; +}; + +struct DCE2_SmbFileChunk +{ + uint64_t offset; + uint32_t length; + uint8_t* data; +}; + +struct DCE2_SmbFileTracker +{ + int fid; // A signed integer so it can be set to sentinel + uint16_t uid; + uint16_t tid; + bool is_ipc; + char* file_name; + + union + { + struct + { + // If pipe has been set to byte mode via TRANS_SET_NMPIPE_STATE + bool byte_mode; + + // For Windows 2000 + bool used; + + // For WriteAndX requests that use raw mode flag + // Windows only + DCE2_SmbWriteAndXRaw* writex_raw; + + // Connection-oriented DCE/RPC tracker + DCE2_CoTracker* co_tracker; + } nmpipe; + + struct + { + uint64_t file_size; + uint64_t file_offset; + uint64_t bytes_processed; + DCE2_List* file_chunks; + uint32_t bytes_queued; + DCE2_SmbFileDirection file_direction; + bool sequential_only; + } file; + } tracker; + +#define fp_byte_mode tracker.nmpipe.byte_mode +#define fp_used tracker.nmpipe.used +#define fp_writex_raw tracker.nmpipe.writex_raw +#define fp_co_tracker tracker.nmpipe.co_tracker +#define ff_file_size tracker.file.file_size +#define ff_file_offset tracker.file.file_offset +#define ff_bytes_processed tracker.file.bytes_processed +#define ff_file_direction tracker.file.file_direction +#define ff_file_chunks tracker.file.file_chunks +#define ff_bytes_queued tracker.file.bytes_queued +#define ff_sequential_only tracker.file.sequential_only +}; + +struct DCE2_SmbTransactionTracker +{ + int smb_type; + uint8_t subcom; + bool one_way; + bool disconnect_tid; + bool pipe_byte_mode; + uint32_t tdcnt; + uint32_t dsent; + DCE2_Buffer* dbuf; + uint32_t tpcnt; + uint32_t psent; + DCE2_Buffer* pbuf; + // For Transaction2/Query File Information + uint16_t info_level; +}; + +struct DCE2_SmbRequestTracker +{ + int smb_com; + + int mid; // A signed integer so it can be set to sentinel + uint16_t uid; + uint16_t tid; + uint16_t pid; + + // For WriteRaw + bool writeraw_writethrough; + uint32_t writeraw_remaining; + + // For Transaction/Transaction2/NtTransact + DCE2_SmbTransactionTracker ttracker; + + // Client can chain a write to an open. Need to write data, but also + // need to associate tracker with fid returned from server + DCE2_Queue* ft_queue; + + // This is a reference to an existing file tracker + DCE2_SmbFileTracker* ftracker; + + // Used for requests to cache data that will ultimately end up in + // the file tracker upon response. + char* file_name; + uint64_t file_size; + uint64_t file_offset; + bool sequential_only; + + // For TreeConnect to know whether it's to IPC + bool is_ipc; +}; struct DCE2_SmbSsnData { DCE2_SsnData sd; // This member must be first - // FIXIT-M add all the remaining fields + + DCE2_Policy policy; + + int dialect_index; + int ssn_state_flags; + + DCE2_SmbDataState cli_data_state; + DCE2_SmbDataState srv_data_state; + + DCE2_SmbPduState pdu_state; + + int uid; // A signed integer so it can be set to sentinel + int tid; // A signed integer so it can be set to sentinel + DCE2_List* uids; + DCE2_List* tids; + + // For tracking files and named pipes + DCE2_SmbFileTracker ftracker; + DCE2_List* ftrackers; // List of DCE2_SmbFileTracker + + // For tracking requests / responses + DCE2_SmbRequestTracker rtracker; + //DCE2_Queue *rtrackers; + uint16_t max_outstanding_requests; + uint16_t outstanding_requests; + + // The current pid/mid node for this request/response + DCE2_SmbRequestTracker* cur_rtracker; + + // Used for TCP segmentation to get full PDU + DCE2_Buffer* cli_seg; + DCE2_Buffer* srv_seg; + + // These are used for commands we don't need to process + uint32_t cli_ignore_bytes; + uint32_t srv_ignore_bytes; + + // The file API supports one concurrent upload/download per session. + // This is a reference to a file tracker so shouldn't be freed. + DCE2_SmbFileTracker* fapi_ftracker; + +#ifdef ACTIVE_RESPONSE + DCE2_SmbFileTracker* fb_ftracker; + bool block_pdus; +#endif + + // Maximum file depth as returned from file API + int64_t max_file_depth; }; inline uint32_t NbssLen(const NbssHdr* nb) diff --git a/src/service_inspectors/dce_rpc/dce_smb_module.cc b/src/service_inspectors/dce_rpc/dce_smb_module.cc index be6c4c70e..4b002056d 100644 --- a/src/service_inspectors/dce_rpc/dce_smb_module.cc +++ b/src/service_inspectors/dce_rpc/dce_smb_module.cc @@ -218,86 +218,81 @@ ProfileStats* Dce2SmbModule::get_profile( return &dce2_smb_pstat_new_session; case 3: - name = "dce smb session_state"; - parent = "dce smb session"; - return &dce2_smb_pstat_session_state; - - case 4: name = "dce smb detect"; parent = "dce smb main"; return &dce2_smb_pstat_detect; - case 5: + case 4: name = "dce smb log"; parent = "dce smb main"; return &dce2_smb_pstat_log; - case 6: + case 5: name = "dce smb connection-oriented segment"; parent = "dce smb main"; return &dce2_smb_pstat_co_seg; - case 7: + case 6: name = "dce smb connection-oriented fragment"; parent = "dce smb main"; return &dce2_smb_pstat_co_frag; - case 8: + case 7: name = "dce smb connection-oriented reassembly"; parent = "dce smb main"; return &dce2_smb_pstat_co_reass; - case 9: + case 8: name = "dce smb connection-oriented context"; parent = "dce smb main"; return &dce2_smb_pstat_co_ctx; - case 10: + case 9: name = "dce smb segment"; parent = "dce smb main"; return &dce2_smb_pstat_smb_seg; - case 11: + case 10: name = "dce smb request"; parent = "dce smb main"; return &dce2_smb_pstat_smb_req; - case 12: + case 11: name = "dce smb uid"; parent = "dce smb main"; return &dce2_smb_pstat_smb_uid; - case 13: + case 12: name = "dce smb tid"; parent = "dce smb main"; return &dce2_smb_pstat_smb_tid; - case 14: + case 13: name = "dce smb fid"; parent = "dce smb main"; return &dce2_smb_pstat_smb_fid; - case 15: + case 14: name = "dce smb file"; parent = "dce smb main"; return &dce2_smb_pstat_smb_file; - case 16: + case 15: name = "dce smb file detect"; parent = "dce smb file"; return &dce2_smb_pstat_smb_file_detect; - case 17: + case 16: name = "dce smb file api"; parent = "dce smb file"; return &dce2_smb_pstat_smb_file_api; - case 18: + case 17: name = "dce smb fingerprint"; parent = "dce smb main"; return &dce2_smb_pstat_smb_fingerprint; - case 19: + case 18: name = "dce smb negotiate"; parent = "dce smb main"; return &dce2_smb_pstat_smb_negotiate; diff --git a/src/service_inspectors/dce_rpc/dce_smb_paf.cc b/src/service_inspectors/dce_rpc/dce_smb_paf.cc index 5246302db..160f07224 100644 --- a/src/service_inspectors/dce_rpc/dce_smb_paf.cc +++ b/src/service_inspectors/dce_rpc/dce_smb_paf.cc @@ -86,6 +86,7 @@ StreamSplitter::Status dce2_smb_paf(DCE2_PafSmbData* ss, Flow* flow, const uint8 StreamSplitter::Status ps = StreamSplitter::SEARCH; uint32_t nb_hdr; uint32_t nb_len; + DCE2_SmbSsnData* sd = get_dce2_smb_session_data(flow); DebugFormat(DEBUG_DCE_SMB, "%s\n", DCE2_DEBUG__PAF_START_MSG_SMB); DebugFormat(DEBUG_DCE_SMB, "SMB: %u bytes of data\n", len); @@ -99,7 +100,7 @@ StreamSplitter::Status dce2_smb_paf(DCE2_PafSmbData* ss, Flow* flow, const uint8 UNUSED(flags); #endif - if (dce2_paf_abort(flow)) + if (dce2_paf_abort(flow, (DCE2_SsnData*)sd)) { DebugFormat(DEBUG_DCE_SMB, "%s\n", DCE2_DEBUG__PAF_END_MSG); return StreamSplitter::ABORT; diff --git a/src/service_inspectors/dce_rpc/dce_tcp.cc b/src/service_inspectors/dce_rpc/dce_tcp.cc index 77e6fe830..e0a371eea 100644 --- a/src/service_inspectors/dce_rpc/dce_tcp.cc +++ b/src/service_inspectors/dce_rpc/dce_tcp.cc @@ -24,12 +24,15 @@ #include "dce_tcp_module.h" #include "main/snort_debug.h" +Dce2TcpFlowData::Dce2TcpFlowData() : FlowData(flow_id) +{ +} + THREAD_LOCAL dce2TcpStats dce2_tcp_stats; THREAD_LOCAL ProfileStats dce2_tcp_pstat_main; THREAD_LOCAL ProfileStats dce2_tcp_pstat_session; THREAD_LOCAL ProfileStats dce2_tcp_pstat_new_session; -THREAD_LOCAL ProfileStats dce2_tcp_pstat_session_state; THREAD_LOCAL ProfileStats dce2_tcp_pstat_detect; THREAD_LOCAL ProfileStats dce2_tcp_pstat_log; THREAD_LOCAL ProfileStats dce2_tcp_pstat_co_seg; @@ -47,6 +50,84 @@ DCE2_TcpSsnData* get_dce2_tcp_session_data(Flow* flow) return fd ? &fd->dce2_tcp_session : nullptr; } +static DCE2_TcpSsnData* set_new_dce2_tcp_session(Packet* p) +{ + Dce2TcpFlowData* fd = new Dce2TcpFlowData; + + p->flow->set_application_data(fd); + return(&fd->dce2_tcp_session); +} + +static DCE2_TcpSsnData* dce2_create_new_tcp_session(Packet* p, dce2TcpProtoConf config) +{ + DCE2_TcpSsnData* dce2_tcp_sess = NULL; + Profile profile(dce2_tcp_pstat_new_session); + + if (DCE2_TcpAutodetect(p)) + { + DebugMessage(DEBUG_DCE_TCP, "DCE over TCP packet detected\n"); + DebugMessage(DEBUG_DCE_TCP, "Creating new session\n"); + + dce2_tcp_sess = set_new_dce2_tcp_session(p); + + if ( dce2_tcp_sess ) + { + DCE2_CoInitTracker(&dce2_tcp_sess->co_tracker); + DCE2_ResetRopts(&dce2_tcp_sess->sd.ropts); + + dce2_tcp_stats.tcp_sessions++; + DebugFormat(DEBUG_DCE_TCP,"Created (%p)\n", (void*)dce2_tcp_sess); + + dce2_tcp_sess->sd.trans = DCE2_TRANS_TYPE__TCP; + dce2_tcp_sess->sd.server_policy = config.common.policy; + dce2_tcp_sess->sd.client_policy = DCE2_POLICY__WINXP; + dce2_tcp_sess->sd.wire_pkt = p; + + DCE2_SsnSetAutodetected(&dce2_tcp_sess->sd, p); + } + } + + return dce2_tcp_sess; +} + +DCE2_TcpSsnData* dce2_handle_tcp_session(Packet* p, dce2TcpProtoConf& config) +{ + Profile profile(dce2_tcp_pstat_session); + + DCE2_TcpSsnData* dce2_tcp_sess = get_dce2_tcp_session_data(p->flow); + + if (dce2_tcp_sess == nullptr) + { + dce2_tcp_sess = dce2_create_new_tcp_session(p, config); + } + else + { + DCE2_SsnData* sd = (DCE2_SsnData*)dce2_tcp_sess; + sd->wire_pkt = p; + + if (DCE2_SsnAutodetected(sd) && !(p->packet_flags & sd->autodetect_dir)) + { + /* Try to autodetect in opposite direction */ + if (!DCE2_TcpAutodetect(p)) + { + DebugMessage(DEBUG_DCE_TCP, "Bad autodetect.\n"); + DCE2_SsnNoInspect(sd); + dce2_tcp_stats.sessions_aborted++; + dce2_tcp_stats.bad_autodetects++; + return NULL; + } + + DCE2_SsnClearAutodetected(sd); + } + } + + DebugFormat(DEBUG_DCE_TCP, "Session pointer: %p\n", (void*)dce2_tcp_sess); + + // FIXIT-M add remaining session handling logic + + return dce2_tcp_sess; +} + //------------------------------------------------------------------------- // class stuff //------------------------------------------------------------------------- @@ -79,18 +160,25 @@ void Dce2Tcp::show(SnortConfig*) void Dce2Tcp::eval(Packet* p) { - DCE2_TcpSsnData* dce2_sess = get_dce2_tcp_session_data(p->flow); + DCE2_TcpSsnData* dce2_tcp_sess; + Profile profile(dce2_tcp_pstat_main); + + assert(p->has_tcp_data()); + assert(p->flow); - if (dce2_sess == nullptr) + if (p->flow->get_session_flags() & SSNFLAG_MIDSTREAM) { - /*Check if it is a DCE2 over TCP packet*/ - - if (DCE2_TcpAutodetect(p)) - { - DebugMessage(DEBUG_DCE_TCP, "DCE over TCP packet detected\n"); - } - + DebugMessage(DEBUG_DCE_TCP, + "Midstream - not inspecting.\n"); + return; } + + dce2_tcp_sess = dce2_handle_tcp_session(p, config); + if (!dce2_tcp_sess) + { + return; + } + dce2_tcp_stats.tcp_pkts++; } //------------------------------------------------------------------------- @@ -120,6 +208,11 @@ static void dce2_tcp_dtor(Inspector* p) delete p; } +static void dce2_tcp_init() +{ + Dce2TcpFlowData::init(); +} + const InspectApi dce2_tcp_api = { { @@ -138,7 +231,7 @@ const InspectApi dce2_tcp_api = (uint16_t)PktType::PDU, nullptr, // buffers "dce_tcp", - nullptr, + dce2_tcp_init, nullptr, // pterm nullptr, // tinit nullptr, // tterm diff --git a/src/service_inspectors/dce_rpc/dce_tcp.h b/src/service_inspectors/dce_rpc/dce_tcp.h index 09e08c83e..4f63d5a79 100644 --- a/src/service_inspectors/dce_rpc/dce_tcp.h +++ b/src/service_inspectors/dce_rpc/dce_tcp.h @@ -114,7 +114,7 @@ inline bool DCE2_TcpAutodetect(Packet* p) struct DCE2_TcpSsnData { DCE2_SsnData sd; // This member must be first - // FIXIT-M add all the remaining fields + DCE2_CoTracker co_tracker; }; class Dce2TcpFlowData : public FlowData diff --git a/src/service_inspectors/dce_rpc/dce_tcp_module.cc b/src/service_inspectors/dce_rpc/dce_tcp_module.cc index 0bb1edc79..ccf5ad9a4 100644 --- a/src/service_inspectors/dce_rpc/dce_tcp_module.cc +++ b/src/service_inspectors/dce_rpc/dce_tcp_module.cc @@ -143,36 +143,31 @@ ProfileStats* Dce2TcpModule::get_profile( return &dce2_tcp_pstat_new_session; case 3: - name = "dce tcp session state"; - parent = "dce tcp session"; - return &dce2_tcp_pstat_session_state; - - case 4: name = "dce tcp detect"; parent = "dce tcp main"; return &dce2_tcp_pstat_detect; - case 5: + case 4: name = "dce tcp log"; parent = "dce_tcp_main"; return &dce2_tcp_pstat_log; - case 6: + case 5: name = "dce tcp connection-oriented segment"; parent = "dce tcp main"; return &dce2_tcp_pstat_co_seg; - case 7: + case 6: name = "dce tcp connection-oriented fragment"; parent = "dce tcp main"; return &dce2_tcp_pstat_co_frag; - case 8: + case 7: name = "dce tcp connection-oriented reassembly"; parent = "dce tcp main"; return &dce2_tcp_pstat_co_reass; - case 9: + case 8: name = "dce tcp connection-oriented context"; parent = "dce tcp main"; return &dce2_tcp_pstat_co_ctx; diff --git a/src/service_inspectors/dce_rpc/dce_tcp_paf.cc b/src/service_inspectors/dce_rpc/dce_tcp_paf.cc index e30cbac23..a279a5292 100644 --- a/src/service_inspectors/dce_rpc/dce_tcp_paf.cc +++ b/src/service_inspectors/dce_rpc/dce_tcp_paf.cc @@ -56,7 +56,7 @@ static StreamSplitter::Status dce2_tcp_paf(DCE2_PafTcpData* ds, Flow* flow, cons DebugMessage(DEBUG_DCE_TCP, "Packet from Server\n"); #endif - if (dce2_paf_abort(flow)) + if (dce2_paf_abort(flow, (DCE2_SsnData*)sd)) { DebugFormat(DEBUG_DCE_TCP, "%s\n", DCE2_DEBUG__PAF_END_MSG); return StreamSplitter::ABORT; diff --git a/src/service_inspectors/dce_rpc/dce_utils.h b/src/service_inspectors/dce_rpc/dce_utils.h index afef4a020..e923f1ed4 100644 --- a/src/service_inspectors/dce_rpc/dce_utils.h +++ b/src/service_inspectors/dce_rpc/dce_utils.h @@ -17,8 +17,8 @@ // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //-------------------------------------------------------------------------- -#ifndef _DCE_UTILS_H_ -#define _DCE_UTILS_H_ +#ifndef DCE_UTILS_H +#define DCE_UTILS_H #include #include @@ -50,6 +50,14 @@ enum DCE2_Ret DCE2_RET__DUPLICATE }; +enum DCE2_TransType +{ + DCE2_TRANS_TYPE__NONE = 0, + DCE2_TRANS_TYPE__SMB, + DCE2_TRANS_TYPE__TCP, + DCE2_TRANS_TYPE__MAX +}; + enum DCE2_IntType { DCE2_INT_TYPE__INT8, @@ -76,6 +84,15 @@ struct Uuid uint8_t node[6]; }; +struct DCE2_Buffer +{ + uint8_t* data; + uint32_t len; + uint32_t size; + uint32_t min_add_size; + uint32_t offset; +}; + /******************************************************************** * Inline function prototypes ********************************************************************/