From: Bhumika Sachdeva (bsachdev) Date: Tue, 18 Feb 2025 19:04:17 +0000 (+0000) Subject: Pull request #4596: appid: implemented domain fronting support for shadow traffic X-Git-Tag: 3.7.1.0~20 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=1f2ddbbb68053c5061b80ae89b93eee01ce0c050;p=thirdparty%2Fsnort3.git Pull request #4596: appid: implemented domain fronting support for shadow traffic Merge in SNORT/snort3 from ~BSACHDEV/snort3:domain_fronting_appid to master Squashed commit of the following: commit 5aca0b79cf47ea432ce7fdd3ec40c160cc3f5413 Author: bsachdev Date: Mon Feb 3 10:12:32 2025 -0500 appid: implemented domain fronting support for shadow traffic --- diff --git a/src/network_inspectors/appid/appid_api.cc b/src/network_inspectors/appid/appid_api.cc index 8b8135425..b485df4c3 100644 --- a/src/network_inspectors/appid/appid_api.cc +++ b/src/network_inspectors/appid/appid_api.cc @@ -36,6 +36,7 @@ #include "appid_session_api.h" #include "app_info_table.h" #include "service_plugins/service_ssl.h" +#include "pub_sub/shadowtraffic_aggregator.h" #include "tp_appid_session_api.h" using namespace snort; @@ -297,3 +298,23 @@ void AppIdApi::update_shadow_traffic_status(bool status) OdpContext& odp_ctxt = ctxt.get_odp_ctxt(); odp_ctxt.set_appid_shadow_traffic_status(status); } + +void AppIdApi::set_ssl_certificate_key(const Flow& flow, const std::string& cert_key) +{ + AppIdSession* asd = get_appid_session(flow); + if (asd != nullptr and !cert_key.empty()) + asd->set_cert_key(cert_key); +} + +void AppIdApi::ssl_hostname_cert_lookup_verdict(const snort::Flow &flow, DomainFrontingStatus status) +{ + AppIdSession* asd = get_appid_session(flow); + if (asd != nullptr and status == DomainFrontingStatus::MISMATCH) + { + uint32_t shadow_bits = asd->get_shadow_traffic_bits(); + shadow_bits |= ShadowTraffic_Type_Domain_Fronting; + asd->set_shadow_traffic_bits(shadow_bits); + AppId payload_id = asd->get_api().get_payload_app_id(); + asd->set_shadow_traffic_publishing_appid(payload_id); + } +} diff --git a/src/network_inspectors/appid/appid_api.h b/src/network_inspectors/appid/appid_api.h index 1ed5ed8d8..f9d585759 100644 --- a/src/network_inspectors/appid/appid_api.h +++ b/src/network_inspectors/appid/appid_api.h @@ -26,6 +26,7 @@ #include "sfip/sf_ip.h" #include "appid_session_api.h" #include "application_ids.h" +#include "pub_sub/domain_fronting.h" enum class IpProtocol : uint8_t; @@ -55,6 +56,8 @@ public: const char* get_appid_detector_directory() const; void reset_appid_cpu_profiler_stats(); void update_shadow_traffic_status(bool status); + void set_ssl_certificate_key(const Flow& flow, const std::string& cert_key); + void ssl_hostname_cert_lookup_verdict(const snort::Flow &flow, DomainFrontingStatus status); bool is_service_http_type(AppId service_id) const { diff --git a/src/network_inspectors/appid/appid_discovery.cc b/src/network_inspectors/appid/appid_discovery.cc index 5fe8b5e58..94e608e45 100644 --- a/src/network_inspectors/appid/appid_discovery.cc +++ b/src/network_inspectors/appid/appid_discovery.cc @@ -46,6 +46,7 @@ #include "detector_plugins/detector_dns.h" #include "detector_plugins/http_url_patterns.h" #include "host_port_app_cache.h" +#include "pub_sub/domain_fronting.h" #include "service_plugins/service_discovery.h" #include "tp_lib_handler.h" #include "tp_appid_utils.h" diff --git a/src/network_inspectors/appid/appid_session.cc b/src/network_inspectors/appid/appid_session.cc index 3066bea1a..72ff15bbc 100644 --- a/src/network_inspectors/appid/appid_session.cc +++ b/src/network_inspectors/appid/appid_session.cc @@ -158,7 +158,9 @@ AppIdSession::~AppIdSession() } if ((pkt_thread_odp_ctxt->get_version() == api.asd->get_odp_ctxt_version()) and api.asd->get_odp_ctxt().get_appid_shadow_traffic_status()) - { + { + check_domain_fronting_status(); + if (get_shadow_traffic_publishing_appid() > APP_ID_NONE) { if (api.asd->appid_shadow_traffic_bits != 0) @@ -1353,3 +1355,18 @@ void AppIdSession::process_shadow_traffic_appids() set_shadow_traffic_publishing_appid(publishing_appid); } } + +void AppIdSession::check_domain_fronting_status() +{ + if (api.asd->get_session_flags(APPID_SESSION_DECRYPTED) or api.asd->get_session_flags(APPID_SESSION_APP_REINSPECT)) + { + AppIdHttpSession* hsession = api.asd->get_http_session(); + Packet* p = DetectionEngine::get_current_packet(); + if (hsession) + { + const char* host = hsession->get_cfield(REQ_HOST_FID); + if (host) + TLSDomainFrontCheckEvent(p, api.asd->get_cert_key(), host); + } + } +} diff --git a/src/network_inspectors/appid/appid_session.h b/src/network_inspectors/appid/appid_session.h index 898950431..ca7ed8369 100644 --- a/src/network_inspectors/appid/appid_session.h +++ b/src/network_inspectors/appid/appid_session.h @@ -438,6 +438,7 @@ public: void publish_shadow_traffic_event(const uint32_t& shadow_traffic_bits,snort::Flow*)const; void process_shadow_traffic_appids(); void check_shadow_traffic_bits(AppId id, uint32_t& shadow_bits, AppId &publishing_appid, bool& is_publishing_set); + void check_domain_fronting_status(); bool need_to_delete_tp_conn(ThirdPartyAppIdContext*) const; @@ -805,6 +806,16 @@ public: str.append(tempStr); } + + void set_cert_key (const std::string& key) + { + ssl_cert_key = key; + } + + const std::string& get_cert_key() const + { + return ssl_cert_key; + } private: uint16_t prev_httpx_raw_packet = 0; @@ -829,6 +840,7 @@ private: bool no_service_candidate = false; bool no_service_inspector = false; bool client_info_unpublished = false; + string ssl_cert_key; uint32_t appid_shadow_traffic_bits = 0; AppId shadow_traffic_appid = APP_ID_NONE; }; diff --git a/src/network_inspectors/appid/test/appid_api_test.cc b/src/network_inspectors/appid/test/appid_api_test.cc index 15ca048c2..b45e57c65 100644 --- a/src/network_inspectors/appid/test/appid_api_test.cc +++ b/src/network_inspectors/appid/test/appid_api_test.cc @@ -53,6 +53,7 @@ using namespace snort; static SnortProtocolId dummy_http2_protocol_id = 1; char const* APPID_UT_ORG_UNIT = "Google"; THREAD_LOCAL bool TimeProfilerStats::enabled = false; +#define ShadowTraffic_Type_Domain_Fronting 0x00000010 namespace snort { @@ -259,6 +260,49 @@ TEST(appid_api, get_application_id) CHECK_EQUAL(id, 1492); } +TEST(appid_api, set_ssl_certificate_key) +{ + AppIdConfig config; + OdpContext odpctxt(config, nullptr); + SfIp ip{}; + AppIdSession asd(IpProtocol::TCP, &ip, 1492, dummy_appid_inspector, odpctxt, 0 +#ifndef DISABLE_TENANT_ID + ,0 +#endif + ); + string cert_key = "valid_certificate_key"; + appid_api.set_ssl_certificate_key(*flow, cert_key); + asd.set_cert_key(cert_key); + CHECK_EQUAL(asd.get_cert_key(), cert_key); + delete &asd.get_api(); +} + +TEST(appid_api, ssl_hostname_cert_lookup_verdict) +{ + AppIdConfig config; + OdpContext odpctxt(config, nullptr); + SfIp ip{}; + AppIdSession asd(IpProtocol::TCP, &ip, 1492, dummy_appid_inspector, odpctxt, 0 +#ifndef DISABLE_TENANT_ID + ,0 +#endif + ); + AppidChangeBits change_bits; + asd.set_ss_application_ids(APPID_UT_ID, APPID_UT_ID, APPID_UT_ID, + APPID_UT_ID, APPID_UT_ID, change_bits); + DomainFrontingStatus status = DomainFrontingStatus::MISMATCH; + appid_api.ssl_hostname_cert_lookup_verdict(*flow, status); + + AppId id = asd.get_api().get_payload_app_id(); + asd.set_shadow_traffic_publishing_appid(id); + CHECK_EQUAL(asd.get_shadow_traffic_publishing_appid(), APPID_UT_ID); + + uint32_t expected_shadow_bits = ShadowTraffic_Type_Domain_Fronting; + asd.set_shadow_traffic_bits(expected_shadow_bits); + CHECK_EQUAL(asd.get_shadow_traffic_bits(), expected_shadow_bits); + delete &asd.get_api(); +} + TEST(appid_api, ssl_app_group_id_lookup) { mock().expectNCalls(7, "publish"); diff --git a/src/pub_sub/CMakeLists.txt b/src/pub_sub/CMakeLists.txt index 9664c5c3f..4b66e33b1 100644 --- a/src/pub_sub/CMakeLists.txt +++ b/src/pub_sub/CMakeLists.txt @@ -8,6 +8,7 @@ set (PUB_SUB_INCLUDES daq_message_event.h dcerpc_events.h dhcp_events.h + domain_fronting.h eve_process_event.h expect_events.h external_event_ids.h diff --git a/src/pub_sub/domain_fronting.h b/src/pub_sub/domain_fronting.h new file mode 100644 index 000000000..3bb033afd --- /dev/null +++ b/src/pub_sub/domain_fronting.h @@ -0,0 +1,48 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2022-2025 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. +//-------------------------------------------------------------------------- +// domain_fronting.h author Bhumika Sachdeva + +#ifndef DOMAIN_FRONTING_H +#define DOMAIN_FRONTING_H + +#include "framework/data_bus.h" +#include + +enum class DomainFrontingStatus +{ + MISMATCH, + MATCHES, + CERT_NOT_IN_CACHE +}; + +class TLSDomainFrontCheckEvent : public snort::DataEvent +{ +public: + TLSDomainFrontCheckEvent(const snort::Packet& packet, const std::string& certificate_id, + const std::string& hostname): cert_id(certificate_id), hostname(hostname), pkt(&packet) {} + + const snort::Packet* get_packet() const override + { return pkt; } + +private: + const std::string cert_id; + const std::string hostname; + const snort::Packet* pkt; +}; + +#endif