prelude-correlator/master: Context class improvement: overwrite, and new update method

[email protected] Sat, 16 Jan 2010 17:24:06 +0100 (CET)
Newsgroups gmane.comp.security.ids.prelude.cvs
Message-ID <[email protected]>
commit 09ba679044b70608144e24b01dae982011469682
Author: Yoann Vandoorselaere <[email protected]>
Date:   Sat Jan 16 16:51:41 2010 +0100

    Context class improvement: overwrite, and new update method
    
    Context initialization now take an optional 'overwrite' argument. This
    argument, if set to False, mean that the Context() will be returned
    un-modified if it already exist. If it doesn't, it will be created.
    
    There is now a new update() method, which provide exactly the same
    functionality as calling Context() with the 'update=True' argument.
    This is useful since some plugin might want to defer an update to
    another place in the code.
    
    Remove CheckAndDecThreshold() method. This function can be replaced
    by using Context.update() along with the Context.getUpdateCount()
    method.
    
    [Scan]: Update to the new Context API (removal of CheckAndDecThreshold).
    Improve the plugin so that it report CorrelationAlert after the
    timer expire, not after the threshold is reached.


========================================

 PreludeCorrelator/context.py      |   79 +++++++++++++++++++++---------------
 PreludeCorrelator/plugins/scan.py |   33 +++++----------
 2 files changed, 57 insertions(+), 55 deletions(-)

========================================

diff --git a/PreludeCorrelator/context.py b/PreludeCorrelator/context.py
index 82978bf..f2d8853 100644
--- a/PreludeCorrelator/context.py
+++ b/PreludeCorrelator/context.py
@@ -79,65 +79,78 @@ class Context(IDMEF, Timer):
                 Timer.__setstate__(self, dict)
                 IDMEF.__setstate__(self, dict)
 
-        def __init__(self, name, options={}, update=False, idmef=None):
-                is_update = update and hasattr(self, "_name")
-                if is_update is False:
-                        self._options = { "threshold": -1, "expire": 0, "alert_on_expire": False }
-                        IDMEF.__init__(self)
-                        Timer.__init__(self, 0)
-
-                self._options.update(options)
-                self.setOptions(self._options)
-
-                if is_update is True:
+        def __init__(self, name, options={}, overwrite=True, update=False, idmef=None):
+                already_initialized = (update or (overwrite is False)) and hasattr(self, "_name")
+                if already_initialized is True:
                         return
 
+                self._options = { "threshold": -1, "expire": 0, "alert_on_expire": False }
+                IDMEF.__init__(self)
+                Timer.__init__(self, 0)
+
                 self._name = name
                 self._update_count = 0
-                self._threshold_count = 0
+
+                if _CONTEXT_TABLE.has_key(name): # Make sure any timer is deleted on overwrite
+                    _CONTEXT_TABLE[name].destroy()
+
                 _CONTEXT_TABLE[name] = self
 
+                self._options.update(options)
+                self.setOptions(self._options)
+
                 if idmef:
                         self.addAlertReference(idmef)
 
-        def __new__(cls, name, options={}, update=False, idmef=None):
-                if update:
+        def __new__(cls, name, options={}, overwrite=True, update=False, idmef=None):
+                if update or (overwrite is False):
                         ctx = search(name)
                         if ctx:
-                                ctx._update_count += 1
-                                if idmef:
-                                        ctx.addAlertReference(idmef)
-
-                                if ctx.running():
-                                        ctx.reset()
+                                if update:
+                                        ctx.update(options, idmef)
+                                        return ctx
 
-                                return ctx
+                                if overwrite is False:
+                                        return ctx
 
                 return super(Context, cls).__new__(cls)
 
-        def CheckAndDecThreshold(self):
-                self._threshold_count += 1
-                if self._threshold_count == self._options["threshold"]:
-                        return True
-                else:
-                        return False
-
         def _timerExpireCallback(self):
+                threshold = self._options["threshold"]
                 alert_on_expire = self._options["alert_on_expire"]
+
                 if alert_on_expire:
+                    if threshold == -1 or (self._update_count + 1) >= threshold:
                         if callable(alert_on_expire):
                                 alert_on_expire(self)
+                                return
                         else:
                                 self.alert()
 
                 self.destroy()
 
+        def update(self, options={}, idmef=None):
+                self._update_count += 1
+
+                if idmef:
+                        self.addAlertReference(idmef)
+
+                if self.running():
+                        self.reset()
+
+                self._options.update(options)
+                self.setOptions(self._options)
+
         def stats(self, log_func, now=time.time()):
-                if not self._timer_start:
-                    log_func("[%s]: threshold=%d/%d update=%d" % (self._name, self._threshold_count, self._options["threshold"], self._update_count))
-                else:
-                    log_func("[%s]: threshold=%d/%d update=%d expire=%d/%d" % (self._name, self._threshold_count, self._options["threshold"],
-                                                                               self._update_count, self.elapsed(now), self._options["expire"]))
+                str = ""
+
+                if self._options["threshold"] != -1:
+                        str += " threshold=%d/%d" % (self._update_count + 1, self._options["threshold"])
+
+                if self._timer_start:
+                        str += " expire=%d/%d" % (self.elapsed(now), self._options["expire"])
+
+                log_func("[%s]: update=%d%s" % (self._name, self._update_count, str))
 
         def getOptions(self):
                 return self._options
diff --git a/PreludeCorrelator/plugins/scan.py b/PreludeCorrelator/plugins/scan.py
index 8f24600..9246689 100644
--- a/PreludeCorrelator/plugins/scan.py
+++ b/PreludeCorrelator/plugins/scan.py
@@ -33,13 +33,11 @@ class EventScanPlugin(Plugin):
 
         for saddr in source:
             for daddr in target:
-                ctx = Context("SCAN_EVENTSCAN_" + saddr + daddr, { "expire": 60, "threshold": 30 }, update = True, idmef=idmef)
-                if ctx.CheckAndDecThreshold():
+                ctx = Context("SCAN_EVENTSCAN_" + saddr + daddr, { "expire": 60, "threshold": 30, "alert_on_expire": True }, update = True, idmef=idmef)
+                if ctx.getUpdateCount() == 0:
                     ctx.Set("alert.correlation_alert.name", "A single host has played many events against a single target. This may be a vulnerability scan")
                     ctx.Set("alert.classification.text", "Eventscan")
                     ctx.Set("alert.assessment.impact.severity", "high")
-                    ctx.alert()
-                    ctx.destroy()
 
 
 # Detect Eventsweep:
@@ -54,27 +52,20 @@ class EventSweepPlugin(Plugin):
             return
 
         for saddr in source:
-            ctx = Context("SCAN_EVENTSWEEP_" + classification + saddr, { "expire": 60, "threshold": 30 }, update = True)
-            insert = True
+            ctx = Context("SCAN_EVENTSWEEP_" + classification + saddr, { "expire": 60, "threshold": 30, "alert_on_expire": True }, overwrite = False)
+            if ctx.getUpdateCount() == 0:
+                ctx.Set("alert.correlation_alert.name", "A single host has played the same event against multiple targets. This may be a network scan for a specific vulnerability")
+                ctx.Set("alert.classification.text", "Eventsweep")
+                ctx.Set("alert.assessment.impact.severity", "high")
 
             cur = ctx.Get("alert.target(*).node.address(*).address")
             if cur:
                 for address in target:
                     if address in cur:
                         insert = False
-                        break
-
-            if insert:
-                ctx.addAlertReference(idmef)
-
-                if ctx.CheckAndDecThreshold():
-                    ctx.Set("alert.correlation_alert.name", "A single host has played the same event against multiple targets. This may be a network scan for a specific vulnerability")
-                    ctx.Set("alert.classification.text", "Eventsweep")
-                    ctx.Set("alert.assessment.impact.severity", "high")
-                    ctx.alert()
-                    ctx.destroy()
-
+                        return
 
+            ctx.update(idmef=idmef)
 
 
 # Detect Eventstorm:
@@ -86,11 +77,9 @@ class EventStormPlugin(Plugin):
             return
 
         for saddr in source:
-            ctx = Context("SCAN_EVENTSTORM_" + saddr, { "expire": 120, "threshold": 150 }, update = True, idmef = idmef)
-            if ctx.CheckAndDecThreshold():
+            ctx = Context("SCAN_EVENTSTORM_" + saddr, { "expire": 120, "threshold": 150, "alert_on_expire": True }, update = True, idmef = idmef)
+            if ctx.getUpdateCount() == 0:
                 ctx.Set("alert.correlation_alert.name", "A single host is producing an unusual amount of events")
                 ctx.Set("alert.classification.text", "Eventstorm")
                 ctx.Set("alert.assessment.impact.severity", "high")
-                ctx.alert()
-                ctx.destroy()
 
_______________________________________________
Prelude-cvslog site list
[email protected]
http://lists.prelude-ids.org/mailman/listinfo/prelude-cvslog