]> git.ipfire.org Git - thirdparty/snort3.git/commitdiff
Pull request #4596: appid: implemented domain fronting support for shadow traffic
authorBhumika Sachdeva (bsachdev) <bsachdev@cisco.com>
Tue, 18 Feb 2025 19:04:17 +0000 (19:04 +0000)
committerChris Sherwin (chsherwi) <chsherwi@cisco.com>
Tue, 18 Feb 2025 19:04:17 +0000 (19:04 +0000)
Merge in SNORT/snort3 from ~BSACHDEV/snort3:domain_fronting_appid to master

Squashed commit of the following:

commit 5aca0b79cf47ea432ce7fdd3ec40c160cc3f5413
Author: bsachdev <bsachdev@cisco.com>
Date:   Mon Feb 3 10:12:32 2025 -0500

    appid: implemented domain fronting support for shadow traffic

src/network_inspectors/appid/appid_api.cc
src/network_inspectors/appid/appid_api.h
src/network_inspectors/appid/appid_discovery.cc
src/network_inspectors/appid/appid_session.cc
src/network_inspectors/appid/appid_session.h
src/network_inspectors/appid/test/appid_api_test.cc
src/pub_sub/CMakeLists.txt
src/pub_sub/domain_fronting.h [new file with mode: 0644]

index 8b81354257d95b27d059deaeeb21ef605f44d368..b485df4c3eb00fd7fd3908396011e8416e3d6ac5 100644 (file)
@@ -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);
+    }  
+} 
index 1ed5ed8d803ff1f8269b039aa5e98daafb2cbfd5..f9d585759b6f8221d37e4b94771d1b6278a661cb 100644 (file)
@@ -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
     {
index 5fe8b5e580d53ec436e2ca20e08c91f6f25264c4..94e608e45f3741cfcba03341144e78ece71946b2 100644 (file)
@@ -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"
index 3066bea1afea1fdc7f9f668cd46693aab922a58c..72ff15bbcbac74cc097ea3e8599ab64181705e7f 100644 (file)
@@ -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); 
+        } 
+    } 
+} 
index 89895043144da89b66bce8db4ac68d46f1611780..ca7ed8369bf4e03410329067b71986a5077ab572 100644 (file)
@@ -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;
 };
index 15ca048c27b9905d8e597b55352a47d812d11cd8..b45e57c65bea48cf000e49bb5751fc2f3c5f04c0 100644 (file)
@@ -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");
index 9664c5c3fedc8106d6548b0634e25417c7345cdc..4b66e33b1f8cc2ab7789aaf0b02ffcdb2e1ba3c7 100644 (file)
@@ -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 (file)
index 0000000..3bb033a
--- /dev/null
@@ -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 <bsachdev@cisco.com>
+
+#ifndef DOMAIN_FRONTING_H
+#define DOMAIN_FRONTING_H
+
+#include "framework/data_bus.h"
+#include <string>
+
+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