[Snort-devel] Stream5: RST handling + 'STREAM5_BAD_RST' alert

Bram bram-fabeg at ...3414...
Fri Aug 23 05:40:41 EDT 2013


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.

-------------- next part --------------
A non-text attachment was scrubbed...
Name: client.cap
Type: application/octet-stream
Size: 764 bytes
Desc: not available
URL: <https://lists.snort.org/pipermail/snort-devel/attachments/20130823/abf515c2/attachment.obj>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: server.cap
Type: application/octet-stream
Size: 794 bytes
Desc: not available
URL: <https://lists.snort.org/pipermail/snort-devel/attachments/20130823/abf515c2/attachment-0001.obj>


More information about the Snort-devel mailing list