Source code for idstools.maps

# Copyright (c) 2013 Jason Ish
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.

"""Provide mappings from ID's to descriptions.

Includes mapping classes for event ID messages and classification
information.
"""

from __future__ import print_function

import re

[docs]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 = {}
[docs] def size(self): return len(self.map)
[docs] 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
[docs] 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
[docs] 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
[docs]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)
[docs] def size(self): return len(self.id_map)
[docs] def add(self, classification): """Add a classification to the map.""" self.id_map.append(classification) self.name_map[classification["name"]] = classification
[docs] 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
[docs] 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
[docs] 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))})