Tuesday, May 02, 2006

Snort Dynamic Rules Preview

On my flights to and from the GFIRST 2006 conference this week, I got a chance to read the manual for Snort 2.6.0RC1. The most obvious addition to Snort 2.6 is the ability to add preprocessors, detection capabilities, and rules as dynamically loadable modules. This feature is activated by running configure with the --enable-dynamicplugin switch. Preprocessors and detection capabilities are more of an issue for Snort developers, since few Snort users code their own features. The advantage of the dynamic engine is that developers can write their own modules without having to patch Snort itself.

Most Snort users customize Snort by writing their own rules. Beginning with Snort 2.6.0RC1, the new C-style rule language is in place. If you read the snort_manual.pdf included with snort-2.6.0RC1.tar.gz, you will see a discussion of the new format starting in section 5.1.5 (Dynamic Rules). Here is an example of a rule in the old format:

alert tcp $HOME_NET 12345:12346 -> $EXTERNAL_NET any (msg:"BACKDOOR netbus active";
flow:from_server,established; content:"NetBus"; reference:arachnids,401;
classtype:misc-activity; sid:109; rev:5;)

Here is an example of the same rule in the new format. You can find this rule, sid109.c, in the /src/snort-2.6.0RC1/src/dynamic-examples/dynamic-rule directory.

It looks like this:

/*
* sid109.c
*
* Copyright (C) 2006 Sourcefire,Inc
* Steven A. Sturges
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* Description:
*
* This file is part of an example of a dynamically loadable rules library.
*
* NOTES:
*
*/

#include "sf_snort_plugin_api.h"
#include "sf_snort_packet.h"
#include "detection_lib_meta.h"

/*
* C-language example for SID 109
*
* alert tcp $HOME_NET 12345:12346 -> $EXTERNAL_NET any * (msg:"BACKDOOR netbus active";
* flow:from_server,established; * content:"NetBus"; reference:arachnids,401;
* classtype:misc-activity; * sid:109; rev:5;)
*
*/

/* flow:established, from_server; */
static FlowFlags sid109flow =
{
FLOW_ESTABLISHED|FLOW_TO_CLIENT
};

static RuleOption sid109option1 =
{
OPTION_TYPE_FLOWFLAGS,
{
&sid109flow
}
};

/* content:"NetBus"; */
static ContentInfo sid109content =
{
"NetBus", /* pattern to search for */
0, /* depth */
0, /* offset */
CONTENT_BUF_NORMALIZED, /* flags */
NULL, /* holder for boyer/moore info */
NULL, /* holder for byte representation of "NetBus" */
0, /* holder for length of byte representation */
0 /* holder of increment length */
};

static RuleOption sid109option2 =
{
OPTION_TYPE_CONTENT,
{
&sid109content
}
};

/* references for sid 109 */
static RuleReference sid109ref_arachnids =
{
"arachnids", /* Type */
"401" /* value */
};

static RuleReference *sid109refs[] =
{
&sid109ref_arachnids,
NULL
};

RuleOption *sid109options[] =
{
&sid109option1,
&sid109option2,
NULL
};

Rule sid109 =
{
/* protocol header, akin to => tcp any any -> any any */
{
IPPROTO_TCP, /* proto */
HOME_NET, /* source IP */
"12345:12346", /* source port(s) */
0, /* direction, uni-directional */
EXTERNAL_NET, /* destination IP */
ANY_PORT /* destination port(s) */
},
/* metadata */
{
3, /* genid -- use 3 to distinguish a C rule */
109, /* sigid */
5, /* revision */
"misc-activity", /* classification */
0, /* priority */
"BACKDOOR netbus active", /* message */
sid109refs /* ptr to references */
},
sid109options, /* ptr to rule options */
NULL, /* Use internal eval func */
0, /* Holder, not yet initialized, used internally */
0, /* Holder, option count, used internally */
0, /* Holder, no alert used internally for flowbits */
NULL /* Holder, rule data, used internally */
};

For an explanation of this rule, please see the snort_manual.pdf packaged with Snort 2.6.0RC1. It is not yet online.

For a simple rule like sid 109, the new structure looks very "heavy." However, consider a rule like the following, sid 2258:

alert tcp $EXTERNAL_NET any -> $HOME_NET 445 (msg:"NETBIOS SMB-DS DCERPC Messenger Service
buffer overflow attempt"; flow:to_server,established; content:"|FF|SMB%"; depth:5; offset:4;
nocase; content:"&|00|"; within:2; distance:56; content:"|5C 00|P|00|I|00|P|00|E|00 5C 00|";
within:12; distance:5; nocase; content:"|04 00|"; within:2; byte_test:1,>,15,2,relative;
byte_jump:4,86,little,align,relative; byte_jump:4,8,little,align,relative;
byte_test:4,>,1024,0,little,relative; reference:bugtraq,8826; reference:cve,2003-0717;
reference:nessus,11888; reference:nessus,11890;
reference:url,www.microsoft.com/technet/security/bulletin/MS03-043.mspx;
classtype:attempted-admin; sid:2258; rev:9;)

That rule demonstrates the difficulty of writing more complex rules. The new rules structure should make writing rules like sid 2258 easier.

The sid109.c example shown above, and the material in the snort_manual.pdf packaged with Snort 2.6.0RC1,, may not exactly be what is shipped with Snort 2.6.0 or even Snort 3.0.0. Sourcefire has not determined if it will completely replace the old style rule format in favor of the new format. I expect to see Snort 3.0.0 ship with rules in the new format.

9 comments:

Anonymous said...

So gone will be the days of users editing, and fat fingering their rules.

I've yet to look at Snort 2.6 much, but if you have these rules compiled in, or in a .so is there yet a format to turn them on and off via the conf file?

Nigel said...

Yes, hopefully folks will gain benefit from this :)

Yes, there is a way to include dynamic rules from both the command line and in the snort.conf. They aren't just loaded up by default, you have to tell snort to go load them. You can do it on a per-directory basis or each one individually. This is also true for the dynamic pre-processors and dynamic engine objects.

Nigel said...

Here's the relevant section from the snort.conf

###################################################
# Step #2: Configure dynamic loaded libraries
#
# If snort was configured to use dynamically loaded libraries,
# those libraries can be loaded here.
#
# Each of the following configuration options can be done via
# the command line as well.
#
# Load all dynamic preprocessors from the install path
# (same as command line option --dynamic-preprocessor-lib-dir)
#
# dynamicpreprocessor directory /usr/local/lib/snort_dynamicpreprocessor/
#
# Load a specific dynamic preprocessor library from the install path
# (same as command line option --dynamic-preprocessor-lib)
#
# dynamicpreprocessor file /usr/local/lib/snort_dynamicpreprocessor/libdynamicexample.so
#
# Load a dynamic engine from the install path
# (same as command line option --dynamic-engine-lib)
#
# dynamicengine /usr/local/lib/snort_dynamicengine/libsf_engine.so
#
# Load all dynamic rules libraries from the install path
# (same as command line option --dynamic-detection-lib-dir)
#
# dynamicdetection directory /usr/local/lib/snort_dynamicrule/
#
# Load a specific dynamic rule library from the install path
# (same as command line option --dynamic-detection-lib)
#
# dynamicdetection file /usr/local/lib/snort_dynamicrule/libdynamicexamplerule.so
#

So, if I wanted to say include a dynamic rule named sid1000001.so (after compilation obviously), I would have two options. I could do it on the command line like this:

snort -i eth0 -c /usr/local/etc/snort.conf --dynamic-detection-lib=/usr/local/etc/snort/dynamic-rules/sid1000001.so

Or, I could put this line in the snort.conf:

dynamicdetection file /usr/local/etc/snort/dynamic-rules/sid1000001.so

Now, extending that a little, I might just load up all the dynamic rules from the directory like this:

snort -i eth0 -c /usr/local/etc/snort.conf --dynamic-detection-lib-dir=/usr/local/etc/snort/dynamic-rules/

or add this line to snort.conf:

dynamicdetection directory /usr/local/etc/snort/dynamic-rules/

grep said...

I wonder if this will give me the ability to do basic arithmetic on packets.. For example, given the following packet:

| $size (2 bytes) | $string (12 bytes) |

Would I be able to compare the size of $string to the value stored in $size and ensure that $string doesn't exceed the value given there?

That'd be a nice capability.

Nigel said...

This is already possible using byte_test and dsize. With byte_test you can read in a length value and compare that against what is actually there, determine if it is greater than, less than or equal to (along with gt than or equal to and lt or equal to) the value you set. With dsize you can compare the datagram size to a value in the same way.

grep said...

I understand that I can use dsize as such:

dsize:>20;

Meaning that, if the packet size is greater than 20 bytes, then yield true.

What I can't do, is say:

dsize:>$size;

Where size is a value within the packet itself and not something defined at run-time.

Joel Esler said...

Byte_test, byte_jump. You can do that.

But I do understand what you are saying grep..

http://www.snort.org/docs/snort_htmanuals/htmanual_2.4/rc1/node19.html#SECTION004511000000000000000

http://www.snort.org/docs/snort_htmanuals/htmanual_2.4/rc1/node19.html#SECTION004512000000000000000

grep said...

Maybe I am missing something important here.

With byte_test or byte_jump I could do the following:

Given the packet -

| $value (2 bytes) |

i could test the value of $value against a static value.. for example:

byte_test:2,>,32,0;

That would tell me if $value is > 32.

What if I wanted to to test that dsize:=$value?

For example:

Say you have a protocol based on ICMP called the Size protocol. The format of the packet is

| ETH Header | IP Header | ICMP Header | Data |

Data =:: | Size of Data in network byte-order |

Such that your data portion of the packet looked like

| 00 02 |

Indicating that the data portion of the packet was 2 bytes big... or it could look like

| 00 00 03 |

How would you write a signature to detect that someone isn't sending you:

| 00 00 03 00 |

Alastair said...

Just curious if this will reduce the memory footprint of Snort? I would love to get it running on my WRT54GS but it just doesn't have the RAM for version 2.4 with a recent Sourcefire rule set.