[Snort-users] Snort DoS Fallacies

Martin Roesch roesch at ...1935...
Tue Sep 13 20:56:32 EDT 2005

Hash: SHA1

Ok, I'll put aside all assumptions about your motives and just lay it  
out as straight as I'm able.

On Sep 13, 2005, at 7:43 PM, Ferguson, Justin (IARC) wrote:

>  As for whether the other tcp options can cause the same problem I
> am not sure, you are correct in that I did not go that far into it,  
> however
> because the pointer was not checked, it feasibly *could* happen,

If you can't execute a code path that could result in an execution  
fault then it's not a bug.  For example, we don't validate that the  
Packet pointer that gets passed into the decoder is valid because the  
design of the program.  Anyway...

> I will take your word on it, and you are correct I did not bother  
> to check
> through the entire source tree, believe it or not, that's your job,  
> not
> mine--

It becomes your job when you speak up, pundits add no value to open  
source projects and when you stepped up to "set the record straight"  
with incorrect or incomplete data you don't get to get off the hook  
by saying "it's not my job".

Let's go thru the code:

First off, check out in spo_alert_fast.c:

     208     if(p && data->packet_flag)
     209     {
     210         fputc('\n', data->file);
     212         if(p->iph)
     213             PrintIPPkt(data->file, p->iph->ip_proto, p);
     214         else if(p->ah)
     215             PrintArpHeader(data->file, p);
     216     }

So we only hit this code if data->packet_flag is set.  Where are we  
getting data->packet_flag from?  Let's grep for it in the file:

     134 void AlertFast(Packet *p, char *msg, void *arg, Event *event)
     135 {
     136     char timestamp[TIMEBUF_SIZE];
     137     SpoAlertFastData *data = (SpoAlertFastData *)arg;

So data is SpoAlertFastData, the config options for the plugin.  Ok,  
where does it get set?  Grep again for packet_flag:

     237 SpoAlertFastData *ParseAlertFastArgs(char *args)
     238 {
     239     char **toks;
     240     int num_toks;
     241     char *filename;
     242     SpoAlertFastData *data;
     244     data = (SpoAlertFastData *)SnortAlloc(sizeof 
     263     if(num_toks > 1)
     264     {
     265         if(strcasecmp(toks[1], "packet") == 0)
     266         {
     267             data->packet_flag = 1;
     268         }
     269         else
     270         {
     271             FatalError("Unrecognized alert_fast option: %s 
\n", toks[1]);
     272         }
     273     }

It only gets set if the "packet" argument is  passed in.   
Traditionally (~always) and by default, it's not set.  How about when  
we come in from the command line with the -A switch?  Command line  
stuff is found easily in snort.c by grepping for getopt:

     948     while((ch = getopt(argc, argv, valid_options)) != -1)
     949     {
     950         DEBUG_WRAP(DebugMessage(DEBUG_INIT, "Processing cmd  
line switch: %c\n", ch););
     951         switch(ch)
     952         {
     953             case 'A':                /* alert mode */
     954                 if(!strcasecmp(optarg, "none"))
     955                 {
     956                     pv.alert_mode = ALERT_NONE;
     957                 }
     958                 else if(!strcasecmp(optarg, "full"))
     959                 {
     960                     pv.alert_mode = ALERT_FULL;
     961                 }
     962                 else if(!strcasecmp(optarg, "fast"))
     963                 {
     964                     pv.alert_mode = ALERT_FAST;
     965                 }

So let's grep for ALERT_FAST and see who's using it:

    2081 static int ProcessAlertCommandLine()
    2082 {
    2084     if(!pv.alert_cmd_override)
    2085     {
    2086         /* Setup the default output plugin */
    2087         ActivateOutputPlugin("alert_full", NULL);
    2088     }
    2089     else
    2090     {
    2091         switch(pv.alert_mode)
    2092         {
    2093             case ALERT_FAST:
    2094                 ActivateOutputPlugin("alert_fast", NULL);
    2095                 break;

Ok, so now we know that ActivateOutputPlugin is using it, grep for it  
and we see it's in plugbase.c:

     596 int ActivateOutputPlugin(char *plugin_name, char  
     597 {
     598     OutputKeywordNode *plugin;
     600     if(!plugin_name)
     601         return -1;
     603     /* get the output plugin node */
     604     if(!(plugin = GetOutputPlugin(plugin_name)))
     605         return -1;
     607     switch(plugin->node_type)
     608     {
     609         case NT_OUTPUT_SPECIAL: /* both alert & logging in  
one plugin */
     610             plugin->func(plugin_options);
     611             break;
     612         case NT_OUTPUT_ALERT:
     613             plugin->func(plugin_options);
     614             break;
     615         case NT_OUTPUT_LOG:
     616             plugin->func(plugin_options);
     617             break;
     618     }
     620     return 0;
     621 }

So plugin_options is the argument string that gets passed in to the  
setup function for the output plugin.  It's being set to NULL when  
it's passed in from snort.c, so it's in the default configuration and  
not vulnerable.

Case closed on that one, don't use the "packet" option if you're  
using "output alert_fast" in snort.conf and otherwise you're set at  
the command line with "-A fast".

>> (EVER!!!) RUN WITH ASCII LOGGING!!!  Everyone should be using
>> unified, database or binary (pcap) logging for production sensors,
>> period end of story.  ASCII logging is suitable only for testing and
>> lab environments, that's it.
> While I will not disagree with you, I have seen this in several  
> production
> environments.

Ok, we'll make it easy for people and remove it in 2.5.  Been meaning  
to do that for years now anyway, just was trying to maintain  
backwards compatibility.  People don't tune Snort properly either,  
that's why we're going to be target-based someday.

> Regardless of whether someone should be doing this or not is
> beside the point, here is another execution path that could lead to  
> the same
> result. You arguing that no one should be doing this is akin to DJB  
> arguing
> that because people should be using ulimits that there isn't a bug in
> qmail-- the point still remains that there is/was a bug.

If you run with ASCII logging you can be DoS'd at any time (fairly  
trivially) and that code will not be fixed because it works as  
designed.  This problem pales in comparison IMO.

>> That's debug code there, we (developers) only enable that when we're
>> testing stuff out.  If you turn on all Snort's debug code you aren't
>> running an IDS anymore, you're running chargen. :)
>    snort/src/preprocessors/spp_frag3.c
> 2929 Frag3Rebuild()
>      [...]
> 3117 #ifdef DEBUG_FRAG3
>      [...]
> 3122     if (DEBUG_FRAG & GetDebugLevel())
> 3123     {
>      [...]
> 3126         PrintIPPkt(stdout, defrag_pkt->iph->ip_proto,  
> defrag_pkt);
>      [...]
> 3129     }
> 3130 #endif
>       [...]
> 3133         PrintIPPkt(stdout, defrag_pkt->iph->ip_proto,  
> defrag_pkt);
> Re-read the code snippet, developer. Notice the closing curly bracket
> follwed by the #endif. You have two identical pieces of code, one  
> is wrapped
> in a #ifdef and a if (DEBUG ...) and the other isn't. So, one is  
> only there
> if debugging is enabled, the other exists in the preprocessor  
> regardless.

I pointed out that developers use the DEBUG statements because you  
didn't seem to be familiar with how that code is used and why it's  
there.  If you really dig into it you'll see that not only do you  
have to --enable-debug when you run ./configure but you also have to  
set an environment variable (export SNORT_DEBUG=131072) even if debug  
mode is enabled.

Regardless, that's not the way the code is in 2.4.0 or CVS nor in any  
of the initial development versions from when I wrote it.  I don't  
know where you got that code from but it looks like a bad CVS merge  
on your end.  Download 2.4.0 or CVS on the SNORT_2_4 branch to see  
the actual code (I did).

> This may be the case, and I won't argue the point as I didn't  
> research into
> the feasibility of whether it was possible to cause a null pointer  
> there,
> just that it *could* happen, as I said.

There's no logical code path that would get you there.  My point is  
that it can't happen under any circumstance outside a debugger.  The  
code could be written more safely, that's for sure, but the bug won't  
manifest itself for any other option/length combination.  For giggles  
I rigged the POC to try every possible value of TCP option and length  
with a variety of option sizes, it didn't cause any problems so I'd  
say that the code is conclusively not vulnerable to this issue  
outside of the SACK option in TCP option processing.

>> BTW, you missed that we also call PrintTCPHeader in spo_alert_full.c,
>> which is actually done in the default config case, so this is
>> something you might want to worry about if you're using full alerting
>> for whatever reason.  For the record, the recommended alerting modes
>> for a production sensor are unified, syslog or database.
> Thank you for adding to my point. This makes what 3 possible routes of
> execution + the -v route for a total of 4 without debugging, and 6 if
> debugging was to be enabled. Still quite a long ways from the 'only  
> if you
> are using -v'.

I was merely pointing out the real actual issue as I saw it, you  
didn't make the point when in fact it was the most serious one.  The  
DEBUG code is only called if you manually enable debug mode at  
compile time and set an appropriate environment variable so that's a  
complete non-issue as far as this is concerned.

> Is this about saving face or what? I mean, I can understand writing a
> function that somewhere in it doesn't check that a pointer is not  
> null, it
> happens from time to time. What I cannot see is telling everyone  
> that uses
> your product that there is only one way to get into the bug when in  
> fact
> there is at least a few.

Saving face has nothing to do with it.  You made claims to be  
pointing out other issues that were incorrect  or required  
expansion.  The one real issue that people were likely to run into  
with the default configuration of a relatively commonly used option  
was missed.  You also made several assumptions without testing them  
and proposed to broaden the scope of the vulnerability to different  
inputs without any proof or, ultimately, merit.

To recap, your claims were:

1) The DoS effects "-A fast"  - this was wrong, it only effects the  
output directive with a little used optional argument
2) ASCII logging also has the problem - right, but if you run with  
ASCII logging you're subject to other DoS's as well that are endemic  
to the configuration
3) Other TCP options can cause the same DoS  - this was wrong
4) The DoS effects frag3 and stream4 - this was wrong, there's no  
practical configuration that  will result in a DoS with frag3/stream4

The really important one was "-A full" can exercise the DoS, which is  
important to people running that mode, which you didn't mention.

We payed attention to the initial data from the original reporter and  
said "yep, -v is a problem" and left it at that.  Once you pointed  
out that there are some other paths in there it spurred me to take a  
look (we haven't really done much with that code in years, it really  
should not be used for production as I've said) and the full analysis  
is as I described in this email and the last.  The fixes in CVS fix  
all the problems just like the fix the original problem, so if you  
absolutely must have a fix today then do as I said and grab log.c  
from CVS and recompile.

It doesn't appear that there's anything more to say on this topic  
unless you have some more observations you'd like to make.

- --
Martin Roesch - Founder/CTO, Sourcefire Inc. - +1-410-290-1616
Sourcefire - Discover.  Determine.  Defend.
roesch at ...1935... - http://www.sourcefire.com
Snort: Open Source Network IDS - http://www.snort.org

Version: GnuPG v1.4.1 (Darwin)


More information about the Snort-users mailing list