]> git.ipfire.org Git - thirdparty/snort3.git/commitdiff
Pull request #3862: stream_tcp: account for data from zero window probes
authorSteven Baigal (sbaigal) <sbaigal@cisco.com>
Wed, 31 May 2023 14:02:06 +0000 (14:02 +0000)
committerSteven Baigal (sbaigal) <sbaigal@cisco.com>
Wed, 31 May 2023 14:02:06 +0000 (14:02 +0000)
Merge in SNORT/snort3 from ~JALIIMRA/snort3:zero_window_block_master to master

Squashed commit of the following:

commit 494f3504d6db1dae1979aba9032e8f890465c544
Author: Juweria Ali Imran <jaliimra@cisco.com>
Date:   Wed May 17 08:51:34 2023 -0400

    stream_tcp: account for data from zero window probes

src/stream/tcp/segment_overlap_editor.cc
src/stream/tcp/segment_overlap_editor.h
src/stream/tcp/tcp_defs.h
src/stream/tcp/tcp_module.cc
src/stream/tcp/tcp_module.h
src/stream/tcp/tcp_normalizer.cc
src/stream/tcp/tcp_normalizer.h
src/stream/tcp/tcp_normalizers.h
src/stream/tcp/tcp_session.cc
src/stream/tcp/tcp_stream_tracker.cc

index 254948bd5b98b98331aade717c4ac778f704ba82..0069c2828ec9ba79fe734645e295b843b563e14d 100644 (file)
@@ -180,6 +180,9 @@ void SegmentOverlapEditor::eval_right(TcpReassemblerState& trs)
             tcpStats.overlaps++;
             trs.sos.overlap_count++;
             insert_full_overlap(trs);
+            
+            if ( trs.sos.keep_segment == false )
+                return;
         }
     }
 }
@@ -343,9 +346,18 @@ void SegmentOverlapEditor::right_overlap_truncate_new(TcpReassemblerState& trs)
 // REASSEMBLY_POLICY_VISTA:
 void SegmentOverlapEditor::full_right_overlap_truncate_new(TcpReassemblerState& trs)
 {
+
     if ( trs.sos.tcp_ips_data == NORM_MODE_ON )
     {
         unsigned offset = trs.sos.right->i_seq - trs.sos.tsd->get_seq();
+
+        if ( !offset && zwp_data_mismatch(trs, *trs.sos.tsd, trs.sos.right->i_len))
+        {
+            trs.tracker->normalizer.session_blocker(*trs.sos.tsd);
+            trs.sos.keep_segment = false;
+            return;
+        }
+        
         trs.sos.tsd->rewrite_payload(offset, trs.sos.right->data, trs.sos.right->i_len);
     }
 
@@ -450,6 +462,19 @@ void SegmentOverlapEditor::full_right_overlap_os5(TcpReassemblerState& trs)
     full_right_overlap_truncate_new(trs);
 }
 
+bool SegmentOverlapEditor::zwp_data_mismatch(
+    TcpReassemblerState& trs, TcpSegmentDescriptor& tsd, uint32_t overlap)
+{
+    if ( overlap == ZERO_WIN_PROBE_LEN
+        and trs.sos.right->i_seq == trs.tracker->normalizer.get_zwp_seq()
+        and (trs.sos.right->data[0] != tsd.get_pkt()->data[0]) )
+    {
+        return tsd.is_nap_policy_inline();
+    }
+    
+    return false;
+}
+
 void SegmentOverlapEditor::print(TcpReassemblerState& trs)
 {
     LogMessage("    seglist_base_seq:   %X\n", trs.sos.seglist_base_seq);
index 2fab7e1d1f61fa55c98b85d1aaaed43347be199b..80eb817727c92e9910b81927e3761017e53a38a5 100644 (file)
@@ -108,6 +108,7 @@ protected:
 
     virtual bool is_segment_retransmit(TcpReassemblerState&, bool*);
     virtual void drop_old_segment(TcpReassemblerState&);
+    virtual bool zwp_data_mismatch(TcpReassemblerState&, TcpSegmentDescriptor&, uint32_t);
 
     virtual void left_overlap_keep_first(TcpReassemblerState&);
     virtual void left_overlap_trim_first(TcpReassemblerState&);
index af778b7b65ccd7b5b55ebae3e16ef8c1d775235c..8b768c64c38464f68cb504fd9fd7a9f37dc2b5d5 100644 (file)
@@ -61,6 +61,8 @@ struct Packet;
 
 #define SLAM_MAX 4
 
+#define ZERO_WIN_PROBE_LEN 1
+
 // target-based policy types - changes to this enum require changes to stream.h::TCP_POLICIES
 enum StreamPolicy : uint8_t
 {
index 701e0c01016b8d4314b3b42c4fefee8adae3d23a..9686f1207a6babaf27c1f9f3acd12833d9c05bb6 100644 (file)
@@ -115,6 +115,7 @@ const PegInfo tcp_pegs[] =
     { CountType::MAX, "max_segs", "maximum number of segments queued in any flow" },
     { CountType::MAX, "max_bytes", "maximum number of bytes queued in any flow" },
     { CountType::SUM, "zero_len_tcp_opt", "number of zero length tcp options" },
+    { CountType::SUM, "zero_win_probes", "number of tcp zero window probes" },
     { CountType::END, nullptr, nullptr }
 };
 
index eb5985190e07f9f82106575ff170358326d100eb..d741525348621c7654a71a9b4889d3db9f42cf70 100644 (file)
@@ -113,6 +113,7 @@ struct TcpStats
     PegCount max_segs;
     PegCount max_bytes;
     PegCount zero_len_tcp_opt;
+    PegCount zero_win_probes;
 };
 
 extern THREAD_LOCAL struct TcpStats tcpStats;
index 74b29821a99d05ce1ab7ab7df4fb4cf0243ec137..f83d7ebcfa1671104bca7e95b966956b5e0a2b43 100644 (file)
@@ -28,6 +28,7 @@
 #include "tcp_module.h"
 #include "tcp_stream_session.h"
 #include "tcp_stream_tracker.h"
+#include "packet_tracer/packet_tracer.h"
 
 using namespace snort;
 
@@ -66,6 +67,19 @@ bool TcpNormalizer::strip_tcp_timestamp(
     return false;
 }
 
+void TcpNormalizer::session_blocker(
+    TcpNormalizerState&, TcpSegmentDescriptor& tsd)
+{
+    Packet *p = tsd.get_pkt();
+    DetectionEngine::disable_all(p);
+    p->active->block_session(p, true);
+    p->active->set_drop_reason("normalizer");
+    if (PacketTracer::is_active())
+        {
+            PacketTracer::log("Normalizer: TCP Zero Window Probe byte data mismatch\n");
+        }
+}
+
 bool TcpNormalizer::packet_dropper(
     TcpNormalizerState& tns, TcpSegmentDescriptor& tsd, NormFlags f)
 {
@@ -144,6 +158,12 @@ void TcpNormalizer::ecn_stripper(
     }
 }
 
+uint32_t TcpNormalizer::get_zwp_seq(
+    TcpNormalizerState& tns)
+{
+    return tns.zwp_seq;
+}
+
 // don't use the window if we may have missed scaling
 // one way zero window is uninitialized
 // two way zero window is actually closed (regardless of scaling)
@@ -381,6 +401,12 @@ int TcpNormalizer::handle_paws(
     }
 }
 
+void TcpNormalizer::set_zwp_seq(
+    TcpNormalizerState& tns, uint32_t seq)
+{
+    tns.zwp_seq = seq;
+}
+
 uint16_t TcpNormalizer::set_urg_offset(
     TcpNormalizerState&, const tcp::TCPHdr* tcph, uint16_t dsize)
 {
index 8f7ded25dcc365ebc43a37501ca2b6ae80393fb1..d7ccfd7e8bf7f0d79a5f39974e030adcea9b6db2 100644 (file)
@@ -43,6 +43,7 @@ struct TcpNormalizerState
 
     int32_t paws_ts_fudge = 0;
     int tcp_ts_flags = 0;
+    uint32_t zwp_seq = 0;
 
     int8_t trim_syn = 0;
     int8_t trim_rst = 0;
@@ -64,6 +65,7 @@ public:
     virtual ~TcpNormalizer() = default;
 
     virtual void init(State&) { }
+    virtual void session_blocker(State&, TcpSegmentDescriptor&);
     virtual bool packet_dropper(State&, TcpSegmentDescriptor&, NormFlags);
     virtual bool trim_syn_payload(State&, TcpSegmentDescriptor&, uint32_t max = 0);
     virtual void trim_rst_payload(State&, TcpSegmentDescriptor&, uint32_t max = 0);
@@ -72,12 +74,14 @@ public:
     virtual void trim_mss_payload(State&, TcpSegmentDescriptor&, uint32_t max = 0);
     virtual void ecn_tracker(State&, const snort::tcp::TCPHdr*, bool req3way);
     virtual void ecn_stripper(State&, TcpSegmentDescriptor&);
+    virtual uint32_t get_zwp_seq(State&);
     virtual uint32_t get_stream_window(State&, TcpSegmentDescriptor&);
     virtual uint32_t get_tcp_timestamp(State&, TcpSegmentDescriptor&, bool strip);
     virtual int handle_paws(State&, TcpSegmentDescriptor&);
     virtual bool validate_rst(State&, TcpSegmentDescriptor&);
     virtual int handle_repeated_syn(State&, TcpSegmentDescriptor&) = 0;
     virtual uint16_t set_urg_offset(State&, const snort::tcp::TCPHdr* tcph, uint16_t dsize);
+    virtual void set_zwp_seq(State&, uint32_t seq);
 
     static void reset_stats();
 
index 7e5a9f9e7cdc7c07bbe84c0ed4d4f61f6ca26b6b..0a24ce69314997c29b618eac0b0f151cea8d63a2 100644 (file)
@@ -50,6 +50,9 @@ public:
     void reset()
     { init(StreamPolicy::OS_DEFAULT, nullptr, nullptr, nullptr); }
 
+    void session_blocker(TcpSegmentDescriptor& tsd)
+    { norm->session_blocker(tns, tsd); }
+
     bool packet_dropper(TcpSegmentDescriptor& tsd, NormFlags nflags)
     { return norm->packet_dropper(tns, tsd, nflags); }
 
@@ -71,6 +74,9 @@ public:
     void ecn_stripper(TcpSegmentDescriptor& tsd)
     { norm->ecn_stripper(tns, tsd); }
 
+    uint32_t get_zwp_seq()
+    { return norm->get_zwp_seq(tns); }
+
     uint32_t get_stream_window(TcpSegmentDescriptor& tsd)
     { return norm->get_stream_window(tns, tsd); }
 
@@ -86,6 +92,9 @@ public:
     int handle_repeated_syn(TcpSegmentDescriptor& tsd)
     { return norm->handle_repeated_syn(tns, tsd); }
 
+    void set_zwp_seq(uint32_t seq)
+    { return norm->set_zwp_seq(tns, seq); }
+
     uint16_t set_urg_offset(const snort::tcp::TCPHdr* tcph, uint16_t dsize)
     { return norm->set_urg_offset(tns, tcph, dsize); }
 
index 61c08328c21ec2e4bdc9a94ad63d1da13e58b10a..bb3061edd0cefe8aa4a1ec639c44ecd947371389 100644 (file)
@@ -54,6 +54,7 @@
 #include "profiler/profiler.h"
 #include "protocols/eth.h"
 #include "pub_sub/intrinsic_event_ids.h"
+#include "packet_tracer/packet_tracer.h"
 
 #include "stream_tcp.h"
 #include "tcp_ha.h"
@@ -344,6 +345,14 @@ bool TcpSession::flow_exceeds_config_thresholds(TcpSegmentDescriptor& tsd)
                 else
                     (const_cast<tcp::TCPHdr*>(tsd.get_pkt()->ptrs.tcph))->set_seq(listener->max_queue_seq_nxt);
             }
+
+            if ( inline_mode || listener->normalizer.get_trim_win() == NORM_MODE_ON)
+            {
+                tsd.get_pkt()->active->set_drop_reason("stream");
+                if (PacketTracer::is_active())
+                    PacketTracer::log("Stream: Flow exceeded the configured max byte threshold (%u)\n", tcp_config->max_queued_bytes);
+            }
+
             listener->normalizer.trim_win_payload(tsd, space_left, inline_mode);
             return ret_val;
         }
@@ -368,6 +377,14 @@ bool TcpSession::flow_exceeds_config_thresholds(TcpSegmentDescriptor& tsd)
                 else
                     (const_cast<tcp::TCPHdr*>(tsd.get_pkt()->ptrs.tcph))->set_seq(listener->max_queue_seq_nxt);
             }
+
+            if ( inline_mode || listener->normalizer.get_trim_win() == NORM_MODE_ON)
+            {
+                tsd.get_pkt()->active->set_drop_reason("stream");
+                if (PacketTracer::is_active())
+                    PacketTracer::log("Stream: Flow exceeded the configured max segment threshold (%u)\n", tcp_config->max_queued_segs);
+            }
+
             listener->normalizer.trim_win_payload(tsd, 0, inline_mode);
             return true;
         }
@@ -451,8 +468,16 @@ int TcpSession::process_tcp_data(TcpSegmentDescriptor& tsd)
         if ( tcp_config->policy != StreamPolicy::OS_PROXY
             and listener->normalizer.get_stream_window(tsd) == 0 )
         {
-            listener->normalizer.trim_win_payload(tsd);
-            return STREAM_UNALIGNED;
+            if (tsd.get_len() == ZERO_WIN_PROBE_LEN)
+            {
+                tcpStats.zero_win_probes++;
+                listener->normalizer.set_zwp_seq(seq);
+            }
+            else
+            {
+                listener->normalizer.trim_win_payload(tsd);
+                return STREAM_UNALIGNED;
+            }
         }
 
         /* move the ack boundary up, this is the only way we'll accept data */
@@ -478,6 +503,9 @@ int TcpSession::process_tcp_data(TcpSegmentDescriptor& tsd)
         if ( tcp_config->policy != StreamPolicy::OS_PROXY
             and listener->normalizer.get_stream_window(tsd) == 0 )
         {
+            if (tsd.get_len() == ZERO_WIN_PROBE_LEN)
+                tcpStats.zero_win_probes++;
+            
             listener->normalizer.trim_win_payload(tsd);
             return STREAM_UNALIGNED;
         }
index 124ffe0a180fa7660bf893b743dd415f3bd757ab..47fe573487fabb394c8cd8ecbb6bba4d3937affa 100644 (file)
@@ -494,6 +494,9 @@ void TcpStreamTracker::update_tracker_ack_recv(TcpSegmentDescriptor& tsd)
         if ( snd_nxt < snd_una )
             snd_nxt = snd_una;
     }
+    if ( !tsd.get_len() and snd_wnd == 0
+        and SEQ_LT(tsd.get_seq(), r_win_base) )
+        tcpStats.zero_win_probes++;
 }
 
 // In no-ack policy, data is implicitly acked immediately.