Commits

jviide committed 21f45b2

abusehelper.contrib.abusech.zeus*bot: Collected shared code & logic to common helper functions for ZeuS bots. Fixed parsing functions to crash less on unexpected inputs.

Comments (0)

Files changed (5)

abusehelper/contrib/abusech/__init__.py

+import re
 import socket
+import urlparse
+
+from abusehelper.core import events
+from abusehelper.contrib.rssbot.rssbot import RSSBot
 
 
 def is_ip(string):
 
 def resolve_level(value):
     return tuple(_levels.get(value, []))
+
+
+def host_or_ip_from_url(url):
+    parsed = urlparse.urlparse(url)
+    if is_ip(parsed.netloc):
+        return "ip", parsed.netloc
+    else:
+        return "host", parsed.netloc
+
+
+def split_description(description):
+    for part in description.split(","):
+        pair = part.split(":", 1)
+        if len(pair) < 2:
+            continue
+
+        key = pair[0].lower().strip()
+        value = pair[1].strip()
+        if not key or not value:
+            continue
+
+        yield key, value
+
+
+class AbuseCHFeedBot(RSSBot):
+    feed_name = "abuse.ch"
+    feed_malware = []
+    feed_type = []
+
+    def parse_link(self, link):
+        yield "description url", link
+
+    def parse_title(self, title):
+        parts = title.split()
+        if len(parts) < 2:
+            return
+
+        tstamp = parts[1]
+        tstamp = re.sub("[()]", "", tstamp)
+        yield "source time", tstamp
+
+    def parse(self, input_key, input_value):
+        parser = getattr(self, "parse_" + input_key, None)
+        if not callable(parser):
+            return
+
+        for output_key, output_value in parser(input_value):
+            yield output_key, output_value
+
+    def create_event(self, **keys):
+        event = events.Event({
+            "feed": self.feed_name,
+            "malware": self.feed_malware,
+            "type": self.feed_type
+        })
+
+        for input_key, input_value in keys.iteritems():
+            for output_key, output_value in self.parse(input_key, input_value):
+                if isinstance(output_value, basestring):
+                    event.add(output_key, output_value)
+                else:
+                    event.update(output_key, output_value)
+
+        return event

abusehelper/contrib/abusech/zeusbinarybot.py

 """
 
 import re
-import urlparse
-from abusehelper.core import bot, events
-from abusehelper.contrib.rssbot.rssbot import RSSBot
+from abusehelper.core import bot
 
-from . import is_ip
+from . import host_or_ip_from_url, split_description, AbuseCHFeedBot
 
 
-class ZeusBinaryBot(RSSBot):
+class ZeusBinaryBot(AbuseCHFeedBot):
+    feed_malware = "ZeuS"
+    feed_type = "malware"
+
     feeds = bot.ListParam(default=["https://zeustracker.abuse.ch/monitor.php?urlfeed=binaries"])
 
-    def create_event(self, **keys):
-        event = events.Event()
-        # handle link data
-        link = keys.get("link", None)
-        if link:
-            event.add("description url", link)
-        # handle title data
-        br = re.compile('[()]')
-        title = keys.get("title")
-        parts = []
-        parts = title.split()
-        tstamp = parts[1]
-        tstamp = br.sub('', tstamp)
-        event.add("source time", tstamp)
-        # handle description data
-        description = keys.get("description", None)
-        if description:
-            for part in description.split(","):
-                pair = part.split(":", 1)
-                if len(pair) < 2:
-                    continue
-
-                key = pair[0].strip()
-                value = pair[1].strip()
-                if not key or not value:
-                    continue
-                if key == "URL":
-                    proto = re.compile('^http:\/\/')
-                    url = proto.sub('hxxp://', value)
-                    event.add("url", url)
-                    parsed = urlparse.urlparse(value)
-                    host = parsed.netloc
-                    if is_ip(host):
-                        event.add("ip", host)
-                    else:
-                        event.add("host", host)
-                if key in ["Virustotal", "Status"]:
-                    event.add(key.lower(), value)
-                if key == "MD5 hash":
-                    event.add("md5", value)
-        event.add("feed", "abuse.ch")
-        event.add("malware", "ZeuS")
-        event.add("type", "malware")
-        return event
+    def parse_description(self, description):
+        for key, value in split_description(description):
+            if key == "url":
+                url = re.sub("^http:\/\/", "hxxp://", value)
+                yield "url", url
+                yield host_or_ip_from_url(url)
+            if key in ["virustotal", "status"]:
+                yield key, value
+            if key == "md5 hash":
+                yield "md5", value
 
 if __name__ == "__main__":
     ZeusBinaryBot.from_command_line().execute()

abusehelper/contrib/abusech/zeusccbot.py

 """
 
 import re
-from abusehelper.core import bot, events
-from abusehelper.contrib.rssbot.rssbot import RSSBot
+from abusehelper.core import bot
 
-from . import is_ip, resolve_level
+from . import is_ip, resolve_level, split_description, AbuseCHFeedBot
 
 
-class ZeusCcBot(RSSBot):
+class ZeusCcBot(AbuseCHFeedBot):
+    feed_malware = "ZeuS"
+    feed_type = "c&c"
+
     feeds = bot.ListParam(default=["https://zeustracker.abuse.ch/rss.php"])
     # If treat_as_dns_source is set, the feed ip is dropped.
     treat_as_dns_source = bot.BoolParam()
 
-    def create_event(self, **keys):
-        event = events.Event()
-        # handle link data
-        link = keys.get("link", None)
-        if link:
-            event.add("description url", link)
-        # handle title data
-        title = keys.get("title", None)
-        if title:
-            t = []
-            t = title.split()
-            host = t[0]
-            date = " ".join(t[1:])
-            if is_ip(host):
-                event.add("ip", host)
-            else:
-                event.add("host", host)
-            br = re.compile('[()]')
-            date = br.sub('', date)
-            date = date + " UTC"
-            event.add("source time", date)
-        # handle description data
-        description = keys.get("description", None)
-        if description:
-            for part in description.split(","):
-                pair = part.split(":", 1)
-                if len(pair) < 2:
-                    continue
-                key = pair[0].strip()
-                value = pair[1].strip()
-                if not key or not value:
-                    continue
-                if key == "Status":
-                    event.add(key.lower(), value)
-                elif key == "level":
-                    event.update("description", resolve_level(value))
-                elif key == "SBL" and value != "Not listed":
-                    key = key.lower() + " id"
-                    event.add(key, value)
-                elif key == "IP address":
-                    if not self.treat_as_dns_source:
-                        event.add("ip", value)
-        event.add("feed", "abuse.ch")
-        event.add("malware", "ZeuS")
-        event.add("type", "c&c")
-        return event
+    def parse_title(self, title):
+        pieces = title.split(None, 1)
+
+        host = pieces[0]
+        if is_ip(host):
+            yield "ip", host
+        else:
+            yield "host", host
+
+        if len(pieces) > 1:
+            date = pieces[1]
+            date = re.sub("[()]", "", date)
+            yield "source time", date + " UTC"
+
+    def parse_description(self, description):
+        for key, value in split_description(description):
+            if key == "status":
+                yield key, value
+            elif key == "level":
+                yield "description", resolve_level(value)
+            elif key == "sbl" and value.lower() != "not listed":
+                yield key + " id", value
+            elif key == "ip address" and not self.treat_as_dns_source:
+                yield "ip", value
 
 if __name__ == "__main__":
     ZeusCcBot.from_command_line().execute()

abusehelper/contrib/abusech/zeusconfigbot.py

 Maintainer: Lari Huttunen <mit-code@huttu.net>
 """
 
-import re
-import urlparse
-from abusehelper.core import bot, events
-from abusehelper.contrib.rssbot.rssbot import RSSBot
+from abusehelper.core import bot
 
-from . import is_ip
+from . import host_or_ip_from_url, split_description, AbuseCHFeedBot
 
 
-class ZeusConfigBot(RSSBot):
+class ZeusConfigBot(AbuseCHFeedBot):
+    feed_malware = "ZeuS"
+    feed_type = "malware configuration"
+
     feeds = bot.ListParam(default=["https://zeustracker.abuse.ch/monitor.php?urlfeed=configs"])
 
-    def create_event(self, **keys):
-        event = events.Event()
-        # handle link data
-        link = keys.get("link", None)
-        if link:
-            event.add("description url", link)
-        # handle title data
-        br = re.compile('[()]')
-        title = keys.get("title")
-        parts = []
-        parts = title.split()
-        tstamp = parts[1]
-        tstamp = br.sub('', tstamp)
-        event.add("source time", tstamp)
-        # handle description data
-        description = keys.get("description", None)
-        if description:
-            for part in description.split(","):
-                pair = part.split(":", 1)
-                if len(pair) < 2:
-                    continue
-                key = pair[0].strip()
-                value = pair[1].strip()
-                if not key or not value:
-                    continue
-                if key in ["Status", "version"]:
-                    event.add(key.lower(), value)
-                elif key == "URL":
-                    event.add("url", value)
-                    parsed = urlparse.urlparse(value)
-                    host = parsed.netloc
-                    if is_ip(host):
-                        event.add("ip", host)
-                    else:
-                        event.add("host", host)
-                elif key == "MD5 hash":
-                    event.add("md5", value)
-        event.add("feed", "abuse.ch")
-        event.add("malware", "ZeuS")
-        event.add("type", "malware configuration")
-        return event
+    def parse_description(self, description):
+        for key, value in split_description(description):
+            if key == ["status", "version"]:
+                yield key, value
+            elif key == "md5 hash":
+                yield "md5", value
+            elif key == "URL":
+                yield "url", value
+                yield host_or_ip_from_url(value)
 
 if __name__ == "__main__":
     ZeusConfigBot.from_command_line().execute()

abusehelper/contrib/abusech/zeusdropzonebot.py

 Maintainer: Lari Huttunen <mit-code@huttu.net>
 """
 
-import re
-import urlparse
-from abusehelper.core import bot, events
-from abusehelper.contrib.rssbot.rssbot import RSSBot
+from abusehelper.core import bot
 
-from . import is_ip
+from . import host_or_ip_from_url, split_description, AbuseCHFeedBot
 
 
-class ZeusDropzoneBot(RSSBot):
+class ZeusDropzoneBot(AbuseCHFeedBot):
+    feed_malware = "ZeuS"
+    feed_type = "dropzone"
+
     feeds = bot.ListParam(default=["https://zeustracker.abuse.ch/monitor.php?urlfeed=dropzones"])
 
-    def create_event(self, **keys):
-        event = events.Event()
-        # handle link data
-        link = keys.get("link", None)
-        if link:
-            event.add("description url", link)
-        # handle title data
-        br = re.compile('[()]')
-        title = keys.get("title")
-        parts = []
-        parts = title.split()
-        tstamp = parts[1]
-        tstamp = br.sub('', tstamp)
-        event.add("source time", tstamp)
-        # handle description data
-        description = keys.get("description", None)
-        if description:
-            for part in description.split(","):
-                pair = part.split(":", 1)
-                if len(pair) < 2:
-                    continue
-                key = pair[0].strip()
-                value = pair[1].strip()
-                if not key or not value:
-                    continue
-                if key == "Status":
-                    event.add(key.lower(), value)
-                elif key == "URL":
-                    event.add("url", value)
-                    parsed = urlparse.urlparse(value)
-                    host = parsed.netloc
-                    if is_ip(host):
-                        event.add("ip", host)
-                    else:
-                        event.add("host", host)
-        event.add("feed", "abuse.ch")
-        event.add("malware", "ZeuS")
-        event.add("type", "dropzone")
-        return event
+    def parse_description(self, description):
+        for key, value in split_description(description):
+            if key == "status":
+                yield key, value
+            elif key == "url":
+                yield "url", value
+                yield host_or_ip_from_url(value)
 
 if __name__ == "__main__":
     ZeusDropzoneBot.from_command_line().execute()