# Snort3, Snort2lua, and the Emerging Threats Snort 2.9 ruleset

**URL:** <https://community.emergingthreats.net/t/snort3-snort2lua-and-the-emerging-threats-snort-2-9-ruleset/475>\
**Category:** Show and Tell\
**Tags:** snort3, snort2lua, etopen, etpro\
**Created:** [April 12, 2023, 11:46pm UTC](https://community.emergingthreats.net/t/snort3-snort2lua-and-the-emerging-threats-snort-2-9-ruleset/475 "2023-04-12T23:46:10Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![trobinson667](https://sea2.discourse-cdn.com/flex016/user_avatar/community.emergingthreats.net/trobinson667/32/544_2.png) [@trobinson667](https://community.emergingthreats.net/u/trobinson667)\
**Post date:** [April 12, 2023, 11:46pm UTC](https://community.emergingthreats.net/t/snort3-snort2lua-and-the-emerging-threats-snort-2-9-ruleset/475/1 "2023-04-12T23:46:10Z")

</div>

# Summary

Thanks to some teamwork, the Emerging Threats Snort 2.9 ruleset is 99% compatible with Snort3. ETOPEN consumers, and/or ETPRO customers who do not use the scada or scada\_special ruleset should not experience any problems. The notable exceptions are rules from the following categories/files:

- deleted.rules
- scada.rules
- scada\_special.rules

## Problems in these rulesets should not affect the majority of customers.

By default, the problematic rules in the `scada.rules` and `scada_special.rules` files are not enabled. Additionally, the rules that are causing some issues from both the scada and scada\_special rulesets are ETPRO only. Per Emerging Threats policy, any rules in `deleted.rules` are no longer supported by Proofpoint or the Emerging Threats team, and are always marked as disabled when moved to the deleted rules category. They are moved for a number of reasons mostly pertaining to relevance, and are kept for historical purposes.

# What this Achievement means for consumers of the Emerging Threats Snort 2.9 Ruleset:

Currently the Snort2.9 ruleset passes the following checks successfully:

- when passed to snort2lua via the `-c` and `-r` options, snort2lua will successfully create a .rules file, plus a corresponding `snort.lua` file that contains a list of thresholds that were present in any rules processed, since inline thresholding (e.g. limits/thresholds defined within the rule body itself) are no longer a feature of snort3. In a round about way, there is [`detection_filter`](https://docs.snort.org/rules/options/post/detection_filter), but it’s not exactly the same.
- Emerging Threats rules processed by `snort2lua` and included in the user’s lua configuration files (usually `snort.lua`) or command line arguments (`--rule-path /path/to/rules/dir/` or `-R /path/to/your.rules` will successfully pass Snort `-T` (configuration testing) with zero warnings or errors.

# Compatibility does not Imply Optimization

Be aware that compatibility with Snort3 does not imply that these rules are optimized or that they will not require further analysis to make use of Snort3’s features and/or enhance their performance. Let me provide an example of what I’m talking about. Let’s look at a recent infostealer rule to detect the Pennywise Infostealer. Here is the snort2.9 version of the rule:

```auto
alert tcp $HOME_NET any -> $EXTERNAL_NET 5001 (msg:"ET MALWARE PennyWise Stealer Exfil"; flow:established,to_server; content:"|2a|PennyWise|20|v1|2e|"; fast_pattern; content:"Worker|3a 20|"; content:"IP|3a 20|"; content:"Username|3a 20|"; content:"PC|3a 20|"; content:"System|3a 20|"; content:"|2a|Browsers|3a 2a|"; reference:url,twitter.com/crep1x/status/1638596449226170370; classtype:trojan-activity; sid:2044748; rev:2; metadata:affected_product Windows_XP_Vista_7_8_10_Server_32_64_Bit, attack_target Client_Endpoint, created_at 2023_03_22, deployment Perimeter, former_category MALWARE, malware_family PennyWise, performance_impact Low, confidence High, signature_severity Major, updated_at 2023_03_30;)

```

and here is the Suricata 5 version:

```auto
alert http $HOME_NET any -> $EXTERNAL_NET 5001 (msg:"ET MALWARE PennyWise Stealer Exfil"; flow:established,to_server; http.request_line; content:"POST|20|/uploadfile|20|"; http.request_body; content:"|2a|PennyWise|20|v1|2e|"; fast_pattern; content:"Worker|3a 20|"; content:"IP|3a 20|"; content:"Username|3a 20|"; content:"PC|3a 20|"; content:"System|3a 20|"; reference:url,twitter.com/crep1x/status/1638596449226170370; classtype:trojan-activity; sid:2044748; rev:2; metadata:affected_product Windows_XP_Vista_7_8_10_Server_32_64_Bit, attack_target Client_Endpoint, created_at 2023_03_22, deployment Perimeter, former_category MALWARE, malware_family PennyWise, performance_impact Low, confidence High, signature_severity Major, updated_at 2023_03_30;)

```

If you compare the two of them, you’ll notice that the Suricata rule is protocol aware, and is able to use HTTP sticky buffers. Suricata doesn’t care what port http traffic is on. If it detects it as HTTP traffic, you can use the http application layer protocol rule header, and you can use http sticky buffers and modifiers. Snort on the other hand…

The use of [application layer rule headers](https://docs.snort.org/rules/headers/protocols#services-in-place-of-protocols), via automatic service inspection (e.g. [The “Wizard” system](https://docs.snort.org/start/wizard_binder)) is a relatively new feature for Snort3. Snort3 is able to automatically detect Application layer protocol traffic based on a set of criteria now, instead of using a portvar, such as `HTTP_PORTS`. Traditionally, Snort relied on customers knowing what ports to expect what traffic from - through the use of `portvar` variables in `snort.conf`.

Seeing as how TCP/IP ports are entirely arbitrary, that means bad guys could choose any TCP port they desire to host a web server. Snort 2.x wouldn’t do a terribly great job of analyzing this traffic if it wasn’t defined in the `HTTP_PORTS` port variable, in the TCP stream reassembly ports of the `stream5` preprocessor, and in the inspection ports of the `http_inspect` preprocessor. This would cause a cascading chain of failures, making network detection more difficult.

If a snort rule used `$HTTP_PORTS` in the rule header, the malicious traffic would never be compared to that rule. Because the port isn’t in the `stream5` preprocessor, The traffic wouldn’t have its TCP stream reassembled. Then as a direct result the `http_inspect` preprocessor, which requires stream reassembly to work, snort would not recognize the malicious traffic as HTTP traffic.

That means modifiers like `http_method`, `http_uri`, `urilen`, `http_header`, etc. would not work. So we would be required to write rules for this traffic without HTTP content modifiers or `http_inspect` features. That also means that if threat actors were especially cheeky and decided to throw in random HTTP `Expect: 100-continue` headers to divide http header traffic, and http client body traffic across multiple TCP packets, that we would have to somehow account for this, since we have zero stream reassembly for this malicious traffic. This is why the Pennywise infostealer rule above for snort 2.9 is written the way it is.

So any rules we wrote in the past for snort 2.9 that accounted for these gaps, will look roughly the same for snort3 – a syntactically correct rule, but entirely un-optimized.

# How do I use `snort2lua`?

Snort2lua is [included](https://github.com/snort3/snort3/tree/master/tools/snort2lua) with the snort3.x source files, and is automatically compiled while compiling snort3. To my knowledge, there is no standalone snort2lua project or binary available at this time. Also to my knowledge, most Linux distributions do not provide packages for snort3, so manual compilation seems to be the only way to acquire it at this time. For those looking for guidance on how to install snort3 on their distro of choice, I would recommend visiting [snort.org’s documentation page](https://snort.org/documents) and review the Snort3 Setup Guides section. Alternatively, in the not-so-distant past, I used a (now [hidden](https://snort.org/documents/snort-3-1-17-0-on-ubuntu-18-20)) Setup guide for snort3 on Ubuntu 18 and 20, and created a [script to automatically install snort3](https://github.com/da667/autosnort3). I can confirm that this script works on Ubuntu 18.04, 20.04, and 22.04. **Use whatever method you prefer, but from here on out I’m going to assume that you have both snort3, and snort2lua installed.** This can be confirmed by running:

```auto
which snort2lua snort
snort -V

```

by default they get installed to:

```auto
/usr/local/bin/snort2lua
/usr/local/bin/snort

```

If snort3 was installed and ran with the `-V` option, your output should look something like this:

```auto
   ,,_ -*> Snort++ <*-
  o" )~ Version 3.1.57.0
   '''' By Martin Roesch & The Snort Team
           http://snort.org/contact#team
           Copyright (C) 2014-2023 Cisco and/or its affiliates. All rights reserved.
           Copyright (C) 1998-2013 Sourcefire, Inc., et al.
           Using DAQ version 3.0.11
           Using LuaJIT version 2.1.0-beta3
           Using OpenSSL 3.0.2 15 Mar 2022
           Using libpcap version 1.10.1 (with TPACKET_V3)
           Using PCRE version 8.45 2021-06-15
           Using ZLIB version 1.2.11
           Using Hyperscan version 5.4.0 2021-01-26
           Using LZMA version 5.2.5

```

Before we get started with snort2lua, you’ll need a copy of the ETOPEN or ETPRO ruleset for snort2.9. You may download the ruleset using `wget` or whatever rule manage you use to process snort2.9 rules. However, I highly recommend that your rule manager be configured to dump all of your active snort rules into a single unified rules file. Snort2lua does **not** have an option to batch process all rules file in a directory, but can only process one rule file at a time. For those of you who do not use a rule management tool, here is a quick an dirty list of commands I recommend running to download the ET/ETPRO rules to the `~/snort2lua` directory, moving deleted.rules, scada.rules, and scada\_special.rules to `~/snort2lua/rules/disabled`, concatenating the remaining rule files into a single rule file located in `~/snort2lua/conversion`, then running snort2lua on the concatenated file:

```auto
mkdir ~/snort2lua
cd ~/snort2lua

```

ETOPEN users will run:

```auto
wget http://rules.emergingthreats.net/open/snort-2.9.0/emerging.rules.tar.gz
tar -xzvf emerging.rules.tar.gz

```

ETPRO customers will run:

```auto
wget https://rules.emergingthreatspro.com/<your-etpro-pro-oinkcode-here>/snort-2.9.0/etpro.rules.tar.gz
tar -xzvf etpro.rules.tar.gz

```

From here, both etpro and etopen users will run the following commands:

```auto
mkdir ~/snort2lua/rules/disabled
mkdir ~/snort2lua/conversion

```

ETPRO customers will need to run:

```auto
mv ~/snort2lua/rules/*scada*.rules ~/snort2lua/rules/disabled

```

Then both ETPRO and ETOPEN users will run:

```auto
mv ~/snort2lua/rules/*deleted.rules ~/snort2lua/rules/disabled
cat ~/snort2lua/rules/*.rules >> ~/snort2lua/conversion/et_all.rules
cd ~/snort2lua/conversion
snort2lua -c et_all.rules -r et_snort3_all.rules

```

Snort2lua has a variety of different configuration options and switches, but the two options we are concerned with are `-c` and `-r`. These options take the snort 2.9 rules (`-c`) and convert them to snort3 (`-r`). After running snort2lua, the conversions directory should have the files `et_snort3_all.rules` file, and a `snort.lua` file. This file contains a list of thresholds (suppressions, limits, etc.) that were defined inline in a snort2.9 rule body. Unfortunately, snort3 no longer supports the threshold rule option. There is [`detection_filter`](https://docs.snort.org/rules/options/post/detection_filter), but its not exactly the same.

## Turning snort2lua’s snort.lua file into et\_thresholds.lua

In addition to inline thresholds, this snort.lua file will dump every single line comment (this includes all commented out/disabled rules) and place them into a lua-style comment block at the end of the file, causing the size of this file to be somewhat large for whats just a config file. I’ve devised a `sed` command that will remove the massive comment block and save the changes to a new file titled `et_thresholds.tmp.lua`:

```auto
sed '/^--\[\[/,/\]\]/d;s/--\[\[.*$//' snort.lua >> et_thresholds.tmp.lua

```

```auto
du -h *.lua
336K et_thresholds.tmp.lua
5.4M snort.lua

```

that looks a lot better now, doesn’t it?

The next thing we need to do, is remove these lines from the start of the file:

```auto
---------------------------------------------------------------------------
-- Snort++ prototype configuration
---------------------------------------------------------------------------

---------------------------------------------------------------------------
-- setup environment
---------------------------------------------------------------------------
-- given:
-- export DIR=/install/path
-- configure --prefix=$DIR
-- make install
--
-- then:
-- export SNORT_LUA_PATH=$DIR/conf/
---------------------------------------------------------------------------

dir = os.getenv('SNORT_LUA_PATH')

if ( not dir ) then
    dir = '.'
end

dofile(dir .. '/snort_defaults.lua')

wizard = default_wizard

```

and these lines from the new end of the file:

```auto

binder =
{
    { use = { type = 'wizard', }, },
}

```

If you have a preferred text editor, use whatever you are most comfortable with to remove these lines manually, or you can use these commands to do it for you:

```auto
sed -i 1,26d et_thresholds.tmp.lua
head -n -6 et_thresholds.tmp.lua >> et_thresholds.lua

```

# Implementing and Testing Your Newly Converted Rules

With all these tasks completed, all that remains is to:

- place the `et_snort3_all.rules` file into your snort3 rule directory either defined in your snort.lua, or via the `--rule-directory` or the `-R` option.
- place the `et_thresholds.lua` into the directory with your other snort3 lua configuration files, and append an include statement telling snort3 to read your new lua file on startup.

In my case, my snort3 rule directory is:  
`/usr/local/etc/rules`

and my snort3 lua config file directory is:  
`/usr/local/etc/snort`

So here is a quick and dirty set of commands to achieve our objectives. These commands will copy our rules file and thresholds file to the proper directories, make a backup of `/usr/local/etc/snort/snort.lua` (just in case something goes wrong), then append our include statements to `/usr/local/etc/snort/snort.lua`. Change the directory paths and names as necessary for your snort3 deployment:

```auto
cp ~/snort2lua/conversion/et_snort3_all.rules /usr/local/etc/rules
cp ~/snort2lua/conversion/et_thresholds.lua /usr/local/etc/snort
cp /usr/local/etc/snort/snort.lua /usr/local/etc/snort/snort_lua.bak
echo "-- this include statement is necessary for emerging threats rule thresholds to operate correctly" >> /usr/local/etc/snort/snort.lua
echo "include 'et_thresholds.lua'" >> /usr/local/etc/snort/snort.lua

```

The quickest way to make sure you’ve done all of this correctly is to run snort3 with the `-T` flag to confirm there are no warnings or errors. Try this, replacing the directory path for snort, config files, rules, and plugins as necessary for your deployment:

```auto
/usr/local/bin/snort -c /usr/local/etc/snort/snort.lua -u snort -g snort --plugin-path=/usr/local/lib/snort --rule-path /usr/local/etc/rules -T

```

If everything ran successfully, the last lines should read:

```auto
Snort successfully validated the configuration (with 0 warnings).
o")~ Snort exiting

```

Congratulations! You’re good to go. We have a lot of work to do in better supporting Snort3, but we hope you’ll agree that this is a great first step. Stay tuned for more information.

---

<div class="post-metadata">

**Author:** ![trobinson667](https://sea2.discourse-cdn.com/flex016/user_avatar/community.emergingthreats.net/trobinson667/32/544_2.png) [@trobinson667](https://community.emergingthreats.net/u/trobinson667)\
**Post date:** [April 13, 2023, 6:08pm UTC](https://community.emergingthreats.net/t/snort3-snort2lua-and-the-emerging-threats-snort-2-9-ruleset/475/2 "2023-04-13T18:08:53Z")

</div>

# Summary: Several IP and Port Variables need to be defined to use certain rules not enabled by default.

For users of the `scada.rules`, `scada_special.rules`, and some rules that are disabled by default in the ruleset, several IP variables and port variables need to be manually defined, because snort3 does define them in the default configuration files. These IP and port variables are required for certain rules in the ETPRO `scada.rules` and `scada_special.rules` file, as well as a small collection of rules that still utilize the `SHELLCODE_PORTS` port variable.

The IP variables in question are:

```auto
ENIP_CLIENT
ENIP_SERVER
DNP3_CLIENT
DNP3_SERVER
MODBUS_SERVER
MODBUS_CLIENT

```

While the port variables are:

```auto
DNP3_PORTS
SHELLCODE_PORTS

```

## How do I configure snort3 to define these variables?

Some of the rules in these rulesets were made with the cooperation and collaboration of DigitalBond (with proper referencing, of course). That being said, a lot of the rules in these rulesets use custom IP and Port variables. By default, snort3 doesn’t define any of these variables. For the `scada.rules` file, this is a big reason why the rules won’t work (in spite of snort2lua transforming them properly).

Here are the ipvars some of the rules use that aren’t defined by default:

```auto
ENIP_CLIENT
ENIP_SERVER
DNP3_CLIENT
DNP3_SERVER
MODBUS_SERVER
MODBUS_CLIENT

```

And custom portvars:

```auto
DNP3_PORTS
SHELLCODE_PORTS

```

In a stock snort3 installation, IP and Port variables are defined in `snort_defaults.lua`, and included into the main `snort.lua` file – just like the `thresholds.lua` file we made in the previous post. We’re going to make the necessary edits to `snort_defaults.lua` to define these IP and Port variables. First, before we get started, you will need to know:

- What networks contain your PLCs
- What networks contain the SCADA software/servers responsible for managing those PLCs
- What industrial control system network protocol(s) your organization uses for communicating between management systems and PLCs – MODBUS, DNP3, or CIP/ENIP

In my case, I don’t actually have any PLCs or SCADA servers, so I’m cheating, and I’m going to define the new IP variables to HOME\_NET. But you will need to define IP addresses and network ranges as suitable for your network. Fortunately, snort3’s IP variables are formatted almost identically to snort 2.x:

IP Variables can be defined as:

A single IP address:

- `DNP3_SERVERS = [[10.0.0.1]]`

A CIDR Range:

- `MODBUS_CLIENT = [[172.16.0.0/12]]`

Another IP variable:

- `'ENIP_CLIENT = HOME_NET`
- Notice how there is no `$` in front of the variable if you’re calling another ip variable, and no need for the double brackets (`[[]]`)

Or a combination:

- `DNP3_CLIENT = [[$HOME_NET,10.0.0.1,172.16.2.0/24]]`
- Note in this case that if you are combining an IP variable with other IP addresses, you’ll need the ‘$’ in front of the variable for snort3 to recognize it.
- Notice that there are no spaces between any of the values, only commas

As a final note on this section, please be aware that snort3 **does** support using IPv6 and IPv4 IP addresses in IP variables.

With that out of the way, the next thing to do would be to make a backup copy of your `snort_defaults.lua` file. Just like did for the `snort.lua` file in the previous post. In this example, I’ll be using `/usr/local/etc/snort` as the directory used to hold snort3’s lua config files, Adjust as necessary for your snort3 deployment.

```auto
cp /usr/local/etc/snort/snort_defaults.lua /usr/local/etc/snort/snort_defaults_lua.bak

```

Next, use your favorite text editor and open up `snort_defaults.lua` for editing. Navigate to this section:

```auto
---------------------------------------------------------------------------
-- default networks - used in Talos rules
---------------------------------------------------------------------------
-- define servers on your network you want to protect

DNS_SERVERS = HOME_NET
FTP_SERVERS = HOME_NET
HTTP_SERVERS = HOME_NET
SIP_SERVERS = HOME_NET
SMTP_SERVERS = HOME_NET
SQL_SERVERS = HOME_NET
SSH_SERVERS = HOME_NET
TELNET_SERVERS = HOME_NET

```

Immediately below the `TELNET_SERVERS = HOME_NET` line, add:

```auto
ENIP_CLIENT = HOME_NET
ENIP_SERVER = HOME_NET
DNP3_CLIENT = HOME_NET
DNP3_SERVER = HOME_NET
MODBUS_SERVER = HOME_NET
MODBUS_CLIENT = HOME_NET

```

Again, adjust these IP variables as necessary to meet the needs of your deployment. If there are ICS network protocols that you do not utilize for your sensor deployment, then use the setting of `HOME_NET` above as a default.

Next, move to the following section of the `snort_defaults.lua` file:

```auto
---------------------------------------------------------------------------
-- default ports - used in Talos rules
---------------------------------------------------------------------------
-- define ports on your network you want to protect
-- where possible, use the wizard for inspection instead of explicit port
-- bindings. this gives you some port independence and allows you find c&c
-- channels hard port bindings would miss. Talos rules will still use these
-- ports if there is no match on service.

FTP_PORTS = ' 21 2100 3535'

HTTP_PORTS =
[[
    80 81 311 383 591 593 901 1220 1414 1741 1830 2301 2381 2809 3037 3128
    3702 4343 4848 5250 6988 7000 7001 7144 7145 7510 7777 7779 8000 8008
    8014 8028 8080 8085 8088 8090 8118 8123 8180 8181 8243 8280 8300 8800
    8888 8899 9000 9060 9080 9090 9091 9443 9999 11371 34443 34444 41080
    50002 55555
]]

MAIL_PORTS = ' 110 143'

ORACLE_PORTS = ' 1024:'

SIP_PORTS = ' 5060 5061 5600'

SSH_PORTS = ' 22'

FILE_DATA_PORTS = HTTP_PORTS .. MAIL_PORTS

```

Once again, we’ll be appending two lines. This time, directly below `FILE_DATA_PORTS = HTTP_PORTS .. MAIL_PORTS`:

```auto
DNP3_PORTS = ' 20000'

SHELLCODE_PORTS = ' !80'

```

Usually the default TCP port for DNP3 traffic is 20000, while the default setting for `SHELLCODE_PORTS` was set to `!80`. However if you want to modify SHELLCODE\_PORTS and/or DNP3\_PORTS, that can be done here and now. The syntax to define a port variable for snort3 are pretty similar to those for snort2.9.

Rather than list every example out, take a look at the other port variables that snort defines by default. If you ever have need to define a custom port variable, or change the port(s) for the `DNP3_PORTS` and/or `SHELLCODE_PORTS` variables.

With those two edits made, we have two more areas of the `snort_defaults.lua` file to edit. Navigate to this section:

```auto
default_variables =
{
    nets =
    {
        HOME_NET = HOME_NET,
        EXTERNAL_NET = EXTERNAL_NET,
        DNS_SERVERS = DNS_SERVERS,
        FTP_SERVERS = FTP_SERVERS,
        HTTP_SERVERS = HTTP_SERVERS,
        SIP_SERVERS = SIP_SERVERS,
        SMTP_SERVERS = SMTP_SERVERS,
        SQL_SERVERS = SQL_SERVERS,
        SSH_SERVERS = SSH_SERVERS,
        TELNET_SERVERS = TELNET_SERVERS,
    },

```

Under `TELNET_SERVERS = TELNET_SERVERS,` **Within the brackets** , add:

```auto
        ENIP_CLIENT = ENIP_CLIENT,
        ENIP_SERVER = ENIP_SERVER,
        DNP3_CLIENT = DNP3_CLIENT,
        DNP3_SERVER = DNP3_SERVER,
        MODBUS_SERVER = MODBUS_SERVER,
        MODBUS_CLIENT = MODBUS_CLIENT,

```

**Please be aware that the spacing and comma and the end of each line here is important.** Next, navigate to this section:

```auto
    ports =
    {
        FTP_PORTS = FTP_PORTS,
        HTTP_PORTS = HTTP_PORTS,
        MAIL_PORTS = MAIL_PORTS,
        ORACLE_PORTS = ORACLE_PORTS,
        SIP_PORTS = SIP_PORTS,
        SSH_PORTS = SSH_PORTS,
        FILE_DATA_PORTS = FILE_DATA_PORTS,
    }
}

```

And immediately under `FILE_DATA_PORTS = FILE_DATA_PORTS,` add the lines:

```auto
        DNP3_PORTS = DNP3_PORTS,
        SHELLCODE_PORTS = SHELLCODE_PORTS,

```

Again, ensure that the spacing is correct, there is a comma after both the lines added, and that they are placed WITHIN the brackets correctly.

Save your modified copy of snort\_defaults.lua, and try running snort3 again with the -T flag, like we did in the first post of this thread, and confirm that snort3 exits with zero errors and zero warnings.

At this point, you’ll need to enable/uncomment the rules in the `scada.rules` category, and follow the steps listed in the first post to either convert the scada.rules file separately via snort2lua, have it concatenated with the other rules from the rule category and re-convert the whole ruleset, or modify your rule management tool of choice, to provide a single unified snort rules file with the rules in the scada.rules file enabled.

All of this ground covered, and I still have a little bit more to cover with the scada\_special rules, and changes to the DNP3 and ENIP/CIP preprocessors. Stay tuned.

---

<div class="post-metadata">

**Author:** ![trobinson667](https://sea2.discourse-cdn.com/flex016/user_avatar/community.emergingthreats.net/trobinson667/32/544_2.png) [@trobinson667](https://community.emergingthreats.net/u/trobinson667)\
**Post date:** [April 14, 2023, 7:07pm UTC](https://community.emergingthreats.net/t/snort3-snort2lua-and-the-emerging-threats-snort-2-9-ruleset/475/3 "2023-04-14T19:07:54Z")

</div>

# Summary

The final post on this subject will cover some of the differences in the ICS preprocessors/inspectors supported by snort2.x, vs. those supported by Snort3, as well as difficulties and differences in rule options and their syntax that I encountered while trying to convert various snort 2.x ICS rules for use with Snort3.

## About scada\_special.rules..

As part of this procress of ensuring that our rules were able to be “translated” via `snort2lua`, we went through our ruleset, and identified rules that were not able to be processed correctly through an output file that snort2lua generates called “`snort.rej`”. At first we had somewhere in the neighborhood of 200 rules that required manual review.

Initially, the thought never crossed my mine to look at rules that are still in the ET ruleset, but are disabled by default. One `sed -i s/^#alert/alert/g et_all.rules` later, and we had all of the rules enabled, and about another 100 or so rules that needed tending to.

Whenever we disable rules in the ETOPEN/ETPRO rulesets that are **not** in the disabled category, we usually do so for rules that we believe are extremely useful, but are computationally expensive, or may be prone to false positives, etc.. So we leave it as an exercise to the user community and their rule management tools to decide what rules they want to enable. But don’t take my word for it, [check out our FAQ](https://community.emergingthreats.net/t/frequently-asked-questions/56#why-would-a-rule-ship-disabled-5).

For an example of this, in the ETPRO HUNTING category, we have a series of rules to identify java deserialization/RCE gadgets (Common Java RCE Gadgets Observed), but we leave them disabled by default because currently, there is no way to use content matches that are decoded using `base64_decode` and `base64_data` in a fast\_pattern match, so we don’t have a strong fast\_pattern candidate for these rules, and so they end up being “checked” in traffic frequently, which eats CPU time, and causes performance problems if they are enabled. But if you host java web applications on your network, (which, who doesn’t these days) you might see the value of enabling them.

Back to the topic at hand, a significant number of these disabled by default rules were from the ETPRO `scada_special` rule category, as well as some ETPRO rules in the `scada` category. Aside from the IP address and Port variable problems that we solved in the previous post, some of the disabled rules also used specific preprocessor options and modifiers for their rules. Additionally, these rules didn’t really have any strong `fast_pattern` candidates, either. Which is likely a contributing factor towards most of them being disabled by default.

Snort2lua didn’t like a vast portion of these rules, because the syntax for these options have changed a bit, and in some cases, some of the options have disappeared entirely, or have been renamed to something else.

I’ve gone through the process of analyzing the rules manually updating them for use with the new syntax and options Snort3 uses for its ICS preprocessors, however:

- They are all ETPRO rules
- As of right now, we do not have an official pipeline for distributing Snort3 rules. But we are addressing this problem, a pipeline for this process is being built, and gears are turning behind the scenes.

So instead, if ICS networks are your concern, and you need `scada_special` rules, or need to know how to write your own ICS rules for Snort3, here is a little primer on the changes I encountered, and problems to be aware of for writing (or updating) your own ICS rules.

## Snort2.x, Snort3, and their ICS Preprocessors and features

Snort 2.x has both a modbus and DNP3 preprocessor. By default, the modbus preprocessor analyzes traffic on port 502/tcp, while DNP3 defaults to port 20000.

### Modbus

Snort 2.x provides a read me file for their preprocessors, and the rule options they enable. In this case, [README.modbus](https://www.snort.org/faq/readme-modbus).

For the most part, we had no problems processing MODBUS rules in the ET/ETPRO ruleset. Our rules don’t really make use of the MODBUS rule options Which are:

- `modbus_func`: accepts a modbus function code between 0 and 255, or accepts a string that respresents the function of a modbus function code. For example:

`modbus_func:read_coils;`  
`modbus_func:1;`

Mean the same thing, we’re looking at modbus traffic over port 502/tcp, for the modbus function code that shows a read coils request.

- `modbus_unit` : a number between 0 and 255 that represents the modbus unit field in the modbus protocol header.

`modbus_unit:137;`

- `modbus_data`: according to the documentation, this option sets the detection pointer to the modbus data portion of a modbus request/response. According to the syntax, this looks like it may be a sticky buffer.

`modbus_data; content:"ayylmao";`

would look for the string `ayylmao` in the data portion of a modbus request/response.

For snort3, it looks like this preprocessor hasn’t changed much if at all. Something I will make note of is that Cisco has two different pages for information regarding their preprocessors/“inspectors”.

One reference is in the [Snort3 rule writing guide](https://docs.snort.org/rules/options/payload/modbus). While the other is a Cisco webpage titled “[Snort3 Inspector Reference](https://www.cisco.com/c/en/us/td/docs/security/secure-firewall/snort3-inspectors/snort-3-inspector-reference/modbus-inspector.html)”. Whats interesting is that the Snort3 Rule writing guide informs users that the `modbus_func` keyword accepts strings in place of some of the function codes (just like with snort2.x), but then tells readers “go look in the source code if you want to know what they are”, while the Inspector Reference page gives readers a nicely formatted table that shows what string maps to what modbus function code. This won’t be the last time you see me cite both of these resources. My personal advice is to take advantage of both of these documentation sources.

As another aside, as previously mentioned, Snort3 has automatic application layer protocol detection now. Based on their Wizard/Binder system. By default, Snort3 is configured to detect modbus traffic. What this means is that users can write rules using the modbus rule header option:

`alert modbus any any -> any any (msg:"ayy"; sid:1000000; rev:1;)`

### DNP3

Just like with modbus, snort 2.x has a [README.dnp3](https://www.snort.org/faq/readme-dnp3) file for specific DNP3 preprocessor features. The readme defines 4 DNP3 specific rule options:

- `dnp3_func` : not unlike modbus, this option allows users to define either a string or a number between 0 and 255 that corresponds to a DNP3 function.

`dnp3_func:read;`  
`dnp3_func:1;`

refer to the same function.

- `dnp3_ind`: This option allows users to specify Internal Indicator flags present in a DNP3 Application Response Header. This option takes strings only.

`dnp3_ind:need_time;`

- `dnp3_obj`: Refers to DNP3 object headers in requests/responses. This option appears to take two numbers between 0 and 255, separated by a comma. The first number is a group id number, while the second is a var id number.

`dnp3_obj:50,1;`

The example above (in the README.dnp3) refers to a DNP3 Date and Time object.

- `dnp3_data`: The DNP3 protocol’s application data includes CRCs every 16 bytes. this rule content option (like `modbus_data`) allows users to write rules looking in the DNP3 application data portion of network traffic, without having to be cognizant of the CRC calculations every 16 bytes. It appears to be a sticky buffer.

`dnp3_data; content:"ayy";`

It appears in the distant past, when the DNP3 rules in the ET ruleset were written, some of these rule options were different, and some no longer exist. For example, instead of `dnp3_func` there was a similar rule option called `dnp3_cmd_fc` which was similar in nature. I also spotted a few rules with the options `dnp3_cmd_ot` and `dnp3_resp_ot`. These rule options no longer exist, and they do not map to any of the supported rule options for the Snort3 DNP3 inspector.

Aside from those problems, Just like with the modbus preprocessor, the Snort3 rule writing guide has some information about the various [DNP3 preprocessor options](https://docs.snort.org/rules/options/payload/dnp3), but then tells readers to go root around in the snort3 source files to see what strings are supported for the `dnp3_func`, and `dnp3_ind` rule options, while the [Snort 3 Inspector Reference](https://www.cisco.com/c/en/us/td/docs/security/secure-firewall/snort3-inspectors/snort-3-inspector-reference/dnp3-inspector.html) spells out all of the supported strings up front.

The most important difference between Snort 2.x and Snort3 is that the `dnp3_obj` rule option syntax has changed. Snort3 expects the following syntax:

`dnp3_obj:group 50, var 1;`

Snort 3 expects the keywords `group` and `var` to be placed before the obj group and var numbers respectively. The syntax presented in the Snort3 Inspector Reference is NOT correct.

### ENIP/CIP

This is the last preprocessor I’m gonna be covering in this post, as it was the last one to give me trouble, while also being the most interesting. I’m relatively new member to the Emerging Threats team, so sometime looking at parts of the ruleset is like doing archeology. This was one of those times. In the not-so-distant past, [Digital Bond led an effort to write an ENIP/CIP preprocessor for CIP (Common Industrial Protocol)](https://www.linkedin.com/pulse/community-effort-revives-ethernetip-ids-rules-dale-peterson/). Over time, updates to snort broke the preprocessor. For a while the preprocessor was continually maintained, but was eventually scrapped. Community members ported ENIP/CIP rules over to Suricata some time in 2017.

For some period of time, these ENIP/CIP rules that used the ENIP/CIP preprocessor options remained in the Emerging Threats ruleset, disabled by default, long after the preprocessor was gone. However with Snort3, the CIP Inspector/Preprocessor exists. Interestingly, the Snort 3 Rule writing guide states that [the CIP inspector is disabled by default](https://docs.snort.org/rules/options/payload/cip), but the stock `snort.lua` that ships with the snort3 source has both the correct service inspector and binder configuration options present to where it is enabled.

Once again, there are some discrepancies between the Snort 3 Inspector Reference, and the Snort 3 Rule writing guide regarding the features of the CIP Inspector. The Inspector Reference [lists 8 rule options for the CIP Inspector](https://www.cisco.com/c/en/us/td/docs/security/secure-firewall/snort3-inspectors/snort-3-inspector-reference/cip-inspector.html), while [the rule writing guide states that there are 11](https://docs.snort.org/rules/options/payload/cip) – the 8 in the Inspector reference plus three more rule options specifically for enip – `enip_command`, `enip_req` and `enip_resp`.

The one option that gave me the most trouble, seeing as how it was present in the most snort 2.x rules I converted was the `cip_service` option.

In the old snort 2.x CIP preprocessor, the `cip_service` option expected a single numeric value between 0 and 127:

`cip_service:78;`

However with snort3, they’ve enabled the ability to use comparison logic with service numbers. Snort2lua failed to validate rules looking for a single `cip_service` number unless there was an `=` next to it like so:

`cip_service: =78;`  
“We’re only looking for values where the CIP service is set to 78.”

However, using calling cip\_service with no comparison symbols next to it will validate perfectly fine with snort3’s -T option, assuming the rest of the rule is syntactically correct. With the rules being able to use comparison logic, you can do things like this, if there are multiple service codes that mean roughly the same thing:

`cip_service: 77<=>78`  
“We’re only looking for values where the CIP service is less than or equal to 78, and greater than or equal to 77.”

The Snort 3 rule writing guide entry for the CIP Inspector provides a whole list of range/comparison operations that this rule option supports.

A lot of the other CIP Inspector rule options, such as `cip_attribute`, `cip_class`, `cip_conn_path_class`, `cip_status`, and `enip_command`. The only major difference between these various options are the maximum number ranges they support:

`cip_attribute`: 0-65535  
`cip_class`: 0-65535  
`cip_conn_path_class`: 0-65535  
`cip_instance`: 0-4294967295  
`cip_service`: 0-127  
`cip_status`: 0-255  
`enip_command`: 0-65535

The remaining options for the inspector are:  
`cip_req`: match on CIP request packets.  
`cip_rsp`: match on CIP response packets.  
`enip_req`: match on ENIP request packets.  
`enip_rsp`: match on ENIP response packets.

None of the options above take any arguments.

# Conclusion

That about covers it so far. Bear in mind, this wasn’t meant to be a comprehensive guide to Snort2.x’s or Snort3’s ICS protocol preprocessors, but to cover some of the issues I ran into specifically with ICS rules during the process of making our snort 2.9.x ruleset compatible with snort3 via snort2lua.

You might be interesting in reviewing some of the different Inspector options and/or preprocessor alerts, as well as taking a look at the IEC 104 and S7comm Inspectors as well. Until next time, happy hunting!
