prelude-correlator/master: Use setuptools for distribution and plugins.
[email protected] Wed, 17 Jun 2009 15:46:58 +0200 (CEST)
| Newsgroups | gmane.comp.security.ids.prelude.cvs |
|---|---|
| Message-ID | <[email protected]> |
commit 04df89e00cf7ab7d30ab76e32b0017b8839b3205 Author: Yoann Vandoorselaere <[email protected]> Date: Mon Jun 15 13:27:32 2009 +0200 Use setuptools for distribution and plugins. ======================================== PreludeCorrelator/main.py | 163 +++++++++++++ PreludeCorrelator/pluginmanager.py | 66 +++++ PreludeCorrelator/plugins.py | 74 ------ PreludeCorrelator/plugins/bruteforce.py | 74 ++++++ PreludeCorrelator/plugins/businesshour.py | 44 ++++ PreludeCorrelator/plugins/dshield.py | 89 +++++++ PreludeCorrelator/plugins/firewall.py | 52 ++++ PreludeCorrelator/plugins/opensshauth.py | 56 +++++ PreludeCorrelator/plugins/scan.py | 110 +++++++++ PreludeCorrelator/plugins/worm.py | 58 +++++ ez_setup.py | 276 ++++++++++++++++++++++ prelude_correlator.egg-info/PKG-INFO | 34 +++ prelude_correlator.egg-info/SOURCES.txt | 23 ++ prelude_correlator.egg-info/dependency_links.txt | 1 + prelude_correlator.egg-info/entry_points.txt | 14 + prelude_correlator.egg-info/top_level.txt | 1 + ruleset/brute-force.py | 74 ------ ruleset/business-hour.py | 44 ---- ruleset/dshield.py | 89 ------- ruleset/firewall.py | 52 ---- ruleset/openssh-multiple-authtypes.py | 56 ----- ruleset/scan.py | 110 --------- ruleset/worm.py | 58 ----- scripts/prelude-correlator | 153 ------------ setup.py | 77 +++++- 25 files changed, 1124 insertions(+), 724 deletions(-) ======================================== diff --git a/PreludeCorrelator/main.py b/PreludeCorrelator/main.py new file mode 100644 index 0000000..08e7b5a --- /dev/null +++ b/PreludeCorrelator/main.py @@ -0,0 +1,163 @@ +#!/usr/bin/env python +# +# Copyright (C) 2009 PreludeIDS Technologies. All Rights Reserved. +# Author: Yoann Vandoorselaere <[email protected]> +# +# This file is part of the Prewikka program. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# 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 +# along with this program; see the file COPYING. If not, write to +# the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + +import pkg_resources +import sys, os, time, signal +from PreludeCorrelator import idmef, pluginmanager, context, siteconfig +from optparse import OptionParser +from PreludeEasy import ClientEasy, CheckVersion + + +prelude_client = None +VERSION = pkg_resources.get_distribution('prelude-correlator').version + + +if not CheckVersion(siteconfig.libprelude_required_version): + raise Exception, ("Libprelude version '%s' is required" % siteconfig.libprelude_required_version) + +class PreludeClient: + def __init__(self, print_input=None, print_output=None, dry_run=False): + self._message_processed = 0 + self._alert_generated = 0 + self._print_input = print_input + self._print_output = print_output + self._continue = True + self._dry_run = dry_run + + self._pm = pluginmanager.PluginManager() + print ("%d plugin have been loaded." % (self._pm.getPluginCount())) + + self._client = ClientEasy("prelude-correlator", ClientEasy.PERMISSION_IDMEF_READ|ClientEasy.PERMISSION_IDMEF_WRITE, + "Prelude-Correlator", "Correlator", "PreludeIDS Technologies", VERSION) + self._client.Start() + + + def _handle_event(self, idmef): + if self._print_input: + self._print_input.write(str(idmef)) + + self._pm.run(idmef) + self._message_processed += 1 + + def stats(self): + print("%d message received, %d correlationAlert generated." % (self._message_processed, self._alert_generated)) + + def correlationAlert(self, idmef): + self._alert_generated = self._alert_generated + 1 + + if not self._dry_run: + self._client.SendIDMEF(idmef) + + if self._print_output: + self._print_output.write(str(idmef)) + + def recvEvent(self): + msg = idmef.IDMEF() + + last = time.time() + while self._continue: + try: + r = self._client.RecvIDMEF(msg, 1000) + except: + r = 0 + + if r: + if msg.Get("alert.create_time"): + self._handle_event(msg) + msg.reset() + + now = time.time() + if now - last >= 1: + context.wakeup(now) + last = now + + def stop(self): + self._continue = False + + +def handle_signal(signum, frame): + print 'Signal handler called with signal', signum + if signum == signal.SIGQUIT: + prelude_client.stats() + context.stats() + else: + prelude_client.stop() + +def main(): + global prelude_client + + parser = OptionParser(usage="%prog", version="%prog " + VERSION) + parser.add_option("-c", "--config", action="store", dest="config", type="string", help="Configuration file to use", metavar="FILE") + parser.add_option("", "--dry-run", action="store_true", dest="dry_run", help="No report to the specified Manager will occur", default=False) + parser.add_option("-d", "--daemon", action="store_true", dest="daemon", help="Run in daemon mode") + parser.add_option("-P", "--pidfile", action="store", dest="pidfile", type="string", help="Write Prelude Correlator PID to specified file", metavar="FILE") + parser.add_option("", "--print-input", action="store", dest="print_input", type="string", help="Dump alert input from manager to the specified file", metavar="FILE") + parser.add_option("", "--print-output", action="store", dest="print_output", type="string", help="Dump alert output to the specified file", metavar="FILE") + parser.add_option("--debug", action="store", dest="debug", type="int", help="Enable debug ouptut (optional debug level argument)", metavar="LEVEL") + (options, args) = parser.parse_args() + + ifd = None + if options.print_input: + if options.print_input == "-": + ifd = sys.stdout + else: + ifd = open(options.print_input, "w") + + ofd = None + if options.print_output: + if options.print_output == "-": + ofd = sys.stdout + else: + ofd = open(options.print_output, "w") + + if options.daemon: + if os.fork(): + os._exit(0) + + os.setsid() + if os.fork(): + os._exit(0) + + os.umask(077) + + fd = os.open('/dev/null', os.O_RDWR) + for i in xrange(3): + os.dup2(fd, i) + + os.close(fd) + if options.pidfile: + open(pidfile, "w").write(str(os.getpid())) + + prelude_client = PreludeClient(print_input=ifd, print_output=ofd, dry_run=options.dry_run) + idmef.set_prelude_client(prelude_client) + + signal.signal(signal.SIGTERM, handle_signal) + signal.signal(signal.SIGINT, handle_signal) + signal.signal(signal.SIGQUIT, handle_signal) + + # restore previous context. + context.load() + + prelude_client.recvEvent() + + # save existing context + context.save() + diff --git a/PreludeCorrelator/pluginmanager.py b/PreludeCorrelator/pluginmanager.py new file mode 100644 index 0000000..a24e7b4 --- /dev/null +++ b/PreludeCorrelator/pluginmanager.py @@ -0,0 +1,66 @@ +# Copyright (C) 2009 PreludeIDS Technologies. All Rights Reserved. +# Author: Yoann Vandoorselaere <[email protected]> +# +# This file is part of the Prewikka program. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# 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 +# along with this program; see the file COPYING. If not, write to +# the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + +import pkg_resources +import ConfigParser, sys, os, traceback +from PreludeCorrelator import siteconfig + + +config = ConfigParser.ConfigParser() +config.read(siteconfig.conf_dir + '/plugins.conf') + +ENTRYPOINT = 'PreludeCorrelator.plugins' + +class Plugin(object): + enable = True + + def getConfigValue(self, key, replacement=None): + if not config.has_section(self.__class__.__name__): + return replacement + + try: + return config.get(self.__class__.__name__, key) + except ConfigParser.NoOptionError: + return replacement + + def run(self, idmef): + pass + + +class PluginManager: + def __init__(self): + self._count = 0 + self._instance = [] + + for entrypoint in pkg_resources.iter_entry_points(ENTRYPOINT): + plugin_class = entrypoint.load() + + self._instance.append(plugin_class()) + self._count += 1 + + def getPluginCount(self): + return self._count + + def run(self, idmef): + for plugin in self.__instances: + try: + plugin.run(idmef) + except Exception, e: + traceback.print_exc() + diff --git a/PreludeCorrelator/plugins.py b/PreludeCorrelator/plugins.py deleted file mode 100644 index 486eb38..0000000 --- a/PreludeCorrelator/plugins.py +++ /dev/null @@ -1,74 +0,0 @@ -# Copyright (C) 2009 PreludeIDS Technologies. All Rights Reserved. -# Author: Yoann Vandoorselaere <[email protected]> -# -# This file is part of the Prewikka program. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2, or (at your option) -# any later version. -# -# 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 -# along with this program; see the file COPYING. If not, write to -# the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - -from PreludeCorrelator import siteconfig -import ConfigParser, sys, os, traceback - - -config = ConfigParser.ConfigParser() -config.read(siteconfig.conf_dir + '/plugins.conf') - - -class Plugin(object): - enable = True - - def getConfigValue(self, key, replacement=None): - if not config.has_section(self.__module__): - return replacement - - try: - return config.get(self.__module__, key) - except ConfigParser.NoOptionError: - return replacement - - def run(self, idmef): - pass - - -class PluginManager: - __instances = [] - - def __initPlugin(self, plugin): - p = plugin() - if p.enable: - self.__instances.append(p) - - self._count = self._count + 1 - - def __init__(self): - self._count = 0 - - sys.path.insert(0, siteconfig.ruleset_dir) - - for file in os.listdir(siteconfig.ruleset_dir): - pl = __import__(os.path.splitext(file)[0], None, None, ['']) - - for plugin in Plugin.__subclasses__(): - self.__initPlugin(plugin) - - def getPluginCount(self): - return self._count - - def run(self, idmef): - for plugin in self.__instances: - try: - plugin.run(idmef) - except Exception, e: - traceback.print_exc() - diff --git a/PreludeCorrelator/plugins/__init__.py b/PreludeCorrelator/plugins/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/PreludeCorrelator/plugins/bruteforce.py b/PreludeCorrelator/plugins/bruteforce.py new file mode 100644 index 0000000..72c6af9 --- /dev/null +++ b/PreludeCorrelator/plugins/bruteforce.py @@ -0,0 +1,74 @@ +# Copyright (C) 2006 G Ramon Gomez <gene at gomezbrothers dot com> +# Copyright (C) 2009 PreludeIDS Technologies <[email protected]> +# +# This file is part of the Prelude-Correlator program. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# 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 +# along with this program; see the file COPYING. If not, write to +# the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + +import re +from PreludeCorrelator.pluginmanager import Plugin +from PreludeCorrelator.context import Context + +class BruteForcePlugin(Plugin): + def _BruteForce(self, idmef): + sadd = idmef.Get("alert.source(*).node.address(*).address") + tadd = idmef.Get("alert.target(*).node.address(*).address") + if not sadd or not tadd: + return + + for source in sadd: + for target in tadd: + ctx = Context("BRUTE_ST_" + source + target, { "expire": 2, "threshold": 5 }, update = True) + ctx.Set("alert.source(>>)", idmef.Get("alert.source")) + ctx.Set("alert.target(>>)", idmef.Get("alert.target")) + ctx.Set("alert.correlation_alert.alertident(>>).alertident", idmef.Get("alert.messageid")) + ctx.Set("alert.correlation_alert.alertident(-1).analyzerid", idmef.Get("alert.analyzer(*).analyzerid")[-1]) + + if ctx.CheckAndDecThreshold(): + ctx.Set("alert.classification.text", "Brute force attack") + ctx.Set("alert.correlation_alert.name", "Multiple failed login") + ctx.Set("alert.assessment.impact.severity", "high") + ctx.Set("alert.assessment.impact.description", "Multiple failed attempts have been made to login to a user account") + ctx.alert() + ctx.destroy() + + def _BruteUserForce(self, idmef): + userid = idmef.Get("alert.target(*).user.user_id(*).name"); + if not userid: + return + + for user in userid: + ctx = Context("BRUTE_U_" + user, { "expire": 120, "threshold": 2 }, update = True) + ctx.Set("alert.source(>>)", idmef.Get("alert.source")) + ctx.Set("alert.target(>>)", idmef.Get("alert.target")) + ctx.Set("alert.correlation_alert.alertident(>>).alertident", idmef.Get("alert.messageid")) + ctx.Set("alert.correlation_alert.alertident(-1).analyzerid", idmef.Get("alert.analyzer(*).analyzerid")[-1]) + + if ctx.CheckAndDecThreshold(): + ctx.Set("alert.classification.text", "Brute force attack") + ctx.Set("alert.correlation_alert.name", "Multiple failed login") + ctx.Set("alert.assessment.impact.severity", "high") + ctx.Set("alert.assessment.impact.description", "Multiple failed attempts have been made to login to a user account") + ctx.alert() + ctx.destroy() + + + def run(self, idmef): + if not idmef.match("alert.classification.text", re.compile("[Ll]ogin|[Aa]uthentication"), + "alert.assessment.impact.completion", "failed"): + return + + self._BruteForce() + self._BruteUserForce() diff --git a/PreludeCorrelator/plugins/businesshour.py b/PreludeCorrelator/plugins/businesshour.py new file mode 100644 index 0000000..d450737 --- /dev/null +++ b/PreludeCorrelator/plugins/businesshour.py @@ -0,0 +1,44 @@ +# Copyright (C) 2009 PreludeIDS Technologies. All Rights Reserved. +# Author: Yoann Vandoorselaere <[email protected]> +# +# This file is part of the Prelude-Correlator program. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# 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 +# along with this program; see the file COPYING. If not, write to +# the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + +import time +from PreludeCorrelator.idmef import IDMEF +from PreludeCorrelator.pluginmanager import Plugin + +# Alert only on saturday and sunday, and everyday from 6:00pm to 9:00am. + +class BusinessHourPlugin(Plugin): + def run(self, idmef): + + t = time.localtime(int(idmef.Get("alert.create_time"))) + + if not (t.tm_wday == 5 or t.tm_wday == 6 or t.tm_hour < 9 or t.tm_hour > 17): + return + + if idmef.Get("alert.assessment.impact.completion") != "succeeded": + return + + ca = IDMEF() + ca.Set("alert.source", idmef.Get("alert.source")) + ca.Set("alert.target", idmef.Get("alert.target")) + ca.Set("alert.classification", idmef.Get("alert.classification")) + ca.Set("alert.correlation_alert.alertident(>>).alertident", idmef.Get("alert.messageid")) + ca.Set("alert.correlation_alert.alertident(-1).analyzerid", idmef.Get("alert.analyzer(*).analyzerid")[-1]) + ca.Set("alert.correlation_alert.name", "Critical system activity on day off") + ca.alert() diff --git a/PreludeCorrelator/plugins/dshield.py b/PreludeCorrelator/plugins/dshield.py new file mode 100644 index 0000000..7c0dbf6 --- /dev/null +++ b/PreludeCorrelator/plugins/dshield.py @@ -0,0 +1,89 @@ +# Copyright (C) 2009 PreludeIDS Technologies. All Rights Reserved. +# Author: Yoann Vandoorselaere <[email protected]> +# Author: Sebastien Tricaud <[email protected]> +# +# This file is part of the Prelude-Correlator program. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# 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 +# along with this program; see the file COPYING. If not, write to +# the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + +import os, httplib, time +from PreludeCorrelator import siteconfig +from PreludeCorrelator.idmef import IDMEF +from PreludeCorrelator.pluginmanager import Plugin +from PreludeCorrelator.context import Context, Timer + + +class DshieldPlugin(Plugin): + DSHIELD_RELOAD = 7 * 24 * 60 * 60 + DSHIELD_SERVER = "www.dshield.org" + DSHIELD_URI = "www.dshield.org/ipsascii.html?limit=10000" + + def __loadData(self, fname, age=0): + cnt = 0 + self.__iphash.clear() + + for line in open(fname, "r"): + if line[0] != '#': + self.__iphash[line.split('\t')[0]] = True + cnt = cnt + 1 + + Timer(self.__reload - age, self.__retrieveData) + + def __retrieveData(self, timer=None): + fname = siteconfig.lib_dir + "/dshield.dat" + + try: + st = os.stat(fname) + if time.time() - st.st_mtime < self.__reload: + return self.__loadData(fname, time.time() - st.st_mtime) + except: + pass + + print("Downloading host list from dshield, this might take some time...") + con = httplib.HTTPConnection(self.__server) + con.request("GET", self.__uri) + r = con.getresponse() + if r.status != 200: + return + + fd = open(fname, "w") + fd.write(r.read()) + fd.close() + + print("Downloading done, processing data.") + self.__loadData(fname) + + + def __init__(self): + self.__iphash = { } + self.__reload = self.getConfigValue("reload", self.DSHIELD_RELOAD) + self.__server = self.getConfigValue("server", self.DSHIELD_SERVER) + self.__uri = self.getConfigValue("uri", self.DSHIELD_URI) + + self.__retrieveData() + + def run(self, idmef): + for source in idmef.Get("alert.source(*).node.address(*).address"): + if self.__iphash.has_key(source): + ca = IDMEF() + ca.Set("alert.source(>>)", idmef.Get("alert.source")) + ca.Set("alert.target(>>)", idmef.Get("alert.target")) + ca.Set("alert.correlation_alert.alertident(>>).alertident", idmef.Get("alert.messageid")) + ca.Set("alert.correlation_alert.alertident(-1).analyzerid", idmef.Get("alert.analyzer(*).analyzerid")[-1]) + ca.Set("alert.classification.text", "IP source matching Dshield database") + ca.Set("alert.correlation_alert.name", "IP source matching Dshield database") + ca.Set("alert.assessment.impact.description", "Dshield gather IP addresses tagged from firewall logs drops") + ca.Set("alert.assessment.impact.severity", "high") + ca.alert() diff --git a/PreludeCorrelator/plugins/firewall.py b/PreludeCorrelator/plugins/firewall.py new file mode 100644 index 0000000..fd05eac --- /dev/null +++ b/PreludeCorrelator/plugins/firewall.py @@ -0,0 +1,52 @@ +# Copyright (C) 2009 PreludeIDS Technologies. All Rights Reserved. +# Author: Yoann Vandoorselaere <[email protected]> +# +# This file is part of the Prewikka program. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# 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 +# along with this program; see the file COPYING. If not, write to +# the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + +import re +from PreludeCorrelator import context +from PreludeCorrelator.pluginmanager import Plugin + +class FirewallPlugin(Plugin): + def run(self, idmef): + source = idmef.Get("alert.source(0).node.address(0).address") + sport = idmef.Get("alert.source(0).service.port", 0) + target = idmef.Get("alert.target(0).node.address(0).address") + dport = idmef.Get("alert.target(0).service.port", 0) + + if not source or not target: + return + + ctxname = "FIREWALL_" + source + str(sport) + target + str(dport) + + if idmef.match("alert.classification.text", re.compile("[Pp]acket [Dd]ropped|[Dd]enied")): + # Update context if any, removing the alert_on_expire attribute. + ctx = context.Context(ctxname, { "expire": 10 }, update = True) + else: + # Begins a timer for every event that contains a source and a target + # address which has not been matched by an observed packet denial. If a packet + # denial is not observed in the next 10 seconds, an event alert is generated. + + if not context.search(ctxname): + ctx = context.Context(ctxname, { "expire": 10, "alert_on_expire": True }) + ctx.Set("alert.source", idmef.Get("alert.source")) + ctx.Set("alert.target", idmef.Get("alert.target")) + ctx.Set("alert.assessment", idmef.Get("alert.assessment")) + ctx.Set("alert.classification", idmef.Get("alert.classification")) + ctx.Set("alert.correlation_alert.name", "Events to firewall correlation") + ctx.Set("alert.correlation_alert.alertident(0).analyzerid", idmef.Get("alert.analyzer(*).analyzerid")[-1]) + ctx.Set("alert.correlation_alert.alertident(0).alertident", idmef.Get("alert.messageid")) diff --git a/PreludeCorrelator/plugins/opensshauth.py b/PreludeCorrelator/plugins/opensshauth.py new file mode 100644 index 0000000..1872bc9 --- /dev/null +++ b/PreludeCorrelator/plugins/opensshauth.py @@ -0,0 +1,56 @@ +# Copyright (C) 2009 PreludeIDS Technologies. All Rights Reserved. +# Author: Sebastien Tricaud <[email protected]> +# Author: Yoann Vandoorselaere <[email protected]> +# +# This file is part of the Prelude-Correlator program. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# 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 +# along with this program; see the file COPYING. If not, write to +# the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + +from PreludeCorrelator.pluginmanager import Plugin +from PreludeCorrelator.context import Context + + +class OpenSSHAuthPlugin(Plugin): + def run(self, idmef): + if idmef.Get("alert.analyzer(-1).manufacturer") != "OpenSSH": + return + + if idmef.Get("alert.assessment.impact.completion") != "succeeded": + return + + try: + idx = idmef.Get("alert.additional_data(*).meaning").index("Authentication method") + except: + return + + data = idmef.Get("alert.additional_data(%d).data" % idx) + + for username in idmef.Get("alert.target(*).user.user_id(*).name"): + for target in idmef.Get("alert.target(*).node.address(*).address"): + ctx = Context("SSH_MAT_" + target + username, {"threshold": 1}, update = True) + ctx.Set("alert.source(>>)", idmef.Get("alert.source")) + ctx.Set("alert.target(>>)", idmef.Get("alert.target")) + ctx.Set("alert.correlation_alert.alertident(>>).alertident", idmef.Get("alert.messageid")) + ctx.Set("alert.correlation_alert.alertident(-1).analyzerid", idmef.Get("alert.analyzer(*).analyzerid")[-1]) + + if not hasattr(ctx, "authtype"): + ctx.authtype = data + elif ctx.authtype != data: + ctx.Set("alert.classification.text", "Multiple authentication methods") + ctx.Set("alert.correlation_alert.name", "Multiple authentication methods") + ctx.Set("alert.assessment.impact.severity", "medium") + ctx.Set("alert.assessment.impact.description", "Multiple ways of authenticating a single user have been found over SSH. If passphrase is the only allowed method, make sure you disable passwords.") + ctx.alert() + ctx.destroy() diff --git a/PreludeCorrelator/plugins/scan.py b/PreludeCorrelator/plugins/scan.py new file mode 100644 index 0000000..309ce7a --- /dev/null +++ b/PreludeCorrelator/plugins/scan.py @@ -0,0 +1,110 @@ +# Copyright (C) 2006 G Ramon Gomez <gene at gomezbrothers dot com> +# Copyright (C) 2009 PreludeIDS Technologies <[email protected]> +# +# This file is part of the Prelude-Correlator program. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# 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 +# along with this program; see the file COPYING. If not, write to +# the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + +# Detect Eventscan: +# Playing multiple events from a single host against another single host + +from PreludeCorrelator.context import Context +from PreludeCorrelator.pluginmanager import Plugin + +class EventScanPlugin(Plugin): + def run(self, idmef): + source = idmef.Get("alert.source(*).node.address(*).address") + target = idmef.Get("alert.target(*).node.address(*).address") + + if not source or not target: + return + + for saddr in source: + for daddr in target: + ctx = Context("SCAN_EVENTSCAN_" + saddr + daddr, { "expire": 60, "threshold": 30 }, update = True) + ctx.Set("alert.correlation_alert.alertident(>>).alertident", idmef.Get("alert.messageid")) + ctx.Set("alert.correlation_alert.alertident(-1).analyzerid", idmef.Get("alert.analyzer(*).analyzerid")[-1]) + ctx.Set("alert.source(>>)", idmef.Get("alert.source")) + ctx.Set("alert.target(>>)", idmef.Get("alert.target")) + + if ctx.CheckAndDecThreshold(): + 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: +# Playing the same event from a single host against multiple hosts +class EventSweepPlugin(Plugin): + def run(self, idmef): + classification = idmef.Get("alert.classification.text") + source = idmef.Get("alert.source(*).node.address(*).address") + target = idmef.Get("alert.target(*).node.address(*).address") + + if not source or not target or not classification: + return + + for saddr in source: + ctx = Context("SCAN_EVENTSWEEP_" + classification + saddr, { "expire": 60, "threshold": 30 }, update = True) + insert = True + + cur = ctx.Get("alert.target(*).node.address(*).address") + if cur: + for address in target: + if address in cur: + insert = False + break + + if insert: + ctx.Set("alert.source(>>)", idmef.Get("alert.source")) + ctx.Set("alert.target(>>)", idmef.Get("alert.target")) + ctx.Set("alert.correlation_alert.alertident(>>).alertident", idmef.Get("alert.messageid")) + ctx.Set("alert.correlation_alert.alertident(-1).analyzerid", idmef.Get("alert.analyzer(*).analyzerid")[-1]) + + 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() + + + + +# Detect Eventstorm: +# Playing excessive events by a single host +class EventStormPlugin(Plugin): + def run(self, idmef): + source = idmef.Get("alert.source(*).node.address(*).address") + if not source: + return + + for saddr in source: + ctx = Context("SCAN_EVENTSTORM_" + saddr, { "expire": 120, "threshold": 150 }, update = True) + + ctx.Set("alert.source(>>)", idmef.Get("alert.source")) + ctx.Set("alert.target(>>)", idmef.Get("alert.target")) + ctx.Set("alert.correlation_alert.alertident(>>).alertident", idmef.Get("alert.messageid")) + ctx.Set("alert.correlation_alert.alertident(-1).analyzerid", idmef.Get("alert.analyzer(*).analyzerid")[-1]) + + if ctx.CheckAndDecThreshold(): + 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() + diff --git a/PreludeCorrelator/plugins/worm.py b/PreludeCorrelator/plugins/worm.py new file mode 100644 index 0000000..6b90760 --- /dev/null +++ b/PreludeCorrelator/plugins/worm.py @@ -0,0 +1,58 @@ +# Copyright (C) 2006 G Ramon Gomez <gene at gomezbrothers dot com> +# Copyright (C) 2009 PreludeIDS Technologies <[email protected]> +# All Rights Reserved. +# +# This file is part of the Prelude-Correlator program. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# 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 +# along with this program; see the file COPYING. If not, write to +# the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + +# This rule looks for events against a host, records the messageid, then sets +# a timer of 600 seconds. If the host then replays the event against +# other hosts multiple times, an event is generated. + +from PreludeCorrelator import context +from PreludeCorrelator.pluginmanager import Plugin + +class WormPlugin(Plugin): + def run(self, idmef): + ctxt = idmef.Get("alert.classification.text") + if not ctxt: + return + + # Create context for classification combined with all the target. + for target in idmef.Get("alert.target(*).node.address(*).address"): + ctx = context.Context("WORM_HOST_" + ctxt + target, { "expire": 300, "threshold": 5 }, update = True) + + for source in idmef.Get("alert.source(*).node.address(*).address"): + # We are trying to see whether a previous target is now attacking other hosts + # thus, we check whether a context exist with this classification combined to + # this source. + ctx = context.search("WORM_HOST_" + ctxt + source) + if not ctx: + continue + + ctx.Set("alert.source(>>)", idmef.Get("alert.source")) + ctx.Set("alert.target(>>)", idmef.Get("alert.target")) + ctx.Set("alert.correlation_alert.alertident(>>).alertident", idmef.Get("alert.messageid")) + ctx.Set("alert.correlation_alert.alertident(-1).analyzerid", idmef.Get("alert.analyzer(*).analyzerid")[-1]) + + # Increase and check the context threshold. + if ctx.CheckAndDecThreshold(): + ctx.Set("alert.classification.text", "Possible Worm Activity") + ctx.Set("alert.correlation_alert.name", "Source host repeating actions taken against it recently") + ctx.Set("alert.assessment.impact.severity", "high") + ctx.Set("alert.assessment.impact.description", source + " has repeated actions taken against it recently at least 5 times. It may have been infected with a worm.") + ctx.alert() + ctx.destroy() diff --git a/ez_setup.py b/ez_setup.py new file mode 100644 index 0000000..d24e845 --- /dev/null +++ b/ez_setup.py @@ -0,0 +1,276 @@ +#!python +"""Bootstrap setuptools installation + +If you want to use setuptools in your package's setup.py, just include this +file in the same directory with it, and add this to the top of your setup.py:: + + from ez_setup import use_setuptools + use_setuptools() + +If you want to require a specific version of setuptools, set a download +mirror, or use an alternate download directory, you can do so by supplying +the appropriate options to ``use_setuptools()``. + +This file can also be run as a script to install or upgrade setuptools. +""" +import sys +DEFAULT_VERSION = "0.6c9" +DEFAULT_URL = "http://pypi.python.org/packages/%s/s/setuptools/" % sys.version[:3] + +md5_data = { + 'setuptools-0.6b1-py2.3.egg': '8822caf901250d848b996b7f25c6e6ca', + 'setuptools-0.6b1-py2.4.egg': 'b79a8a403e4502fbb85ee3f1941735cb', + 'setuptools-0.6b2-py2.3.egg': '5657759d8a6d8fc44070a9d07272d99b', + 'setuptools-0.6b2-py2.4.egg': '4996a8d169d2be661fa32a6e52e4f82a', + 'setuptools-0.6b3-py2.3.egg': 'bb31c0fc7399a63579975cad9f5a0618', + 'setuptools-0.6b3-py2.4.egg': '38a8c6b3d6ecd22247f179f7da669fac', + 'setuptools-0.6b4-py2.3.egg': '62045a24ed4e1ebc77fe039aa4e6f7e5', + 'setuptools-0.6b4-py2.4.egg': '4cb2a185d228dacffb2d17f103b3b1c4', + 'setuptools-0.6c1-py2.3.egg': 'b3f2b5539d65cb7f74ad79127f1a908c', + 'setuptools-0.6c1-py2.4.egg': 'b45adeda0667d2d2ffe14009364f2a4b', + 'setuptools-0.6c2-py2.3.egg': 'f0064bf6aa2b7d0f3ba0b43f20817c27', + 'setuptools-0.6c2-py2.4.egg': '616192eec35f47e8ea16cd6a122b7277', + 'setuptools-0.6c3-py2.3.egg': 'f181fa125dfe85a259c9cd6f1d7b78fa', + 'setuptools-0.6c3-py2.4.egg': 'e0ed74682c998bfb73bf803a50e7b71e', + 'setuptools-0.6c3-py2.5.egg': 'abef16fdd61955514841c7c6bd98965e', + 'setuptools-0.6c4-py2.3.egg': 'b0b9131acab32022bfac7f44c5d7971f', + 'setuptools-0.6c4-py2.4.egg': '2a1f9656d4fbf3c97bf946c0a124e6e2', + 'setuptools-0.6c4-py2.5.egg': '8f5a052e32cdb9c72bcf4b5526f28afc', + 'setuptools-0.6c5-py2.3.egg': 'ee9fd80965da04f2f3e6b3576e9d8167', + 'setuptools-0.6c5-py2.4.egg': 'afe2adf1c01701ee841761f5bcd8aa64', + 'setuptools-0.6c5-py2.5.egg': 'a8d3f61494ccaa8714dfed37bccd3d5d', + 'setuptools-0.6c6-py2.3.egg': '35686b78116a668847237b69d549ec20', + 'setuptools-0.6c6-py2.4.egg': '3c56af57be3225019260a644430065ab', + 'setuptools-0.6c6-py2.5.egg': 'b2f8a7520709a5b34f80946de5f02f53', + 'setuptools-0.6c7-py2.3.egg': '209fdf9adc3a615e5115b725658e13e2', + 'setuptools-0.6c7-py2.4.egg': '5a8f954807d46a0fb67cf1f26c55a82e', + 'setuptools-0.6c7-py2.5.egg': '45d2ad28f9750e7434111fde831e8372', + 'setuptools-0.6c8-py2.3.egg': '50759d29b349db8cfd807ba8303f1902', + 'setuptools-0.6c8-py2.4.egg': 'cba38d74f7d483c06e9daa6070cce6de', + 'setuptools-0.6c8-py2.5.egg': '1721747ee329dc150590a58b3e1ac95b', + 'setuptools-0.6c9-py2.3.egg': 'a83c4020414807b496e4cfbe08507c03', + 'setuptools-0.6c9-py2.4.egg': '260a2be2e5388d66bdaee06abec6342a', + 'setuptools-0.6c9-py2.5.egg': 'fe67c3e5a17b12c0e7c541b7ea43a8e6', + 'setuptools-0.6c9-py2.6.egg': 'ca37b1ff16fa2ede6e19383e7b59245a', +} + +import sys, os +try: from hashlib import md5 +except ImportError: from md5 import md5 + +def _validate_md5(egg_name, data): + if egg_name in md5_data: + digest = md5(data).hexdigest() + if digest != md5_data[egg_name]: + print >>sys.stderr, ( + "md5 validation of %s failed! (Possible download problem?)" + % egg_name + ) + sys.exit(2) + return data + +def use_setuptools( + version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir, + download_delay=15 +): + """Automatically find/download setuptools and make it available on sys.path + + `version` should be a valid setuptools version number that is available + as an egg for download under the `download_base` URL (which should end with + a '/'). `to_dir` is the directory where setuptools will be downloaded, if + it is not already available. If `download_delay` is specified, it should + be the number of seconds that will be paused before initiating a download, + should one be required. If an older version of setuptools is installed, + this routine will print a message to ``sys.stderr`` and raise SystemExit in + an attempt to abort the calling script. + """ + was_imported = 'pkg_resources' in sys.modules or 'setuptools' in sys.modules + def do_download(): + egg = download_setuptools(version, download_base, to_dir, download_delay) + sys.path.insert(0, egg) + import setuptools; setuptools.bootstrap_install_from = egg + try: + import pkg_resources + except ImportError: + return do_download() + try: + pkg_resources.require("setuptools>="+version); return + except pkg_resources.VersionConflict, e: + if was_imported: + print >>sys.stderr, ( + "The required version of setuptools (>=%s) is not available, and\n" + "can't be installed while this script is running. Please install\n" + " a more recent version first, using 'easy_install -U setuptools'." + "\n\n(Currently using %r)" + ) % (version, e.args[0]) + sys.exit(2) + else: + del pkg_resources, sys.modules['pkg_resources'] # reload ok + return do_download() + except pkg_resources.DistributionNotFound: + return do_download() + +def download_setuptools( + version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir, + delay = 15 +): + """Download setuptools from a specified location and return its filename + + `version` should be a valid setuptools version number that is available + as an egg for download under the `download_base` URL (which should end + with a '/'). `to_dir` is the directory where the egg will be downloaded. + `delay` is the number of seconds to pause before an actual download attempt. + """ + import urllib2, shutil + egg_name = "setuptools-%s-py%s.egg" % (version,sys.version[:3]) + url = download_base + egg_name + saveto = os.path.join(to_dir, egg_name) + src = dst = None + if not os.path.exists(saveto): # Avoid repeated downloads + try: + from distutils import log + if delay: + log.warn(""" +--------------------------------------------------------------------------- +This script requires setuptools version %s to run (even to display +help). I will attempt to download it for you (from +%s), but +you may need to enable firewall access for this script first. +I will start the download in %d seconds. + +(Note: if this machine does not have network access, please obtain the file + + %s + +and place it in this directory before rerunning this script.) +---------------------------------------------------------------------------""", + version, download_base, delay, url + ); from time import sleep; sleep(delay) + log.warn("Downloading %s", url) + src = urllib2.urlopen(url) + # Read/write all in one block, so we don't create a corrupt file + # if the download is interrupted. + data = _validate_md5(egg_name, src.read()) + dst = open(saveto,"wb"); dst.write(data) + finally: + if src: src.close() + if dst: dst.close() + return os.path.realpath(saveto) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +def main(argv, version=DEFAULT_VERSION): + """Install or upgrade setuptools and EasyInstall""" + try: + import setuptools + except ImportError: + egg = None + try: + egg = download_setuptools(version, delay=0) + sys.path.insert(0,egg) + from setuptools.command.easy_install import main + return main(list(argv)+[egg]) # we're done here + finally: + if egg and os.path.exists(egg): + os.unlink(egg) + else: + if setuptools.__version__ == '0.0.1': + print >>sys.stderr, ( + "You have an obsolete version of setuptools installed. Please\n" + "remove it from your system entirely before rerunning this script." + ) + sys.exit(2) + + req = "setuptools>="+version + import pkg_resources + try: + pkg_resources.require(req) + except pkg_resources.VersionConflict: + try: + from setuptools.command.easy_install import main + except ImportError: + from easy_install import main + main(list(argv)+[download_setuptools(delay=0)]) + sys.exit(0) # try to force an exit + else: + if argv: + from setuptools.command.easy_install import main + main(argv) + else: + print "Setuptools version",version,"or greater has been installed." + print '(Run "ez_setup.py -U setuptools" to reinstall or upgrade.)' + +def update_md5(filenames): + """Update our built-in md5 registry""" + + import re + + for name in filenames: + base = os.path.basename(name) + f = open(name,'rb') + md5_data[base] = md5(f.read()).hexdigest() + f.close() + + data = [" %r: %r,\n" % it for it in md5_data.items()] + data.sort() + repl = "".join(data) + + import inspect + srcfile = inspect.getsourcefile(sys.modules[__name__]) + f = open(srcfile, 'rb'); src = f.read(); f.close() + + match = re.search("\nmd5_data = {\n([^}]+)}", src) + if not match: + print >>sys.stderr, "Internal error!" + sys.exit(2) + + src = src[:match.start(1)] + repl + src[match.end(1):] + f = open(srcfile,'w') + f.write(src) + f.close() + + +if __name__=='__main__': + if len(sys.argv)>2 and sys.argv[1]=='--md5update': + update_md5(sys.argv[2:]) + else: + main(sys.argv[1:]) + + + + + + diff --git a/prelude_correlator.egg-info/PKG-INFO b/prelude_correlator.egg-info/PKG-INFO new file mode 100644 index 0000000..cac97ff --- /dev/null +++ b/prelude_correlator.egg-info/PKG-INFO @@ -0,0 +1,34 @@ +Metadata-Version: 1.0 +Name: prelude-correlator +Version: 0.1 +Summary: Prelude-Correlator perform real time correlation of events received by Prelude +Home-page: http://www.prelude-ids.com +Author: Yoann Vandoorselaere +Author-email: [email protected] +License: UNKNOWN +Download-URL: http://www.prelude-ids.com/development/download/ +Description: + Prelude-Correlator perform real time correlation of events received by Prelude. + + Several isolated alerts, generated from different sensors, can thus + trigger a single CorrelationAlert should the events be related. This + CorrelationAlert then appears within the Prewikka interface and + indicates the potential target information via the set of correlation + rules. + + Signature creation with Prelude-Correlator is based on the Python + programming language. Prelude's integrated correlation engine is + distributed with a default set of correlation rules, yet you still + have the opportunity to modify and create any correlation rule that + suits your needs. + +Platform: UNKNOWN +Classifier: Development Status :: 4 - Beta +Classifier: Environment :: Console +Classifier: Intended Audience :: System Administrators +Classifier: License :: OSI Approved :: GNU General Public License (GPL) +Classifier: Natural Language :: English +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Topic :: Security +Classifier: Topic :: System :: Monitoring diff --git a/prelude_correlator.egg-info/SOURCES.txt b/prelude_correlator.egg-info/SOURCES.txt new file mode 100644 index 0000000..f2c3567 --- /dev/null +++ b/prelude_correlator.egg-info/SOURCES.txt @@ -0,0 +1,23 @@ +README +ez_setup.py +setup.py +PreludeCorrelator/__init__.py +PreludeCorrelator/context.py +PreludeCorrelator/idmef.py +PreludeCorrelator/main.py +PreludeCorrelator/pluginmanager.py +PreludeCorrelator/siteconfig.py +PreludeCorrelator/utils.py +PreludeCorrelator/plugins/__init__.py +PreludeCorrelator/plugins/bruteforce.py +PreludeCorrelator/plugins/businesshour.py +PreludeCorrelator/plugins/dshield.py +PreludeCorrelator/plugins/firewall.py +PreludeCorrelator/plugins/opensshauth.py +PreludeCorrelator/plugins/scan.py +PreludeCorrelator/plugins/worm.py +prelude_correlator.egg-info/PKG-INFO +prelude_correlator.egg-info/SOURCES.txt +prelude_correlator.egg-info/dependency_links.txt +prelude_correlator.egg-info/entry_points.txt +prelude_correlator.egg-info/top_level.txt \ No newline at end of file diff --git a/prelude_correlator.egg-info/dependency_links.txt b/prelude_correlator.egg-info/dependency_links.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/prelude_correlator.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/prelude_correlator.egg-info/entry_points.txt b/prelude_correlator.egg-info/entry_points.txt new file mode 100644 index 0000000..c1f83c7 --- /dev/null +++ b/prelude_correlator.egg-info/entry_points.txt @@ -0,0 +1,14 @@ +[PreludeCorrelator.plugins] +OpenSSHAuthPlugin = PreludeCorrelator.plugins.opensshauth:OpenSSHAuthPlugin +EventSweepPlugin = PreludeCorrelator.plugins.scan:EventSweepPlugin +BusinessHourPlugin = PreludeCorrelator.plugins.businesshour:BusinessHourPlugin +WormPlugin = PreludeCorrelator.plugins.worm:WormPlugin +FirewallPlugin = PreludeCorrelator.plugins.firewall:FirewallPlugin +BruteForcePlugin = PreludeCorrelator.plugins.bruteforce:BruteForcePlugin +EventStormPlugin = PreludeCorrelator.plugins.scan:EventStormPlugin +DshieldPlugin = PreludeCorrelator.plugins.dshield:DshieldPlugin +EventScanPlugin = PreludeCorrelator.plugins.scan:EventScanPlugin + +[console_scripts] +prelude-correlator = PreludeCorrelator.main:main + diff --git a/prelude_correlator.egg-info/top_level.txt b/prelude_correlator.egg-info/top_level.txt new file mode 100644 index 0000000..b386f92 --- /dev/null +++ b/prelude_correlator.egg-info/top_level.txt @@ -0,0 +1 @@ +PreludeCorrelator diff --git a/ruleset/brute-force.py b/ruleset/brute-force.py deleted file mode 100644 index dfa4e69..0000000 --- a/ruleset/brute-force.py +++ /dev/null @@ -1,74 +0,0 @@ -# Copyright (C) 2006 G Ramon Gomez <gene at gomezbrothers dot com> -# Copyright (C) 2009 PreludeIDS Technologies <[email protected]> -# -# This file is part of the Prelude-Correlator program. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2, or (at your option) -# any later version. -# -# 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 -# along with this program; see the file COPYING. If not, write to -# the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - -import re -from PreludeCorrelator.plugins import Plugin -from PreludeCorrelator.context import Context - -class BrutePlugin(Plugin): - def _BruteForce(self, idmef): - sadd = idmef.Get("alert.source(*).node.address(*).address") - tadd = idmef.Get("alert.target(*).node.address(*).address") - if not sadd or not tadd: - return - - for source in sadd: - for target in tadd: - ctx = Context("BRUTE_ST_" + source + target, { "expire": 2, "threshold": 5 }, update = True) - ctx.Set("alert.source(>>)", idmef.Get("alert.source")) - ctx.Set("alert.target(>>)", idmef.Get("alert.target")) - ctx.Set("alert.correlation_alert.alertident(>>).alertident", idmef.Get("alert.messageid")) - ctx.Set("alert.correlation_alert.alertident(-1).analyzerid", idmef.Get("alert.analyzer(*).analyzerid")[-1]) - - if ctx.CheckAndDecThreshold(): - ctx.Set("alert.classification.text", "Brute force attack") - ctx.Set("alert.correlation_alert.name", "Multiple failed login") - ctx.Set("alert.assessment.impact.severity", "high") - ctx.Set("alert.assessment.impact.description", "Multiple failed attempts have been made to login to a user account") - ctx.alert() - ctx.destroy() - - def _BruteUserForce(self, idmef): - userid = idmef.Get("alert.target(*).user.user_id(*).name"); - if not userid: - return - - for user in userid: - ctx = Context("BRUTE_U_" + user, { "expire": 120, "threshold": 2 }, update = True) - ctx.Set("alert.source(>>)", idmef.Get("alert.source")) - ctx.Set("alert.target(>>)", idmef.Get("alert.target")) - ctx.Set("alert.correlation_alert.alertident(>>).alertident", idmef.Get("alert.messageid")) - ctx.Set("alert.correlation_alert.alertident(-1).analyzerid", idmef.Get("alert.analyzer(*).analyzerid")[-1]) - - if ctx.CheckAndDecThreshold(): - ctx.Set("alert.classification.text", "Brute force attack") - ctx.Set("alert.correlation_alert.name", "Multiple failed login") - ctx.Set("alert.assessment.impact.severity", "high") - ctx.Set("alert.assessment.impact.description", "Multiple failed attempts have been made to login to a user account") - ctx.alert() - ctx.destroy() - - - def run(self, idmef): - if not idmef.match("alert.classification.text", re.compile("[Ll]ogin|[Aa]uthentication"), - "alert.assessment.impact.completion", "failed"): - return - - self._BruteForce() - self._BruteUserForce() diff --git a/ruleset/business-hour.py b/ruleset/business-hour.py deleted file mode 100644 index db4f6cb..0000000 --- a/ruleset/business-hour.py +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright (C) 2009 PreludeIDS Technologies. All Rights Reserved. -# Author: Yoann Vandoorselaere <[email protected]> -# -# This file is part of the Prelude-Correlator program. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2, or (at your option) -# any later version. -# -# 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 -# along with this program; see the file COPYING. If not, write to -# the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - -import time -from PreludeCorrelator.idmef import IDMEF -from PreludeCorrelator.plugins import Plugin - -# Alert only on saturday and sunday, and everyday from 6:00pm to 9:00am. - -class BusinessHourPlugin(Plugin): - def run(self, idmef): - - t = time.localtime(int(idmef.Get("alert.create_time"))) - - if not (t.tm_wday == 5 or t.tm_wday == 6 or t.tm_hour < 9 or t.tm_hour > 17): - return - - if idmef.Get("alert.assessment.impact.completion") != "succeeded": - return - - ca = IDMEF() - ca.Set("alert.source", idmef.Get("alert.source")) - ca.Set("alert.target", idmef.Get("alert.target")) - ca.Set("alert.classification", idmef.Get("alert.classification")) - ca.Set("alert.correlation_alert.alertident(>>).alertident", idmef.Get("alert.messageid")) - ca.Set("alert.correlation_alert.alertident(-1).analyzerid", idmef.Get("alert.analyzer(*).analyzerid")[-1]) - ca.Set("alert.correlation_alert.name", "Critical system activity on day off") - ca.alert() diff --git a/ruleset/dshield.py b/ruleset/dshield.py deleted file mode 100644 index 24d0780..0000000 --- a/ruleset/dshield.py +++ /dev/null @@ -1,89 +0,0 @@ -# Copyright (C) 2009 PreludeIDS Technologies. All Rights Reserved. -# Author: Yoann Vandoorselaere <[email protected]> -# Author: Sebastien Tricaud <[email protected]> -# -# This file is part of the Prelude-Correlator program. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2, or (at your option) -# any later version. -# -# 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 -# along with this program; see the file COPYING. If not, write to -# the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - -import os, httplib, time -from PreludeCorrelator import siteconfig -from PreludeCorrelator.idmef import IDMEF -from PreludeCorrelator.plugins import Plugin -from PreludeCorrelator.context import Context, Timer - - -class DshieldPlugin(Plugin): - DSHIELD_RELOAD = 7 * 24 * 60 * 60 - DSHIELD_SERVER = "www.dshield.org" - DSHIELD_URI = "www.dshield.org/ipsascii.html?limit=10000" - - def __loadData(self, fname, age=0): - cnt = 0 - self.__iphash.clear() - - for line in open(fname, "r"): - if line[0] != '#': - self.__iphash[line.split('\t')[0]] = True - cnt = cnt + 1 - - Timer(self.__reload - age, self.__retrieveData) - - def __retrieveData(self, timer=None): - fname = siteconfig.lib_dir + "/dshield.dat" - - try: - st = os.stat(fname) - if time.time() - st.st_mtime < self.__reload: - return self.__loadData(fname, time.time() - st.st_mtime) - except: - pass - - print("Downloading host list from dshield, this might take some time...") - con = httplib.HTTPConnection(self.__server) - con.request("GET", self.__uri) - r = con.getresponse() - if r.status != 200: - return - - fd = open(fname, "w") - fd.write(r.read()) - fd.close() - - print("Downloading done, processing data.") - self.__loadData(fname) - - - def __init__(self): - self.__iphash = { } - self.__reload = self.getConfigValue("reload", self.DSHIELD_RELOAD) - self.__server = self.getConfigValue("server", self.DSHIELD_SERVER) - self.__uri = self.getConfigValue("uri", self.DSHIELD_URI) - - self.__retrieveData() - - def run(self, idmef): - for source in idmef.Get("alert.source(*).node.address(*).address"): - if self.__iphash.has_key(source): - ca = IDMEF() - ca.Set("alert.source(>>)", idmef.Get("alert.source")) - ca.Set("alert.target(>>)", idmef.Get("alert.target")) - ca.Set("alert.correlation_alert.alertident(>>).alertident", idmef.Get("alert.messageid")) - ca.Set("alert.correlation_alert.alertident(-1).analyzerid", idmef.Get("alert.analyzer(*).analyzerid")[-1]) - ca.Set("alert.classification.text", "IP source matching Dshield database") - ca.Set("alert.correlation_alert.name", "IP source matching Dshield database") - ca.Set("alert.assessment.impact.description", "Dshield gather IP addresses tagged from firewall logs drops") - ca.Set("alert.assessment.impact.severity", "high") - ca.alert() diff --git a/ruleset/firewall.py b/ruleset/firewall.py deleted file mode 100644 index 9d9bc43..0000000 --- a/ruleset/firewall.py +++ /dev/null @@ -1,52 +0,0 @@ -# Copyright (C) 2009 PreludeIDS Technologies. All Rights Reserved. -# Author: Yoann Vandoorselaere <[email protected]> -# -# This file is part of the Prewikka program. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2, or (at your option) -# any later version. -# -# 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 -# along with this program; see the file COPYING. If not, write to -# the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - -import re -from PreludeCorrelator import context -from PreludeCorrelator.plugins import Plugin - -class FirewallPlugin(Plugin): - def run(self, idmef): - source = idmef.Get("alert.source(0).node.address(0).address") - sport = idmef.Get("alert.source(0).service.port", 0) - target = idmef.Get("alert.target(0).node.address(0).address") - dport = idmef.Get("alert.target(0).service.port", 0) - - if not source or not target: - return - - ctxname = "FIREWALL_" + source + str(sport) + target + str(dport) - - if idmef.match("alert.classification.text", re.compile("[Pp]acket [Dd]ropped|[Dd]enied")): - # Update context if any, removing the alert_on_expire attribute. - ctx = context.Context(ctxname, { "expire": 10 }, update = True) - else: - # Begins a timer for every event that contains a source and a target - # address which has not been matched by an observed packet denial. If a packet - # denial is not observed in the next 10 seconds, an event alert is generated. - - if not context.search(ctxname): - ctx = context.Context(ctxname, { "expire": 10, "alert_on_expire": True }) - ctx.Set("alert.source", idmef.Get("alert.source")) - ctx.Set("alert.target", idmef.Get("alert.target")) - ctx.Set("alert.assessment", idmef.Get("alert.assessment")) - ctx.Set("alert.classification", idmef.Get("alert.classification")) - ctx.Set("alert.correlation_alert.name", "Events to firewall correlation") - ctx.Set("alert.correlation_alert.alertident(0).analyzerid", idmef.Get("alert.analyzer(*).analyzerid")[-1]) - ctx.Set("alert.correlation_alert.alertident(0).alertident", idmef.Get("alert.messageid")) diff --git a/ruleset/openssh-multiple-authtypes.py b/ruleset/openssh-multiple-authtypes.py deleted file mode 100644 index 1b98a29..0000000 --- a/ruleset/openssh-multiple-authtypes.py +++ /dev/null @@ -1,56 +0,0 @@ -# Copyright (C) 2009 PreludeIDS Technologies. All Rights Reserved. -# Author: Sebastien Tricaud <[email protected]> -# Author: Yoann Vandoorselaere <[email protected]> -# -# This file is part of the Prelude-Correlator program. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2, or (at your option) -# any later version. -# -# 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 -# along with this program; see the file COPYING. If not, write to -# the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - -from PreludeCorrelator.plugins import Plugin -from PreludeCorrelator.context import Context - - -class OpenSSHMultipleAuthTypesPlugin(Plugin): - def run(self, idmef): - if idmef.Get("alert.analyzer(-1).manufacturer") != "OpenSSH": - return - - if idmef.Get("alert.assessment.impact.completion") != "succeeded": - return - - try: - idx = idmef.Get("alert.additional_data(*).meaning").index("Authentication method") - except: - return - - data = idmef.Get("alert.additional_data(%d).data" % idx) - - for username in idmef.Get("alert.target(*).user.user_id(*).name"): - for target in idmef.Get("alert.target(*).node.address(*).address"): - ctx = Context("SSH_MAT_" + target + username, {"threshold": 1}, update = True) - ctx.Set("alert.source(>>)", idmef.Get("alert.source")) - ctx.Set("alert.target(>>)", idmef.Get("alert.target")) - ctx.Set("alert.correlation_alert.alertident(>>).alertident", idmef.Get("alert.messageid")) - ctx.Set("alert.correlation_alert.alertident(-1).analyzerid", idmef.Get("alert.analyzer(*).analyzerid")[-1]) - - if not hasattr(ctx, "authtype"): - ctx.authtype = data - elif ctx.authtype != data: - ctx.Set("alert.classification.text", "Multiple authentication methods") - ctx.Set("alert.correlation_alert.name", "Multiple authentication methods") - ctx.Set("alert.assessment.impact.severity", "medium") - ctx.Set("alert.assessment.impact.description", "Multiple ways of authenticating a single user have been found over SSH. If passphrase is the only allowed method, make sure you disable passwords.") - ctx.alert() - ctx.destroy() diff --git a/ruleset/scan.py b/ruleset/scan.py deleted file mode 100644 index cf8bc10..0000000 --- a/ruleset/scan.py +++ /dev/null @@ -1,110 +0,0 @@ -# Copyright (C) 2006 G Ramon Gomez <gene at gomezbrothers dot com> -# Copyright (C) 2009 PreludeIDS Technologies <[email protected]> -# -# This file is part of the Prelude-Correlator program. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2, or (at your option) -# any later version. -# -# 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 -# along with this program; see the file COPYING. If not, write to -# the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - -# Detect Eventscan: -# Playing multiple events from a single host against another single host - -from PreludeCorrelator.context import Context -from PreludeCorrelator.plugins import Plugin - -class EventScanPlugin(Plugin): - def run(self, idmef): - source = idmef.Get("alert.source(*).node.address(*).address") - target = idmef.Get("alert.target(*).node.address(*).address") - - if not source or not target: - return - - for saddr in source: - for daddr in target: - ctx = Context("SCAN_EVENTSCAN_" + saddr + daddr, { "expire": 60, "threshold": 30 }, update = True) - ctx.Set("alert.correlation_alert.alertident(>>).alertident", idmef.Get("alert.messageid")) - ctx.Set("alert.correlation_alert.alertident(-1).analyzerid", idmef.Get("alert.analyzer(*).analyzerid")[-1]) - ctx.Set("alert.source(>>)", idmef.Get("alert.source")) - ctx.Set("alert.target(>>)", idmef.Get("alert.target")) - - if ctx.CheckAndDecThreshold(): - 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: -# Playing the same event from a single host against multiple hosts -class EventSweepPlugin(Plugin): - def run(self, idmef): - classification = idmef.Get("alert.classification.text") - source = idmef.Get("alert.source(*).node.address(*).address") - target = idmef.Get("alert.target(*).node.address(*).address") - - if not source or not target or not classification: - return - - for saddr in source: - ctx = Context("SCAN_EVENTSWEEP_" + classification + saddr, { "expire": 60, "threshold": 30 }, update = True) - insert = True - - cur = ctx.Get("alert.target(*).node.address(*).address") - if cur: - for address in target: - if address in cur: - insert = False - break - - if insert: - ctx.Set("alert.source(>>)", idmef.Get("alert.source")) - ctx.Set("alert.target(>>)", idmef.Get("alert.target")) - ctx.Set("alert.correlation_alert.alertident(>>).alertident", idmef.Get("alert.messageid")) - ctx.Set("alert.correlation_alert.alertident(-1).analyzerid", idmef.Get("alert.analyzer(*).analyzerid")[-1]) - - 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() - - - - -# Detect Eventstorm: -# Playing excessive events by a single host -class EventStormPlugin(Plugin): - def run(self, idmef): - source = idmef.Get("alert.source(*).node.address(*).address") - if not source: - return - - for saddr in source: - ctx = Context("SCAN_EVENTSTORM_" + saddr, { "expire": 120, "threshold": 150 }, update = True) - - ctx.Set("alert.source(>>)", idmef.Get("alert.source")) - ctx.Set("alert.target(>>)", idmef.Get("alert.target")) - ctx.Set("alert.correlation_alert.alertident(>>).alertident", idmef.Get("alert.messageid")) - ctx.Set("alert.correlation_alert.alertident(-1).analyzerid", idmef.Get("alert.analyzer(*).analyzerid")[-1]) - - if ctx.CheckAndDecThreshold(): - 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() - diff --git a/ruleset/worm.py b/ruleset/worm.py deleted file mode 100644 index 15fcd64..0000000 --- a/ruleset/worm.py +++ /dev/null @@ -1,58 +0,0 @@ -# Copyright (C) 2006 G Ramon Gomez <gene at gomezbrothers dot com> -# Copyright (C) 2009 PreludeIDS Technologies <[email protected]> -# All Rights Reserved. -# -# This file is part of the Prelude-Correlator program. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2, or (at your option) -# any later version. -# -# 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 -# along with this program; see the file COPYING. If not, write to -# the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - -# This rule looks for events against a host, records the messageid, then sets -# a timer of 600 seconds. If the host then replays the event against -# other hosts multiple times, an event is generated. - -from PreludeCorrelator import context -from PreludeCorrelator.plugins import Plugin - -class WormPlugin(Plugin): - def run(self, idmef): - ctxt = idmef.Get("alert.classification.text") - if not ctxt: - return - - # Create context for classification combined with all the target. - for target in idmef.Get("alert.target(*).node.address(*).address"): - ctx = context.Context("WORM_HOST_" + ctxt + target, { "expire": 300, "threshold": 5 }, update = True) - - for source in idmef.Get("alert.source(*).node.address(*).address"): - # We are trying to see whether a previous target is now attacking other hosts - # thus, we check whether a context exist with this classification combined to - # this source. - ctx = context.search("WORM_HOST_" + ctxt + source) - if not ctx: - continue - - ctx.Set("alert.source(>>)", idmef.Get("alert.source")) - ctx.Set("alert.target(>>)", idmef.Get("alert.target")) - ctx.Set("alert.correlation_alert.alertident(>>).alertident", idmef.Get("alert.messageid")) - ctx.Set("alert.correlation_alert.alertident(-1).analyzerid", idmef.Get("alert.analyzer(*).analyzerid")[-1]) - - # Increase and check the context threshold. - if ctx.CheckAndDecThreshold(): - ctx.Set("alert.classification.text", "Possible Worm Activity") - ctx.Set("alert.correlation_alert.name", "Source host repeating actions taken against it recently") - ctx.Set("alert.assessment.impact.severity", "high") - ctx.Set("alert.assessment.impact.description", source + " has repeated actions taken against it recently at least 5 times. It may have been infected with a worm.") - ctx.alert() - ctx.destroy() diff --git a/scripts/prelude-correlator b/scripts/prelude-correlator deleted file mode 100755 index 94b1c98..0000000 --- a/scripts/prelude-correlator +++ /dev/null @@ -1,153 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (C) 2009 PreludeIDS Technologies. All Rights Reserved. -# Author: Yoann Vandoorselaere <[email protected]> -# -# This file is part of the Prewikka program. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2, or (at your option) -# any later version. -# -# 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 -# along with this program; see the file COPYING. If not, write to -# the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - -import sys, os, time, signal -from PreludeCorrelator import idmef, plugins, context, siteconfig -from optparse import OptionParser -from PreludeEasy import ClientEasy, CheckVersion - -if not CheckVersion(siteconfig.libprelude_required_version): - raise Exception, ("Libprelude version '%s' is required" % siteconfig.libprelude_required_version) - -class PreludeClient: - def __init__(self, print_input=None, print_output=None, dry_run=False): - self._message_processed = 0 - self._alert_generated = 0 - self._print_input = print_input - self._print_output = print_output - self._continue = True - self._dry_run = dry_run - - self._pm = plugins.PluginManager() - print ("%d plugin have been loaded." % (self._pm.getPluginCount())) - - self._client = ClientEasy("prelude-correlator", ClientEasy.PERMISSION_IDMEF_READ|ClientEasy.PERMISSION_IDMEF_WRITE, - "Prelude-Correlator", "Correlator", "PreludeIDS Technologies", siteconfig.version) - self._client.Start() - - - def _handle_event(self, idmef): - if self._print_input: - self._print_input.write(str(idmef)) - - self._pm.run(idmef) - self._message_processed += 1 - - def stats(self): - print("%d message received, %d correlationAlert generated." % (self._message_processed, self._alert_generated)) - - def correlationAlert(self, idmef): - self._alert_generated = self._alert_generated + 1 - - if not self._dry_run: - self._client.SendIDMEF(idmef) - - if self._print_output: - self._print_output.write(str(idmef)) - - def recvEvent(self): - msg = idmef.IDMEF() - - last = time.time() - while self._continue: - try: - r = self._client.RecvIDMEF(msg, 1000) - except: - r = 0 - - if r: - if msg.Get("alert.create_time"): - self._handle_event(msg) - msg.reset() - - now = time.time() - if now - last >= 1: - context.wakeup(now) - last = now - - def stop(self): - self._continue = False - -parser = OptionParser(usage="%prog", version="%%prog %s" % siteconfig.version) -parser.add_option("-c", "--config", action="store", dest="config", type="string", help="Configuration file to use", metavar="FILE") -parser.add_option("", "--dry-run", action="store_true", dest="dry_run", help="No report to the specified Manager will occur", default=False) -parser.add_option("-d", "--daemon", action="store_true", dest="daemon", help="Run in daemon mode") -parser.add_option("-P", "--pidfile", action="store", dest="pidfile", type="string", help="Write Prelude Correlator PID to specified file", metavar="FILE") -parser.add_option("", "--print-input", action="store", dest="print_input", type="string", help="Dump alert input from manager to the specified file", metavar="FILE") -parser.add_option("", "--print-output", action="store", dest="print_output", type="string", help="Dump alert output to the specified file", metavar="FILE") -parser.add_option("--debug", action="store", dest="debug", type="int", help="Enable debug ouptut (optional debug level argument)", metavar="LEVEL") -(options, args) = parser.parse_args() - -ifd = None -if options.print_input: - if options.print_input == "-": - ifd = sys.stdout - else: - ifd = open(options.print_input, "w") - -ofd = None -if options.print_output: - if options.print_output == "-": - ofd = sys.stdout - else: - ofd = open(options.print_output, "w") - -if options.daemon: - if os.fork(): - os._exit(0) - - os.setsid() - if os.fork(): - os._exit(0) - - os.umask(077) - - fd = os.open('/dev/null', os.O_RDWR) - for i in xrange(3): - os.dup2(fd, i) - - os.close(fd) - if options.pidfile: - open(pidfile, "w").write(str(os.getpid())) - - -def handle_signal(signum, frame): - print 'Signal handler called with signal', signum - if signum == signal.SIGQUIT: - prelude_client.stats() - context.stats() - else: - prelude_client.stop() - -signal.signal(signal.SIGTERM, handle_signal) -signal.signal(signal.SIGINT, handle_signal) -signal.signal(signal.SIGQUIT, handle_signal) - -# restore previous context. -context.load() - -prelude_client = PreludeClient(print_input=ifd, print_output=ofd, dry_run=options.dry_run) -idmef.set_prelude_client(prelude_client) -prelude_client.recvEvent() - -# save existing context -context.save() - diff --git a/setup.py b/setup.py index a231436..8f2bad1 100644 --- a/setup.py +++ b/setup.py @@ -1,8 +1,11 @@ #!/usr/bin/env python -import os, glob -from distutils.command.install import install -from distutils.core import setup +from ez_setup import use_setuptools +use_setuptools() + +import os +from setuptools import setup, find_packages +from setuptools.command.install import install PRELUDE_CORRELATOR_VERSION = "0.1" LIBPRELUDE_REQUIRED_VERSION = "0.9.23" @@ -23,18 +26,64 @@ class my_install(install): def init_siteconfig(self): config = open("PreludeCorrelator/siteconfig.py", "w") print >> config, "conf_dir = '%s'" % os.path.abspath(self.conf_prefix) - print >> config, "ruleset_dir = '%s'" % os.path.abspath(self.conf_prefix + "/ruleset") print >> config, "lib_dir = '%s'" % os.path.abspath(self.prefix + "/var/lib/prelude-correlator") - print >> config, "version = '%s'" % PRELUDE_CORRELATOR_VERSION print >> config, "libprelude_required_version = '%s'" % LIBPRELUDE_REQUIRED_VERSION config.close() -setup(name="prelude-corelator", - version=PRELUDE_CORRELATOR_VERSION, - maintainer = "Yoann Vandoorselaere", - maintainer_email = "[email protected]", - url = "http://www.prelude-ids.com", - packages=[ 'PreludeCorrelator'], - data_files=[('etc/prelude-correlator/ruleset', glob.glob("ruleset/*.py"))], - scripts=[ "scripts/prelude-correlator" ], - cmdclass={ 'install': my_install }) + +setup( + name="prelude-correlator", + version=PRELUDE_CORRELATOR_VERSION, + maintainer = "Yoann Vandoorselaere", + maintainer_email = "[email protected]", + author = "Yoann Vandoorselaere", + author_email = "[email protected]", + url = "http://www.prelude-ids.com", + download_url = "http://www.prelude-ids.com/development/download/", + description = "Prelude-Correlator perform real time correlation of events received by Prelude", + long_description = """ +Prelude-Correlator perform real time correlation of events received by Prelude. + +Several isolated alerts, generated from different sensors, can thus +trigger a single CorrelationAlert should the events be related. This +CorrelationAlert then appears within the Prewikka interface and +indicates the potential target information via the set of correlation +rules. + +Signature creation with Prelude-Correlator is based on the Python +programming language. Prelude's integrated correlation engine is +distributed with a default set of correlation rules, yet you still +have the opportunity to modify and create any correlation rule that +suits your needs. +""", + classifiers = [ "Development Status :: 4 - Beta", + "Environment :: Console", + "Intended Audience :: System Administrators", + "License :: OSI Approved :: GNU General Public License (GPL)", + "Natural Language :: English", + "Operating System :: OS Independent", + "Programming Language :: Python", + "Topic :: Security", + "Topic :: System :: Monitoring" ], + + packages = find_packages(), + entry_points = { + 'console_scripts': [ + 'prelude-correlator = PreludeCorrelator.main:main', + ], + + 'PreludeCorrelator.plugins': [ + 'BruteForcePlugin = PreludeCorrelator.plugins.bruteforce:BruteForcePlugin', + 'BusinessHourPlugin = PreludeCorrelator.plugins.businesshour:BusinessHourPlugin', + 'DshieldPlugin = PreludeCorrelator.plugins.dshield:DshieldPlugin', + 'FirewallPlugin = PreludeCorrelator.plugins.firewall:FirewallPlugin', + 'OpenSSHAuthPlugin = PreludeCorrelator.plugins.opensshauth:OpenSSHAuthPlugin', + 'EventScanPlugin = PreludeCorrelator.plugins.scan:EventScanPlugin', + 'EventStormPlugin = PreludeCorrelator.plugins.scan:EventStormPlugin', + 'EventSweepPlugin = PreludeCorrelator.plugins.scan:EventSweepPlugin', + 'WormPlugin = PreludeCorrelator.plugins.worm:WormPlugin' + ] + }, + + cmdclass = { 'install': my_install } +) _______________________________________________ Prelude-cvslog site list [email protected] http://lists.prelude-ids.org/mailman/listinfo/prelude-cvslog