diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 17:43:34 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 17:43:34 +0000 |
commit | 0fcce96a175531ec6042cde1b11a0052aa261dd5 (patch) | |
tree | 898a1e161c4984b41e6a732866bd73b24f0f7b7a /suricata/update/maps.py | |
parent | Initial commit. (diff) | |
download | suricata-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.py | 215 |
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))}) |