From: Shravan Rangarajuvenkata (shrarang) Date: Wed, 3 Jun 2020 19:43:12 +0000 (+0000) Subject: Merge pull request #2226 in SNORT/snort3 from ~EBURMAI/snort3:dce_tcp_pinhole to... X-Git-Tag: 3.0.1-5~30 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=96aacbaf33856182524461468a700552b950fe34;p=thirdparty%2Fsnort3.git Merge pull request #2226 in SNORT/snort3 from ~EBURMAI/snort3:dce_tcp_pinhole to master Squashed commit of the following: commit fe674926599fc7ff9b42dd8cbe624e23eb747e63 Author: Eduard Burmai Date: Wed May 13 16:00:18 2020 -0400 dce_rpc: suppport for DCE/RPC future session --- diff --git a/src/network_inspectors/appid/appid_dcerpc_event_handler.h b/src/network_inspectors/appid/appid_dcerpc_event_handler.h new file mode 100644 index 000000000..3e2aed796 --- /dev/null +++ b/src/network_inspectors/appid/appid_dcerpc_event_handler.h @@ -0,0 +1,64 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2020-2020 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. +//-------------------------------------------------------------------------- + +// appid_dcerpc_event_handler.h author Eduard Burmai + +#ifndef APPID_DCERPC_EVENT_HANDLER_H +#define APPID_DCERPC_EVENT_HANDLER_H + +#include "pub_sub/dcerpc_events.h" + +#include "appid_session.h" +#include "service_plugins/service_detector.h" + +class DceExpSsnEventHandler : public snort::DataHandler +{ +public: + DceExpSsnEventHandler() : DataHandler(MOD_NAME) { } + + void handle(snort::DataEvent& event, snort::Flow* flow) override + { + assert(flow); + + AppIdSession* asd = snort::appid_api.get_appid_session(*flow); + if (!asd) + return; // appid disabled + + DceExpectedSessionEvent& map_resp_event = static_cast(event); + + const snort::Packet* pkt = map_resp_event.get_packet(); + const snort::SfIp* src_ip = map_resp_event.get_src_ip(); + const snort::SfIp* dst_ip = map_resp_event.get_dst_ip(); + uint16_t src_port = map_resp_event.get_src_port(); + uint16_t dst_port = map_resp_event.get_dst_port(); + IpProtocol proto = map_resp_event.get_ip_proto(); + SnortProtocolId protocol_id = map_resp_event.get_proto_id(); + + AppIdSession* fp = AppIdSession::create_future_session(pkt, src_ip, src_port, + dst_ip, dst_port, proto, protocol_id); + + if (fp) // initialize data session + { + fp->service.set_id(APP_ID_DCE_RPC, asd->ctxt.get_odp_ctxt()); + asd->initialize_future_session(*fp, APPID_SESSION_IGNORE_ID_FLAGS, + APP_ID_FROM_RESPONDER); + } + } +}; + +#endif // APPID_DCERPC_EVENT_HANDLER_H diff --git a/src/network_inspectors/appid/appid_inspector.cc b/src/network_inspectors/appid/appid_inspector.cc index 71df6fc11..9aad875a7 100644 --- a/src/network_inspectors/appid/appid_inspector.cc +++ b/src/network_inspectors/appid/appid_inspector.cc @@ -35,6 +35,7 @@ #include "app_forecast.h" #include "appid_data_decrypt_event_handler.h" +#include "appid_dcerpc_event_handler.h" #include "appid_debug.h" #include "appid_discovery.h" #include "appid_http_event_handler.h" @@ -124,6 +125,8 @@ bool AppIdInspector::configure(SnortConfig* sc) DataBus::subscribe_global(DATA_DECRYPT_EVENT, new DataDecryptEventHandler(), sc); + DataBus::subscribe_global(DCERPC_EXP_SESSION_EVENT_KEY, new DceExpSsnEventHandler(), sc); + return true; } diff --git a/src/network_inspectors/appid/appid_session.cc b/src/network_inspectors/appid/appid_session.cc index 39c78c35c..afe857ca4 100644 --- a/src/network_inspectors/appid/appid_session.cc +++ b/src/network_inspectors/appid/appid_session.cc @@ -170,7 +170,7 @@ static inline PktType get_pkt_type_from_ip_proto(IpProtocol proto) AppIdSession* AppIdSession::create_future_session(const Packet* ctrlPkt, const SfIp* cliIp, uint16_t cliPort, const SfIp* srvIp, uint16_t srvPort, IpProtocol proto, - SnortProtocolId snort_protocol_id, int /*flags*/) + SnortProtocolId snort_protocol_id) { char src_ip[INET6_ADDRSTRLEN]; char dst_ip[INET6_ADDRSTRLEN]; @@ -216,6 +216,42 @@ AppIdSession* AppIdSession::create_future_session(const Packet* ctrlPkt, const S return asd; } +void AppIdSession::initialize_future_session(AppIdSession& expected, uint64_t flags, + AppidSessionDirection dir) +{ + if (dir == APP_ID_FROM_INITIATOR) + { + expected.set_session_flags(flags | + get_session_flags( + APPID_SESSION_INITIATOR_CHECKED | + APPID_SESSION_INITIATOR_MONITORED | + APPID_SESSION_RESPONDER_CHECKED | + APPID_SESSION_RESPONDER_MONITORED)); + } + else if (dir == APP_ID_FROM_RESPONDER) + { + if (get_session_flags(APPID_SESSION_INITIATOR_CHECKED)) + flags |= APPID_SESSION_RESPONDER_CHECKED; + + if (get_session_flags(APPID_SESSION_INITIATOR_MONITORED)) + flags |= APPID_SESSION_RESPONDER_MONITORED; + + if (get_session_flags(APPID_SESSION_RESPONDER_CHECKED)) + flags |= APPID_SESSION_INITIATOR_CHECKED; + + if (get_session_flags(APPID_SESSION_RESPONDER_MONITORED)) + flags |= APPID_SESSION_INITIATOR_MONITORED; + } + + expected.set_session_flags(flags | + get_session_flags( + APPID_SESSION_SPECIAL_MONITORED | + APPID_SESSION_DISCOVER_APP | + APPID_SESSION_DISCOVER_USER)); + + expected.service_disco_state = APPID_DISCO_STATE_FINISHED; + expected.client_disco_state = APPID_DISCO_STATE_FINISHED; +} void AppIdSession::reinit_session_data(AppidChangeBits& change_bits) { misc_app_id = APP_ID_NONE; diff --git a/src/network_inspectors/appid/appid_session.h b/src/network_inspectors/appid/appid_session.h index 276d3728f..f836d5604 100644 --- a/src/network_inspectors/appid/appid_session.h +++ b/src/network_inspectors/appid/appid_session.h @@ -209,8 +209,8 @@ public: static AppIdSession* allocate_session(const snort::Packet*, IpProtocol, AppidSessionDirection, AppIdInspector*); static AppIdSession* create_future_session(const snort::Packet*, const snort::SfIp*, uint16_t, - const snort::SfIp*, - uint16_t, IpProtocol, SnortProtocolId, int); + const snort::SfIp*, uint16_t, IpProtocol, SnortProtocolId); + void initialize_future_session(AppIdSession&, uint64_t, AppidSessionDirection); size_t size_of() override { return sizeof(*this); } diff --git a/src/network_inspectors/appid/detector_plugins/detector_sip.cc b/src/network_inspectors/appid/detector_plugins/detector_sip.cc index 3cf16fe38..56960e38d 100644 --- a/src/network_inspectors/appid/detector_plugins/detector_sip.cc +++ b/src/network_inspectors/appid/detector_plugins/detector_sip.cc @@ -184,8 +184,7 @@ void SipServiceDetector::createRtpFlow(AppIdSession& asd, const Packet* pkt, con // snort_protocol_id. AppIdSession* fp = AppIdSession::create_future_session( - pkt, cliIp, cliPort, srvIp, srvPort, protocol, app_id, - APPID_EARLY_SESSION_FLAG_FW_RULE); + pkt, cliIp, cliPort, srvIp, srvPort, protocol, app_id); if ( fp ) { @@ -195,17 +194,16 @@ void SipServiceDetector::createRtpFlow(AppIdSession& asd, const Packet* pkt, con // FIXIT-M : snort 2.9.x updated the flag to APPID_SESSION_EXPECTED_EVALUATE. // Check if it is needed here as well. - //initialize_expected_session(asd, fp, APPID_SESSION_EXPECTED_EVALUATE); + // asd.initialize_future_session(*fp, APPID_SESSION_EXPECTED_EVALUATE, APP_ID_APPID_SESSION_DIRECTION_MAX); - initialize_expected_session( - asd, *fp, APPID_SESSION_IGNORE_ID_FLAGS, APP_ID_APPID_SESSION_DIRECTION_MAX); + asd.initialize_future_session(*fp, APPID_SESSION_IGNORE_ID_FLAGS, + APP_ID_APPID_SESSION_DIRECTION_MAX); } // create an RTCP flow as well AppIdSession* fp2 = AppIdSession::create_future_session( - pkt, cliIp, cliPort + 1, srvIp, srvPort + 1, protocol, app_id, - APPID_EARLY_SESSION_FLAG_FW_RULE); + pkt, cliIp, cliPort + 1, srvIp, srvPort + 1, protocol, app_id); if ( fp2 ) { @@ -214,10 +212,10 @@ void SipServiceDetector::createRtpFlow(AppIdSession& asd, const Packet* pkt, con fp2->service.set_id(APP_ID_RTCP, asd.ctxt.get_odp_ctxt()); // FIXIT-M : same comment as above - //initialize_expected_session(asd, fp2, APPID_SESSION_EXPECTED_EVALUATE); + // asd.initialize_future_session(*fp2, APPID_SESSION_EXPECTED_EVALUATE, APP_ID_APPID_SESSION_DIRECTION_MAX); - initialize_expected_session( - asd, *fp2, APPID_SESSION_IGNORE_ID_FLAGS, APP_ID_APPID_SESSION_DIRECTION_MAX); + asd.initialize_future_session(*fp2, APPID_SESSION_IGNORE_ID_FLAGS, + APP_ID_APPID_SESSION_DIRECTION_MAX); } } diff --git a/src/network_inspectors/appid/lua_detector_api.cc b/src/network_inspectors/appid/lua_detector_api.cc index 4b131c548..10b1a13d0 100644 --- a/src/network_inspectors/appid/lua_detector_api.cc +++ b/src/network_inspectors/appid/lua_detector_api.cc @@ -2415,8 +2415,7 @@ static int create_future_flow(lua_State* L) } AppIdSession* fp = AppIdSession::create_future_session(lsd->ldp.pkt, &client_addr, - client_port, &server_addr, server_port, proto, snort_protocol_id, - APPID_EARLY_SESSION_FLAG_FW_RULE); + client_port, &server_addr, server_port, proto, snort_protocol_id); if (fp) { fp->service.set_id(service_id, ud->get_odp_ctxt()); diff --git a/src/network_inspectors/appid/lua_detector_flow_api.cc b/src/network_inspectors/appid/lua_detector_flow_api.cc index 3d045b260..4ed4d359b 100644 --- a/src/network_inspectors/appid/lua_detector_flow_api.cc +++ b/src/network_inspectors/appid/lua_detector_flow_api.cc @@ -213,7 +213,7 @@ static int create_detector_flow(lua_State* L) LuaDetectorManager::add_detector_flow(detector_flow); detector_flow->asd = AppIdSession::create_future_session(lsd->ldp.pkt, &saddr, sport, - &daddr, dport, proto, 0, 0); + &daddr, dport, proto, 0); if (!detector_flow->asd) { diff --git a/src/network_inspectors/appid/service_plugins/service_detector.cc b/src/network_inspectors/appid/service_plugins/service_detector.cc index c09ba3eff..20837389f 100644 --- a/src/network_inspectors/appid/service_plugins/service_detector.cc +++ b/src/network_inspectors/appid/service_plugins/service_detector.cc @@ -175,40 +175,3 @@ int ServiceDetector::fail_service(AppIdSession& asd, const Packet* pkt, AppidSes { return static_cast(handler)->fail_service(asd, pkt, dir, this); } - -void ServiceDetector::initialize_expected_session(const AppIdSession& parent, AppIdSession& expected, - uint64_t flags, AppidSessionDirection dir) -{ - if (dir == APP_ID_FROM_INITIATOR) - { - expected.set_session_flags(flags | - parent.get_session_flags( - APPID_SESSION_INITIATOR_CHECKED | - APPID_SESSION_INITIATOR_MONITORED | - APPID_SESSION_RESPONDER_CHECKED | - APPID_SESSION_RESPONDER_MONITORED)); - } - else if (dir == APP_ID_FROM_RESPONDER) - { - if (parent.get_session_flags(APPID_SESSION_INITIATOR_CHECKED)) - flags |= APPID_SESSION_RESPONDER_CHECKED; - - if (parent.get_session_flags(APPID_SESSION_INITIATOR_MONITORED)) - flags |= APPID_SESSION_RESPONDER_MONITORED; - - if (parent.get_session_flags(APPID_SESSION_RESPONDER_CHECKED)) - flags |= APPID_SESSION_INITIATOR_CHECKED; - - if (parent.get_session_flags(APPID_SESSION_RESPONDER_MONITORED)) - flags |= APPID_SESSION_INITIATOR_MONITORED; - } - - expected.set_session_flags(flags | - parent.get_session_flags( - APPID_SESSION_SPECIAL_MONITORED | - APPID_SESSION_DISCOVER_APP | - APPID_SESSION_DISCOVER_USER)); - - expected.service_disco_state = APPID_DISCO_STATE_FINISHED; - expected.client_disco_state = APPID_DISCO_STATE_FINISHED; -} diff --git a/src/network_inspectors/appid/service_plugins/service_detector.h b/src/network_inspectors/appid/service_plugins/service_detector.h index 489467d8a..4683e3f1a 100644 --- a/src/network_inspectors/appid/service_plugins/service_detector.h +++ b/src/network_inspectors/appid/service_plugins/service_detector.h @@ -25,8 +25,6 @@ #include "appid_detector.h" #include "service_discovery.h" -#define APPID_EARLY_SESSION_FLAG_FW_RULE 1 - class ServiceDetector : public AppIdDetector { public: @@ -58,8 +56,6 @@ public: asd.misc_app_id = miscId; } - void initialize_expected_session(const AppIdSession&, AppIdSession&, uint64_t flags, AppidSessionDirection dir); - private: int update_service_data(AppIdSession&, const snort::Packet*, AppidSessionDirection dir, AppId, const char* vendor, const char* version, AppidChangeBits& change_bits); diff --git a/src/network_inspectors/appid/service_plugins/service_ftp.cc b/src/network_inspectors/appid/service_plugins/service_ftp.cc index 357359e7f..44b673091 100644 --- a/src/network_inspectors/appid/service_plugins/service_ftp.cc +++ b/src/network_inspectors/appid/service_plugins/service_ftp.cc @@ -879,8 +879,7 @@ static inline void WatchForCommandResult(ServiceFTPData* fd, AppIdSession& asd, } void FtpServiceDetector::create_expected_session(AppIdSession& asd, const Packet* pkt, const SfIp* cliIp, - uint16_t cliPort, const SfIp* srvIp, uint16_t srvPort, IpProtocol protocol, - int flags, AppidSessionDirection dir) + uint16_t cliPort, const SfIp* srvIp, uint16_t srvPort, IpProtocol protocol, AppidSessionDirection dir) { // FIXIT-M - Avoid thread locals static THREAD_LOCAL SnortProtocolId ftp_data_snort_protocol_id = UNKNOWN_PROTOCOL_ID; @@ -888,7 +887,7 @@ void FtpServiceDetector::create_expected_session(AppIdSession& asd, const Packet ftp_data_snort_protocol_id = pkt->context->conf->proto_ref->find("ftp-data"); AppIdSession* fp = AppIdSession::create_future_session(pkt, cliIp, cliPort, srvIp, srvPort, - protocol, ftp_data_snort_protocol_id, flags); + protocol, ftp_data_snort_protocol_id); if (fp) // initialize data session { @@ -903,7 +902,7 @@ void FtpServiceDetector::create_expected_session(AppIdSession& asd, const Packet fp->service.set_id(APP_ID_FTP_DATA, asd.ctxt.get_odp_ctxt()); } - initialize_expected_session(asd, *fp, APPID_SESSION_IGNORE_ID_FLAGS | encrypted_flags, dir); + asd.initialize_future_session(*fp, APPID_SESSION_IGNORE_ID_FLAGS | encrypted_flags, dir); } } @@ -962,8 +961,8 @@ int FtpServiceDetector::validate(AppIdDiscoveryArgs& args) &fd->address, &fd->port) == 0) { const SfIp* dip = args.pkt->ptrs.ip_api.get_dst(); - create_expected_session(args.asd, args.pkt, dip, 0, &fd->address, fd->port, args.asd.protocol, - APPID_EARLY_SESSION_FLAG_FW_RULE, APP_ID_FROM_RESPONDER); + create_expected_session(args.asd, args.pkt, dip, 0, &fd->address, fd->port, + args.asd.protocol, APP_ID_FROM_RESPONDER); WatchForCommandResult(fd, args.asd, FTP_CMD_PORT_EPRT); } } @@ -975,8 +974,8 @@ int FtpServiceDetector::validate(AppIdDiscoveryArgs& args) &fd->address, &fd->port) == 0) { const SfIp* dip = args.pkt->ptrs.ip_api.get_dst(); - create_expected_session(args.asd, args.pkt, dip, 0, &fd->address, fd->port, args.asd.protocol, - APPID_EARLY_SESSION_FLAG_FW_RULE, APP_ID_FROM_RESPONDER); + create_expected_session(args.asd, args.pkt, dip, 0, &fd->address, fd->port, + args.asd.protocol, APP_ID_FROM_RESPONDER); WatchForCommandResult(fd, args.asd, FTP_CMD_PORT_EPRT); } } @@ -1244,14 +1243,12 @@ int FtpServiceDetector::validate(AppIdDiscoveryArgs& args) addr = htonl(address); ip.set(&addr, AF_INET); create_expected_session(args.asd, args.pkt, dip, 0, &ip, port, - args.asd.protocol, APPID_EARLY_SESSION_FLAG_FW_RULE, - APP_ID_FROM_INITIATOR); + args.asd.protocol, APP_ID_FROM_INITIATOR); if (!ip.fast_eq6(*sip)) { create_expected_session(args.asd, args.pkt, dip, 0, sip, port, - args.asd.protocol, APPID_EARLY_SESSION_FLAG_FW_RULE, - APP_ID_FROM_INITIATOR); + args.asd.protocol, APP_ID_FROM_INITIATOR); } add_payload(args.asd, APP_ID_FTP_PASSIVE); } @@ -1268,8 +1265,8 @@ int FtpServiceDetector::validate(AppIdDiscoveryArgs& args) { const SfIp* dip = args.pkt->ptrs.ip_api.get_dst(); const SfIp* sip = args.pkt->ptrs.ip_api.get_src(); - create_expected_session(args.asd, args.pkt, dip, 0, sip, port, args.asd.protocol, - APPID_EARLY_SESSION_FLAG_FW_RULE, APP_ID_FROM_INITIATOR); + create_expected_session(args.asd, args.pkt, dip, 0, sip, port, + args.asd.protocol, APP_ID_FROM_INITIATOR); add_payload(args.asd, APP_ID_FTP_PASSIVE); } diff --git a/src/network_inspectors/appid/service_plugins/service_ftp.h b/src/network_inspectors/appid/service_plugins/service_ftp.h index dccb2928d..ee26742a1 100644 --- a/src/network_inspectors/appid/service_plugins/service_ftp.h +++ b/src/network_inspectors/appid/service_plugins/service_ftp.h @@ -36,7 +36,7 @@ public: private: void create_expected_session(AppIdSession&, const snort::Packet*, const snort::SfIp* cliIp, uint16_t cliPort, const snort::SfIp* srvIp, - uint16_t srvPort, IpProtocol, int flags, AppidSessionDirection); + uint16_t srvPort, IpProtocol, AppidSessionDirection); }; #endif diff --git a/src/network_inspectors/appid/service_plugins/service_rexec.cc b/src/network_inspectors/appid/service_plugins/service_rexec.cc index e301ba252..c3ed30e01 100644 --- a/src/network_inspectors/appid/service_plugins/service_rexec.cc +++ b/src/network_inspectors/appid/service_plugins/service_rexec.cc @@ -168,7 +168,7 @@ int RexecServiceDetector::validate(AppIdDiscoveryArgs& args) dip = args.pkt->ptrs.ip_api.get_dst(); sip = args.pkt->ptrs.ip_api.get_src(); AppIdSession* pf = AppIdSession::create_future_session(args.pkt, dip, 0, sip, (uint16_t)port, - IpProtocol::TCP, rexec_snort_protocol_id, APPID_EARLY_SESSION_FLAG_FW_RULE); + IpProtocol::TCP, rexec_snort_protocol_id); if (pf) { ServiceREXECData* tmp_rd = (ServiceREXECData*)snort_calloc( @@ -186,7 +186,7 @@ int RexecServiceDetector::validate(AppIdDiscoveryArgs& args) } pf->service_disco_state = APPID_DISCO_STATE_STATEFUL; pf->scan_flags |= SCAN_HOST_PORT_FLAG; - initialize_expected_session(args.asd, *pf, REXEC_EXPECTED_SESSION_FLAGS, APP_ID_FROM_RESPONDER); + args.asd.initialize_future_session(*pf, REXEC_EXPECTED_SESSION_FLAGS, APP_ID_FROM_RESPONDER); pf->service_disco_state = APPID_DISCO_STATE_STATEFUL; rd->child = tmp_rd; rd->state = REXEC_STATE_SERVER_CONNECT; diff --git a/src/network_inspectors/appid/service_plugins/service_rpc.cc b/src/network_inspectors/appid/service_plugins/service_rpc.cc index 8be459026..ffbeb96d3 100644 --- a/src/network_inspectors/appid/service_plugins/service_rpc.cc +++ b/src/network_inspectors/appid/service_plugins/service_rpc.cc @@ -413,7 +413,7 @@ int RpcServiceDetector::validate_packet(const uint8_t* data, uint16_t size, Appi AppIdSession* pf = AppIdSession::create_future_session( pkt, dip, 0, sip, (uint16_t)tmp, - (IpProtocol)ntohl((uint32_t)rd->proto), sunrpc_snort_protocol_id, 0); + (IpProtocol)ntohl((uint32_t)rd->proto), sunrpc_snort_protocol_id); if (pf) { pf->add_flow_data_id((uint16_t)tmp, this); diff --git a/src/network_inspectors/appid/service_plugins/service_rshell.cc b/src/network_inspectors/appid/service_plugins/service_rshell.cc index b261b2e21..0f14d8798 100644 --- a/src/network_inspectors/appid/service_plugins/service_rshell.cc +++ b/src/network_inspectors/appid/service_plugins/service_rshell.cc @@ -161,7 +161,7 @@ int RshellServiceDetector::validate(AppIdDiscoveryArgs& args) const SfIp* dip = args.pkt->ptrs.ip_api.get_dst(); const SfIp* sip = args.pkt->ptrs.ip_api.get_src(); AppIdSession* pf = AppIdSession::create_future_session(args.pkt, dip, 0, sip, - (uint16_t)port, IpProtocol::TCP, rsh_error_snort_protocol_id, APPID_EARLY_SESSION_FLAG_FW_RULE); + (uint16_t)port, IpProtocol::TCP, rsh_error_snort_protocol_id); if (pf) { ServiceRSHELLData* tmp_rd = (ServiceRSHELLData*)snort_calloc( @@ -178,10 +178,10 @@ int RshellServiceDetector::validate(AppIdDiscoveryArgs& args) return APPID_ENOMEM; } pf->scan_flags |= SCAN_HOST_PORT_FLAG; - initialize_expected_session(args.asd, *pf, - APPID_SESSION_CONTINUE | APPID_SESSION_REXEC_STDERR | APPID_SESSION_NO_TPI | + args.asd.initialize_future_session(*pf, APPID_SESSION_CONTINUE | APPID_SESSION_REXEC_STDERR | APPID_SESSION_NO_TPI | APPID_SESSION_NOT_A_SERVICE | APPID_SESSION_PORT_SERVICE_DONE, APP_ID_FROM_RESPONDER); + pf->service_disco_state = APPID_DISCO_STATE_STATEFUL; rd->child = tmp_rd; rd->state = RSHELL_STATE_SERVER_CONNECT; diff --git a/src/network_inspectors/appid/service_plugins/service_snmp.cc b/src/network_inspectors/appid/service_plugins/service_snmp.cc index b6c15f797..4bd8d1f09 100644 --- a/src/network_inspectors/appid/service_plugins/service_snmp.cc +++ b/src/network_inspectors/appid/service_plugins/service_snmp.cc @@ -471,7 +471,7 @@ int SnmpServiceDetector::validate(AppIdDiscoveryArgs& args) const SfIp* dip = args.pkt->ptrs.ip_api.get_dst(); const SfIp* sip = args.pkt->ptrs.ip_api.get_src(); AppIdSession* pf = AppIdSession::create_future_session(args.pkt, dip, 0, sip, - args.pkt->ptrs.sp, args.asd.protocol, snmp_snort_protocol_id, 0); + args.pkt->ptrs.sp, args.asd.protocol, snmp_snort_protocol_id); if (pf) { tmp_sd = (ServiceSNMPData*)snort_calloc(sizeof(ServiceSNMPData)); @@ -484,7 +484,7 @@ int SnmpServiceDetector::validate(AppIdDiscoveryArgs& args) tmp_sd->state = SNMP_STATE_ERROR; return APPID_ENULL; } - initialize_expected_session(args.asd, *pf, APPID_SESSION_EXPECTED_EVALUATE, APP_ID_APPID_SESSION_DIRECTION_MAX); + args.asd.initialize_future_session(*pf, APPID_SESSION_EXPECTED_EVALUATE, APP_ID_APPID_SESSION_DIRECTION_MAX); pf->service_disco_state = APPID_DISCO_STATE_STATEFUL; pf->scan_flags |= SCAN_HOST_PORT_FLAG; pf->common.initiator_ip = *sip; diff --git a/src/network_inspectors/appid/service_plugins/service_ssl.cc b/src/network_inspectors/appid/service_plugins/service_ssl.cc index 0dfe0ee9a..6e2b97dca 100644 --- a/src/network_inspectors/appid/service_plugins/service_ssl.cc +++ b/src/network_inspectors/appid/service_plugins/service_ssl.cc @@ -809,4 +809,3 @@ bool is_service_over_ssl(AppId appId) return false; } - diff --git a/src/network_inspectors/appid/service_plugins/service_tftp.cc b/src/network_inspectors/appid/service_plugins/service_tftp.cc index 8abfd2db4..2dbe4c762 100644 --- a/src/network_inspectors/appid/service_plugins/service_tftp.cc +++ b/src/network_inspectors/appid/service_plugins/service_tftp.cc @@ -192,7 +192,7 @@ int TftpServiceDetector::validate(AppIdDiscoveryArgs& args) dip = args.pkt->ptrs.ip_api.get_dst(); sip = args.pkt->ptrs.ip_api.get_src(); pf = AppIdSession::create_future_session(args.pkt, dip, 0, sip, - args.pkt->ptrs.sp, args.asd.protocol, tftp_snort_protocol_id, APPID_EARLY_SESSION_FLAG_FW_RULE); + args.pkt->ptrs.sp, args.asd.protocol, tftp_snort_protocol_id); if (pf) { data_add(*pf, tmp_td, &snort_free); @@ -203,7 +203,7 @@ int TftpServiceDetector::validate(AppIdDiscoveryArgs& args) tmp_td->state = TFTP_STATE_ERROR; return APPID_ENOMEM; } - initialize_expected_session(args.asd, *pf, APPID_SESSION_EXPECTED_EVALUATE, APP_ID_FROM_RESPONDER); + args.asd.initialize_future_session(*pf, APPID_SESSION_EXPECTED_EVALUATE, APP_ID_FROM_RESPONDER); pf->common.initiator_ip = *sip; pf->service_disco_state = APPID_DISCO_STATE_STATEFUL; pf->scan_flags |= SCAN_HOST_PORT_FLAG; diff --git a/src/network_inspectors/appid/test/CMakeLists.txt b/src/network_inspectors/appid/test/CMakeLists.txt index dbe838878..e7b7d63c7 100644 --- a/src/network_inspectors/appid/test/CMakeLists.txt +++ b/src/network_inspectors/appid/test/CMakeLists.txt @@ -25,10 +25,6 @@ add_cpputest( appid_discovery_test SOURCES $ ) -add_cpputest( appid_expected_flags_test - SOURCES $ -) - add_cpputest( appid_http_event_test SOURCES $ ) diff --git a/src/network_inspectors/appid/test/appid_expected_flags_test.cc b/src/network_inspectors/appid/test/appid_expected_flags_test.cc deleted file mode 100644 index 5cfcfedb8..000000000 --- a/src/network_inspectors/appid/test/appid_expected_flags_test.cc +++ /dev/null @@ -1,154 +0,0 @@ -//-------------------------------------------------------------------------- -// Copyright (C) 2017-2020 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. -//-------------------------------------------------------------------------- - -// appid_expected_flags_test.cc author maya dagon - -#include "network_inspectors/appid/appid_detector.cc" -#include "network_inspectors/appid/service_plugins/service_detector.cc" - -#include "appid_mock_definitions.h" -#include "appid_mock_inspector.h" -#include "appid_mock_session.h" - -#include -#include - -snort::Inspector* snort::InspectorManager::get_inspector( - char const*, bool, const snort::SnortConfig*) { return nullptr; } - -void ApplicationDescriptor::set_id( - const Packet&, AppIdSession&, AppidSessionDirection, AppId, AppidChangeBits&) { } - -void AppIdHttpSession::set_http_change_bits(AppidChangeBits&, HttpFieldIds) {} - -class MockServiceDetector : public ServiceDetector -{ -public: - int validate(AppIdDiscoveryArgs&) override { return 0; } -}; - -AppIdSession* parent = nullptr; -AppIdSession* expected = nullptr; -MockServiceDetector test_detector; - -uint64_t allFlags = APPID_SESSION_RESPONDER_MONITORED | APPID_SESSION_INITIATOR_MONITORED | - APPID_SESSION_SPECIAL_MONITORED | APPID_SESSION_RESPONDER_CHECKED | - APPID_SESSION_INITIATOR_CHECKED | APPID_SESSION_DISCOVER_APP | - APPID_SESSION_DISCOVER_USER; - -static inline void cleanup() -{ - parent->clear_session_flags(allFlags); - expected->clear_session_flags(allFlags); -} - -TEST_GROUP(appid_expected_flags) -{ - void setup() override - { - MemoryLeakWarningPlugin::turnOffNewDeleteOverloads(); - parent = new AppIdSession(IpProtocol::TCP, nullptr, 1492, dummy_appid_inspector); - expected = new AppIdSession(IpProtocol::TCP, nullptr, 1492, dummy_appid_inspector); - } - - void teardown() override - { - delete parent; - delete expected; - MemoryLeakWarningPlugin::turnOnNewDeleteOverloads(); - } -}; - -TEST(appid_expected_flags, test1) -{ - // Test 1 - expected flow same direction as parent, initiator monitored - parent->set_session_flags(APPID_SESSION_INITIATOR_CHECKED | APPID_SESSION_INITIATOR_MONITORED); - test_detector.initialize_expected_session(*parent, *expected, 0, APP_ID_FROM_INITIATOR); - - CHECK(expected->get_session_flags(allFlags) == - (APPID_SESSION_INITIATOR_CHECKED | APPID_SESSION_INITIATOR_MONITORED)); - - cleanup(); -} - -TEST(appid_expected_flags, test2) -{ - // Test 2 - expected flow same direction as parent, responder monitored - parent->set_session_flags(APPID_SESSION_RESPONDER_CHECKED | APPID_SESSION_RESPONDER_MONITORED); - test_detector.initialize_expected_session(*parent, *expected, 0, APP_ID_FROM_INITIATOR); - - CHECK(expected->get_session_flags(allFlags) == - (APPID_SESSION_RESPONDER_CHECKED | APPID_SESSION_RESPONDER_MONITORED)); - - cleanup(); -} - -TEST(appid_expected_flags, test3) -{ - // Test 3 - expected flow same direction as parent, all flags set - parent->set_session_flags(allFlags); - test_detector.initialize_expected_session(*parent, *expected, 0, APP_ID_FROM_INITIATOR); - - CHECK(expected->get_session_flags(allFlags) == allFlags); - - cleanup(); -} - -TEST(appid_expected_flags, test4) -{ - // Test 4 - expected flow opposite direction as parent, initiator monitored - parent->set_session_flags(APPID_SESSION_INITIATOR_CHECKED | APPID_SESSION_INITIATOR_MONITORED); - test_detector.initialize_expected_session(*parent, *expected, 0, APP_ID_FROM_RESPONDER); - - CHECK(expected->get_session_flags(allFlags) == - (APPID_SESSION_RESPONDER_CHECKED | APPID_SESSION_RESPONDER_MONITORED)); - - cleanup(); -} - -TEST(appid_expected_flags, test5) -{ - // Test 5 - expected flow opposite direction as parent, responder monitored - parent->set_session_flags(APPID_SESSION_RESPONDER_CHECKED | APPID_SESSION_RESPONDER_MONITORED); - test_detector.initialize_expected_session(*parent, *expected, 0, APP_ID_FROM_RESPONDER); - - CHECK(expected->get_session_flags(allFlags) == - (APPID_SESSION_INITIATOR_CHECKED | APPID_SESSION_INITIATOR_MONITORED)); - - cleanup(); -} - -TEST(appid_expected_flags, test6) -{ - // Test 6 - expected flow opposite direction as parent, all flags set - parent->set_session_flags(allFlags); - test_detector.initialize_expected_session(*parent, *expected, 0, APP_ID_FROM_RESPONDER); - - CHECK(expected->get_session_flags(allFlags) == allFlags); - - cleanup(); -} - -int main(int argc, char** argv) -{ - mock_init_appid_pegs(); - int rc = CommandLineTestRunner::RunAllTests(argc, argv); - mock_cleanup_appid_pegs(); - return rc; -} - diff --git a/src/pub_sub/CMakeLists.txt b/src/pub_sub/CMakeLists.txt index fc79d7568..5c62a5681 100644 --- a/src/pub_sub/CMakeLists.txt +++ b/src/pub_sub/CMakeLists.txt @@ -3,6 +3,7 @@ set (PUB_SUB_INCLUDES cip_events.h data_decrypt_event.h daq_message_event.h + dcerpc_events.h expect_events.h finalize_packet_event.h http_events.h diff --git a/src/pub_sub/dcerpc_events.h b/src/pub_sub/dcerpc_events.h new file mode 100644 index 000000000..3b28ff800 --- /dev/null +++ b/src/pub_sub/dcerpc_events.h @@ -0,0 +1,63 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2020-2020 Cisco and/or its affiliates. All rights reserved. +//-------------------------------------------------------------------------- + +#ifndef DCERPC_EVENTS_H +#define DCERPC_EVENTS_H + +#include "framework/data_bus.h" + +#define DCERPC_EXP_SESSION_EVENT_KEY "dcerpc_expected_session_event" + +namespace snort +{ +struct SfIp; +} + +class DceExpectedSessionEvent : public snort::DataEvent +{ +public: + + DceExpectedSessionEvent(snort::Packet* p, + const snort::SfIp* src_ip, const uint16_t src_port, + const snort::SfIp* dst_ip, const uint16_t dst_port, + IpProtocol proto, SnortProtocolId protocol_id) : + p(p), src_ip(src_ip), src_port(src_port), + dst_ip(dst_ip), dst_port(dst_port), + proto(proto), protocol_id(protocol_id) { } + + const snort::Packet* get_packet() override + { return p; } + + const snort::SfIp* get_src_ip() const + { return src_ip; } + + const snort::SfIp* get_dst_ip() const + { return dst_ip; } + + uint16_t get_src_port() const + { return src_port; } + + uint16_t get_dst_port() const + { return dst_port; } + + SnortProtocolId get_proto_id() const + { return protocol_id; } + + IpProtocol get_ip_proto() const + { return proto; } + +private: + const snort::Packet* p; + + const snort::SfIp* src_ip; + uint16_t src_port; + + const snort::SfIp* dst_ip; + uint16_t dst_port; + + IpProtocol proto; + SnortProtocolId protocol_id; +}; + +#endif // DCERPC_EVENTS_H diff --git a/src/service_inspectors/dce_rpc/CMakeLists.txt b/src/service_inspectors/dce_rpc/CMakeLists.txt index aa055282e..c94c51bab 100644 --- a/src/service_inspectors/dce_rpc/CMakeLists.txt +++ b/src/service_inspectors/dce_rpc/CMakeLists.txt @@ -6,6 +6,8 @@ set( FILE_LIST dce_common.h dce_context_data.cc dce_context_data.h + dce_expected_session.cc + dce_expected_session.h dce_http_proxy.cc dce_http_proxy_module.cc dce_http_proxy_module.h diff --git a/src/service_inspectors/dce_rpc/dce_co.cc b/src/service_inspectors/dce_rpc/dce_co.cc index 066867da1..ac88475f3 100644 --- a/src/service_inspectors/dce_rpc/dce_co.cc +++ b/src/service_inspectors/dce_rpc/dce_co.cc @@ -27,16 +27,140 @@ #include "utils/util.h" +#include "dce_expected_session.h" #include "dce_smb.h" #include "dce_smb_module.h" #include "dce_smb_utils.h" #include "dce_tcp.h" -#include "dce_tcp_module.h" using namespace snort; static THREAD_LOCAL int co_reassembled = 0; +/* [MS-RPCE] 2.2.5 - 64-Bit Network Data Representation */ +static const Uuid uuid_ndr64 = { 0x71710533, 0xbeba, 0x4937, 0x83, 0x19, + { 0xb5, 0xdb, 0xef, 0x9c, 0xcc, 0x36 } }; + +/******************************************************************** + * Function: DCE2_CoEptMapResponse() + * + * Handles the processing of EPT_MAP response. + * The response consists of a tower array that embeds tower pointers. + * Pointers, in turn, consist of floors. Our target is 4 & 5 floors, + * which contain info about future sessions. + * + ********************************************************************/ +static void DCE2_CoEptMapResponse(const DCE2_CoTracker* cot, const DceRpcCoHdr* co_hdr, + const uint8_t* stub_data, uint16_t dlen) +{ + DCE2_CoCtxIdNode* ctx_id_node; + uint64_t actual_count; + uint64_t tptr_length; /* Tower pointer length */ + unsigned int i; + int ndr_flen = 4; /* 4-bytes fields in default NDR */ + int offset = 0; + int floor3_start; + int proto_offset; + int port_offset; + int ip_addr_offset; + uint16_t ept_port; + SfIp ept_ip_addr; + DceRpcBoFlag byte_order; + + if (stub_data == nullptr || dlen == 0) + return; + + ctx_id_node = (DCE2_CoCtxIdNode*)DCE2_ListFind(cot->ctx_ids, + (void*)(uintptr_t)cot->ctx_id); + + if (ctx_id_node == nullptr) + return; + + if (ctx_id_node->transport == DCE2_CO_CTX_TRANS_SYNTAX_NDR64) + ndr_flen = 8; /* 8-bytes fields in NDR64 */ + + /* 20 4 + * +---------------------------------------+--------+ + * | Handle | N Twrs | + * +---------------------------------------+--------+ + * Length of the next fields depends on transport + * (NDR/NDR64, 4/8 bytes). Conformant & Varying Arr hdrs. + * +---------------+---------------+----------------+ + * | Max Count | Offset | Actual Count | + * +---------------+---------------+----------------+ */ + offset += DCE2_CO_MAP_HANDLE_OFS + DCE2_CO_MAP_NUM_TOWERS_OFS + 2 * ndr_flen; + + /* Get the actual count of pointers in tower array */ + byte_order = DceRpcCoByteOrder(co_hdr); + offset += DCE2_GetNdrUint3264(stub_data + offset, actual_count, + offset, byte_order, ctx_id_node->transport); + + /* Skipping Referent IDs and moving to deferred pointers representation */ + offset += actual_count * ndr_flen; + dce2_move(stub_data, dlen, offset); + + for (i = 0; i < actual_count; i++) + { + int fc_offset; + uint16_t floor_count; + /* 4/8 4 + * +---------------+--------+ + * | Length | Length | + * +---------------+--------+ + * The first len field seems to be a conformant array header, + * the second one tower length field. + * 2 + * +-------------+---------+---------+---------+---------+ + * | floor count | floor 1 | floor 2 | ... | floor n | + * +-------------+---------+---------+---------+---------+ + * The target is 4th & 5th floors */ + + /* Get tower length and determine the floor count offset */ + fc_offset = DCE2_GetNdrUint3264(stub_data, tptr_length, + offset, byte_order, ctx_id_node->transport) + DCE2_CO_MAP_TWR_LEN_OFS; + if (dlen < tptr_length) + return; + + floor_count = DceRpcNtohs((const uint16_t*)(stub_data + fc_offset), + DceRpcCoByteOrder(co_hdr)); + + offset += fc_offset; + dce2_move(stub_data, dlen, fc_offset); + + /* No needed data for the pinhole creation */ + if (floor_count < 5) + continue; + + floor3_start = 2 * DCE2_CO_MAP_TWR_FLOOR12_OFS + + DCE2_CO_MAP_FLR_COUNT_OFS; + + /* Skipping 1st & 2nd floors up to 3rd floor protocol id */ + proto_offset = floor3_start + + DCE2_CO_MAP_FLR_LHS_RHS_OFS; + + /* Check protocol, expected to be connection-oriented */ + if (*(stub_data + proto_offset) != DCE2_CO_PROTO_ID_CO) + return; + + port_offset = floor3_start + DCE2_CO_MAP_TWR_FLOOR34_OFS + + 2 * DCE2_CO_MAP_FLR_LHS_RHS_OFS + DCE2_CO_MAP_FLR_PROTO_ID_OFS; + + ip_addr_offset = port_offset + + DCE2_CO_MAP_TWR_FLOOR34_OFS; + + ept_port = DceRpcNtohs((const uint16_t*)(stub_data + port_offset), + DCERPC_BO_FLAG__BIG_ENDIAN); + + /* According to DCE RPC 1.1, host address is 4 octets, big-endian order */ + ept_ip_addr.set(stub_data + ip_addr_offset, AF_INET); + + DceExpSsnManager::create_expected_session(&ept_ip_addr, ept_port, DCE2_TCP_NAME); + + offset += tptr_length; + dce2_move(stub_data, dlen, tptr_length); + } +} + /******************************************************************** * Function: DCE2_CoInitTracker() * @@ -585,6 +709,7 @@ static DCE2_CoCtxIdNode* dce_co_process_ctx_id(DCE2_SsnData* sd,DCE2_CoTracker* ctx_node->iface_vers_maj = if_vers_maj; ctx_node->iface_vers_min = if_vers_min; ctx_node->state = DCE2_CO_CTX_STATE__PENDING; + ctx_node->transport = DCE2_CO_CTX_TRANS_SYNTAX_NDR_DEF; return ctx_node; } @@ -639,7 +764,8 @@ static void DCE2_CoCtxReq(DCE2_SsnData* sd, DCE2_CoTracker* cot, const DceRpcCoH } static void dce_co_process_ctx_result(DCE2_SsnData*, DCE2_CoTracker* cot, - const DceRpcCoHdr* co_hdr,DCE2_Policy policy, uint16_t result) + const DceRpcCoHdr* co_hdr, DCE2_Policy policy, uint16_t result, + const Uuid* transport) { DCE2_CoCtxIdNode* ctx_node, * existing_ctx_node; DCE2_Ret status; @@ -657,6 +783,13 @@ static void dce_co_process_ctx_result(DCE2_SsnData*, DCE2_CoTracker* cot, ctx_node->state = DCE2_CO_CTX_STATE__ACCEPTED; if (DceRpcCoPduType(co_hdr) == DCERPC_PDU_TYPE__BIND_ACK) cot->got_bind = 1; + + /* Need to check accepted transfer syntax + * for further EPT_MAP response parsing */ + if (!DCE2_UuidCompare(transport, &uuid_ndr64)) + { + ctx_node->transport = DCE2_CO_CTX_TRANS_SYNTAX_NDR64; + } } else { @@ -804,6 +937,7 @@ static void DCE2_CoBindAck(DCE2_SsnData* sd, DCE2_CoTracker* cot, for (i = 0; i < num_ctx_results; i++) { const DceRpcCoContResult* ctx_result; + const Uuid* transport; uint16_t result; if (ctx_len < sizeof(DceRpcCoContResult)) @@ -812,6 +946,7 @@ static void DCE2_CoBindAck(DCE2_SsnData* sd, DCE2_CoTracker* cot, return; } ctx_result = (const DceRpcCoContResult*)ctx_data; + transport = DceRpcCoContResTransport(ctx_result); result = DceRpcCoContRes(co_hdr, ctx_result); dce2_move(ctx_data, ctx_len, sizeof(DceRpcCoContResult)); @@ -819,7 +954,7 @@ static void DCE2_CoBindAck(DCE2_SsnData* sd, DCE2_CoTracker* cot, if (DCE2_QueueIsEmpty(cot->pending_ctx_ids)) return; - dce_co_process_ctx_result(sd,cot,co_hdr,policy,result); + dce_co_process_ctx_result(sd,cot,co_hdr,policy,result,transport); } } @@ -1706,6 +1841,30 @@ static void DCE2_CoResponse(DCE2_SsnData* sd, DCE2_CoTracker* cot, (uint16_t)(frag_len - (uint16_t)auth_len)); } } + + /* If this is the last fragment, we can proceed with stub data processing */ + if (DceRpcCoLastFrag(co_hdr)) + { + const uint8_t* stub_data; + uint16_t stub_data_len; + if (DCE2_BufferIsEmpty(cot->frag_tracker.srv_stub_buf)) + { + /* Data reassembled from multiple TSDUs */ + stub_data = frag_ptr; + stub_data_len = frag_len; + } + else + { + /* Data received from single TSDU */ + stub_data = cot->frag_tracker.srv_stub_buf->data; + stub_data_len = cot->frag_tracker.srv_stub_buf->len; + } + + if (cot->opnum == DCE2_CO_EPT_MAP) + { + DCE2_CoEptMapResponse(cot, co_hdr, stub_data, stub_data_len); + } + } } /******************************************************************** diff --git a/src/service_inspectors/dce_rpc/dce_co.h b/src/service_inspectors/dce_rpc/dce_co.h index 5a26a62da..78671c632 100644 --- a/src/service_inspectors/dce_rpc/dce_co.h +++ b/src/service_inspectors/dce_rpc/dce_co.h @@ -39,6 +39,21 @@ #define DCE2_CO_FRAG_DIFF_OPNUM 38 #define DCE2_CO_FRAG_DIFF_CTX_ID 39 +/* Map response fields offsets */ +#define DCE2_CO_MAP_HANDLE_OFS 20 +#define DCE2_CO_MAP_NUM_TOWERS_OFS 4 +#define DCE2_CO_MAP_TWR_FLOOR12_OFS 25 +#define DCE2_CO_MAP_TWR_FLOOR34_OFS 7 +#define DCE2_CO_MAP_TWR_LEN_OFS 4 +#define DCE2_CO_MAP_FLR_COUNT_OFS 2 +#define DCE2_CO_MAP_FLR_LHS_RHS_OFS 2 +#define DCE2_CO_MAP_FLR_PROTO_ID_OFS 1 + +/* Protocol IDs. Not a full list. + * Refer to DCE RPC 1.1 Appendix I */ +#define DCE2_CO_PROTO_DOD_TCP 0x07 +#define DCE2_CO_PROTO_ID_CO 0x0b + #define DCE2_CO_BAD_MAJOR_VERSION_STR "connection oriented DCE/RPC - invalid major version" #define DCE2_CO_BAD_MINOR_VERSION_STR "connection oriented DCE/RPC - invalid minor version" #define DCE2_CO_BAD_PDU_TYPE_STR "connection-oriented DCE/RPC - invalid PDU type" @@ -242,6 +257,25 @@ enum DCE2_CoCtxState DCE2_CO_CTX_STATE__PENDING }; +enum DCE2_CoCtxTransport +{ + /* Default 32-Bit NDR defined in DCE RPC 1.1 */ + DCE2_CO_CTX_TRANS_SYNTAX_NDR_DEF, + /* 64-Bit NDR defined in [MS-RPCE] */ + DCE2_CO_CTX_TRANS_SYNTAX_NDR64 +}; + +enum DCE2_CoEpmOpnum { + DCE2_CO_EPT_NONE = -1, + DCE2_CO_EPT_INSERT = 0, + DCE2_CO_EPT_DELETE, + DCE2_CO_EPT_LOOKUP, + DCE2_CO_EPT_MAP, + DCE2_CO_EPT_LKUP_HANDLE_FREE, + DCE2_CO_EPT_INQ_OBJECT, + DCE2_CO_EPT_MGMT_DELETE +}; + struct DCE2_CoCtxIdNode { uint16_t ctx_id; /* The context id */ @@ -252,6 +286,7 @@ struct DCE2_CoCtxIdNode /* Whether or not the server accepted or rejected the client bind/alter context * request. Initially set to pending until server response */ DCE2_CoCtxState state; + DCE2_CoCtxTransport transport; }; enum DceRpcCoAuthLevelType @@ -349,6 +384,11 @@ inline uint16_t DceRpcCoContRes(const DceRpcCoHdr* co, const DceRpcCoContResult* return DceRpcNtohs(&cocr->result, DceRpcCoByteOrder(co)); } +inline const Uuid* DceRpcCoContResTransport(const DceRpcCoContResult* cocr) +{ + return &cocr->transfer_syntax.if_uuid; +} + inline int DceRpcCoObjectFlag(const DceRpcCoHdr* co) { return co->pfc_flags & DCERPC_CO_PFC_FLAGS__OBJECT_UUID; @@ -409,6 +449,54 @@ inline uint16_t DceRpcCoCtxId(const DceRpcCoHdr* co, const DceRpcCoRequest* cor) return DceRpcNtohs(&cor->context_id, DceRpcCoByteOrder(co)); } +inline int DCE2_GetNdrUint32(const uint8_t* data_ptr, uint32_t& data, + int offset, DceRpcBoFlag bo_flag) +{ + const uint32_t* ptr; + int align_offset = 0; + + /* Alignment */ + if (offset % 4) + { + align_offset = 4 - (offset % 4); + } + + ptr = (const uint32_t*)(data_ptr + align_offset); + data = DceRpcNtohl(ptr, bo_flag); + + return align_offset + 4; +} + +inline int DCE2_GetNdrUint64(const uint8_t* data_ptr, uint64_t& data, + int offset, DceRpcBoFlag bo_flag) +{ + const uint64_t* ptr; + int align_offset = 0; + + /* Alignment */ + if (offset % 8) + { + align_offset = 8 - (offset % 8); + } + + ptr = (const uint64_t*)(data_ptr + align_offset); + data = DceRpcNtohl64(ptr, bo_flag); + + return align_offset + 8; +} + +inline int DCE2_GetNdrUint3264(const uint8_t* data_ptr, uint64_t& data, + int offset, DceRpcBoFlag bo_flag, DCE2_CoCtxTransport transport) +{ + if (transport == DCE2_CO_CTX_TRANS_SYNTAX_NDR64) + return DCE2_GetNdrUint64(data_ptr, data, offset, bo_flag); + + uint32_t val = 0; + offset = DCE2_GetNdrUint32(data_ptr, val, offset, bo_flag); + data = val; + return offset; +} + void DCE2_CoInitTracker(DCE2_CoTracker*); void DCE2_CoProcess(DCE2_SsnData*, DCE2_CoTracker*, const uint8_t*, uint16_t); diff --git a/src/service_inspectors/dce_rpc/dce_expected_session.cc b/src/service_inspectors/dce_rpc/dce_expected_session.cc new file mode 100644 index 000000000..e6a58fe91 --- /dev/null +++ b/src/service_inspectors/dce_rpc/dce_expected_session.cc @@ -0,0 +1,93 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2020-2020 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_expected_session.cc author Eduard Burmai + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "dce_expected_session.h" + +#include "managers/inspector_manager.h" +#include "pub_sub/dcerpc_events.h" +#include "stream/stream.h" + +#include "dce_tcp.h" + +using namespace snort; + +DceExpSsnManager::DceExpSsnManager(const char* protocol, + IpProtocol proto, PktType type): proto(proto), type(type) +{ + protocol_id = SnortConfig::get_conf()->proto_ref->add(protocol); +} + +void DceExpSsnManager::create_expected_session(const SfIp* ept_ip, + uint16_t ept_port, const char* mod_name) +{ + Packet* pkt = DetectionEngine::get_current_packet(); + Dce2Tcp* inspector = (Dce2Tcp*)InspectorManager::get_inspector(mod_name, true); + DceExpSsnManager& esm = inspector->get_esm(); + + const SfIp* src_ip = pkt->ptrs.ip_api.get_dst(); + PktType type = esm.get_pkt_type(); + IpProtocol proto = esm.get_ip_proto(); + SnortProtocolId protocol_id = esm.get_proto_id(); + + if (esm.create_expected_session_impl(pkt, src_ip, 0, + ept_ip, ept_port, type, proto, protocol_id)) + return; + + dce2_tcp_stats.tcp_expected_sessions++; + + DceExpectedSessionEvent map_resp_event(pkt, src_ip, 0, + ept_ip, ept_port, proto, protocol_id); + DataBus::publish(DCERPC_EXP_SESSION_EVENT_KEY, map_resp_event, pkt->flow); +} + +DceTcpExpSsnManager::DceTcpExpSsnManager(dce2TcpProtoConf& config) : + DceExpSsnManager("dce-tcp", IpProtocol::TCP, PktType::TCP), + pc(config) { } + +int DceTcpExpSsnManager::create_expected_session_impl(Packet* pkt, + const snort::SfIp* src_ip, uint16_t src_port, + const snort::SfIp* dst_ip, uint16_t dst_port, + PktType type, IpProtocol proto, SnortProtocolId protocol_id) +{ + Dce2TcpFlowData* fd = new Dce2TcpFlowData; + + fd->state = DCE2_TCP_FLOW__EXPECTED; + memset(&fd->dce2_tcp_session, 0, sizeof(DCE2_TcpSsnData)); + DCE2_CoInitTracker(&fd->dce2_tcp_session.co_tracker); + DCE2_ResetRopts(&fd->dce2_tcp_session.sd, pkt); + + fd->dce2_tcp_session.sd.trans = DCE2_TRANS_TYPE__TCP; + fd->dce2_tcp_session.sd.server_policy = pc.common.policy; + fd->dce2_tcp_session.sd.client_policy = DCE2_POLICY__WINXP; + fd->dce2_tcp_session.sd.config = (void*)&pc; + + if (Stream::set_snort_protocol_id_expected(pkt, type, + proto, src_ip, src_port, dst_ip, dst_port, protocol_id, fd)) + { + delete fd; + return -1; + } + + return 0; +} diff --git a/src/service_inspectors/dce_rpc/dce_expected_session.h b/src/service_inspectors/dce_rpc/dce_expected_session.h new file mode 100644 index 000000000..0db9bd29e --- /dev/null +++ b/src/service_inspectors/dce_rpc/dce_expected_session.h @@ -0,0 +1,81 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2020-2020 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_expected_session.h author Eduard Burmai + +#ifndef DCE_EXPECTED_SESSION_H +#define DCE_EXPECTED_SESSION_H + +#include "framework/decode_data.h" +#include "target_based/snort_protocols.h" + +namespace snort +{ +struct Packet; +struct SfIp; +} + +struct dce2TcpProtoConf; + +// Expected Session Manager +class DceExpSsnManager +{ +public: + DceExpSsnManager(const char*, IpProtocol, PktType); + virtual ~DceExpSsnManager() = default; + + SnortProtocolId get_proto_id() const + { return protocol_id; } + + IpProtocol get_ip_proto() const + { return proto; } + + PktType get_pkt_type() const + { return type; } + + static void create_expected_session(const snort::SfIp*, uint16_t, const char*); + +private: + virtual int create_expected_session_impl(snort::Packet*, + const snort::SfIp*, uint16_t, const snort::SfIp*, uint16_t, + PktType, IpProtocol, SnortProtocolId) = 0; + +private: + SnortProtocolId protocol_id; + IpProtocol proto; + PktType type; +}; + +class DceTcpExpSsnManager : public DceExpSsnManager +{ +public: + DceTcpExpSsnManager() = delete; + DceTcpExpSsnManager(dce2TcpProtoConf&); + DceTcpExpSsnManager(const DceTcpExpSsnManager&) = delete; + DceTcpExpSsnManager& operator=(const DceTcpExpSsnManager&) =delete; + +private: + int create_expected_session_impl(snort::Packet*, + const snort::SfIp*, uint16_t, const snort::SfIp*, uint16_t, + PktType, IpProtocol, SnortProtocolId) override; + +private: + const dce2TcpProtoConf& pc; +}; + +#endif // DCE_EXPECTED_SESSION_H diff --git a/src/service_inspectors/dce_rpc/dce_tcp.cc b/src/service_inspectors/dce_rpc/dce_tcp.cc index 88c8877f0..31e28a28c 100644 --- a/src/service_inspectors/dce_rpc/dce_tcp.cc +++ b/src/service_inspectors/dce_rpc/dce_tcp.cc @@ -30,8 +30,6 @@ #include "dce_context_data.h" #include "dce_common.h" -#include "dce_tcp_module.h" -#include "dce_tcp_paf.h" using namespace snort; @@ -57,6 +55,13 @@ unsigned Dce2TcpFlowData::inspector_id = 0; DCE2_TcpSsnData* get_dce2_tcp_session_data(Flow* flow) { Dce2TcpFlowData* fd = (Dce2TcpFlowData*)flow->get_flow_data(Dce2TcpFlowData::inspector_id); + + // check whether this session was expected and mark it as realized + if (fd && fd->state == DCE2_TCP_FLOW__EXPECTED) + { + fd->state = DCE2_TCP_FLOW__REALIZED; + dce2_tcp_stats.tcp_expected_realized_sessions++; + } return fd ? &fd->dce2_tcp_session : nullptr; } @@ -64,6 +69,7 @@ static DCE2_TcpSsnData* set_new_dce2_tcp_session(Packet* p) { Dce2TcpFlowData* fd = new Dce2TcpFlowData; + fd->state = DCE2_TCP_FLOW__COMMON; memset(&fd->dce2_tcp_session,0,sizeof(DCE2_TcpSsnData)); p->flow->set_flow_data(fd); return(&fd->dce2_tcp_session); @@ -104,28 +110,8 @@ static DCE2_TcpSsnData* dce2_handle_tcp_session(Packet* p, dce2TcpProtoConf* con //------------------------------------------------------------------------- // class stuff //------------------------------------------------------------------------- - -class Dce2Tcp : public Inspector -{ -public: - Dce2Tcp(const dce2TcpProtoConf&); - - void show(const SnortConfig*) const override; - void eval(Packet*) override; - void clear(Packet*) override; - StreamSplitter* get_splitter(bool c2s) override - { - return new Dce2TcpSplitter(c2s); - } - -private: - dce2TcpProtoConf config; -}; - -Dce2Tcp::Dce2Tcp(const dce2TcpProtoConf& pc) -{ - config = pc; -} +Dce2Tcp::Dce2Tcp(const dce2TcpProtoConf& pc) : + config(pc), esm(config) { } void Dce2Tcp::show(const SnortConfig*) const { diff --git a/src/service_inspectors/dce_rpc/dce_tcp.h b/src/service_inspectors/dce_rpc/dce_tcp.h index df3f15092..3a3380cf0 100644 --- a/src/service_inspectors/dce_rpc/dce_tcp.h +++ b/src/service_inspectors/dce_rpc/dce_tcp.h @@ -22,9 +22,13 @@ #ifndef DCE_TCP_H #define DCE_TCP_H +#include "framework/inspector.h" #include "profiler/profiler_defs.h" #include "dce_co.h" +#include "dce_expected_session.h" +#include "dce_tcp_module.h" +#include "dce_tcp_paf.h" #define DCE2_TCP_NAME "dce_tcp" #define DCE2_TCP_HELP "dce over tcp inspection" @@ -64,6 +68,8 @@ struct dce2TcpStats /*DCE TCP specific*/ PegCount tcp_sessions; + PegCount tcp_expected_sessions; + PegCount tcp_expected_realized_sessions; PegCount tcp_pkts; PegCount concurrent_sessions; PegCount max_concurrent_sessions; @@ -103,6 +109,13 @@ struct DCE2_TcpSsnData DCE2_CoTracker co_tracker; }; +enum DCE2_TcpFlowState +{ + DCE2_TCP_FLOW__COMMON, + DCE2_TCP_FLOW__EXPECTED, + DCE2_TCP_FLOW__REALIZED +}; + class Dce2TcpFlowData : public snort::FlowData { public: @@ -118,6 +131,27 @@ public: public: static unsigned inspector_id; DCE2_TcpSsnData dce2_tcp_session; + DCE2_TcpFlowState state; +}; + +class Dce2Tcp : public snort::Inspector +{ +public: + Dce2Tcp(const dce2TcpProtoConf&); + + void show(const snort::SnortConfig*) const override; + void eval(snort::Packet*) override; + void clear(snort::Packet*) override; + + DceTcpExpSsnManager& get_esm() + { return esm; } + + snort::StreamSplitter* get_splitter(bool c2s) override + { return new Dce2TcpSplitter(c2s); } + +private: + dce2TcpProtoConf config; + DceTcpExpSsnManager esm; }; DCE2_TcpSsnData* get_dce2_tcp_session_data(snort::Flow*); diff --git a/src/service_inspectors/dce_rpc/dce_tcp_module.cc b/src/service_inspectors/dce_rpc/dce_tcp_module.cc index 1dfc2f3a2..b1e4d5fa1 100644 --- a/src/service_inspectors/dce_rpc/dce_tcp_module.cc +++ b/src/service_inspectors/dce_rpc/dce_tcp_module.cc @@ -112,6 +112,8 @@ static const PegInfo dce2_tcp_pegs[] = { CountType::SUM, "server_frags_reassembled", "total connection-oriented server fragments reassembled" }, { CountType::SUM, "tcp_sessions", "total tcp sessions" }, + { CountType::SUM, "tcp_expected_sessions", "total tcp dynamic endpoint expected sessions" }, + { CountType::SUM, "tcp_expected_realized", "total tcp dynamic endpoint expected realized sessions" }, { CountType::SUM, "tcp_packets", "total tcp packets" }, { CountType::NOW, "concurrent_sessions", "total concurrent sessions" }, { CountType::MAX, "max_concurrent_sessions", "maximum concurrent sessions" }, diff --git a/src/service_inspectors/dce_rpc/dce_utils.h b/src/service_inspectors/dce_rpc/dce_utils.h index d48e20f9f..2379297da 100644 --- a/src/service_inspectors/dce_rpc/dce_utils.h +++ b/src/service_inspectors/dce_rpc/dce_utils.h @@ -344,6 +344,36 @@ inline uint32_t DceRpcHtonl(const uint32_t* ptr, const DceRpcBoFlag bo_flag) return DceRpcNtohl(ptr, bo_flag); } +inline uint64_t DceRpcNtohl64(const uint64_t* ptr, const DceRpcBoFlag bo_flag) +{ + uint64_t value; + + if (ptr == nullptr) + return 0; + + value = *ptr; + + if (bo_flag == DCERPC_BO_FLAG__NONE) + return value; + +#ifdef WORDS_BIGENDIAN + if (bo_flag == DCERPC_BO_FLAG__BIG_ENDIAN) +#else + if (bo_flag == DCERPC_BO_FLAG__LITTLE_ENDIAN) +#endif /* WORDS_BIGENDIAN */ + return value; + + return ((value & 0xff00000000000000) >> 56) | ((value & 0x00ff000000000000) >> 40) | + ((value & 0x0000ff0000000000) >> 24) | ((value & 0x000000ff00000000) >> 8) | + ((value & 0x00000000000000ff) << 56) | ((value & 0x000000000000ff00) << 40) | + ((value & 0x0000000000ff0000) << 24) | ((value & 0x00000000ff000000) << 8); +} + +inline uint64_t DceRpcHtonl64(const uint64_t* ptr, const DceRpcBoFlag bo_flag) +{ + return DceRpcNtohl64(ptr, bo_flag); +} + inline void DCE2_CopyUuid(Uuid* dst_uuid, const Uuid* pkt_uuid, const DceRpcBoFlag byte_order) { dst_uuid->time_low = DceRpcNtohl(&pkt_uuid->time_low, byte_order); @@ -410,4 +440,3 @@ inline void dce2_move(const uint8_t*& data_ptr, int64_t& data_len, int amount) } #endif -