[Snort-devel] Possible Stream5 evasion by using very small packets

Yun Zheng Hu yunzheng.hu at ...2499...
Fri Mar 12 08:54:56 EST 2010


I have ran into some problems with the stream5 preprocessor and
reassembling a session that contains a lot of small segments.
For example, I have created a pcap where I want to detect a PDF
containing JavaScript. The PDF was served by a Python HTTP server that
seems to send very small segments. I analyzed the captured pcap; the
tcp stream that contained the PDF was distributed over 341 packets..

The custom rule is based on the Emerging Threats rule 2010882 rev 2 to
reduce false positives:

alert tcp $EXTERNAL_NET $HTTP_PORTS -> $HOME_NET any (msg:"PDF File
Containing Javascript"; flow:established,to_client; content:"%PDF-";
depth:500; content:"/JavaScript"; distance:0; classtype:misc-activity;
priority:3; sid:20100312; rev:1;)

When I ran Snort over this pcap it did not trigger my custom rule.
However, with a download of the same PDF that was served by an Apache
webserver it did trigger, because the session consisted of only 2

So I did some heavy digging in the source code and found out that the
stream5 preprocessor seems to reassembly the segments until it reaches
it's 'flush point'.
This 'flush point' looks like the maximum number of bytes it can/will
reassemble before it sends it to the processing engine.

The code in question is in ./src/preprocessors/Stream5/snort_stream5_tcp.c:
static uint32_t g_static_points[RAND_FLUSH_POINTS] =
                         { 128, 217, 189, 130, 240, 221, 134, 129,
                           250, 232, 141, 131, 144, 177, 201, 130,
                           230, 190, 177, 142, 130, 200, 173, 129,
                           250, 244, 174, 151, 201, 190, 180, 198,
                           220, 201, 142, 185, 219, 129, 194, 140,
                           145, 191, 197, 183, 199, 220, 231, 245,
                           233, 135, 143, 158, 174, 194, 200, 180,
                           201, 142, 153, 187, 173, 199, 143, 201 };

/*  F U N C T I O N S  **********************************************/
static INLINE uint32_t GenerateFlushPoint(FlushPointList *flush_point_list)
    return (rand() % flush_point_list->flush_range) +
            if(get_q_footprint(talker) >= talker->flush_mgr.flush_pt)
                flushed = flush_ackd(tcpssn, talker, p,
                        GET_DST_IP(p), GET_SRC_IP(p),
                        p->tcph->th_dport, p->tcph->th_sport, dir);
                    purge_ackd(tcpssn, talker);

The flush_ackd() function does the flushing (if it has more than 2
segments) and as you can see it is called when the footprint >= flush
Also, the g_static_points matrix consists of fairly small numbers..
which brings us to my problem.

When analyzing my pcap with wireshark it shows that the '%PDF-' header
is in the 2nd segment, but the '/JavaScript' string is in the 4th.
However Snort stops at the 3rd packet because it reached it's

When outputting the footprints I see a very small number, in this case: 162.

The server sent the HTTP Response containing the PDF data with these
packet sizes:

segment 1) HTTP OK - 17 bytes
segment 2) HTTP Headers + Start of PDF header - 215 bytes
segment 3) 36 bytes of pdf
segment 4) 23 bytes of pdf (containing /JavaScript)
segment 5) 25 bytes of pdf
segment 6) etc, etc.

When the Stream5 preprocessors reassembles this stream it will flush
at segment 3, because 17+215 = 232, and 232 >= 162.
That means the signature will never see the '/JavaScript' part..

I also could not find how to change the flush policy or customize the
footprint sizes.

When I double the number returned in GenerateFlushPoint() my signature
*does* trigger.
So, can someone explain why these numbers are used and if they are
supposed to be so small?
Can we increase these or would that impact the overall performance of Snort?

Because with these small numbers you can easily evade the IDS using
small segments and I do not think that is intended.


More information about the Snort-devel mailing list