summaryrefslogtreecommitdiffstats
path: root/suricata/update/maps.py
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 17:43:34 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 17:43:34 +0000
commit0fcce96a175531ec6042cde1b11a0052aa261dd5 (patch)
tree898a1e161c4984b41e6a732866bd73b24f0f7b7a /suricata/update/maps.py
parentInitial commit. (diff)
downloadsuricata-update-0fcce96a175531ec6042cde1b11a0052aa261dd5.tar.xz
suricata-update-0fcce96a175531ec6042cde1b11a0052aa261dd5.zip
Adding upstream version 1.3.2.upstream/1.3.2
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--suricata/update/maps.py215
1 files changed, 215 insertions, 0 deletions
diff --git a/suricata/update/maps.py b/suricata/update/maps.py
new file mode 100644
index 0000000..8a34f27
--- /dev/null
+++ b/suricata/update/maps.py
@@ -0,0 +1,215 @@
+# Copyright (C) 2017 Open Information Security Foundation
+# Copyright (c) 2013 Jason Ish
+#
+# You can copy, redistribute or modify this Program under the terms of
+# the GNU General Public License version 2 as published by the Free
+# Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# version 2 along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.
+
+"""Provide mappings from ID's to descriptions.
+
+Includes mapping classes for event ID messages and classification
+information.
+"""
+
+from __future__ import print_function
+
+import re
+
+class SignatureMap(object):
+ """SignatureMap maps signature IDs to a signature info dict.
+
+ The signature map can be build up from classification.config,
+ gen-msg.map, and new and old-style sid-msg.map files.
+
+ The dict's in the map will have at a minimum the following
+ fields:
+
+ * gid *(int)*
+ * sid *(int)*
+ * msg *(string)*
+ * refs *(list of strings)*
+
+ Signatures loaded from a new style sid-msg.map file will also have
+ *rev*, *classification* and *priority* fields.
+
+ Example::
+
+ >>> from idstools import maps
+ >>> sigmap = maps.SignatureMap()
+ >>> sigmap.load_generator_map(open("tests/gen-msg.map"))
+ >>> sigmap.load_signature_map(open("tests/sid-msg-v2.map"))
+ >>> print(sigmap.get(1, 2495))
+ {'classification': 'misc-attack', 'rev': 8, 'priority': 0, 'gid': 1,
+ 'sid': 2495,
+ 'msg': 'GPL NETBIOS SMB DCEPRC ORPCThis request flood attempt',
+ 'ref': ['bugtraq,8811', 'cve,2003-0813', 'nessus,12206',
+ 'url,www.microsoft.com/technet/security/bulletin/MS04-011.mspx']}
+
+ """
+
+ def __init__(self):
+ self.map = {}
+
+ def size(self):
+ return len(self.map)
+
+ def get(self, generator_id, signature_id):
+ """Get signature info by generator_id and signature_id.
+
+ :param generator_id: The generator id of the signature to lookup.
+ :param signature_id: The signature id of the signature to lookup.
+
+ For convenience, if the generator_id is 3 and the signature is
+ not found, a second lookup will be done using a generator_id
+ of 1.
+
+ """
+
+ key = (generator_id, signature_id)
+ sig = self.map.get(key)
+ if sig is None and generator_id == 3:
+ return self.get(1, signature_id)
+ return sig
+
+ def load_generator_map(self, fileobj):
+ """Load the generator message map (gen-msg.map) from a
+ file-like object.
+
+ """
+ for line in fileobj:
+ line = line.strip()
+ if not line or line.startswith("#"):
+ continue
+ gid, sid, msg = [part.strip() for part in line.split("||")]
+ entry = {
+ "gid": int(gid),
+ "sid": int(sid),
+ "msg": msg,
+ "refs": [],
+ }
+ self.map[(entry["gid"], entry["sid"])] = entry
+
+ def load_signature_map(self, fileobj, defaultgid=1):
+ """Load signature message map (sid-msg.map) from a file-like
+ object.
+
+ """
+
+ for line in fileobj:
+ line = line.strip()
+ if not line or line.startswith("#"):
+ continue
+ parts = [p.strip() for p in line.split("||")]
+
+ # If we have at least 6 parts, attempt to parse as a v2
+ # signature map file.
+ try:
+ entry = {
+ "gid": int(parts[0]),
+ "sid": int(parts[1]),
+ "rev": int(parts[2]),
+ "classification": parts[3],
+ "priority": int(parts[4]),
+ "msg": parts[5],
+ "ref": parts[6:],
+ }
+ except:
+ entry = {
+ "gid": defaultgid,
+ "sid": int(parts[0]),
+ "msg": parts[1],
+ "ref": parts[2:],
+ }
+ self.map[(entry["gid"], entry["sid"])] = entry
+
+class ClassificationMap(object):
+ """ClassificationMap maps classification IDs and names to a dict
+ object describing a classification.
+
+ :param fileobj: (Optional) A file like object to load
+ classifications from on initialization.
+
+ The classification dicts stored in the map have the following
+ fields:
+
+ * name *(string)*
+ * description *(string)*
+ * priority *(int)*
+
+ Example::
+
+ >>> from idstools import maps
+ >>> classmap = maps.ClassificationMap()
+ >>> classmap.load_from_file(open("tests/classification.config"))
+
+ >>> classmap.get(3)
+ {'priority': 2, 'name': 'bad-unknown', 'description': 'Potentially Bad Traffic'}
+ >>> classmap.get_by_name("bad-unknown")
+ {'priority': 2, 'name': 'bad-unknown', 'description': 'Potentially Bad Traffic'}
+
+ """
+
+ def __init__(self, fileobj=None):
+ self.id_map = []
+ self.name_map = {}
+
+ if fileobj:
+ self.load_from_file(fileobj)
+
+ def size(self):
+ return len(self.id_map)
+
+ def add(self, classification):
+ """Add a classification to the map."""
+ self.id_map.append(classification)
+ self.name_map[classification["name"]] = classification
+
+ def get(self, class_id):
+ """Get a classification by ID.
+
+ :param class_id: The classification ID to get.
+
+ :returns: A dict describing the classification or None.
+
+ """
+ if 0 < class_id <= len(self.id_map):
+ return self.id_map[class_id - 1]
+ else:
+ return None
+
+ def get_by_name(self, name):
+ """Get a classification by name.
+
+ :param name: The name of the classification
+
+ :returns: A dict describing the classification or None.
+
+ """
+ if name in self.name_map:
+ return self.name_map[name]
+ else:
+ return None
+
+ def load_from_file(self, fileobj):
+ """Load classifications from a Snort style
+ classification.config file object.
+
+ """
+ pattern = "config classification: ([^,]+),([^,]+),([^,]+)"
+ for line in fileobj:
+ m = re.match(pattern, line.strip())
+ if m:
+ self.add({
+ "name": m.group(1),
+ "description": m.group(2),
+ "priority": int(m.group(3))})