Service provider takeaway: A Sourcefire security advisory has made using shared object rules in Snort easier for service providers.
Shared object (SO) rules were introduced in Snort 2.6.0 in early 2006 to provide a means to obscure the exact detection mechanism used in the rule and allow for more flexible detection criteria. However, for the most part, organizations have continued to rely upon traditional Snort rules. This may be about to change, in light of a recent security advisory from Sourcefire. Let's take a look at how to get shared object rules working on Snort sensors.
The first Sourcefire Vulnerability Research Team (VRT) security advisory of 2008 included the following:
"The structure of the "so_rules" directory inside the rule packages has changed. The following is a break out of the new directory structure:
so_rules/
src/
precompiled/
/
/
Where:
is one of the following values:
a. CentOS-4.6
b. CentOS-5.1
c. FC-5
d. OSX-10.4
e. ubuntu-6.01.1
is one of the following values:
a. i386
is one of the following values
a. 2.6.1.5
b. 2.7.0
c. 2.8.0.1
There have been no changes to the src/ directory layout from previous packages.
The reason for this change is two fold. First, due to contract terms with some 3rd party research organizations, a small number of VRT certified rules will now only be delivered as binaries. This change applies only to SO rules. Non-SO rules will not be affected. Additionally, because of this change and to better serve the Snort community the VRT will pre compile the "SO" rules so they are easier to use on the various platforms utilized by the snort community and the VRT subscribers.
If your platform / distribution is not currently listed above this does not mean these shared objects won't work on your platform. Numerous Linux distributions share common...
To continue reading for free, register below or login
To read more you must become a member of SearchSecurityChannel.com
');
// -->

libc versions and it is possible that one of the above distributions and platforms will work on your system. If none of the above combinations work on your platform, please send a note to the snort-sigs mailing list so we can determine the need for additional platforms and distributions to be added to the list of supported platforms."
Preparing a platform
In most Snort Reports I use FreeBSD as my operating system. In this Snort Report I use CentOS 5.1. You'll see why later.
I install CentOS 5.1 but don't select any additional packages during the installation process. I use yum to add the following packages prior to compiling Snort:
# yum install gcc
# yum install libpcap-devel
# yum install pcre-devel
I download and extract snort-2.8.0.1.tar.gz to /usr/local/src and ran ./configure, make and make install.
# ./configure --enable-dynamic-plugin --prefix=/usr/local/snort-2.8.0.1
# make
# make install
I download the Sourcefire VRT registered users rule set for CURRENT dated 2007-12-17 in /usr/local/src/snort-2.8.0.1 and verify the MD5 hash.
# md5sum snortrules-snapshot-CURRENT.tar.gz
789a4a1f244827aa1fe34367554fa0ce snortrules-snapshot-CURRENT.tar.gz
I extract the rules into the /usr/local/src/snort-2.8.0.1 directory. This process creates a directory called so_rules. It contains these files:
Let's take a look at a few of these files, starting with nntp.rules.
SO rules
If you're familiar with traditional Snort rules, this nntp.rules file looks different. You may notice the "metadata" tag. The "engine shared" portion tells Snort this is a shared library rule. The "soid" portion indicates the Shared Library Rule Generator and SID follow. Here we see those values are 3 (meaning "snort dynamic alert" as listed in gen-msg.map) and 12636, which is the Snort ID for this rule.
You might wonder how this rule detects anything. There's no content match, and the only item of interest in the header is source port 119 TCP.
The real work is done in the file nntp_xhdr-bo.c. Let's look at portions of this file for introductory purposes:
/*
* NNTP XHDR buffer overflow attempt
*
* Copyright (C) 2007 Sourcefire, Inc. All Rights Reserved
*
* Written by Patrick Mullen
Notice this file relies on two header files:
#include "sf_snort_plugin_api.h"
#include "sf_snort_packet.h"
Here we see the rule header:
That matches looking for source port 119 TCP as we saw earlier. If you continue reviewing the contents of nntp_xhdr-bo.c you'll see more C code.
Putting SO rules to work
What do we do with this? If you run "make" in the so_rules directory, several new files appear. Looking specifically for output related to nntp, you see the following:
Instead of just nntp.rules and nntp_xhdr-bo.c, we have:
# ls -al nntp*
-rw-r--r-- 1 root root 116 Jan 21 21:31 nntp.c
-rw-r--r-- 1 root root 5140 Jan 21 21:31 nntp.o
-rw-r--r-- 1 1210 1210 335 Jan 21 21:31 nntp.rules
-rwxr-xr-x 1 root root 21608 Jan 21 21:31 nntp.so
-rw-r--r-- 1 1210 1210 5786 Dec 17 16:19 nntp_xhdr-bo.c
-rw-r--r-- 1 root root 12356 Jan 21 21:31 nntp_xhdr-bo.o
The file command tells us more about these:
# file nntp*
nntp.c: ASCII C program text
nntp.o: ELF 32-bit LSB relocatable,
Intel 80386, version 1 (SYSV),
not stripped
nntp.rules: ASCII text
nntp.so: ELF 32-bit LSB shared object,
Intel 80386, version 1 (SYSV),
not stripped
nntp_xhdr-bo.c: ASCII C program text
nntp_xhdr-bo.o: ELF 32-bit LSB relocatable,
Intel 80386, version 1 (SYSV),
not stripped
Remember, we started with nntp.rules and nntp_xhdr-bo.c. In reality, we could've simply started with nntp_xhdr-bo.c, as would be the case if we were writing our own Snort shared object rule. I'll return to that possibility later.
The make command created all of the new files: nntp.c, nntp.o, nntp.so and nntp_xhdr-bo.o.
The file nntp.c is very simple:
The file nntp.o is an object file generated by the compiler. We can run the nm utility to display symbols, or the names of variables and functions in the object file nntp.o.
# nm nntp.o
U ruleNNTP_XHDR_BO
00000000 D rules
The U flag means "the symbol is undefined." The D flag means "the symbol is in the initialized data section." I only run nm here to demonstrate the nature of the file.
The file nntp.so is a shared object, also called a shared library. It'll be used to call the NNTP_XHDR_BO rule when Snort is started. In fact, nntp.so is the actual SO rule file that will be called upon when we start Snort. The file nntp_xhdr-bo.o is an object file created from the nntp_xhdr-bo.c file.
We're ready to see if Snort will include the SO rule nntp.rules when it starts.
Configuring Snort
To use the new shared object rule, we need to adjust the snort.conf file. You can do this by replacing the entries related to dynamic rules in the snort.conf file with entries reflecting your Snort installation or by commenting out the existing entries for dynamic rules in snort.conf and calling the right switches at runtime.
I comment out all of the references to dynamic capabilities in the snort.conf file and use runtime switches. I create a copy of the snort.conf file called snort.conf.2.8.0.1 and make changes there.
To see the runtime options for dynamic rules, I run snort -h (or check the Snort main page and manual):
--dynamic-engine-lib
Load a dynamic detection engine
--dynamic-engine-lib-dir
Load all dynamic engines from directory
--dynamic-detection-lib
Load a dynamic rules library
--dynamic-detection-lib-dir
Load all dynamic rules libraries from directory
--dump-dynamic-rules
Creates stub rule files of all loaded rules libraries
--dynamic-preprocessor-lib
Load a dynamic preprocessor library
--dynamic-preprocessor-lib-dir
Load all dynamic preprocessor libraries from directory
Let's see if we can run Snort and include our new nntp.so SO rule. First I define a variable to decrease the size of the directory paths I need to pass. I'm starting Snort from the directory where I extracted the source and rules (etc/ directory) so I don't need to copy various files into /usr/local/snort-2.8.0.1 as I would do in production. Snort's output follows:
The key items for our purposes appear in the Rules Engine and Rules Object parts of the output. We can see our nntp Rules Object is loaded. The Snort_Dynamic_Rule_Example is loaded because we passed this configuration item at runtime:
--dynamic-detection-lib-dir=$SNORTLIB/snort_dynamicrules
A look in that directory reveals these files. I've replaced:
/usr/local/snort-2.8.0.1/lib//snort_dynamicrules/lib_sfdynamic_example
in the following output with PLACEHOLDER for readability.
So far we seem to be making progress. Did the last invocation of Snort really include our new SO rule?
Including SO rules we compiled
It turns out we need to tell Snort where to find the nntp.rules "stub" file created when we ran "make" in the so_rules directory earlier.
I copy nntp.rules to the Snort rules directory as so_nntp.rules. This prevents a conflict with the normal nntp.rules file containing traditional Snort signatures.
Next I add the following to snort.conf.2.8.0.1:
Finally I rerun Snort using the method last shown.
Previously we saw 8,559 Snort rules read. Now we have 8,560 and two errors. What's wrong?
It turns out we need to add a "stub" file for the example dynamic rules we invoked if we want to remove these errors. We can create this file if we invoke Snort by adding this switch:
--dump-dynamic-rules=/tmp
The result appears next:
...edited...
Dumping dynamic rules...
Dumping dynamic rules for Library nntp 1.0.1
Dumping dynamic rules for Library Snort_Dynamic_Rule_Example 1.0.1
Finished dumping dynamic rules.
==================================================
Snort received 0 packets
...edited...
Snort exiting
We have two new files in /tmp now:
# ls /tmp
nntp.rules Snort_Dynamic_Rule_Example.rules
The first is identical to our so_nntp.rules file. Note that all of this is one line in the original, but here I've broken it for readability.
The second contains entries for gid 3 sid 109 and gid 3 sid 637:
I copy the file to the rules directory and add another entry to snort.conf.2.8.0.1:
# cp /tmp/Snort_Dynamic_Rule_Example.rules ../rules/
include $RULE_PATH/Snort_Dynamic_Rule_Example.rules
When I run Snort again, I don't get the errors and my rule count has increased accordingly:
Initializing rule chains...
8562 Snort rules read
8562 detection rules
0 decoder rules
0 preprocessor rules
8562 Option Chains linked into 495 Chain Headers
0 Dynamic rules
The rule count has increased by two again. This means we have one SO rule loaded by so_nntp.rules and two SO rules loaded by Snort_Dynamic_Rule_Example.rules.
Don't be confused by the line "0 Dynamic rules." Dynamic in this case refers to
Dynamic/Activate rules, which are being phased out in favor of a combination of tagging and flowbits.
At the end of this process we have three shared object rules working in Snort.
Investigating SO rules compiled by Sourcefire
In addition to explaining the new directory structure of the SO rules Sourcefire provides, the rule advisory I mentioned previously also explains how Sourcefire created two SO rules to detect activity related to Microsoft Security Bulletin (MS08-001):
"Shared object rules to detect attacks targeting this vulnerability are included in this release and are identified with GID 3 and SIDs 13287 and 13288."
Since we are using CentOS 5.1, let's see what Sourcefire provides.
A look in the so_rules directory of the VRT Subscription rule set shows the following:
bad-traffic.rules misc.rules p2p.rules src
dos.rules netbios.rules precompiled web-client.rules
exploit.rules nntp.rules smtp.rules
If we grep for 13287 or 13288 we will find which SO rules address MS08-001.
We see that bad-traffic.rules contains the SO rules for MS08-001. Remember that bad-traffic.rules (here the SO version, not the traditional bad-traffic.rules containing normal Snort signatures) is just a stub. We need to find a shared object file called bad-traffic.so precompiled by Sourcefire for our version of Snort and our OS.
A look in the so_rules/src directory shows the following:
bad-traffic_pgm-nak-overflow.c misc_mozilla-sslv2-cmk.c
category-build.pl misc_mysql-com-table-dump.c
dos_igmpv3.c netbios_writex.c
dos_ms06-32.c nntp_xhdr-bo.c
exploit_dhcp-option-overflow.c p2p_winny.c
exploit_imail-ldap.c smtp_exchange-base64.c
exploit_squid-ntlm-auth.c smtp_ipswitch-rcptto-overflow.c
Makefile test.conf
_meta.c web-client_quicktimejpeg-underflow.c
_meta.h
This is similar to the so_rules directory provided in the Sourcefire VRT registered user release. These are C source code for SO rules. You could run "make" in this directory as described earlier. However, none of these address MS08-001.
The so_rules/precompiled directory shows the five platforms for which Sourcefire compiles SO rules:
CentOS-4.6 CentOS-5.0 FC-5 OSX-10.4 ubuntu-6.01.1
Since we are running Centos5.1 we'll look in that directory. Because we're running Snort 2.8.0.1, we choose that path too.
A look in the so_rules/precompiled/CentOS-5.0/i386/2.8.0.1 directory shows the following:
bad-traffic.so exploit.so netbios.so p2p.so web-client.so
dos.so misc.so nntp.so smtp.so
These are precompiled SO rules. The file bad-traffic.so is the SO rule file we need. In fact, if we run nm against it and grep for one of our MS08-001 Snort IDs (say 13287) we get some interesting results:
# nm bad-traffic.so | grep 13287
00001200 D rule13287
000010f4 d rule13287byte_test2
00001160 d rule13287content1
00000720 T rule13287eval
000010e0 d rule13287ip_proto0
00001140 d rule13287option0
00001180 d rule13287option1
00001188 d rule13287option2
000011ac D rule13287options
00001190 d rule13287ref1
00001198 d rule13287ref2
000011a0 d rule13287refs
These symbols show that bad-traffic.so indeed addresses MS08-001.
You can also run the strings command to see mention of MS08-001.
Including SO rules compiled by Sourcefire
Now that we know we want to use the shared object version of bad-traffic.rules and the SO rule bad-traffic.so to detect MS08-001, let's add that to our Snort configuration. First I copy Sourcefire's VRT shared object version of bad-traffic.rules to our rules/ directory as vert-so-bad-traffic.rules. Next I copy the Sourcefire VRT shared object file bad-traffic.so to our so_rules/ directory as vrt-so-bad-traffic.so. (I don't want to overwrite the bad-traffic.so file I compiled myself.) Finally we need to add the following line to snort.conf.2.8.0.1:
include $RULE_PATH/vrt-so-bad-traffic.rules
With these changes made we start Snort again.
During startup I see this:
What's the problem? It turns out the three rules in vrt-so-bad-traffic.rules are all commented out. If I uncomment them Snort works properly.
...edited...
Initializing rule chains...
8565 Snort rules read
8565 detection rules
0 decoder rules
0 preprocessor rules
8565 Option Chains linked into 497 Chain Headers
0 Dynamic rules
...edited...
Rules Engine: SF_SNORT_DETECTION_ENGINE Version 1.6
Rules Object: nntp Version 1.0
Rules Object: bad-traffic Version 1.0
Rules Object: Snort_Dynamic_Rule_Example Version 1.0
...truncated...
The addition of the three rules (8,565 instead of 8,562) and the lack of errors show Snort is running with the new Sourcefire VRT SO rules for MS08-001.
Conclusion
This article has demonstrated how to use shared object rules in your environment. I've asked Sourcefire to precompile SO rules for FreeBSD 6.x and 7.x. At some point in the future I would like to take a deeper look at SO rules as well.
About the author
Richard Bejtlich is the founder of TaoSecurity, author of several books on network security monitoring, including Extrusion Detection: Security Monitoring for Internal Intrusions, and operator of the TaoSecurity blog.