Snort Wizard and Binder

Snort 3 features two new components to help determine the most likely service of a given flow of traffic and then direct that traffic to the right "service inspector": the wizard and binder. These next sections go over each of these components in detail, starting with the wizard.

The Wizard

The wizard uses what are called hexes, spells, and curses to help determine the service present on a given flow. All three look at different parts of the traffic to help identify the service being used. However, the wizard simply helps with service identification; it's the job of the Application Identification inspector to make the definitive determination. Therefore, the wizard's main goal is to quickly identify the most likely service so that it can hand it off to the appropriate inspector to do the rest.

Hexes

Hexes are binary-based patterns that are used to detect binary protocols such as ssl and dnp3. Hexes are configured in Lua as an array of tables, where each table contains keys to define how the hexes should operate. Those keys include the following:

  • service -> the name of the service that would be assigned
  • proto -> the protocol to scan (e.g., tcp)
  • client_first -> boolean flag that indicates if the client is the initiator of the data transfer
  • to_server -> list of text patterns to search in the data sent to the client
  • to_client -> list of text patterns to search in the data sent to the server

Hex patterns contain a series of hexadecimal bytes enclosed | characters. Users can also add an arbitrary number of ? characters to look for any number of hexadecimal digits. For instance, |05 ?4| would match |05 84| as well as |05 34|.

Here's one hex declaration for DNP3 traffic:

{ 
  service = 'dnp3',
  proto = 'tcp',
  client_first = true,
  to_server = { '|05 64|' },
  to_client = { '|05 64|' } 
}

This hex and other built-in hexes can be found in the snort_defaults.lua configuration file here.

Spells

Hexes are binary-based patterns, whereas spells are text-based, meaning they are best used with text-dominant protocols such as http, smtp, sip, and so forth. Spells are configured in the same manner that hexes are, and they also have an identical set of keys/options.

The text strings used in spells are case-insensitive and white-space sensitive. Spells can also contain wildcard characters, denoted with *, to match any number of bytes until a subsequent text pattern.

For example, an SMTP spell definition might look like this:

{ 
  service = 'smtp', 
  proto = 'tcp', 
  client_first = true,
  to_server = { 'HELO', 'EHLO' },
  to_client = { '220*SMTP', '220*MAIL' } 
}

The built-in spells can be found in the snort_defaults.lua configuration file here.

Curses

Curses are a little different than hexes and spells in that they are built as C++ state machines, as opposed to being defined in a Snort configuration. Curses exist for services where identification requires more than just a few string or hexadecimal patterns.

Snort currently has algorithms for five services/protocols:

  • DCE/RPC over UDP
  • DCE/RPC over TCP
  • DCE/RPC over SMB
  • SSLv2
  • S7CommPlus
  • MMS

The code for these algorithms can be found in src/service_inspectors/wizard/curses.cc.

Like hexes and spells, curses also get enabled in a Snort configuration. The following line in snort_defaults.lua enables the six default curse algorithms present in curses.cc:

curses = {'dce_udp', 'dce_tcp', 'dce_smb', 'mms', 's7commplus', 'sslv2'}

One can view the available curses with the following help command:

$ snort --help-config wizard | grep curses
multi wizard.curses: enable service identification based on internal algorithm { dce_smb | dce_udp | dce_tcp | mms | s7commplus | sslv2 }

The Binder

Snort 3's binder is a feature that directs network traffic to a specific service inspector based on some combination of the traffic's detected service, the ports and network ranges involved, and the network protocol being used. Users can create configurations with those options to tell Snort which service inspector to apply to a given network flow.

These configurations are written as an array of Lua tables and placed in one's Snort configuration file(s). These binder entries will typically contain two nested tables, when and use. The when table controls the criteria for invoking the action that's specified in the use table.

Here are some examples of a few different binder entries:

binder =
{
  -- allow all tcp port 22:
  -- (similar to Snort 2 config ignore_ports)
  { when = { proto = 'tcp', ports = '22' }, use = { action = 'allow' } },

  -- select a config file by vlan
  -- (similar to Snort 2 config binding by vlan)
  { when = { vlans = '1024' }, use = { file = 'vlan.lua' } },
  
  -- use the DNS service inspector when the server port is TCP port 53
  { when = { proto = 'tcp', ports = '53', role='server' },  use = { type = 'dns' } },

  -- use a non-default HTTP inspector for port 8080:
  -- (similar to a Snort 2 targeted preprocessor config)
  { when = { nets = '192.168.0.0/16', proto = 'tcp', ports = '8080' },
    use = { name = 'alt_http', type = 'http_inspect' } },

  -- use the default inspectors:
  -- (similar to a Snort 2 default preprocessor config)
  { when = { proto = 'tcp' }, use = { type = 'stream_tcp' } },
  { when = { service = 'http' }, use = { type = 'http_inspect' } },

  -- figure out which inspector to run automatically:
  { use = { type = 'wizard' } }
}

One can view all the available binder table parameters with the following Snort command:

$ snort --help-module binder