Snort mailing list archives

Stream5: RST handling + 'STREAM5_BAD_RST' alert


From: Bram <bram-fabeg () mail wizbit be>
Date: Fri, 23 Aug 2013 11:40:41 +0200

Hi,


While looking at traffic which triggered the 'STREAM5_BAD_RST' alert I've found some (potential) issues..


* snort_stream5_tcp.c: 'ValidRst' function:

This issue is more of a configuration question/remark and not necessarily a bug in the code:

First the comments before the function:
        // per rfc 793 a rst is valid if the seq number is in window
        // for all states but syn-sent (handled above).  however, we
        // validate here based on how various implementations actually
        // handle a rst.

The question however is how the stream5 streamprocessor should be configured?
What if it's a connection between a Linux system and a Windows system?
The code in 'ValidRST' appears to be stricter for Windows than for Linux?
What if the OS of one of the peers is unknown?
And by extension: what if the OS of both peers is unknown? (a DHCP client connection to a server on internet for example)

Looking at the code: this also means Windows (for example) does not accept all (RFC) valid RSTs?
Based on what was this code written? A windows document? Trial and error? ...?

Either way: the message of the alert 'Reset outside window' is at the very least confusing.. When the sequence number is inside the window but if/when the host ignores the RST then (IMO) a different alert should be generated. Right now it looks like an invalid RST packet is send/received which is not really the case..


* snort_stream5_tcp.c: 'Stream5GetWindow' function:

Second issue: this does look like a bug:

Code:
    if ( st->l_window )
    {
        // don't use the window if we may have missed scaling
        if ( !(lwssn->session_state & STREAM5_STATE_MIDSTREAM) )
            return st->l_window;
    }
    // one way zero window is unitialized
    // two way zero window is actually closed (regardless of scaling)
    else if ( TwoWayTraffic(lwssn) )
        return st->l_window;

    // ensure the data is in the window
    window = tdb->end_seq - st->r_win_base;

    if ( window <  0 )
        window = 0;

    return (uint32_t)window;


The 'window scaling' option is part of the SYN and SYN+ACK packet.
If I read the code in 'ProcessTcp' correctly then 'STREAM5_STATE_MIDSTREAM' gets set when data is seen on the connection but the SYN or the SYN+ACK was not seen.

I believe the intent of the code in 'Stream5GetWindow' is to only use 'window scaling' when the SYN and SYN+ACK were seen.
This is however not what the code appears to be doing.

Code:
        // don't use the window if we may have missed scaling
        if ( !(lwssn->session_state & STREAM5_STATE_MIDSTREAM) )
            return st->l_window;


If I read the code correctly:
- When the SYN or SYN+ACK is missing then 'lwssn->session_state & STREAM5_STATE_MIDSTREAM' will return a value.
- This value is then negated.
- Which means: if the 'STREAM5_STATE_MIDSTREAM' flag is set then the code will continue (using scaling), when it's not set it will immediately return 'st->l_window' (not using scaling)

I believe the code should read:
        // don't use the window if we may have missed scaling
        if ( lwssn->session_state & STREAM5_STATE_MIDSTREAM )
            return st->l_window;


This can lead to false positives but no attempt has been made to trigger this particular case.


* false positive?

Attached are two dumps of the same TCP stream: one taken on the client and one taken on the server.
(TCP session recreated based on a SMTP session between two linux hosts.)

Config:
        config checksum_mode: all
        dynamicpreprocessor directory /usr/lib/snort_dynamicpreprocessor/
        preprocessor stream5_global: track_tcp yes, \
           track_udp no, \
           track_icmp no, \
           max_tcp 262144, \
           max_udp 131072
        preprocessor stream5_tcp: policy linux, detect_anomalies

alert ( msg: "STREAM5_BAD_RST"; sid: 15; gid: 129; rev: 1; metadata: rule-type preproc ; )

        output alert_fast: stdout

Running it:
$ snort -v -l /var/log -c /etc/ips/snort.conf --daq-dir /lib/daq/ -r /tmp/client.cap 2>&1 | grep '129:'

$ snort -v -l /var/log -c /etc/ips/snort.conf --daq-dir /lib/daq/ -r /tmp/server.cap 2>&1 | grep '129:' 08/23-11:14:37.257018 [**] [129:15:1] Reset outside window [**] [Priority: 0] {TCP} 10.11.1.2:5556 -> 10.10.1.1:5555

Looking with gdb with the 'server.cap' shows that the alert is generated on the first RST packet (packet 9).
Breaking in the 'ValidRST' function shows:
- tdb->end_seq = 100010
- st->r_win_base = 100020

The first check in the code checks that 'tdb->end_seq' is greater than/or equal to 'st->r_win_base'.
This is not the case --> alert is generated.

My guess is that this happens because the server already send an ACK for the data up to sequence '100020'. The client then sends a RST with sequence '100010' and a RST with sequence '100020'.

This appears to be standard Linux behaviour..
When it sends a RST packet it appears to be using the 'ACK' number of the receiving packet as sequence number.

I'm not sure if this can be considered a false positive or not..

Looking at RFC 793 shows:
  Reset Generation

  As a general rule, reset (RST) must be sent whenever a segment arrives
  which apparently is not intended for the current connection.  A reset
  must not be sent if it is not clear that this is the case.

  There are three groups of states:

    1.  If the connection does not exist (CLOSED) then a reset is sent
    in response to any incoming segment except another reset.  In
    particular, SYNs addressed to a non-existent connection are rejected
    by this means.

    If the incoming segment has an ACK field, the reset takes its
    sequence number from the ACK field of the segment, otherwise the
    reset has sequence number zero and the ACK field is set to the sum
    of the sequence number and segment length of the incoming segment.
    The connection remains in the CLOSED state.

This appears to be what linux is doing.

The same RFC:
  Reset Processing

  In all states except SYN-SENT, all reset (RST) segments are validated
  by checking their SEQ-fields.  A reset is valid if its sequence number
  is in the window.  In the SYN-SENT state (a RST received in response
  to an initial SYN), the RST is acceptable if the ACK field
  acknowledges the SYN.


It does not seem to specify how a RST packet with a sequence number which is lower then the last acked segment should be handled...



Best regards,

Bram



----------------------------------------------------------------
This message was sent using IMP, the Internet Messaging Program.

Attachment: client.cap
Description:

Attachment: server.cap
Description:

------------------------------------------------------------------------------
Introducing Performance Central, a new site from SourceForge and 
AppDynamics. Performance Central is your source for news, insights, 
analysis and resources for efficient Application Performance Management. 
Visit us today!
http://pubads.g.doubleclick.net/gampad/clk?id=48897511&iu=/4140/ostg.clktrk
_______________________________________________
Snort-devel mailing list
Snort-devel () lists sourceforge net
https://lists.sourceforge.net/lists/listinfo/snort-devel
Archive:
http://sourceforge.net/mailarchive/forum.php?forum_name=snort-devel

Please visit http://blog.snort.org for the latest news about Snort!

Current thread: