]> git.ipfire.org Git - thirdparty/snort3.git/commitdiff
Pull request #4425: http2_inspect: add IPS options for frame header and data
authorAdrian Mamolea (admamole) <admamole@cisco.com>
Tue, 17 Sep 2024 12:37:03 +0000 (12:37 +0000)
committerMaya Dagon (mdagon) <mdagon@cisco.com>
Tue, 17 Sep 2024 12:37:03 +0000 (12:37 +0000)
Merge in SNORT/snort3 from ~ADMAMOLE/snort3:http2_header_len to master

Squashed commit of the following:

commit 98609c16c63feb3e8249d4d9f7b2e87456389ef0
Author: Adrian Mamolea <admamole@cisco.com>
Date:   Thu Aug 15 14:46:45 2024 -0400

    http2_inspect: add IPS options for frame header and data

doc/user/http2_inspect.txt
src/service_inspectors/http2_inspect/http2_api.cc
src/service_inspectors/http2_inspect/http2_enum.h
src/service_inspectors/http2_inspect/http2_frame.cc
src/service_inspectors/http2_inspect/http2_headers_frame.cc
src/service_inspectors/http2_inspect/http2_settings_frame.h
src/service_inspectors/http2_inspect/ips_http2.cc

index 4312197c84e15d29d59a3fe4f6bf9227a7fa8d74..f8685a676c9a8f5956e7526f6c873608f4499748 100644 (file)
@@ -55,3 +55,62 @@ large numbers of existing rules. New rules should explicitly specify
 "service http,http2;" if that is the desired behavior. Eventually 
 support for http implies http2 may be deprecated and removed.
 
+Occasionally one needs a rule that looks at the content of the raw HTTP/2 frame, for example to match
+some odd value for an identifier in a settings frame:
+
+    alert http2 (
+        msg:"SETTINGS frame with odd max frame size";
+        flow:to_server,established;
+        http2_frame_header; content:"|04|",offset 3,depth 1;
+        http2_frame_data; content:"|00 05 12 34 56 78|";
+        sid:1;
+    )
+
+Here http2_frame_header represents the 9 bytes of the HTTP/2 header of the frame, and
+http2_frame_data represents the data part of the same frame after any padding was removed.
+
+Support for http2_frame_header is limited to data, headers, settings and push promise frames, while
+support for http2_frame_data is limited to headers, settings, push promise and continuation frames.
+
+For frames that support both http2_frame_header and http2_frame_data the rule has to match both
+on the same frame as in the example above.
+
+When http2_frame_data is matching on a headers or push promise continuation frame, http2_frame_header
+will match on the header of the headers or push promise frame. In the example below the header string
+is matched on a continuation of a headers frame.
+
+    alert http2 (
+        http2_frame_header; content:"|01|", offset 3, depth 1;
+        http2_frame_data; content:"header";
+        sid:1;
+    )
+
+In the example below the header string is matched on a continuation of a push promise frame.
+
+    alert http2 (
+        http2_frame_header; content:"|05|", offset 3, depth 1;
+        http2_frame_data; content:"header";
+        sid:1;
+    )
+
+Matching http2_frame_header on a data frame may be mixed matching on its payload, and, as one would
+expect, the http2_frame_header is the one from the data frame that is matching the payload.
+
+    alert http2 (
+        http2_frame_header; content:"|00|", offset 3, depth 1;
+        file_data; content:"response";
+        sid:1;
+    )
+
+Mixing the two HTTP/2 frame options with HTTP options at the level of an HTTP transaction (where the
+two matches correspond to different HTTP/2 frames) is not recommended. This is an example that will
+not work, it tries to match on the header of a data frame and the payload of a headers frame.
+
+    alert http2 (
+        msg:"DO NOT ATTEMPT - THIS RULE WILL NOT WORK";
+        http2_frame_header; content:"|00|", offset 3, depth 1;
+        http_method; content:"GET";
+        sid:1;
+    )
+
+
index a5c554927432989195f63edede2f5f0ba1518bc5..423fe6c244c49b8f184b571093fdd97aed6b0045 100644 (file)
@@ -39,8 +39,9 @@ Inspector* Http2Api::http2_ctor(Module* mod)
 
 const char* Http2Api::classic_buffer_names[] =
 {
-#ifdef REG_TEST
     "http2_frame_header",
+    "http2_frame_data",
+#ifdef REG_TEST
     "http2_decoded_header",
 #endif
     HTTP_CLASSIC_BUFFER_NAMES,
@@ -75,8 +76,9 @@ const InspectApi Http2Api::http2_api =
     nullptr
 };
 
-#ifdef REG_TEST
 extern const BaseApi* ips_http2_frame_header;
+extern const BaseApi* ips_http2_frame_data;
+#ifdef REG_TEST
 extern const BaseApi* ips_http2_decoded_header;
 #endif
 
@@ -87,8 +89,9 @@ const BaseApi* sin_http2[] =
 #endif
 {
     &Http2Api::http2_api.base,
-#ifdef REG_TEST
     ips_http2_frame_header,
+    ips_http2_frame_data,
+#ifdef REG_TEST
     ips_http2_decoded_header,
 #endif
     nullptr
index d9bae2af5947cc896efd16dae769a809800dbfe2..8e6e0043c65ef400c036b3e6679b40ca6bcba804 100644 (file)
@@ -45,7 +45,11 @@ enum StreamState { STREAM_EXPECT_HEADERS, STREAM_EXPECT_BODY, STREAM_BODY, STREA
 
 // Message buffers available to clients
 // This enum must remain synchronized with Http2Api::classic_buffer_names[]
-enum HTTP2_BUFFER { HTTP2_BUFFER_FRAME_HEADER = 1, HTTP2_BUFFER_DECODED_HEADER, HTTP2_BUFFER__MAX };
+enum HTTP2_BUFFER { HTTP2_BUFFER_FRAME_HEADER = 1, HTTP2_BUFFER_FRAME_DATA,
+#ifdef REG_TEST
+    HTTP2_BUFFER_DECODED_HEADER,
+#endif
+    HTTP2_BUFFER__MAX };
 
 // Peg counts
 // This enum must remain synchronized with Http2Module::peg_names[] in http2_tables.cc
index 1fdb5717d5635460714f70a3cc289c6539117afe..f6e7d0b67a9cc75177e7ce60f2bc8ce77928de2b 100644 (file)
@@ -124,6 +124,8 @@ const Field& Http2Frame::get_buf(unsigned id)
     {
     case HTTP2_BUFFER_FRAME_HEADER:
         return header;
+    case HTTP2_BUFFER_FRAME_DATA:
+        return data;
     default:
         return Field::FIELD_NULL;
     }
index a42918c7dc1b05b4e66fe0d142a19b797740f956..d871ff4ee1aa64a6bdd42d054cdaf92e23581d7d 100644 (file)
@@ -154,8 +154,10 @@ const Field& Http2HeadersFrame::get_buf(unsigned id)
     switch (id)
     {
     // FIXIT-E need to add a buffer for the decoded start line
+#ifdef REG_TEST
     case HTTP2_BUFFER_DECODED_HEADER:
         return http1_header;
+#endif
     default:
         return Http2Frame::get_buf(id);
     }
index c4aa6bccff8ca4f6bec71730a3f444fb64f54191..94f565bf1c9e8e4ebb12c2c3c8bb55d8650d3fa5 100644 (file)
@@ -31,7 +31,6 @@ class Http2SettingsFrame : public Http2Frame
 public:
     friend Http2Frame* Http2Frame::new_frame(const uint8_t*, const uint32_t, const uint8_t*,
         const uint32_t, Http2FlowData*, HttpCommon::SourceId, Http2Stream* stream);
-    bool is_detection_required() const override { return false; }
 
 #ifdef REG_TEST
     void print_frame(FILE* output) override;
index 9af356602582eefd9a3b1c2ae166d0d8a4eaf76f..e9a11dd29230eeebac479fc8d4c27bf2dd2b8fe3 100644 (file)
@@ -82,7 +82,46 @@ IpsOption::EvalStatus Http2IpsOption::eval(Cursor& c, Packet* p)
     return MATCH;
 }
 
-#ifdef REG_TEST
+//-------------------------------------------------------------------------
+// http2_frame_data
+//-------------------------------------------------------------------------
+
+#undef IPS_OPT
+#define IPS_OPT "http2_frame_data"
+#undef IPS_HELP
+#define IPS_HELP "rule option to set detection cursor to the HTTP/2 frame body"
+
+static Module* frame_data_mod_ctor()
+{
+    return new Http2CursorModule(IPS_OPT, IPS_HELP, HTTP2_BUFFER_FRAME_DATA, CAT_SET_OTHER,
+        PSI_FRAME_DATA);
+}
+
+static const IpsApi frame_data_api =
+{
+    {
+        PT_IPS_OPTION,
+        sizeof(IpsApi),
+        IPSAPI_VERSION,
+        1,
+        API_RESERVED,
+        API_OPTIONS,
+        IPS_OPT,
+        IPS_HELP,
+        frame_data_mod_ctor,
+        Http2CursorModule::mod_dtor
+    },
+    OPT_TYPE_DETECTION,
+    0, PROTO_BIT__TCP,
+    nullptr,
+    nullptr,
+    nullptr,
+    nullptr,
+    Http2IpsOption::opt_ctor,
+    Http2IpsOption::opt_dtor,
+    nullptr
+};
+
 //-------------------------------------------------------------------------
 // http2_frame_header
 //-------------------------------------------------------------------------
@@ -122,7 +161,6 @@ static const IpsApi frame_header_api =
     Http2IpsOption::opt_dtor,
     nullptr
 };
-#endif
 
 #ifdef REG_TEST
 //-------------------------------------------------------------------------
@@ -169,8 +207,9 @@ static const IpsApi decoded_header_api =
 //-------------------------------------------------------------------------
 // plugins
 //-------------------------------------------------------------------------
-#ifdef REG_TEST
+const BaseApi* ips_http2_frame_data = &frame_data_api.base;
 const BaseApi* ips_http2_frame_header = &frame_header_api.base;
+#ifdef REG_TEST
 const BaseApi* ips_http2_decoded_header = &decoded_header_api.base;
 #endif