aboutsummaryrefslogtreecommitdiffstats
path: root/config/snort/snort.inc
diff options
context:
space:
mode:
Diffstat (limited to 'config/snort/snort.inc')
-rwxr-xr-x[-rw-r--r--]config/snort/snort.inc884
1 files changed, 819 insertions, 65 deletions
diff --git a/config/snort/snort.inc b/config/snort/snort.inc
index d930c08b..93db9424 100644..100755
--- a/config/snort/snort.inc
+++ b/config/snort/snort.inc
@@ -39,10 +39,13 @@ require_once("filter.inc");
/* package version */
$snort_version = "2.9.2.3";
-$pfSense_snort_version = "2.5.2";
+$pfSense_snort_version = "2.5.3";
$snort_package_version = "Snort {$snort_version} pkg v. {$pfSense_snort_version}";
$snort_rules_file = "snortrules-snapshot-2923.tar.gz";
$emerging_threats_version = "2.9.3";
+$flowbit_rules_file = "flowbit-required.rules";
+$snort_enforcing_rules_file = "snort.rules";
+
define("SNORTDIR", "/usr/local/etc/snort");
define("SNORTLOGDIR", "/var/log/snort");
@@ -719,6 +722,661 @@ function sync_snort_package_config() {
conf_mount_ro();
}
+function build_sid_msg_map($rules_path, $sid_file) {
+
+ /*************************************************************/
+ /* This function reads all the rules file in the passed */
+ /* $rules_path variable and produces a properly formatted */
+ /* sid-msg.map file for use by Snort and/or barnyard2. */
+ /*************************************************************/
+
+ $sidMap = array();
+ $rule_files = array();
+
+ /* First check if we were passed a directory, a single file */
+ /* or an array of filenames to read. Set our $rule_files */
+ /* variable accordingly. If we can't figure it out, return */
+ /* an empty rules map array. */
+ if (is_dir($rules_path))
+ $rule_files = glob($rules_path . "*.rules");
+ elseif (is_file($rules_path))
+ $rule_files = (array)$rules_path;
+ elseif (is_array($rules_path))
+ $rule_files = $rules_path;
+ else
+ return;
+
+ /* Read the rule files into an array, then iterate the list */
+ foreach ($rule_files as $file) {
+
+ /* Don't process files with "deleted" in the filename */
+ if (preg_match('/deleted/i', $file))
+ continue;
+
+ /* Read the file into an array, skipping empty lines. */
+ $rules_array = file($file, FILE_SKIP_EMPTY_LINES);
+ $record = "";
+ $b_Multiline = false;
+
+ /* Read and process each line from the rules in the */
+ /* current file. */
+ foreach ($rules_array as $rule) {
+
+ /* Skip any non-rule lines unless we're in */
+ /* multiline mode.
+ if (!preg_match('/^\s*#*\s*(alert|drop|pass)/i', $rule) && !$b_Multiline)
+ continue;
+
+ /* Test for a multi-line rule, and reassemble the */
+ /* pieces back into a single line. */
+ if (preg_match('/\\\\s*[\n]$/m', $rule)) {
+ $rule = substr($rule, 0, strrpos($rule, '\\'));
+ $record .= $rule;
+ $b_Multiline = true;
+ continue;
+ }
+ /* If the last segment of a multiline rule, then */
+ /* append it onto the previous parts to form a */
+ /* single-line rule for further processing below. */
+ elseif (!preg_match('/\\\\s*[\n]$/m', $rule) && $b_Multiline) {
+ $record .= $rule;
+ $rule = $record;
+ }
+ $b_Multiline = false;
+ $record = "";
+
+ /* Parse the rule to find sid and any references. */
+ $sid = '';
+ $msg = '';
+ $matches = '';
+ $sidEntry = '';
+ if (preg_match('/\bmsg\s*:\s*"(.+?)"\s*;/i', $rule, $matches))
+ $msg = trim($matches[1]);
+ if (preg_match('/\bsid\s*:\s*(\d+)\s*;/i', $rule, $matches))
+ $sid = trim($matches[1]);
+ if (!empty($sid) && !empty($msg)) {
+ $sidEntry = $sid . ' || ' . $msg;
+ preg_match_all('/\breference\s*:\s*([^\;]+)/i', $rule, $matches);
+ foreach ($matches[1] as $ref)
+ $sidEntry .= " || " . trim($ref);
+ $sidEntry .= "\n";
+ $sidMap[$sid] = $sidEntry;
+ }
+ }
+ }
+ /* Sort the generated sid-msg map by sid */
+ ksort($sidMap);
+
+ /* Now print the result to the supplied file */
+ file_put_contents($sid_file, array_values($sidMap));
+}
+
+function snort_merge_reference_configs($cfg_in, $cfg_out) {
+
+ /***********************************************************/
+ /* This function takes a list of "reference.config" files */
+ /* in the $cfg_in array and merges them into a single */
+ /* file specified by $cfg_out. The merging is done so */
+ /* no duplication of lines occurs in the output file. */
+ /***********************************************************/
+
+ $outMap = array();
+ foreach ($cfg_in as $file) {
+ $in = file($file, FILE_SKIP_EMPTY_LINES);
+ foreach ($in as $line) {
+ /* Skip comment lines */
+ if (preg_match('/^\s*#/', $line))
+ continue;
+ if (preg_match('/(\:)\s*(\w+)\s*(.*)/', $line, $matches)) {
+ if (!empty($matches[2]) && !empty($matches[3])) {
+ $matches[2] = trim($matches[2]);
+ if (!array_key_exists($matches[2], $outMap))
+ $outMap[$matches[2]] = trim($matches[3]);
+ }
+ }
+ }
+ }
+ /* Sort the new reference map. */
+ uksort($outMap,'strnatcasecmp');
+
+ /* Format and write it to the supplied output file. */
+ $format = "config reference: %-12s %s\n";
+ foreach ($outMap as $key=>$value)
+ $outMap[$key] = sprintf($format, $key, $value);
+ file_put_contents($cfg_out, array_values($outMap));
+}
+
+function snort_merge_classification_configs($cfg_in, $cfg_out) {
+
+ /************************************************************/
+ /* This function takes a list of "classification.config" */
+ /* files in the $cfg_in array and merges them into a */
+ /* single file specified by $cfg_out. The merging is done */
+ /* so no duplication of lines occurs in the output file. */
+ /************************************************************/
+
+ $outMap = array();
+ foreach ($cfg_in as $file) {
+ $in = file($file, FILE_SKIP_EMPTY_LINES);
+ foreach ($in as $line) {
+ if (preg_match('/(.*:)(\s*.*),(.*),(.*)/', $line, $matches)) {
+ /* Skip comment lines */
+ if (preg_match('/^\s*#/', $line))
+ continue;
+ if (!empty($matches[2]) && !empty($matches[3]) && !empty($matches[4])) {
+ $matches[2] = trim($matches[2]);
+ if (!array_key_exists($matches[2], $outMap))
+ $outMap[$matches[2]] = trim($matches[3]) . "," . trim($matches[4]);
+ }
+ }
+ }
+ }
+ /* Sort the new classification map. */
+ uksort($outMap,'strnatcasecmp');
+
+ /* Format and write it to the supplied output file. */
+ $format = "config classification: %s,%s\n";
+ foreach ($outMap as $key=>$value)
+ $outMap[$key] = sprintf($format, $key, $value);
+ file_put_contents($cfg_out, array_values($outMap));
+}
+
+function snort_load_rules_map($rules_path) {
+
+ /***************************************************************/
+ /* This function loads and returns an array with all the rules */
+ /* found in the *.rules files in the passed rules path. */
+ /* */
+ /* $rules_path can be: */
+ /* a directory (assumed to contain *.rules files) */
+ /* a filename (identifying a specific *.rules file) */
+ /* an array of filenames (identifying *.rules files) */
+ /***************************************************************/
+
+ $map_ref = array();
+ $rule_files = array();
+
+ if (empty($rules_path))
+ return $map_ref;
+
+ /***************************************************************
+ * Read all the rules into the map array.
+ * The structure of the map array is:
+ *
+ * map[gid][sid]['rule']['category']['disabled']['flowbits']
+ *
+ * where:
+ * gid = Generator ID from rule, or 1 if general text
+ * rule
+ * sid = Signature ID from rule
+ * rule = Complete rule text
+ * category = File name of file containing the rule
+ * disabled = 1 if rule is disabled (commented out), 0 if
+ * rule is enabled
+ * flowbits = Array of applicable flowbits if rule contains
+ * flowbits options
+ ***************************************************************/
+
+ /* First check if we were passed a directory, a single file */
+ /* or an array of filenames to read. Set our $rule_files */
+ /* variable accordingly. If we can't figure it out, return */
+ /* an empty rules map array. */
+ if (is_dir($rules_path))
+ $rule_files = glob($rules_path . "*.rules");
+ elseif (is_file($rules_path))
+ $rule_files = (array)$rules_path;
+ elseif (is_array($rules_path))
+ $rule_files = $rules_path;
+ else
+ return $map_ref;
+
+ /* Read the rule files into an array, then iterate the list */
+ /* to process the rules from the files one-by-one. */
+ foreach ($rule_files as $file) {
+
+ /* Don't process files with "deleted" in the filename. */
+ if (preg_match('/deleted/i', $file))
+ continue;
+
+ /* Read the file contents into an array, skipping */
+ /* empty lines. */
+ $rules_array = file($file, FILE_SKIP_EMPTY_LINES);
+ $record = "";
+ $b_Multiline = false;
+
+ /* Read and process each line from the rules in the */
+ /* current file into an array. */
+ foreach ($rules_array as $rule) {
+
+ /* Skip any lines that may be just spaces. */
+ if (trim($rule) == "")
+ continue;
+
+ /* Skip any non-rule lines unless we're in */
+ /* multiline mode. */
+ if (!preg_match('/^\s*#*\s*(alert|drop|pass)/i', $rule) && !$b_Multiline)
+ continue;
+
+ /* Test for a multi-line rule; loop and reassemble */
+ /* the pieces back into a single line. */
+ if (preg_match('/\\\\s*[\n]$/m', $rule)) {
+ $rule = substr($rule, 0, strrpos($rule, '\\'));
+ $record .= $rule;
+ $b_Multiline = true;
+ continue;
+ }
+ /* If the last segment of a multiline rule, then */
+ /* append it onto the previous parts to form a */
+ /* single-line rule for further processing below. */
+ elseif (!preg_match('/\\\\s*[\n]$/m', $rule) && $b_Multiline) {
+ $record .= $rule;
+ $rule = $record;
+ }
+
+ /* We have an actual single-line rule, or else a */
+ /* re-assembled multiline rule that is now a */
+ /* single-line rule, so store it in our rules map. */
+
+ /* Get and test the SID. If we don't find one, */
+ /* ignore and skip this rule as it is invalid. */
+ $sid = snort_get_sid($rule);
+ if (empty($sid)) {
+ $b_Multiline = false;
+ $record = "";
+ continue;
+ }
+
+ $gid = snort_get_gid($rule);
+ $map_ref[$gid][$sid]['rule'] = $rule;
+ $map_ref[$gid][$sid]['category'] = basename($file, ".rules");
+ if (preg_match('/^\s*\#+/', $rule))
+ $map_ref[$gid][$sid]['disabled'] = 1;
+ else
+ $map_ref[$gid][$sid]['disabled'] = 0;
+
+ /* Grab any associated flowbits from the rule. */
+ $map_ref[$gid][$sid]['flowbits'] = snort_get_flowbits($rule);
+
+ /* Reset our local flag and record variables */
+ /* for the next rule in the set. */
+ $b_Multiline = false;
+ $record = "";
+ }
+
+ /* Zero out our processing array and get the next file. */
+ unset($rules_array);
+ }
+ return $map_ref;
+}
+
+function snort_get_gid($rule) {
+
+ /****************************************************************/
+ /* If a gid is defined, then return it, else default to "1" for */
+ /* general text rules match. */
+ /****************************************************************/
+
+ if (preg_match('/\bgid\s*:\s*(\d+)\s*;/i', $rule, $matches))
+ return trim($matches[1]);
+ else
+ return "1";
+}
+
+function snort_get_sid($rule) {
+
+ /***************************************************************/
+ /* If a sid is defined, then return it, else default to an */
+ /* empty value. */
+ /***************************************************************/
+
+ if (preg_match('/\bsid\s*:\s*(\d+)\s*;/i', $rule, $matches))
+ return trim($matches[1]);
+ else
+ return "";
+}
+
+function snort_get_msg($rule) {
+
+ /**************************************************************/
+ /* Return the MSG section of the passed rule as a string. */
+ /**************************************************************/
+
+ $msg = "";
+ if (preg_match('/\bmsg\s*:\s*"(.+?)"\s*;/i', $rule, $matches))
+ $msg = trim($matches[1]);
+ return $msg;
+}
+
+function snort_get_flowbits($rule) {
+
+ /*************************************************************/
+ /* This will pull out "flowbits:" options from the rule text */
+ /* and return them in an array. */
+ /*************************************************************/
+
+ $flowbits = array();
+ if (preg_match_all('/flowbits\b:\s*(set|setx|unset|toggle|isset|isnotset)\s*,([^;]+)/i', $rule, $matches)) {
+ $i = -1;
+ while (++$i < count($matches[1])) {
+ $flowbits[] = trim($matches[1][$i]) ."," . trim($matches[2][$i]);
+ }
+ }
+ return $flowbits;
+}
+
+function snort_get_checked_flowbits(&$rules_map) {
+
+ /*************************************************************/
+ /* This function checks all the currently enabled rules to */
+ /* find any checked flowbits, and returns the checked */
+ /* flowbit names in an array. */
+ /*************************************************************/
+
+ $checked_flowbits = array();
+ foreach (array_keys($rules_map) as $k1) {
+ foreach (array_keys($rules_map[$k1]) as $k2) {
+ if ($rules_map[$k1][$k2]['disabled'] == 1)
+ continue;
+ if (empty($rules_map[$k1][$k2]['flowbits']))
+ continue;
+ foreach (array_values($rules_map[$k1][$k2]['flowbits']) as $flowbit) {
+ $action = substr($flowbit, 0, strpos($flowbit, ","));
+ if (preg_match('/is(not)?set/i', $action)) {
+ $tmp = substr($flowbit, strpos($flowbit, ",") +1 );
+ if (!in_array($tmp, $checked_flowbits))
+ $checked_flowbits[] = $tmp;
+ }
+ }
+ }
+ }
+ return $checked_flowbits;
+}
+
+function snort_get_set_flowbits(&$rules_map) {
+
+ /*********************************************************/
+ /* This function checks all the currently enabled rules */
+ /* to find any set flowbits, and returns the flowbit */
+ /* names in an array. */
+ /*********************************************************/
+
+ $set_flowbits = array();
+ foreach (array_keys($rules_map) as $k1) {
+ foreach (array_keys($rules_map[$k1]) as $k2) {
+ if ($rules_map[$k1][$k2]['disabled'] == 1)
+ continue;
+ if (empty($rules_map[$k1][$k2]['flowbits']))
+ continue;
+ foreach (array_values($rules_map[$k1][$k2]['flowbits']) as $flowbit) {
+ $action = substr($flowbit, 0, strpos($flowbit, ","));
+ if (preg_match('/^set/i', $action)) {
+ $tmp = substr($flowbit, strpos($flowbit, ",") +1 );
+ if (!in_array($tmp, $set_flowbits))
+ $set_flowbits[] = $tmp;
+ }
+ }
+ }
+ }
+ return $set_flowbits;
+}
+
+function snort_find_flowbit_required_rules(&$all_rules, &$unchecked_flowbits) {
+
+ /********************************************************/
+ /* This function finds all rules that must be enabled */
+ /* in order to satisfy the "checked flowbits" used by */
+ /* the currently enabled rules. It returns the list */
+ /* of required rules in an array. */
+ /********************************************************/
+
+ $required_flowbits_rules = array();
+ foreach (array_keys($all_rules) as $k1) {
+ foreach (array_keys($all_rules[$k1]) as $k2) {
+ if (empty($all_rules[$k1][$k2]['flowbits']))
+ continue;
+ foreach (array_values($all_rules[$k1][$k2]['flowbits']) as $flowbit) {
+ $action = substr($flowbit, 0, strpos($flowbit, ","));
+ if (preg_match('/^set/i', $action)) {
+ $tmp = substr($flowbit, strpos($flowbit, ",") +1 );
+ if (in_array($tmp, $unchecked_flowbits)) {
+ $required_flowbits_rules[$k1][$k2]['category'] = $all_rules[$k1][$k2]['category'];
+ if ($all_rules[$k1][$k2]['disabled'] == 0)
+ /* If not disabled, just return the rule text "as is" */
+ $required_flowbits_rules[$k1][$k2]['rule'] = ltrim($all_rules[$k1][$k2]['rule']);
+ else
+ /* If rule is disabled, remove leading '#' to enable it */
+ $required_flowbits_rules[$k1][$k2]['rule'] = ltrim(substr($all_rules[$k1][$k2]['rule'], strpos($all_rules[$k1][$k2]['rule'], "#") + 1));
+ }
+ }
+ }
+ }
+ }
+ return $required_flowbits_rules;
+}
+
+function snort_resolve_flowbits($rule_path) {
+
+ /******************************************************/
+ /* This function auto-resolves flowbit requirements */
+ /* by finding all checked flowbits in the currently */
+ /* enabled rules, and then making sure all the "set" */
+ /* flowbit rules for those "checked" flowbits are */
+ /* enabled. For any that are not enabled, they are */
+ /* copied to an array, enabled, and returned. */
+ /* */
+ /* $rule_path --> rules files of the interface */
+ /* to resolve flowbit dependencies */
+ /* for. This can be either of the */
+ /* following: */
+ /* - directory of *.rules files */
+ /* - array of *.rules filenames */
+ /* - a single *.rules filename */
+ /******************************************************/
+
+ $snortdir = SNORTDIR;
+
+ /* First, load up all the enabled rules. */
+ $rules_map = snort_load_rules_map($rule_path);
+
+ /* Next, find all the "checked" and "set" flowbits. */
+ $checked_flowbits = snort_get_checked_flowbits($rules_map);
+ $set_flowbits = snort_get_set_flowbits($rules_map);
+
+ /* We're done with the first rules array, so cleanup */
+ /* to conserve memory. */
+ unset($rules_map);
+
+ /* Next find any "checked" flowbits without matching */
+ /* "set" flowbit rules in the enabled rule set. */
+ $delta_flowbits = array_diff($checked_flowbits, $set_flowbits);
+
+ /* Cleanup and release the memory we no longer need. */
+ unset($checked_flowbits);
+ unset($set_flowbits);
+
+ /* Now find all the needed "set flowbit" rules from */
+ /* the master list of all rules. */
+ $all_rules_map = snort_load_rules_map("{$snortdir}/rules/");
+ $required_rules = snort_find_flowbit_required_rules($all_rules_map, $delta_flowbits);
+
+ /* Cleanup and release memory we no longer need. */
+ unset($all_rules_map);
+ unset($delta_flowbits);
+
+ return $required_rules;
+}
+
+function snort_write_flowbit_rules_file(&$flowbit_rules, $rule_file) {
+
+ /************************************************/
+ /* This function takes an array of rules in the */
+ /* rules_map format and writes them to the file */
+ /* given. */
+ /************************************************/
+
+ if (empty($flowbit_rules))
+ return;
+
+ /* See if we were passed a directory or full */
+ /* filename to write the rules to, and adjust */
+ /* the destination argument accordingly. */
+ if (is_dir($rule_file))
+ $rule_file = rtrim($rule_file, '/').'/flowbit-required.rules';
+
+ file_put_contents($rule_file, "# These rules set flowbits checked by your other enabled rules. If the\n");
+ file_put_contents($rule_file, "# the dependent flowbits are not set, then some of your chosen rules may\n", FILE_APPEND);
+ file_put_contents($rule_file, "# not fire. Enabling all rules that set these dependent flowbits ensures\n", FILE_APPEND);
+ file_put_contents($rule_file, "# your chosen rules fire as intended.\n#\n", FILE_APPEND);
+ file_put_contents($rule_file, "# If you wish to prevent alerts from any of these rules, add the GID:SID\n", FILE_APPEND);
+ file_put_contents($rule_file, "# of the rule to the Suppression List for the interface.\n", FILE_APPEND);
+ foreach (array_keys($flowbit_rules) as $k1) {
+ foreach (array_keys($flowbit_rules[$k1]) as $k2) {
+ file_put_contents($rule_file, "\n# Category: " . $flowbit_rules[$k1][$k2]['category'], FILE_APPEND);
+ file_put_contents($rule_file, " GID:" . $k1 . " SID:" . $k2 . "\n", FILE_APPEND);
+ file_put_contents($rule_file, $flowbit_rules[$k1][$k2]['rule'], FILE_APPEND);
+ }
+ }
+}
+
+function snort_load_vrt_policy($policy) {
+
+ /************************************************/
+ /* This function returns an array of all rules */
+ /* marked with the passed in $policy metadata. */
+ /* */
+ /* $policy --> desired VRT security policy */
+ /* 1. connectivity */
+ /* 2. balanced */
+ /* 3. security */
+ /************************************************/
+
+ $snortdir = SNORTDIR;
+ $vrt_policy_rules = array();
+
+ /* Create regular expression for searching. */
+ $policy_pcre = "/policy\\s" . $policy . "/i";
+
+ /* First, load up all the rules we have. */
+ $all_rules_map = snort_load_rules_map("{$snortdir}/rules/");
+
+ /* Now walk the rules list and find all those */
+ /* that are defined as active for the chosen */
+ /* security policy. */
+ foreach (array_keys($all_rules_map) as $k1) {
+ foreach (array_keys($all_rules_map[$k1]) as $k2) {
+ if (preg_match($policy_pcre, $all_rules_map[$k1][$k2]['rule'])) {
+ if (!preg_match('/flowbits\s*:\s*noalert/i', $all_rules_map[$k1][$k2]['rule'])) {
+ $vrt_policy_rules[$k1][$k2] = $all_rules_map[$k1][$k2];
+
+ /* Enable the policy rule if disabled */
+ if ($all_rules_map[$k1][$k2]['disabled'] == 1)
+ $vrt_policy_rules[$k1][$k2]['rule'] = ltrim(substr($all_rules_map[$k1][$k2]['rule'], strpos($all_rules_map[$k1][$k2]['rule'], "#") + 1));
+ }
+ }
+ }
+ }
+
+ /* Release memory we no longer need. */
+ unset($all_rules_map);
+
+ /* Return all the rules that match the policy. */
+ return $vrt_policy_rules;
+}
+
+function snort_write_enforcing_rules_file(&$rule_map, $rule_path) {
+
+ /************************************************/
+ /* This function takes a rules map array of */
+ /* the rules chosen for the active rule set */
+ /* and writes them out to the passed path. */
+ /************************************************/
+
+ global $snort_enforcing_rules_file;
+
+ $rule_file = "/snort.rules";
+
+ /* See if we were passed a directory or full */
+ /* filename to write the rules to, and adjust */
+ /* the destination argument accordingly. */
+ if (is_dir($rule_path))
+ $rule_file = rtrim($rule_path, '/').$rule_file;
+ else
+ $rule_file = $rule_path;
+
+ file_put_contents($rule_file, "# These rules are your current set of enforced rules for the protected\n");
+ file_put_contents($rule_file, "# interface. This list was compiled from the categories selected on the\n", FILE_APPEND);
+ file_put_contents($rule_file, "# CATEGORIES tab of the Snort configuration for the interface and/or any\n", FILE_APPEND);
+ file_put_contents($rule_file, "# chosen Snort VRT pre-defined IPS Policy.\n#\n", FILE_APPEND);
+ file_put_contents($rule_file, "# Any enablesid or disablesid customizations you made have been applied\n", FILE_APPEND);
+ file_put_contents($rule_file, "# to the rules in this file.\n\n", FILE_APPEND);
+ foreach (array_keys($rule_map) as $k1) {
+ foreach (array_keys($rule_map[$k1]) as $k2) {
+ file_put_contents($rule_file, $rule_map[$k1][$k2]['rule'], FILE_APPEND);
+ }
+ }
+}
+
+function snort_load_sid_mods($sids, $value) {
+
+ /*****************************************/
+ /* This function parses the string of */
+ /* SID values in $sids and returns an */
+ /* array with the SID as the key and */
+ /* passed $value as the value. The SID */
+ /* values in $sids are assumed to be */
+ /* delimited by "||". */
+ /*****************************************/
+
+ $result = array();
+ if (empty($sids) || empty($value))
+ return $result;
+ $tmp = explode("||", $sids);
+ foreach ($tmp as $v) {
+ if (preg_match('/\s\d+/', $v, $match))
+ $result[trim($match[0])] = $value;
+ }
+ return $result;
+}
+
+function snort_modify_sids(&$rule_map, $snortcfg) {
+
+ /*****************************************/
+ /* This function modifies the rules in */
+ /* the passed rules_map array based on */
+ /* values in the enablesid/disablesid */
+ /* configuration parameters. */
+ /* */
+ /* $rule_map = array of current rules */
+ /* $snortcfg = config settings */
+ /*****************************************/
+
+ if (!isset($snortcfg['rule_sid_on']) && !isset($snortcfg['rule_sid_off']))
+ return;
+
+ /* Load up our enablesid and disablesid */
+ /* arrays with lists of modified SIDs */
+ $enablesid = snort_load_sid_mods($snortcfg['rule_sid_on'], "enablesid");
+ $disablesid = snort_load_sid_mods($snortcfg['rule_sid_off'], "disablesid");
+
+ /* Turn on any rules that need to be */
+ /* forced "on" with enablesid mods. */
+ if (!empty($enablesid)) {
+ foreach ($enablesid as $k2 => $v) {
+ if ($rule_map[1][$k2]['disabled'] == 1)
+ $rule_map[1][$k2]['rule'] = ltrim(substr($rule_map[1][$k2]['rule'], strpos($rule_map[1][$k2]['rule'], "#") + 1));
+ }
+ }
+
+ /* Turn off any rules that need to be */
+ /* forced "off" with disablesid mods. */
+ if (!empty($disablesid)) {
+ foreach ($disablesid as $k2 => $v) {
+ if ($rule_map[1][$k2]['disabled'] == 0)
+ $rule_map[1][$k2]['rule'] = "# " . $rule_map[1][$k2]['rule'];
+ }
+ }
+}
+
/* Start of main config files */
/* open snort.sh for writing" */
function snort_create_rc() {
@@ -987,6 +1645,8 @@ function snort_generate_conf($snortcfg) {
$snortdir = SNORTDIR;
$snortlogdir = SNORTLOGDIR;
+ $flowbit_rules_file = "flowbit-required.rules";
+ $snort_enforcing_rules_file = "snort.rules";
if (!is_array($config['installedpackages']['snortglobal']['rule']))
return;
@@ -1013,7 +1673,7 @@ function snort_generate_conf($snortcfg) {
"{$snortlogdir}/snort_{$if_real}{$snort_uuid}",
"{$snortlogdir}/snort_{$if_real}{$snort_uuid}/barnyard2",
"{$snortcfgdir}/preproc_rules",
- "dynamicrules" => "{$snortcfgdir}/dynamicrules",
+ "dynamicrules" => "/usr/local/lib/snort/dynamicrules",
"dynamicengine" => "/usr/local/lib/snort/dynamicengine",
"dynamicpreprocessor" => "{$snortcfgdir}/dynamicpreprocessor"
);
@@ -1075,7 +1735,7 @@ function snort_generate_conf($snortcfg) {
$ssh_port = "22";
$snort_ports = array(
"dns_ports" => "53", "smtp_ports" => "25", "mail_ports" => "25,143,465,691",
- "http_ports" => "80", "oracle_ports" => "1521", "mssql_ports" => "1433",
+ "http_ports" => "80,901,3128,8080,9000", "oracle_ports" => "1521", "mssql_ports" => "1433",
"telnet_ports" => "23","snmp_ports" => "161", "ftp_ports" => "21",
"ssh_ports" => $ssh_port, "pop2_ports" => "109", "pop3_ports" => "110",
"imap_ports" => "143", "sip_proxy_ports" => "5060:5090,16384:32768",
@@ -1088,7 +1748,7 @@ function snort_generate_conf($snortcfg) {
"DCERPC_NCACN_IP_TCP" => "139,445", "DCERPC_NCADG_IP_UDP" => "138,1024:",
"DCERPC_NCACN_IP_LONG" => "135,139,445,593,1024:", "DCERPC_NCACN_UDP_LONG" => "135,1024:",
"DCERPC_NCACN_UDP_SHORT" => "135,593,1024:", "DCERPC_NCACN_TCP" => "2103,2105,2107",
- "DCERPC_BRIGHTSTORE" => "6503,6504"
+ "DCERPC_BRIGHTSTORE" => "6503,6504", "DNP3_PORTS" => "20000", "MODBUS_PORTS" => "502"
);
$portvardef = "";
@@ -1109,9 +1769,18 @@ preprocessor perfmonitor: time 300 file {$snortlogdir}/snort_{$if_real}{$snort_u
EOD;
- $def_flow_depth_type = '0';
- if (!empty($snortcfg['flow_depth']))
- $def_flow_depth_type = $snortcfg['flow_depth'];
+ $def_server_flow_depth_type = '300';
+ if ((!empty($snortcfg['server_flow_depth'])) || ($snortcfg['server_flow_depth'] == '0'))
+ $def_server_flow_depth_type = $snortcfg['server_flow_depth'];
+
+ $def_client_flow_depth_type = '300';
+ if ((!empty($snortcfg['client_flow_depth'])) || ($snortcfg['client_flow_depth'] == '0'))
+ $def_client_flow_depth_type = $snortcfg['client_flow_depth'];
+
+ if ($snortcfg['noalert_http_inspect'] == 'on')
+ $noalert_http_inspect = "no_alerts ";
+ else
+ $noalert_http_inspect = "";
$http_ports = str_replace(",", " ", $snort_ports['http_ports']);
/* def http_inspect */
@@ -1119,27 +1788,17 @@ EOD;
# HTTP Inspect #
preprocessor http_inspect: global iis_unicode_map unicode.map 1252 compress_depth 65535 decompress_depth 65535
-preprocessor http_inspect_server: server default \
- ports { {$http_ports} } \
- non_strict \
- non_rfc_char { 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 } \
- flow_depth {$def_flow_depth_type} \
- apache_whitespace no \
- directory no \
- iis_backslash no \
- u_encode yes \
- extended_response_inspection \
- inspect_gzip \
- normalize_utf \
- normalize_javascript \
- unlimited_decompress \
- ascii no \
- chunk_length 500000 \
- bare_byte yes \
- double_decode yes \
- iis_unicode no \
- iis_delimiter no \
- multi_slash no
+preprocessor http_inspect_server: server default profile all {$noalert_http_inspect}\
+ ports { {$http_ports} } \
+ http_methods { GET POST PUT SEARCH MKCOL COPY MOVE LOCK UNLOCK NOTIFY POLL BCOPY BDELETE BMOVE LINK UNLINK OPTIONS HEAD DELETE TRACE TRACK CONNECT SOURCE SUBSCRIBE UNSUBSCRIBE PROPFIND PROPPATCH BPROPFIND BPROPPATCH RPC_CONNECT PROXY_SUCCESS BITS_POST CCM_POST SMS_POST RPC_IN_DATA RPC_OUT_DATA RPC_ECHO_DATA } \
+ server_flow_depth {$def_server_flow_depth_type} \
+ client_flow_depth {$def_client_flow_depth_type} \
+ enable_cookie \
+ extended_response_inspection \
+ inspect_gzip \
+ normalize_utf \
+ unlimited_decompress \
+ normalize_javascript
EOD;
@@ -1195,18 +1854,22 @@ EOD;
$pop_preproc = <<<EOD
preprocessor pop: \
ports { {$pop_ports} } \
- qp_decode_depth -1 \
+ memcap 1310700 \
+ qp_decode_depth 0 \
b64_decode_depth 0 \
- bitenc_decode_depth 100
+ bitenc_decode_depth 0
+
EOD;
$imap_ports = str_replace(",", " ", $snort_ports['imap_ports']);
$imap_preproc = <<<EOD
preprocessor imap: \
ports { {$imap_ports} } \
- qp_decode_depth -1 \
+ memcap 1310700 \
+ qp_decode_depth 0 \
b64_decode_depth 0 \
- bitenc_decode_depth 100
+ bitenc_decode_depth 0
+
EOD;
$smtp_ports = str_replace(",", " ", $snort_ports['mail_ports']);
@@ -1217,6 +1880,7 @@ preprocessor SMTP: \
ports { {$smtp_ports} } \
inspection_type stateful \
normalize cmds \
+ ignore_tls_data \
valid_cmds { MAIL RCPT HELP HELO ETRN EHLO EXPN VRFY ATRN SIZE BDAT DEBUG EMAL ESAM ESND ESOM EVFY IDENT NOOP RSET SEND SAML SOML AUTH TURN ETRN PIPELINING \
CHUNKING DATA DSN RSET QUIT ONEX QUEU STARTTLS TICK TIME TURNME VERB X-EXPS X-LINK2STATE XADR XAUTH XCIR XEXCH50 XGEN XLICENSE XQUEU XSTA XTRN XUSR } \
normalize_cmds { MAIL RCPT HELP HELO ETRN EHLO EXPN VRFY ATRN SIZE BDAT DEBUG EMAL ESAM ESND ESOM EVFY IDENT NOOP RSET SEND SAML SOML AUTH TURN ETRN \
@@ -1230,8 +1894,16 @@ PIPELINING CHUNKING DATA DSN RSET QUIT ONEX QUEU STARTTLS TICK TIME TURNME VERB
alt_max_command_line_len 246 { SEND SAML SOML AUTH TURN ETRN PIPELINING CHUNKING DATA DSN RSET QUIT ONEX } \
alt_max_command_line_len 246 { QUEU STARTTLS TICK TIME TURNME VERB X-EXPS X-LINK2STATE XADR } \
alt_max_command_line_len 246 { XAUTH XCIR XEXCH50 XGEN XLICENSE XQUEU XSTA XTRN XUSR } \
- xlink2state { enable }
-
+ xlink2state { enable } \
+ log_mailfrom \
+ log_rcptto \
+ log_email_hdrs \
+ email_hdrs_log_depth 1464 \
+ log_filename \
+ qp_decode_depth 0 \
+ b64_decode_depth 0 \
+ bitenc_decode_depth 0
+
EOD;
/* def sf_portscan */
@@ -1248,8 +1920,9 @@ EOD;
$sun_rpc_ports = str_replace(",", " ", $snort_ports['sun_rpc_ports']);
/* def other_preprocs */
$other_preprocs = <<<EOD
+
# Other preprocs #
-preprocessor rpc_decode: {$sun_rpc_ports}
+preprocessor rpc_decode: {$sun_rpc_ports} no_alert_multiple_requests no_alert_large_fragments no_alert_incomplete
# Back Orifice
preprocessor bo
@@ -1259,11 +1932,11 @@ EOD;
/* def dce_rpc_2 */
$dce_rpc_2 = <<<EOD
# DCE/RPC 2 #
-preprocessor dcerpc2: memcap 102400, events [smb, co, cl]
+preprocessor dcerpc2: memcap 102400, events [co]
preprocessor dcerpc2_server: default, policy WinXP, \
detect [smb [{$snort_ports['smb_ports']}], tcp 135, udp 135, rpc-over-http-server 593], \
autodetect [tcp 1025:, udp 1025:, rpc-over-http-server 1025:], \
- smb_max_chain 3
+ smb_max_chain 3, smb_invalid_shares ["C$", "D$", "ADMIN$"]
EOD;
@@ -1277,6 +1950,26 @@ preprocessor dns: \
EOD;
+ /* def dnp3_preprocessor */
+ $dnp3_ports = str_replace(",", " ", $snort_ports['DNP3_PORTS']);
+ $dnp3_preproc = <<<EOD
+# DNP3 preprocessor #
+preprocessor dnp3: \
+ ports { {$dnp3_ports} } \
+ memcap 262144 \
+ check_crc
+
+EOD;
+
+ /* def modbus_preprocessor */
+ $modbus_ports = str_replace(",", " ", $snort_ports['MODBUS_PORTS']);
+ $modbus_preproc = <<<EOD
+# Modbus preprocessor #
+preprocessor modbus: \
+ ports { {$modbus_ports} }
+
+EOD;
+
$def_ssl_ports_ignore = str_replace(",", " ", $snort_ports['ssl_ports']);
$ssl_preproc = <<<EOD
# Ignore SSL and Encryption #
@@ -1288,20 +1981,26 @@ EOD;
/* stream5 queued settings */
$def_max_queued_bytes_type = '';
- if (!empty($snortcfg['max_queued_bytes']))
+ if ((!empty($snortcfg['max_queued_bytes'])) || ($snortcfg['max_queued_bytes'] == '0'))
$def_max_queued_bytes_type = ", max_queued_bytes {$snortcfg['max_queued_bytes']}";
$def_max_queued_segs_type = '';
- if (!empty($snortcfg['max_queued_segs']))
+ if ((!empty($snortcfg['max_queued_segs'])) || ($snortcfg['max_queued_segs'] == '0'))
$def_max_queued_segs_type = ", max_queued_segs {$snortcfg['max_queued_segs']}";
+ $def_stream5_mem_cap = '';
+ if (!empty($snortcfg['stream5_mem_cap']))
+ $def_stream5_mem_cap = ", memcap {$snortcfg['stream5_mem_cap']}";
+
/* define servers and ports snortdefservers */
$snort_servers = array (
"dns_servers" => "\$HOME_NET", "smtp_servers" => "\$HOME_NET", "http_servers" => "\$HOME_NET",
"www_servers" => "\$HOME_NET", "sql_servers" => "\$HOME_NET", "telnet_servers" => "\$HOME_NET",
"snmp_servers" => "\$HOME_NET", "ftp_servers" => "\$HOME_NET", "ssh_servers" => "\$HOME_NET",
"pop_servers" => "\$HOME_NET", "imap_servers" => "\$HOME_NET", "sip_proxy_ip" => "\$HOME_NET",
- "sip_servers" => "\$HOME_NET", "rpc_servers" => "\$HOME_NET",
+ "sip_servers" => "\$HOME_NET", "rpc_servers" => "\$HOME_NET", "dnp3_server" => "\$HOME_NET",
+ "dnp3_client" => "\$HOME_NET", "modbus_server" => "\$HOME_NET", "modbus_client" => "\$HOME_NET",
+ "enip_server" => "\$HOME_NET", "enip_client" => "\$HOME_NET",
"aim_servers" => "64.12.24.0/23,64.12.28.0/23,64.12.161.0/24,64.12.163.0/24,64.12.200.0/24,205.188.3.0/24,205.188.5.0/24,205.188.7.0/24,205.188.9.0/24,205.188.153.0/24,205.188.179.0/24,205.188.248.0/24"
);
@@ -1318,11 +2017,11 @@ EOD;
"dce_rpc_2" => "dce2_preproc", "dns_preprocessor" => "dns_preproc", "ftp_preprocessor" => "ftptelnet_preproc", "imap_preproc" => "imap_preproc",
"pop_preproc" => "pop_preproc", "reputation_preproc" => "reputation_preproc", "sensitive_data" => "sdf_preproc",
"sip_preproc" => "sip_preproc", "smtp_preprocessor" => "smtp_preproc", "ssh_preproc" => "ssh_preproc",
- "ssl_preproc" => "ssl_preproc"
+ "ssl_preproc" => "ssl_preproc", "dnp3_preproc" => "dnp3_preproc", "modbus_preproc" => "modbus_preproc"
);
$snort_preproc = array (
"perform_stat", "http_inspect", "other_preprocs", "ftp_preprocessor", "smtp_preprocessor", "ssl_preproc",
- "sf_portscan", "dce_rpc_2", "dns_preprocessor", "sensitive_data", "pop_preproc", "imap_preproc"
+ "sf_portscan", "dce_rpc_2", "dns_preprocessor", "sensitive_data", "pop_preproc", "imap_preproc", "dnp3_preproc", "modbus_preproc"
);
$snort_preprocessors = "";
foreach ($snort_preproc as $preproc) {
@@ -1380,21 +2079,58 @@ EOD;
/* generate rule sections to load */
$selected_rules_sections = "";
$dynamic_rules_sections = "";
- if (!empty($snortcfg['rulesets'])) {
- $enabled_rulesets_array = explode("||", $snortcfg['rulesets']);
- foreach($enabled_rulesets_array as $enabled_item) {
- if (file_exists("{$snortdir}/rules/{$enabled_item}") && !file_exists("{$snortcfgdir}/rules/{$enabled_item}"))
- @copy("{$snortdir}/rules/{$enabled_item}", "{$snortcfgdir}/rules/{$enabled_item}");
- if (substr($enabled_item, 0, 5) == "snort" && substr($enabled_item, -9) == ".so.rules") {
- $slib = substr($enabled_item, 6, -6);
- if (!file_exists("{$snort_dirs['dynamicrules']}/{$slib}"))
- @copy("/usr/local/lib/snort/dynamicrules/{$slib}", "{$snort_dirs['dynamicrules']}/{$slib}");
- if (file_exists("{$snort_dirs['dynamicrules']}/{$slib}") &&
- file_exists("{$snortcfgdir}/rules/{$enabled_item}"))
- $selected_rules_sections .= "include \$RULE_PATH/{$enabled_item}\n";
- } else if (file_exists("{$snortcfgdir}/rules/{$enabled_item}"))
- $selected_rules_sections .= "include \$RULE_PATH/{$enabled_item}\n";
+ if (!empty($snortcfg['rulesets']) || $snortcfg['ips_policy_enable'] == 'on') {
+ $enabled_rules = array();
+ $enabled_files = array();
+
+ /* Remove any existing rules files (except custom rules) prior to building a new set. */
+ foreach (glob("{$snortcfgdir}/rules/*.rules") as $file) {
+ if (basename($file, ".rules") != "custom")
+ @unlink($file);
+ }
+
+ /* Create an array with the full path filenames of the enabled */
+ /* rule category files if we have any. */
+ if (!empty($snortcfg['rulesets'])) {
+ foreach (explode("||", $snortcfg['rulesets']) as $file)
+ $enabled_files[] = "{$snortdir}/rules/" . $file;
+
+ /* Load our rules map in preparation for writing the enforcing rules file. */
+ $enabled_rules = snort_load_rules_map($enabled_files);
+ }
+
+ /* Check if a pre-defined Snort VRT policy is selected. If so, */
+ /* add all the VRT policy rules to our enforcing rule set. */
+ if (!empty($snortcfg['ips_policy'])) {
+ $policy_rules = snort_load_vrt_policy($snortcfg['ips_policy']);
+ foreach (array_keys($policy_rules) as $k1) {
+ foreach (array_keys($policy_rules[$k1]) as $k2) {
+ $enabled_rules[$k1][$k2]['rule'] = $policy_rules[$k1][$k2]['rule'];
+ $enabled_rules[$k1][$k2]['category'] = $policy_rules[$k1][$k2]['category'];
+ $enabled_rules[$k1][$k2]['disabled'] = $policy_rules[$k1][$k2]['disabled'];
+ $enabled_rules[$k1][$k2]['flowbits'] = $policy_rules[$k1][$k2]['flowbits'];
+ }
+ }
+ unset($policy_rules);
}
+
+ /* Process any enablesid or disablesid modifications for the selected rules. */
+ snort_modify_sids($enabled_rules, $snortcfg);
+
+ /* Write the enforcing rules file to the Snort interface's "rules" directory. */
+ snort_write_enforcing_rules_file($enabled_rules, "{$snortcfgdir}/rules/{$snort_enforcing_rules_file}");
+ if (file_exists("{$snortcfgdir}/rules/{$snort_enforcing_rules_file}"))
+ $selected_rules_sections = "include \$RULE_PATH/{$snort_enforcing_rules_file}\n";
+
+ /* If auto-flowbit resolution is enabled, generate the dependent flowbits rules file. */
+ if ($snortcfg['autoflowbitrules'] == 'on') {
+ $enabled_files[] = "{$snortcfgdir}/rules/{$snort_enforcing_rules_file}";
+ snort_write_flowbit_rules_file(snort_resolve_flowbits($enabled_files), "{$snortcfgdir}/rules/{$flowbit_rules_file}");
+ }
+
+ /* If we have the depedent flowbits rules file, then include it. */
+ if (file_exists("{$snortcfgdir}/rules/{$flowbit_rules_file}"))
+ $selected_rules_sections .= "include \$RULE_PATH/{$flowbit_rules_file}\n";
}
if (!empty($snortcfg['customrules'])) {
@@ -1403,6 +2139,10 @@ EOD;
} else
@unlink("{$snortcfgdir}/rules/custom.rules");
+ /* Build a new sid-msg.map file from the enabled */
+ /* rules and copy it to the interface directory. */
+ build_sid_msg_map("{$snortcfgdir}/rules/", "{$snortcfgdir}/sid-msg.map");
+
$cksumcheck = "all";
if ($snortcfg['cksumcheck'] == 'on')
$cksumcheck = "none";
@@ -1437,23 +2177,37 @@ config disable_tcpopt_alerts
config disable_ipopt_alerts
config disable_decode_drops
+# Configure PCRE match limitations
+config pcre_match_limit: 3500
+config pcre_match_limit_recursion: 1500
+
# Configure the detection engine #
-config detection: search-method {$snort_performance} max_queue_events 5
-config event_queue: max_queue 8 log 3 order_events content_length
+config detection: search-method {$snort_performance} search-optimize max-pattern-len 20 max_queue_events 5
+config event_queue: max_queue 8 log 5 order_events content_length
-#Configure dynamic loaded libraries
+# Configure protocol aware flushing #
+# For more information see README.stream5 #
+config paf_max: 16000
+
+#Configure dynamically loaded libraries
dynamicpreprocessor directory {$snort_dirs['dynamicpreprocessor']}
dynamicengine directory {$snort_dirs['dynamicengine']}
dynamicdetection directory {$snort_dirs['dynamicrules']}
+# Inline packet normalization. For more information, see README.normalize
+preprocessor normalize_ip4
+preprocessor normalize_tcp: ips ecn stream
+preprocessor normalize_icmp4
+preprocessor normalize_ip6
+preprocessor normalize_icmp6
+
# Flow and stream #
-preprocessor frag3_global: max_frags 8192
-preprocessor frag3_engine: policy bsd detect_anomalies
+preprocessor frag3_global: max_frags 65536
+preprocessor frag3_engine: policy bsd detect_anomalies overlap_limit 10 min_fragment_length 100 timeout 180
-preprocessor stream5_global: track_tcp yes, track_udp yes, track_icmp yes
-preprocessor stream5_tcp: policy BSD, ports both all{$def_max_queued_bytes_type}{$def_max_queued_segs_type}
-preprocessor stream5_udp:
-preprocessor stream5_icmp:
+preprocessor stream5_global: track_tcp yes, track_udp yes, track_icmp no, max_tcp 262144, max_udp 131072, max_active_responses 2, min_response_seconds 5{$def_stream5_mem_cap}
+preprocessor stream5_tcp: policy BSD, overlap_limit 10, timeout 180, ports both all{$def_max_queued_bytes_type}{$def_max_queued_segs_type}
+preprocessor stream5_udp: timeout 180
{$snort_preprocessors}