Example #1
0
    def pehash(self):
        if not HAVE_PEHASH:
            self.log('error', "PEhash is missing. Please copy PEhash to the modules directory of Viper")
            return

        current_pehash = None
        if __sessions__.is_set():
            current_pehash = calculate_pehash(__sessions__.current.file.path)
            self.log('info', "PEhash: {0}".format(bold(current_pehash)))

        if self.args.all or self.args.cluster or self.args.scan:
            db = Database()
            samples = db.find(key='all')

            rows = []
            for sample in samples:
                sample_path = get_sample_path(sample.sha256)
                pe_hash = calculate_pehash(sample_path)
                if pe_hash:
                    rows.append((sample.name, sample.md5, pe_hash))

        if self.args.all:
            self.log('info', "PEhash for all files:")
            header = ['Name', 'MD5', 'PEhash']
            self.log('table', dict(header=header, rows=rows))

        elif self.args.cluster:
            self.log('info', "Clustering files by PEhash...")

            cluster = {}
            for sample_name, sample_md5, pe_hash in rows:
                cluster.setdefault(pe_hash, []).append([sample_name, sample_md5])

            for item in cluster.items():
                if len(item[1]) > 1:
                    self.log('info', "PEhash cluster {0}:".format(bold(item[0])))
                    self.log('table', dict(header=['Name', 'MD5'], rows=item[1]))

        elif self.args.scan:
            if __sessions__.is_set() and current_pehash:
                self.log('info', "Finding matching samples...")

                matches = []
                for row in rows:
                    if row[1] == __sessions__.current.file.md5:
                        continue

                    if row[2] == current_pehash:
                        matches.append([row[0], row[1]])

                if matches:
                    self.log('table', dict(header=['Name', 'MD5'], rows=matches))
                else:
                    self.log('info', "No matches found")
Example #2
0
    def cmd_investigations(self, *args):
        parser = argparse.ArgumentParser(prog='investigations', description="Open a case", epilog="List or switch current investigations")
        group = parser.add_mutually_exclusive_group()
        group.add_argument('-l', '--list', action='store_true', help="List all existing investigations")
        group.add_argument('-s', '--switch', metavar='NAME', help="Switch to the specified investigation")
        group.add_argument('-d', '--delete', type=int, metavar='ID', help="delete investigation by id.")

        try:
            args = parser.parse_args(args)
        except:
            return

        projects_path = os.path.join(os.getcwd(), 'investigations')

        if not os.path.exists(projects_path):
            self.log('info', "The investigations directory does not exist yet")
            return

        if args.list:
            self.log('info', "Current Investigations:")
            rows = []
            items = self.db.get_investigation_list()

            # Populate the list of search results.
            count = 1
            for item in items:
                row = [item.id, item.name]
                rows.append(row)

            self.log('table', dict(header=['ID', 'Name'], rows=rows))
        elif args.switch:
            if __sessions__.is_set():
                __sessions__.close()
                self.log('info', "Closed opened session")

            __project__.open(args.switch, self.db)
            self.log('info', "Switched to investigation {0}".format(bold(args.switch)))

            # Need to re-initialize the Database to open the new SQLite file.
            self.db = Database()
        elif args.delete:
            if __sessions__.is_set():
                __sessions__.close()
                self.log('info', "Closed opened session")

            __project__.delete(args.delete, self.db)
            self.log('info', "Deleted investigation {0}".format(bold(args.delete)))

            # Need to re-initialize the Database to open the new SQLite file.
            self.db = Database()
        else:
            self.log('info', parser.print_usage())
Example #3
0
    def run(self):
        super(Exif, self).run()
        if self.args is None:
            return

        if not __sessions__.is_set():
            self.log('error', "No session opened")
            return

        if not HAVE_EXIF:
            self.log('error', "Missing dependency, install pyexiftool")
            return

        try:
            with exiftool.ExifTool() as et:
                metadata = et.get_metadata(__sessions__.current.file.path)
        except OSError:
            self.log('error', "Exiftool is not installed")
            return

        rows = []
        for key, value in metadata.items():
            rows.append([key, value])

        rows = sorted(rows, key=lambda entry: entry[0])

        self.log('info', "MetaData:")
        self.log('table', dict(header=['Key', 'Value'], rows=rows))
Example #4
0
    def run(self):
        super(Exif, self).run()
        if self.args is None:
            return

        if not __sessions__.is_set():
            self.log('error', "No session opened")
            return

        if not HAVE_EXIF:
            self.log('error', "Missing dependency, install pyexiftool")
            return

        try:
            with exiftool.ExifTool() as et:
                metadata = et.get_metadata(__sessions__.current.file.path)
        except OSError:
            self.log('error', "Exiftool is not installed")
            return

        rows = []
        for key, value in metadata.items():
            rows.append([key, value])

        rows = sorted(rows, key=lambda entry: entry[0])

        self.log('info', "MetaData:")
        self.log('table', dict(header=['Key', 'Value'], rows=rows))
Example #5
0
    def upload(self):
        if not __sessions__.is_set():
            self.log('error', "No session opened")
            return False

        categ = self.categories.get(self.args.categ)
        if self.args.info is not None:
            info = ' '.join(self.args.info)
        else:
            info = None
        if __sessions__.current.misp_event and self.args.event is None:
            event = __sessions__.current.misp_event.event_id
        else:
            event = None
        try:
            out = self.misp.upload_sample(__sessions__.current.file.name, __sessions__.current.file.path,
                                          event, self.args.distrib, self.args.ids, categ, info,
                                          self.args.analysis, self.args.threat)
        except Exception as e:
            self.log('error', e)
            return
        result = out.json()
        if out.status_code == 200:
            if result.get('errors') is not None:
                self.log('error', result.get('errors')[0]['error']['value'][0])
            else:
                if event is not None:
                    full_event = self.misp.get_event(event)
                    return __sessions__.new(misp_event=MispEvent(full_event.json()))
                # TODO: also open a session when upload_sample created a new event
                # (the response doesn't contain the event ID)
                # __sessions__.new(misp_event=MispEvent(result))
                self.log('success', "File uploaded sucessfully")
        else:
            self.log('error', result.get('message'))
Example #6
0
    def run(self):
        super(Reports, self).run()
        if self.args is None:
            return

        if not HAVE_REQUESTS and not HAVE_BS4:
            self.log('error', "Missing dependencies (`pip install requests beautifulsoup4`)")
            return

        if not __sessions__.is_set():
            self.log('error', "No session opened")
            return

        if self.args.malwr:
            self.malwr()
        elif self.args.anubis:
            self.anubis()
        elif self.args.threat:
            self.threat()
        elif self.args.joe:
            self.joe()
        elif self.args.meta:
            self.meta()
        elif self.args.wildfire:
            self.wildfire()
        else:
            self.log('error', 'At least one of the parameters is required')
            self.usage()
Example #7
0
    def get_config(self, family):
        if not __sessions__.is_set():
            self.log('error', "No session opened")
            return

        try:
            module = importlib.import_module('modules.rats.{0}'.format(family))
        except ImportError:
            self.log('error',
                     "There is no module for family {0}".format(bold(family)))
            return

        config = module.config(__sessions__.current.file.data)
        if not config:
            self.log('error', "No Configuration Detected")
            return

        rows = []
        for key, value in config.items():
            rows.append([key, value])

        rows = sorted(rows, key=lambda entry: entry[0])

        self.log('info', "Configuration:")
        self.log('table', dict(header=['Key', 'Value'], rows=rows))
Example #8
0
    def run(self):
        super(Reports, self).run()
        if self.args is None:
            return

        if not HAVE_REQUESTS and not HAVE_BS4:
            self.log(
                'error',
                "Missing dependencies (`pip install requests beautifulsoup4`)")
            return

        if not __sessions__.is_set():
            self.log('error', "No session opened")
            return

        if self.args.malwr:
            self.malwr()
        elif self.args.anubis:
            self.anubis()
        elif self.args.threat:
            self.threat()
        elif self.args.joe:
            self.joe()
        elif self.args.meta:
            self.meta()
        elif self.args.wildfire:
            self.wildfire()
        else:
            self.log('error', 'At least one of the parameters is required')
            self.usage()
Example #9
0
    def run(self):
        super(Editdistance, self).run()
        if self.args is None:
            return

        if not __sessions__.is_set():
            self.log('error', "No session opened")
            return

        self.edit()
Example #10
0
    def run(self):
        super(Editdistance, self).run()
        if self.args is None:
            return

        if not __sessions__.is_set():
            self.log('error', "No session opened")
            return

        self.edit()
Example #11
0
    def keywords(self, data):
        # Check if $self is in the user input data.
        if '$self' in data:
            # Check if there is an open session.
            if __sessions__.is_set():
                # If a session is opened, replace $self with the path to
                # the file which is currently being analyzed.
                data = data.replace('$self', __sessions__.current.file.path)
            else:
                print("No session opened")
                return None

        return data
Example #12
0
    def __check_session(self):
        if not __sessions__.is_set():
            self.log('error', "No session opened")
            return False

        if not self.pe:
            try:
                self.pe = pefile.PE(__sessions__.current.file.path)
            except pefile.PEFormatError as e:
                self.log('error', "Unable to parse PE file: {0}".format(e))
                return False

        return True
Example #13
0
    def __check_session(self):
        if not __sessions__.is_set():
            self.log('error', "No session opened")
            return False

        if not self.pe:
            try:
                self.pe = pefile.PE(__sessions__.current.file.path)
            except pefile.PEFormatError as e:
                self.log('error', "Unable to parse PE file: {0}".format(e))
                return False

        return True
Example #14
0
    def cmd_export(self, *args):
        parser = argparse.ArgumentParser(prog='export', description="Export the current session to file or zip")
        parser.add_argument('-z', '--zip', action='store_true', help="Export session in a zip archive")
        parser.add_argument('value', help="path or archive name")

        try:
            args = parser.parse_args(args)
        except:
            return

        # This command requires a session to be opened.
        if not __sessions__.is_set():
            self.log('error', "No session opened")
            parser.print_usage()
            return

        # Check for valid export path.
        if args.value is None:
            parser.print_usage()
            return

        # TODO: having for one a folder and for the other a full
        # target path can be confusing. We should perhaps standardize this.

        # Abort if the specified path already exists.
        if os.path.isfile(args.value):
            self.log('error', "File at path \"{0}\" already exists, abort".format(args.value))
            return

        # If the argument chosed so, archive the file when exporting it.
        # TODO: perhaps add an option to use a password for the archive
        # and default it to "infected".
        if args.zip:
            try:
                with ZipFile(args.value, 'w') as export_zip:
                    export_zip.write(__sessions__.current.file.path, arcname=__sessions__.current.file.name)
            except IOError as e:
                self.log('error', "Unable to export file: {0}".format(e))
            else:
                self.log('info', "File archived and exported to {0}".format(args.value))
        # Otherwise just dump it to the given directory.
        else:
            # XXX: Export file with the original file name.
            store_path = os.path.join(args.value, __sessions__.current.file.name)

            try:
                shutil.copyfile(__sessions__.current.file.path, store_path)
            except IOError as e:
                self.log('error', "Unable to export file: {0}".format(e))
            else:
                self.log('info', "File exported to {0}".format(store_path))
Example #15
0
    def run(self):
        super(Cuckoo, self).run()
        if self.args is None:
            return

        if not __sessions__.is_set():
            self.log('error', "No session opened")
            return

        if not HAVE_REQUESTS:
            self.log(
                'error',
                "Missing dependency, install requests (`pip install requests`)"
            )
            return

        host = self.args.host
        port = self.args.port

        url = 'http://{0}:{1}/tasks/create/file'.format(host, port)

        files = dict(file=open(__sessions__.current.file.path, 'rb'))

        try:
            response = requests.post(url, files=files)
        except requests.ConnectionError:
            self.log('error',
                     "Unable to connect to Cuckoo API at '{0}'.".format(url))
            return
        except Exception as e:
            self.log('error',
                     "Failed performing request at '{0}': {1}".format(url, e))
            return

        try:
            parsed_response = response.json()
        except Exception as e:
            try:
                parsed_response = response.json
            except Exception as e:
                self.log('error', "Failed parsing the response: {0}".format(e))
                self.log('error', "Data:\n{}".format(response.content))
                return

        if 'task_id' in parsed_response:
            self.log('info', "Task ID: {0}".format(parsed_response['task_id']))
        else:
            self.log(
                'error',
                "Failed to parse the task id from the returned JSON ('{0}'): {1}"
                .format(parsed_response, e))
Example #16
0
    def cmd_tags(self, *args):
        parser = argparse.ArgumentParser(prog='tags', description="Modify tags of the opened file")
        parser.add_argument('-a', '--add', metavar='TAG', help="Add tags to the opened file (comma separated)")
        parser.add_argument('-d', '--delete', metavar='TAG', help="Delete a tag from the opened file")
        try:
            args = parser.parse_args(args)
        except:
            return

        # This command requires a session to be opened.
        if not __sessions__.is_set():
            self.log('error', "No session opened")
            parser.print_usage()
            return

        # If no arguments are specified, there's not much to do.
        # However, it could make sense to also retrieve a list of existing
        # tags from this command, and not just from the "find" command alone.
        if args.add is None and args.delete is None:
            parser.print_usage()
            return

        # TODO: handle situation where addition or deletion of a tag fail.

        db = Database()
        if not db.find(key='sha256', value=__sessions__.current.file.sha256):
            self.log('error', "The opened file is not stored in the database. "
                "If you want to add it use the `store` command.")
            return

        if args.add:
            # Add specified tags to the database's entry belonging to
            # the opened file.
            db.add_tags(__sessions__.current.file.sha256, args.add)
            self.log('info', "Tags added to the currently opened file")

            # We refresh the opened session to update the attributes.
            # Namely, the list of tags returned by the 'info' command
            # needs to be re-generated, or it wouldn't show the new tags
            # until the existing session is closed a new one is opened.
            self.log('info', "Refreshing session to update attributes...")
            __sessions__.new(__sessions__.current.file.path)

        if args.delete:
            # Delete the tag from the database.
            db.delete_tag(args.delete, __sessions__.current.file.sha256)
            # Refresh the session so that the attributes of the file are
            # updated.
            self.log('info', "Refreshing session to update attributes...")
            __sessions__.new(__sessions__.current.file.path)
Example #17
0
    def run(self):
        super(Image, self).run()
        if self.args is None:
            return

        if not __sessions__.is_set():
            self.log('error', "No session opened")
            return

        if self.args.ghiro:
            self.ghiro()
        else:
            self.log('error', 'At least one of the parameters is required')
            self.usage()
Example #18
0
    def __check_session(self):
        if not __sessions__.is_set():
            self.log('error', "No session opened")
            return False

        if not self.elf:
            try:
                fd = open(__sessions__.current.file.path, 'rb')
                self.elf = ELFFile(fd)
            except:
                self.log('error', "Unable to parse ELF file")
                return False

        return True
Example #19
0
    def run(self):
        super(Image, self).run()
        if self.args is None:
            return

        if not __sessions__.is_set():
            self.log('error', "No session opened")
            return

        if self.args.ghiro:
            self.ghiro()
        else:
            self.log('error', 'At least one of the parameters is required')
            self.usage()
Example #20
0
    def run(self):

        super(SWF, self).run()
        if self.args is None:
            return

        if not __sessions__.is_set():
            self.log('error', "No session opened")
            return

        arg_dump = self.args.dump
        if arg_dump is None:
            arg_dump = tempfile.gettempdir()
        self.decompress(arg_dump)
Example #21
0
    def __check_session(self):
        if not __sessions__.is_set():
            self.log('error', "No session opened")
            return False

        if not self.elf:
            try:
                fd = open(__sessions__.current.file.path, 'rb')
                self.elf = ELFFile(fd)
            except:
                self.log('error', "Unable to parse ELF file")
                return False

        return True
Example #22
0
    def run(self):

        super(SWF, self).run()
        if self.args is None:
            return

        if not __sessions__.is_set():
            self.log('error', "No session opened")
            return

        arg_dump = self.args.dump
        if arg_dump is None:
            arg_dump = tempfile.gettempdir()
        self.decompress(arg_dump)
Example #23
0
    def show(self):
        if not __sessions__.is_set():
            self.log('error', "No session opened")
            return False
        if not __sessions__.current.misp_event:
            self.log('error', "Not attached to a MISP event")
            return False

        current_event = copy.deepcopy(__sessions__.current.misp_event.event)

        header = ['type', 'value', 'comment']
        rows = []
        for a in current_event['Event']['Attribute']:
            rows.append([a['type'], a['value'], a['comment']])
        self.log('table', dict(header=header, rows=rows))
Example #24
0
    def show(self):
        if not __sessions__.is_set():
            self.log('error', "No session opened")
            return False
        if not __sessions__.current.misp_event:
            self.log('error', "Not attached to a MISP event")
            return False

        current_event = copy.deepcopy(__sessions__.current.misp_event.event)

        header = ['type', 'value', 'comment']
        rows = []
        for a in current_event['Event']['Attribute']:
            rows.append([a['type'], a['value'], a['comment']])
        self.log('table', dict(header=header, rows=rows))
Example #25
0
    def auto(self):
        if not HAVE_YARA:
            self.log('error', "Missing dependency, install yara (see http://plusvic.github.io/yara/)")
            return

        if not __sessions__.is_set():
            self.log('error', "No session opened")
            return

        rules = yara.compile(os.path.join(CIRTKIT_ROOT, 'data/yara/rats.yara'))
        for match in rules.match(__sessions__.current.file.path):
            if 'family' in match.meta:
                self.log('info', "Automatically detected supported RAT {0}".format(match.rule))
                self.get_config(match.meta['family'])
                return

        self.log('info', "No known RAT detected")
Example #26
0
    def run(self):

        def read_manifest(manifest):
            rows = []
            lines = manifest.split('\r\n')
            for line in lines:
                if len(line) > 1:
                    item, value = line.split(':')
                    rows.append([item, value])

            self.log('info', "Manifest File:")
            self.log('table', dict(header=['Item', 'Value'], rows=rows))

        super(Jar, self).run()
        if self.args is None:
            return

        arg_dump = self.args.dump

        if not __sessions__.is_set():
            self.log('error', "No session opened")
            return

        if not zipfile.is_zipfile(__sessions__.current.file.path):
            self.log('error', "Doesn't Appear to be a valid jar archive")
            return

        with zipfile.ZipFile(__sessions__.current.file.path, 'r') as archive:
            jar_tree = []

            for name in archive.namelist():
                item_data = archive.read(name)

                if name == 'META-INF/MANIFEST.MF':
                    read_manifest(item_data)

                item_md5 = hashlib.md5(item_data).hexdigest()
                jar_tree.append([name, item_md5])

            self.log('info', "Jar Tree:")
            self.log('table', dict(header=['Java File', 'MD5'], rows=jar_tree))

            if arg_dump:
                archive.extractall(arg_dump)
                self.log('info', "Archive content extracted to {0}".format(arg_dump))
Example #27
0
    def run(self):
        super(Cuckoo, self).run()
        if self.args is None:
            return

        if not __sessions__.is_set():
            self.log('error', "No session opened")
            return

        if not HAVE_REQUESTS:
            self.log('error', "Missing dependency, install requests (`pip install requests`)")
            return

        host = self.args.host
        port = self.args.port

        url = 'http://{0}:{1}/tasks/create/file'.format(host, port)

        files = dict(file=open(__sessions__.current.file.path, 'rb'))

        try:
            response = requests.post(url, files=files)
        except requests.ConnectionError:
            self.log('error', "Unable to connect to Cuckoo API at '{0}'.".format(url))
            return
        except Exception as e:
            self.log('error', "Failed performing request at '{0}': {1}".format(url, e))
            return

        try:
            parsed_response = response.json()
        except Exception as e:
            try:
                parsed_response = response.json
            except Exception as e:
                self.log('error', "Failed parsing the response: {0}".format(e))
                self.log('error', "Data:\n{}".format(response.content))
                return

        if 'task_id' in parsed_response:
            self.log('info', "Task ID: {0}".format(parsed_response['task_id']))
        else:
            self.log('error', "Failed to parse the task id from the returned JSON ('{0}'): {1}".format(parsed_response, e))
Example #28
0
 def cmd_info(self, *args):
     if __sessions__.is_set():
         self.log('table', dict(
             header=['Key', 'Value'],
             rows=[
                 ['Name', __sessions__.current.file.name],
                 ['Tags', __sessions__.current.file.tags],
                 ['Path', __sessions__.current.file.path],
                 ['Size', __sessions__.current.file.size],
                 ['Type', __sessions__.current.file.type],
                 ['Mime', __sessions__.current.file.mime],
                 ['MD5', __sessions__.current.file.md5],
                 ['SHA1', __sessions__.current.file.sha1],
                 ['SHA256', __sessions__.current.file.sha256],
                 ['SHA512', __sessions__.current.file.sha512],
                 ['SSdeep', __sessions__.current.file.ssdeep],
                 ['CRC32', __sessions__.current.file.crc32]
             ]
         ))
Example #29
0
    def run(self):
        super(PDF, self).run()
        if self.args is None:
            return

        if not __sessions__.is_set():
            self.log('error', "No session opened")
            return False

        if 'PDF' not in __sessions__.current.file.type:
            self.log('error', "The opened file doesn't appear to be a PDF document")
            return

        if self.args.subname == 'id':
            self.pdf_id()
        elif self.args.subname == 'streams':
            self.streams()
        else:
            self.log('error', 'At least one of the parameters is required')
            self.usage()
Example #30
0
    def cmd_delete(self, *args):
        if __sessions__.is_set():
            while True:
                choice = input("Are you sure you want to delete this binary? Can't be reverted! [y/n] ")
                if choice == 'y':
                    break
                elif choice == 'n':
                    return

            rows = self.db.find('sha256', __sessions__.current.file.sha256)
            if rows:
                malware_id = rows[0].id
                if self.db.delete_file(malware_id):
                    self.log("success", "File deleted")
                else:
                    self.log('error', "Unable to delete file")

            os.remove(__sessions__.current.file.path)
            __sessions__.close()
        else:
            self.log('error', "No session opened")
Example #31
0
    def run(self):
        super(PDF, self).run()
        if self.args is None:
            return

        if not __sessions__.is_set():
            self.log('error', "No session opened")
            return False

        if 'PDF' not in __sessions__.current.file.type:
            self.log('error',
                     "The opened file doesn't appear to be a PDF document")
            return

        if self.args.subname == 'id':
            self.pdf_id()
        elif self.args.subname == 'streams':
            self.streams()
        else:
            self.log('error', 'At least one of the parameters is required')
            self.usage()
Example #32
0
    def run(self):
        super(Radare, self).run()
        if self.args is None:
            return

        if not __sessions__.is_set():
            self.log('error', "No session opened")
            return

        if self.args.webserver:
            self.server = "-c=H"

        filetype = __sessions__.current.file.type
        if 'x86-64' in filetype:
            self.is_64b = True

        arch = '64' if self.is_64b else '32'
        if 'DLL' in filetype:
            self.ext = '.dll'
            to_print = [arch, 'bit DLL (Windows)']
            if "native" in filetype:
                to_print.append('perhaps a driver (.sys)')

            self.log('info', ' '.join(to_print))
        elif 'PE32' in filetype:
            self.ext = '.exe'
            self.log('info', ' '.join([arch, 'bit executable (Windows)']))
        elif 'shared object' in filetype:
            self.ext = '.so'
            self.log('info', ' '.join([arch, 'bit shared object (linux)']))
        elif 'ELF' in filetype:
            self.ext = ''
            self.log('info', ' '.join([arch, 'bit executable (linux)']))
        else:
            self.log('error', "Unknown binary")

        try:
            self.open_radare(__sessions__.current.file.path)
        except:
            self.log('error', "Unable to start Radare2")
Example #33
0
    def download(self):
        ok = False
        data = None
        if self.args.event:
            ok, data = self.misp.download_samples(event_id=self.args.event)
        elif self.args.hash:
            ok, data = self.misp.download_samples(sample_hash=self.args.hash)
        else:
            # Download from current MISP event if possible
            if not __sessions__.is_set():
                self.log('error', "No session opened")
                return False
            if not __sessions__.current.misp_event:
                self.log('error', "Not connected to a MISP event.")
                return False
            ok, data = self.misp.download_samples(
                event_id=__sessions__.current.misp_event.event_id)

        if not ok:
            self.log('error', data)
            return
        to_print = []
        for d in data:
            eid, filename, payload = d
            path = os.path.join(tempfile.gettempdir(), filename)
            with open(path, 'w') as f:
                f.write(payload.getvalue())
            to_print.append((eid, path))

        if len(to_print) == 1:
            self.log(
                'success',
                'The sample has been downloaded from Event {}'.format(
                    to_print[0][0]))
            event = self.misp.get_event(to_print[0][0])
            return __sessions__.new(to_print[0][1], MispEvent(event.json()))
        else:
            self.log('success', 'The following files have been downloaded:')
            for p in to_print:
                self.log('success', '\tEventID: {} - {}'.format(*p))
Example #34
0
    def run(self):
        super(Radare, self).run()
        if self.args is None:
            return

        if not __sessions__.is_set():
            self.log('error', "No session opened")
            return

        if self.args.webserver:
            self.server = "-c=H"

        filetype = __sessions__.current.file.type
        if 'x86-64' in filetype:
            self.is_64b = True

        arch = '64' if self.is_64b else '32'
        if 'DLL' in filetype:
            self.ext = '.dll'
            to_print = [arch, 'bit DLL (Windows)']
            if "native" in filetype:
                to_print.append('perhaps a driver (.sys)')

            self.log('info', ' '.join(to_print))
        elif 'PE32' in filetype:
            self.ext = '.exe'
            self.log('info', ' '.join([arch, 'bit executable (Windows)']))
        elif 'shared object' in filetype:
            self.ext = '.so'
            self.log('info', ' '.join([arch, 'bit shared object (linux)']))
        elif 'ELF' in filetype:
            self.ext = ''
            self.log('info', ' '.join([arch, 'bit executable (linux)']))
        else:
            self.log('error', "Unknown binary")

        try:
            self.open_radare(__sessions__.current.file.path)
        except:
            self.log('error', "Unable to start Radare2")
Example #35
0
    def auto(self):
        if not HAVE_YARA:
            self.log(
                'error',
                "Missing dependency, install yara (see http://plusvic.github.io/yara/)"
            )
            return

        if not __sessions__.is_set():
            self.log('error', "No session opened")
            return

        rules = yara.compile(os.path.join(CIRTKIT_ROOT, 'data/yara/rats.yara'))
        for match in rules.match(__sessions__.current.file.path):
            if 'family' in match.meta:
                self.log(
                    'info', "Automatically detected supported RAT {0}".format(
                        match.rule))
                self.get_config(match.meta['family'])
                return

        self.log('info', "No known RAT detected")
Example #36
0
    def upload(self):
        if not __sessions__.is_set():
            self.log('error', "No session opened")
            return False

        categ = self.categories.get(self.args.categ)
        if self.args.info is not None:
            info = ' '.join(self.args.info)
        else:
            info = None
        if __sessions__.current.misp_event and self.args.event is None:
            event = __sessions__.current.misp_event.event_id
        else:
            event = None
        try:
            out = self.misp.upload_sample(__sessions__.current.file.name,
                                          __sessions__.current.file.path,
                                          event, self.args.distrib,
                                          self.args.ids, categ, info,
                                          self.args.analysis, self.args.threat)
        except Exception as e:
            self.log('error', e)
            return
        result = out.json()
        if out.status_code == 200:
            if result.get('errors') is not None:
                self.log('error', result.get('errors')[0]['error']['value'][0])
            else:
                if event is not None:
                    full_event = self.misp.get_event(event)
                    return __sessions__.new(
                        misp_event=MispEvent(full_event.json()))
                # TODO: also open a session when upload_sample created a new event
                # (the response doesn't contain the event ID)
                # __sessions__.new(misp_event=MispEvent(result))
                self.log('success', "File uploaded sucessfully")
        else:
            self.log('error', result.get('message'))
Example #37
0
    def download(self):
        ok = False
        data = None
        if self.args.event:
            ok, data = self.misp.download_samples(event_id=self.args.event)
        elif self.args.hash:
            ok, data = self.misp.download_samples(sample_hash=self.args.hash)
        else:
            # Download from current MISP event if possible
            if not __sessions__.is_set():
                self.log('error', "No session opened")
                return False
            if not __sessions__.current.misp_event:
                self.log('error', "Not connected to a MISP event.")
                return False
            ok, data = self.misp.download_samples(event_id=__sessions__.current.misp_event.event_id)

        if not ok:
            self.log('error', data)
            return
        to_print = []
        for d in data:
            eid, filename, payload = d
            path = os.path.join(tempfile.gettempdir(), filename)
            with open(path, 'w') as f:
                f.write(payload.getvalue())
            to_print.append((eid, path))

        if len(to_print) == 1:
            self.log('success', 'The sample has been downloaded from Event {}'.format(to_print[0][0]))
            event = self.misp.get_event(to_print[0][0])
            return __sessions__.new(to_print[0][1], MispEvent(event.json()))
        else:
            self.log('success', 'The following files have been downloaded:')
            for p in to_print:
                self.log('success', '\tEventID: {} - {}'.format(*p))
Example #38
0
    def run(self):
        super(Strings, self).run()
        if self.args is None:
            return

        arg_all = self.args.all
        arg_hosts = self.args.hosts

        if not __sessions__.is_set():
            self.log('error', "No session opened")
            return

        if os.path.exists(__sessions__.current.file.path):
            regexp = '[\x20\x30-\x39\x41-\x5a\x61-\x7a\-\.:]{4,}'
            strings = re.findall(regexp, __sessions__.current.file.data)

        if arg_all:
            for entry in strings:
                self.log('', entry)
        elif arg_hosts:
            self.extract_hosts(strings)
        else:
            self.log('error', 'At least one of the parameters is required')
            self.usage()
Example #39
0
    def get_config(self, family):
        if not __sessions__.is_set():
            self.log('error', "No session opened")
            return

        try:
            module = importlib.import_module('modules.rats.{0}'.format(family))
        except ImportError:
            self.log('error', "There is no module for family {0}".format(bold(family)))
            return

        config = module.config(__sessions__.current.file.data)
        if not config:
            self.log('error', "No Configuration Detected")
            return

        rows = []
        for key, value in config.items():
            rows.append([key, value])

        rows = sorted(rows, key=lambda entry: entry[0])

        self.log('info', "Configuration:")
        self.log('table', dict(header=['Key', 'Value'], rows=rows))
Example #40
0
    def run(self):
        super(Strings, self).run()
        if self.args is None:
            return

        arg_all = self.args.all
        arg_hosts = self.args.hosts

        if not __sessions__.is_set():
            self.log('error', "No session opened")
            return

        if os.path.exists(__sessions__.current.file.path):
            regexp = '[\x20\x30-\x39\x41-\x5a\x61-\x7a\-\.:]{4,}'
            strings = re.findall(regexp, __sessions__.current.file.data)

        if arg_all:
            for entry in strings:
                self.log('', entry)
        elif arg_hosts:
            self.extract_hosts(strings)
        else:
            self.log('error', 'At least one of the parameters is required')
            self.usage()
Example #41
0
    def run(self):
        super(Macho, self).run()
        if self.args is None:
            return

        if not __sessions__.is_set():
            self.log('error', "No session opened")
            return

        if not HAVE_MACHO:
            self.log('error', "Missing dependency")
            return
            
        # List general info
        def macho_headers(m):
            self.log('info', "Headers: ")
            magic="magic : 0x%x %s" % (m.header.magic, "- " + m.header.display_magic())
            self.log('item', magic)
            cputype="cputype : 0x%x %s" % (m.header.cputype, "- " + m.header.display_cputype())
            self.log('item', cputype)
            cpu_subtype="cpusubtype : 0x%s" % (m.header.cpusubtype)
            self.log('item', cpu_subtype)
            filetype="filetype : 0x%x %s" % (m.header.filetype, "- " + m.header.display_filetype())
            self.log('item', filetype)
            ncmds="ncmds : %d" % (m.header.ncmds)
            self.log('item', ncmds)
            sizeofcmds="sizeofcmds : %d bytes" % (m.header.sizeofcmds)
            self.log('item', sizeofcmds)
            flags="flags : 0x%x %s" % (m.header.flags, "- " + ", ".join(m.header.display_flags()))
            self.log('item',flags)
            if m.header.is_64():
                reserved="reserved : 0x%x" % (m.header.reserved)
                self.log('item',reserved)
            
            #self.log('item', "filetype: 0x{0}".format(m.header.display_filetype()))
            #self.log('item', "ncmds: 0x{0}".format(m.header.ncmds))
            
    
        #print all load commands
        #TODO replace display method
        def macho_load_commands(m):
            load_commands=" Load Commands (%d)" % len(m.commands)
            self.log('info', load_commands)
            for lc in m.commands:
                lc.display("\t")
    
        #print all segments
        #TODO replace display method
        def macho_segments(m):
            segments=" Segments (%d)" % len(m.segments)
            self.log('info', segments)
            for segment in m.segments:
                segment.display(before="\t")
        
        
        try:
            m = MachO(__sessions__.current.file.path)
        except Exception as e:
            self.log('error', "No Mach0 file, {0}".format(e))
            return
                
        if self.args is None:
            return
        elif self.args.all:
            macho_headers(m)
            macho_segments(m)
            macho_load_commands(m)   
        elif self.args.headers:
            macho_headers(m)
        elif self.args.segments:
            macho_segments(m)
        elif self.args.load_commands:
            macho_load_commands(m)   
Example #42
0
    def run(self):

        super(Shellcode, self).run()
        if self.args is None:
            return

        if not __sessions__.is_set():
            self.log('error', "No session opened")
            return

        collection = [
            {
                'description': 'FS:[30h] shellcode',
                'patterns': [
                    b'\x64\xa1\x30\x00|\x64\x8b\x0d\x30|\x64\x8b\x0d\x30|\x64\x8b\x15\x30|\x64\x8b\x35\x30|\x64\x8b\x3d\x30|\x6a\x30.\x64\x8b|\x33..\xb3\x64\x8b',
                    '64a13000|648b0d30|648b0d30|648b1530|648b3530|648b3d30|6a30..648b|33....b3648b'
                ]
            },
            {
                'description': 'FS:[00h] shellcode',
                'patterns': [
                    b'\x64\x8b\x1d|\x64\xa1\x00|\x64\x8b\x0d|\x64\x8b\x15|\x64\x8b\x35|\x64\x8b\x3d',
                    '648b1d00|64a10000|648b0d00|648b1500|648b3500|648b3d00'
                ]
            },
            {
                'description': 'API hashing',
                'patterns': [
                    b'\x74.\xc1.\x0d\x03|\x74.\xc1.\x07\x03',
                    '74..c1..0d03|74..c1..0703'
                ]
            },
            {
                'description': 'PUSH DWORD[]/CALL[]',
                'patterns': [
                    b'\x00\xff\x75\x00\xff\x55',
                    '00ff7500ff55'
                ]
            },
            {
                'description': 'FLDZ/FSTENV [esp-12]',
                'patterns': [
                    b'\x00\xd9\x00\xee\x00\xd9\x74\x24\x00\xf4\x00\x00',
                    '00d900ee00d9742400f40000'
                ]
            },
            {
                'description': 'CALL next/POP',
                'patterns': [
                    b'\x00\xe8\x00\x00\x00\x00(\x58|\x59|\x5a|\x5b|\x5e|\x5f|\x5d)\x00\x00',
                    '00e800000000(58|59|5a|5b|5e|5f|5d)0000'
                ]
            },
            {
                'description': 'Function prolog',
                'patterns': [
                    b'\x55\x8b\x00\xec\x83\x00\xc4|\x55\x8b\x0ec\x81\x00\xec|\x55\x8b\x00\xec\x8b|\x55\x8b\x00\xec\xe8|\x55\x8b\x00\xec\xe9',
                    '558b00ec8300c4|558b0ec8100ec|558b00ec8b|558b00ece8|558b00ece9'
                ]
            },

        ]

        self.log('info', "Searching for known shellcode patterns...")

        for entry in collection:
            for pattern in entry['patterns']:
                match = re.search(pattern, __sessions__.current.file.data)
                if match:
                    offset = match.start()
                    self.log('info', "{0} pattern matched at offset {1}".format(entry['description'], offset))
                    self.log('', cyan(hexdump(__sessions__.current.file.data[offset:], maxlines=15)))
Example #43
0
    def run(self):

        super(Debup, self).run()
        if self.args is None:
            return

        if not __sessions__.is_set():
            self.log('error', "No session opened")
            return

        if not HAVE_OLE:
            self.log(
                'error',
                "Missing dependency, install olefile (`pip install olefile`)")
            return

        # Check for valid OLE
        if not olefile.isOleFile(__sessions__.current.file.path):
            self.log('error', "Not a valid BUP File")
            return

        # Extract all the contents from the bup file.

        ole = olefile.OleFileIO(__sessions__.current.file.path)
        # We know that BUPS are xor'd with 6A which is dec 106 for the decoder

        # This is the stored file.
        data = self.xordata(ole.openstream('File_0').read(), 106)

        # Get the details page
        data2 = self.xordata(ole.openstream('Details').read(), 106)

        # Close the OLE
        ole.close()

        # Process the details file
        rows = []
        lines = data2.split('\n')
        for line in lines:
            if line.startswith('OriginalName'):
                fullpath = line.split('=')[1]
                pathsplit = fullpath.split('\\')
                filename = str(pathsplit[-1][:-1])
            try:
                k, v = line.split('=')
                rows.append([k, v[:-1]])  # Strip the \r from v
            except:
                pass

        # If we opted to switch session then do that
        if data and self.args.session:
            try:
                tempName = os.path.join('/tmp', filename)
                with open(tempName, 'w') as temp:
                    temp.write(data)
                self.log('info', "Switching Session to Embedded File")
                __sessions__.new(tempName)
                return
            except:
                self.log('error', "Unble to Switch Session")
        # Else jsut print the date
        else:
            self.log('info', "BUP Details:")
            self.log('table', dict(header=['Description', 'Value'], rows=rows))
Example #44
0
    def run(self):

        super(Debup, self).run()
        if self.args is None:
            return

        if not __sessions__.is_set():
            self.log('error', "No session opened")
            return

        if not HAVE_OLE:
            self.log('error', "Missing dependency, install olefile (`pip install olefile`)")
            return
            
        # Check for valid OLE
        if not olefile.isOleFile(__sessions__.current.file.path):
            self.log('error', "Not a valid BUP File")
            return

        # Extract all the contents from the bup file. 

        ole = olefile.OleFileIO(__sessions__.current.file.path)
        # We know that BUPS are xor'd with 6A which is dec 106 for the decoder
        
        # This is the stored file.
        data = self.xordata(ole.openstream('File_0').read(), 106)
        
        # Get the details page
        data2 = self.xordata(ole.openstream('Details').read(), 106)
        
        # Close the OLE
        ole.close()
        
        # Process the details file
        rows = []
        lines = data2.split('\n')
        for line in lines:
            if line.startswith('OriginalName'):
                fullpath = line.split('=')[1]
                pathsplit = fullpath.split('\\')
                filename = str(pathsplit[-1][:-1])
            try:
                k, v = line.split('=')
                rows.append([k, v[:-1]])  # Strip the \r from v
            except:
                pass               
                
        # If we opted to switch session then do that
        if data and self.args.session:
            try:
                tempName = os.path.join('/tmp', filename)
                with open(tempName, 'w') as temp:
                    temp.write(data)
                self.log('info', "Switching Session to Embedded File")
                __sessions__.new(tempName)
                return
            except:
                self.log('error', "Unble to Switch Session")
        # Else jsut print the date
        else:
            self.log('info', "BUP Details:")
            self.log('table', dict(header=['Description', 'Value'], rows=rows))           
Example #45
0
    def run(self):
        super(Office, self).run()
        if self.args is None:
            return

        if not __sessions__.is_set():
            self.log('error', "No session opened")
            return

        if not HAVE_OLE:
            self.log(
                'error',
                "Missing dependency, install OleFileIO (`pip install olefile`)"
            )
            return

        file_data = __sessions__.current.file.data
        if file_data.startswith('<?xml'):
            OLD_XML = file_data
        else:
            OLD_XML = False

        if file_data.startswith(
                'MIME-Version:') and 'application/x-mso' in file_data:
            MHT_FILE = file_data
        else:
            MHT_FILE = False

        # Tests to check for valid Office structures.
        OLE_FILE = olefile.isOleFile(__sessions__.current.file.path)
        XML_FILE = zipfile.is_zipfile(__sessions__.current.file.path)
        if OLE_FILE:
            ole = olefile.OleFileIO(__sessions__.current.file.path)
        elif XML_FILE:
            zip_xml = zipfile.ZipFile(__sessions__.current.file.path, 'r')
        elif OLD_XML:
            pass
        elif MHT_FILE:
            pass
        else:
            self.log('error', "Not a valid office document")
            return

        if self.args.export is not None:
            if OLE_FILE:
                self.export(ole, self.args.export)
            elif XML_FILE:
                self.xml_export(zip_xml, self.args.export)
        elif self.args.meta:
            if OLE_FILE:
                self.metadata(ole)
            elif XML_FILE:
                self.xmlmeta(zip_xml)
        elif self.args.streams:
            if OLE_FILE:
                self.metatimes(ole)
            elif XML_FILE:
                self.xmlstruct(zip_xml)
        elif self.args.oleid:
            if OLE_FILE:
                self.oleid(ole)
            else:
                self.log('error', "Not an OLE file")
        elif self.args.vba or self.args.code:
            self.parse_vba(self.args.code)
        else:
            self.log('error', 'At least one of the parameters is required')
            self.usage()
Example #46
0
    def run(self):
        super(HTMLParse, self).run()
        if self.args is None:
            return

        if not __sessions__.is_set():
            self.log('error', "No session opened")
            return

        try:
            html_data = open(__sessions__.current.file.path).read()
            self.soup = BeautifulSoup(html_data)
        except Exception as e:
            self.log('error', "Something went wrong: {0}".format(e))
            return

        # Set dump path, none if not set.
        arg_dump = self.args.dump

        if self.args.script:

            scripts, script_content = self.parse_scripts()
            if arg_dump:
                self.log('info', "Dumping Output to {0}".format(arg_dump))
                for s in script_content:
                    self.dump_output(s, arg_dump, 'Scripts')
            else:
                self.log('info', "Scripts:")
                self.log('table', dict(header=['Type', 'Source', 'Entropy'], rows=scripts))

        elif self.args.links:
                links = self.parse_hrefs()
                self.log('info', "Links")
                self.log('info', "Target \t Text")
                for link in links:
                    self.log('item', "{0}\t {1}".format(link[0], self.string_clean(link[1])))
        # iFrames
        elif self.args.iframe:
            frames, frame_content = self.parse_iframes()
            if arg_dump:
                self.log('info', "Dumping Output to {0}".format(arg_dump))
                for f in frame_content:
                    self.dump_output(f, arg_dump, 'iframe')
            else:
                self.log('info', "IFrames")
                self.log('table', dict(header=['Source', 'Size', 'Entropy'], rows=frames))

        # Images
        elif self.args.images:
            images = self.parse_images()
            if arg_dump:
                self.log('info', "Dumping Images to {0}".format(arg_dump))
                self.log('error', "Not Implemented Yet")
                # this will need an extra http request to download the images
            else:
                self.log('info', "Images")
                self.log('table', dict(header=['Source', 'Alt', ], rows=images))

        # Embedded
        elif self.args.embed:
            java, flash = self.parse_embedded()
            if arg_dump:
                self.log('info', "Dumping Embedded Items to {0}".format(arg_dump))
                self.log('error', "Not Implemented Yet")
                # this will need an extra http request to download the images
            else:
                if len(java) > 0:
                    self.log('info', "Embedded Java Objects")
                    self.log('table', dict(header=['Archive', 'Code', ], rows=java))
                    print ""
                if len(flash) > 0:
                    self.log('info', "Embedded Flash Objects")
                    self.log('table', dict(header=['Swf Src'], rows=flash))
        else:
            self.log('error', 'At least one of the parameters is required')
            self.usage()
Example #47
0
    def run(self):
        super(HTMLParse, self).run()
        if self.args is None:
            return

        if not __sessions__.is_set():
            self.log('error', "No session opened")
            return

        try:
            html_data = open(__sessions__.current.file.path).read()
            self.soup = BeautifulSoup(html_data)
        except Exception as e:
            self.log('error', "Something went wrong: {0}".format(e))
            return

        # Set dump path, none if not set.
        arg_dump = self.args.dump

        if self.args.script:

            scripts, script_content = self.parse_scripts()
            if arg_dump:
                self.log('info', "Dumping Output to {0}".format(arg_dump))
                for s in script_content:
                    self.dump_output(s, arg_dump, 'Scripts')
            else:
                self.log('info', "Scripts:")
                self.log(
                    'table',
                    dict(header=['Type', 'Source', 'Entropy'], rows=scripts))

        elif self.args.links:
            links = self.parse_hrefs()
            self.log('info', "Links")
            self.log('info', "Target \t Text")
            for link in links:
                self.log(
                    'item', "{0}\t {1}".format(link[0],
                                               self.string_clean(link[1])))
        # iFrames
        elif self.args.iframe:
            frames, frame_content = self.parse_iframes()
            if arg_dump:
                self.log('info', "Dumping Output to {0}".format(arg_dump))
                for f in frame_content:
                    self.dump_output(f, arg_dump, 'iframe')
            else:
                self.log('info', "IFrames")
                self.log(
                    'table',
                    dict(header=['Source', 'Size', 'Entropy'], rows=frames))

        # Images
        elif self.args.images:
            images = self.parse_images()
            if arg_dump:
                self.log('info', "Dumping Images to {0}".format(arg_dump))
                self.log('error', "Not Implemented Yet")
                # this will need an extra http request to download the images
            else:
                self.log('info', "Images")
                self.log('table', dict(header=[
                    'Source',
                    'Alt',
                ], rows=images))

        # Embedded
        elif self.args.embed:
            java, flash = self.parse_embedded()
            if arg_dump:
                self.log('info',
                         "Dumping Embedded Items to {0}".format(arg_dump))
                self.log('error', "Not Implemented Yet")
                # this will need an extra http request to download the images
            else:
                if len(java) > 0:
                    self.log('info', "Embedded Java Objects")
                    self.log('table',
                             dict(header=[
                                 'Archive',
                                 'Code',
                             ], rows=java))
                    print ""
                if len(flash) > 0:
                    self.log('info', "Embedded Flash Objects")
                    self.log('table', dict(header=['Swf Src'], rows=flash))
        else:
            self.log('error', 'At least one of the parameters is required')
            self.usage()
Example #48
0
    def run(self, *args):
        def string_clean(value):
            if value:
                return re.sub("[\n\t\r]", "", value)
            return ""

        def parse_ole_msg(ole):
            stream_dirs = ole.listdir()
            for stream in stream_dirs:
                # get stream that contains the email header
                if stream[0].startswith("__substg1.0_007D"):
                    email_header = ole.openstream(stream).read()
                    if stream[0].endswith("001F"):  # Unicode probably needs something better than just stripping \x00
                        email_header = email_header.replace("\x00", "")
            # If it came from outlook we may need to trim some lines
            try:
                email_header = email_header.split("Version 2.0\x0d\x0a", 1)[1]
            except:
                pass

            # Leaving us an RFC compliant email to parse
            msg = email.message_from_string(email_header)
            return msg

        def parse_ole_attachments(ole):
            # Hard part now, each part of the attachment is in a seperate stream

            # need to get a unique stream id for each att
            # its in the streamname as an 8 digit number.
            for i in range(20):  # arbitrary count of emails. i dont expecet this many
                stream_number = str(i).zfill(8)
                stream_name = "__attach_version1.0_#" + stream_number
                # Unicode
                try:
                    att_filename = ole.openstream(stream_name + "/__substg1.0_3704001F").read()
                    att_mime = ole.openstream(stream_name + "/__substg1.0_370E001F").read()
                    att_data = ole.openstream(stream_name + "/__substg1.0_37010102").read()
                    att_size = len(att_data)
                    att_md5 = hashlib.md5(att_data).hexdigest()
                    print i, att_size, att_md5, att_filename, att_mime
                except:
                    pass
                # ASCII
                try:
                    att_filename = ole.openstream(stream_name + "/__substg1.0_3704001E").read()
                    att_mime = ole.openstream(stream_name + "/__substg1.0_370E001E").read()
                    att_data = ole.openstream(stream_name + "/__substg1.0_37010102").read()
                    att_size = len(att_data)
                    att_md5 = hashlib.md5(att_data).hexdigest()
                    print i, att_size, att_md5, att_filename, att_mime
                except:
                    pass

        def att_session(att_id, msg, ole_flag):
            att_count = 0
            if ole_flag:
                ole = msg
                # Hard part now, each part of the attachment is in a seperate stream

                # need to get a unique stream id for each att
                # its in the streamname as an 8 digit number.
                for i in range(20):  # arbitrary count of emails. i dont expecet this many
                    stream_number = str(i).zfill(8)
                    stream_name = "__attach_version1.0_#" + stream_number
                    # Unicode
                    try:
                        att_filename = ole.openstream(stream_name + "/__substg1.0_3704001F").read()
                        att_filename = att_filename.replace("\x00", "")
                        att_data = ole.openstream(stream_name + "/__substg1.0_37010102").read()
                    except:
                        pass
                    # ASCII
                    try:
                        att_filename = ole.openstream(stream_name + "/__substg1.0_3704001E").read()
                        att_data = ole.openstream(stream_name + "/__substg1.0_37010102").read()
                    except:
                        pass
                    if i == att_id:
                        self.log("info", "Switching session to {0}".format(att_filename))
                        tmp_path = os.path.join(tempfile.gettempdir(), att_filename)
                        with open(tmp_path, "w") as tmp:
                            tmp.write(att_data)
                        __sessions__.new(tmp_path)
                        return

            else:
                for part in msg.walk():
                    if part.get_content_type() == "message/rfc822":
                        rfc822 = True
                    else:
                        rfc822 = False

                    if part.get_content_maintype() == "multipart" or not part.get("Content-Disposition") and not rfc822:
                        continue

                    att_count += 1
                    if att_count == att_id:
                        if rfc822:
                            data = part.as_string()
                            m = re.match("Content-Type: message/rfc822\r?\n\r?\n(.*)", data, flags=re.S)
                            if not m:
                                self.log("error", "Could not extract RFC822 formatted message")
                                return
                            data = m.group(1)
                            att_size = len(data)
                            filename = "rfc822msg_{0}.eml".format(att_size)
                        else:
                            data = part.get_payload(decode=True)
                            filename = part.get_filename()

                        self.log("info", "Switching session to {0}".format(filename))

                        if data:
                            tmp_path = os.path.join(tempfile.gettempdir(), filename)
                            with open(tmp_path, "w") as tmp:
                                tmp.write(data)
                            __sessions__.new(tmp_path)
                            return

        def email_envelope(msg):
            # Envelope
            self.log("info", "Email envelope:")
            rows = [
                ["Subject", msg.get("Subject")],
                ["To", msg.get("To")],
                ["From", msg.get("From")],
                ["Cc", msg.get("Cc")],
                ["Bcc", msg.get("Bcc")],
                ["Date", msg.get("Date")],
            ]
            self.log("table", dict(header=["Key", "Value"], rows=rows))
            return

        def email_header(msg):
            # Headers
            rows = []
            for x in msg.keys():
                # Adding Received to ignore list. this has to be handeled separately if there are more then one line
                if x not in ["Subject", "From", "To", "Date", "Cc", "Bcc", "DKIM-Signature", "Received"]:
                    rows.append([x, string_clean(msg.get(x))])
            for x in msg.get_all("Received"):
                rows.append(["Received", string_clean(x)])
            self.log("info", "Email headers:")
            rows = sorted(rows, key=lambda entry: entry[0])
            self.log("table", dict(header=["Key", "Value"], rows=rows))
            return

        def email_trace(msg, verbose):
            rows = []
            if verbose:
                fields = ["from", "by", "with", "id", "for", "timestamp"]
            else:
                fields = ["from", "by", "timestamp"]
            for x in msg.get_all("Received"):
                x = string_clean(x)
                cre = re.compile(
                    """
                    (?: from \s+ (?P<from>.*?) (?=by|with|id|ID|for|;|$) )?
                    (?: by \s+ (?P<by>.*?) (?=with|id|ID|for|;|$) )?
                    (?: with \s+ (?P<with>.*?) (?=id|ID|for|;|$) )?
                    (?: (id|ID) \s+ (?P<id>.*?) (?=for|;|$) )?
                    (?: for \s+ (?P<for>.*?) (?=;|$) )?
                    (?: \s* ; \s* (?P<timestamp>.*) )?
                    """,
                    flags=re.X | re.I,
                )
                m = cre.search(x)
                if not m:
                    self.log("error", "Received header regex didn't match")
                    return
                t = []
                for groupname in fields:
                    t.append(string_clean(m.group(groupname)))
                rows.insert(0, t)
            self.log("info", "Email path trace:")
            self.log("table", dict(header=fields, rows=rows))
            return

        def email_spoofcheck(msg, dnsenabled):
            self.log("info", "Email spoof check:")

            # test 1: check if From address is the same as Sender, Reply-To, and Return-Path
            rows = [
                ["Sender", string_clean(msg.get("Sender"))],
                ["From", string_clean(msg.get("From"))],
                ["Reply-To", string_clean(msg.get("Reply-To"))],
                ["Return-Path", string_clean(msg.get("Return-Path"))],
            ]
            self.log("table", dict(header=["Key", "Value"], rows=rows))
            addr = {
                "Sender": email.utils.parseaddr(string_clean(msg.get("Sender")))[1],
                "From": email.utils.parseaddr(string_clean(msg.get("From")))[1],
                "Reply-To": email.utils.parseaddr(string_clean(msg.get("Reply-To")))[1],
                "Return-Path": email.utils.parseaddr(string_clean(msg.get("Return-Path")))[1],
            }
            if addr["From"] == "":
                self.log("error", "No From address!")
                return
            elif addr["Sender"] and (addr["From"] != addr["Sender"]):
                self.log("warning", "Email FAILED: From address different than Sender")
            elif addr["Reply-To"] and (addr["From"] != addr["Reply-To"]):
                self.log("warning", "Email FAILED: From address different than Reply-To")
            elif addr["Return-Path"] and (addr["From"] != addr["Return-Path"]):
                self.log("warning", "Email FAILED: From address different than Return-Path")
            else:
                self.log("success", "Email PASSED: From address the same as Sender, Reply-To, and Return-Path")

            # test 2: check to see if first Received: by domain matches sender MX domain
            if not dnsenabled:
                self.log("info", "Unable to run Received by / sender check without dnspython available")
            else:
                r = msg.get_all("Received")[-1]
                m = re.search("by\s+(\S*?)(?:\s+\(.*?\))?\s+with", r)
                if not m:
                    self.log("error", "Received header regex didn't match")
                    return
                byname = m.group(1)
                # this can be either a name or an IP
                m = re.search("(\w+\.\w+|\d+\.\d+\.\d+\.\d+)$", byname)
                if not m:
                    self.log("error", "Could not find domain or IP in Received by field")
                    return
                bydomain = m.group(1)
                domains = [["Received by", bydomain]]
                # if it's an IP, do the reverse lookup
                m = re.search("\.\d+$", bydomain)
                if m:
                    bydomain = str(dns.reversename.from_address(bydomain)).strip(".")
                    domains.append(["Received by reverse lookup", bydomain])
                # if the email has a Sender header, use that
                if addr["Sender"] != "":
                    m = re.search("(\w+\.\w+)$", addr["Sender"])
                    if not m:
                        self.log("error", "Sender header regex didn't match")
                        return
                    fromdomain = m.group(1)
                    domains.append(["Sender", fromdomain])
                # otherwise, use the From header
                else:
                    m = re.search("(\w+\.\w+)$", addr["From"])
                    if not m:
                        self.log("error", "From header regex didn't match")
                        return
                    fromdomain = m.group(1)
                    domains.append(["From", fromdomain])

                bymatch = False
                try:
                    mx = dns.resolver.query(fromdomain, "MX")
                    if mx:
                        for rdata in mx:
                            m = re.search("(\w+\.\w+).$", str(rdata.exchange))
                            if not m:
                                self.log("error", "MX domain regex didn't match")
                                continue
                            domains.append(["MX for " + fromdomain, m.group(1)])
                            if bydomain == m.group(1):
                                bymatch = True
                    self.log("table", dict(header=["Key", "Value"], rows=domains))
                except:
                    domains.append(["MX for " + fromdomain, "not registered in DNS"])
                    self.log("table", dict(header=["Key", "Value"], rows=domains))
                if bymatch:
                    self.log("success", "Email PASSED: Received by domain found in Sender/From MX domains")
                else:
                    self.log("warning", "Email FAILED: Could not match Received by domain to Sender/From MX")

            # test 3: look at SPF records
            rspf = []
            results = set()
            allspf = msg.get_all("Received-SPF")
            if not allspf:
                return
            for spf in allspf:
                # self.log('info', string_clean(spf))
                m = re.search("\s*(\w+)\s+\((.*?):\s*(.*?)\)\s+(.*);", string_clean(spf))
                if not m:
                    self.log("error", "Received-SPF regex didn't match")
                    return
                rspf.append([m.group(2), m.group(1), m.group(3), m.group(4)])
                results = results | {m.group(1)}
            self.log("table", dict(header=["Domain", "Action", "Info", "Additional"], rows=rspf))
            if results & {"fail", "softfail"}:
                self.log("warning", "Email FAILED: Found fail or softfail SPF results")
            elif results & {"none", "neutral"}:
                self.log("warning", "Email NEUTRAL: Found none or neutral SPF results")
            elif results & {"permerror", "temperror"}:
                self.log("warning", "Email NEUTRAL: Found error condition")
            elif results & {"pass"}:
                self.log("success", "Email PASSED: Found SPF pass result")

            return

        def email_attachments(msg, ole_flag):
            # Attachments
            att_count = 0
            rows = []
            links = []
            if ole_flag:
                ole = msg
                # Hard part now, each part of the attachment is in a seperate stream

                # need to get a unique stream id for each att
                # its in the streamname as an 8 digit number.
                for i in range(20):  # arbitrary count of emails. i dont expecet this many
                    stream_number = str(i).zfill(8)
                    stream_name = "__attach_version1.0_#" + stream_number
                    # Unicode
                    try:
                        att_filename = ole.openstream(stream_name + "/__substg1.0_3704001F").read()
                        att_mime = ole.openstream(stream_name + "/__substg1.0_370E001F").read()
                        att_data = ole.openstream(stream_name + "/__substg1.0_37010102").read()
                        att_size = len(att_data)
                        att_md5 = hashlib.md5(att_data).hexdigest()
                        rows.append([i, att_filename, att_mime, att_size, att_md5])
                        att_count += 1
                    except:
                        pass
                    # ASCII
                    try:
                        att_filename = ole.openstream(stream_name + "/__substg1.0_3704001E").read()
                        att_mime = ole.openstream(stream_name + "/__substg1.0_370E001E").read()
                        att_data = ole.openstream(stream_name + "/__substg1.0_37010102").read()
                        att_size = len(att_data)
                        att_md5 = hashlib.md5(att_data).hexdigest()
                        rows.append([i, att_filename, att_mime, att_size, att_md5])
                        att_count += 1
                    except:
                        pass

            else:
                # Walk through email string.
                for part in msg.walk():
                    content_type = part.get_content_type()

                    if content_type == "multipart":
                        continue

                    if content_type in ("text/plain", "text/html"):
                        part_content = part.get_payload(decode=True)
                        for link in re.findall(r'(https?://[^"<>\s]+)', part_content):
                            if link not in links:
                                links.append(link)

                    if content_type == "message/rfc822":
                        part_content = part.as_string()
                        m = re.match("Content-Type: message/rfc822\r?\n\r?\n(.*)", part_content, flags=re.S)
                        if not m:
                            self.log("error", "Could not extract RFC822 formatted message")
                            return
                        part_content = m.group(1)
                        att_size = len(part_content)
                        att_file_name = "rfc822msg_{0}.eml".format(att_size)
                        att_md5 = hashlib.md5(part_content).hexdigest()
                        att_count += 1
                        rows.append([att_count, att_file_name, content_type, att_size, att_md5])
                        continue

                    if not part.get("Content-Disposition"):
                        # These are not attachments.
                        continue

                    att_file_name = part.get_filename()
                    att_size = len(part_content)

                    if not att_file_name:
                        continue

                    att_data = part.get_payload(decode=True)
                    att_md5 = hashlib.md5(att_data).hexdigest()
                    att_count += 1
                    rows.append([att_count, att_file_name, part.get_content_type(), att_size, att_md5])

            self.log("info", "Email attachments (total: {0}):".format(att_count))
            if att_count > 0:
                self.log("table", dict(header=["ID", "FileName", "Content Type", "File Size", "MD5"], rows=rows))

            self.log("info", "Email links:")
            for link in links:
                self.log("item", link)
            return

        # Start Here
        if not __sessions__.is_set():
            self.log("error", "No session opened")
            return

        super(EmailParse, self).run(*args)
        if self.args is None:
            return

        # see if we can load the dns library for MX lookup spoof detecton
        try:
            import dns.resolver
            import dns.reversename

            dnsenabled = True
        except ImportError:
            dnsenabled = False

        # Try to open as an ole msg, if not treat as email string
        try:
            ole = olefile.OleFileIO(__sessions__.current.file.path)
            ole_flag = True
        except:
            ole_flag = False
            email_handle = open(__sessions__.current.file.path)
            msg = email.message_from_file(email_handle)
            email_handle.close()

        if self.args.open is not None:
            if ole_flag:
                msg = ole
            att_session(self.args.open, msg, ole_flag)
        elif self.args.envelope:
            if ole_flag:
                msg = parse_ole_msg(ole)
            email_envelope(msg)
        elif self.args.attach:
            if ole_flag:
                msg = ole
            email_attachments(msg, ole_flag)
        elif self.args.header:
            if ole_flag:
                msg = parse_ole_msg(ole)
            email_header(msg)
        elif self.args.trace:
            if ole_flag:
                msg = parse_ole_msg(ole)
            email_trace(msg, False)
        elif self.args.traceall:
            if ole_flag:
                msg = parse_ole_msg(ole)
            email_trace(msg, True)
        elif self.args.spoofcheck:
            if ole_flag:
                msg = parse_ole_msg(ole)
            email_spoofcheck(msg, dnsenabled)
        elif self.args.all:
            if ole_flag:
                msg = parse_ole_msg(ole)
            email_envelope(msg)
            email_header(msg)
            email_trace(msg, True)
            email_spoofcheck(msg, dnsenabled)
            if ole_flag:
                msg = ole
            email_attachments(msg, ole_flag)
        else:
            self.log("error", "At least one of the parameters is required")
            self.usage()
Example #49
0
    def pehash(self):
        if not HAVE_PEHASH:
            self.log(
                'error',
                "PEhash is missing. Please copy PEhash to the modules directory of Viper"
            )
            return

        current_pehash = None
        if __sessions__.is_set():
            current_pehash = calculate_pehash(__sessions__.current.file.path)
            self.log('info', "PEhash: {0}".format(bold(current_pehash)))

        if self.args.all or self.args.cluster or self.args.scan:
            db = Database()
            samples = db.find(key='all')

            rows = []
            for sample in samples:
                sample_path = get_sample_path(sample.sha256)
                pe_hash = calculate_pehash(sample_path)
                if pe_hash:
                    rows.append((sample.name, sample.md5, pe_hash))

        if self.args.all:
            self.log('info', "PEhash for all files:")
            header = ['Name', 'MD5', 'PEhash']
            self.log('table', dict(header=header, rows=rows))

        elif self.args.cluster:
            self.log('info', "Clustering files by PEhash...")

            cluster = {}
            for sample_name, sample_md5, pe_hash in rows:
                cluster.setdefault(pe_hash,
                                   []).append([sample_name, sample_md5])

            for item in cluster.items():
                if len(item[1]) > 1:
                    self.log('info',
                             "PEhash cluster {0}:".format(bold(item[0])))
                    self.log('table', dict(header=['Name', 'MD5'],
                                           rows=item[1]))

        elif self.args.scan:
            if __sessions__.is_set() and current_pehash:
                self.log('info', "Finding matching samples...")

                matches = []
                for row in rows:
                    if row[1] == __sessions__.current.file.md5:
                        continue

                    if row[2] == current_pehash:
                        matches.append([row[0], row[1]])

                if matches:
                    self.log('table', dict(header=['Name', 'MD5'],
                                           rows=matches))
                else:
                    self.log('info', "No matches found")
Example #50
0
    def run(self):
        super(Fuzzy, self).run()

        if not HAVE_PYDEEP:
            self.log(
                'error',
                "Missing dependency, install pydeep (`pip install pydeep`)")
            return

        arg_verbose = False
        arg_cluster = False
        if self.args:
            if self.args.verbose:
                arg_verbose = self.args.verbose
            if self.args.cluster:
                arg_cluster = self.args.cluster

            db = Database()
            samples = db.find(key='all')

            # Check if we're operating in cluster mode, otherwise we run on the
            # currently opened file.
            if arg_cluster:
                self.log('info',
                         "Generating clusters, this might take a while...")

                clusters = dict()
                for sample in samples:
                    if not sample.ssdeep:
                        continue

                    if arg_verbose:
                        self.log(
                            'info', "Testing file {0} with ssdeep {1}".format(
                                sample.md5, sample.ssdeep))

                    clustered = False
                    for cluster_name, cluster_members in clusters.items():
                        # Check if sample is already in the cluster.
                        if sample.md5 in cluster_members:
                            continue

                        if arg_verbose:
                            self.log(
                                'info', "Testing {0} in cluser {1}".format(
                                    sample.md5, cluster_name))

                        for member in cluster_members:
                            if sample.md5 == member[0]:
                                continue

                            member_hash = member[0]
                            member_name = member[1]

                            member_ssdeep = db.find(
                                key='md5', value=member_hash)[0].ssdeep
                            if pydeep.compare(sample.ssdeep,
                                              member_ssdeep) > 40:
                                if arg_verbose:
                                    self.log(
                                        'info',
                                        "Found home for {0} in cluster {1}".
                                        format(sample.md5, cluster_name))

                                clusters[cluster_name].append(
                                    [sample.md5, sample.name])
                                clustered = True
                                break

                    if not clustered:
                        cluster_id = len(clusters) + 1
                        clusters[cluster_id] = [
                            [sample.md5, sample.name],
                        ]

                ordered_clusters = collections.OrderedDict(
                    sorted(clusters.items()))

                self.log(
                    'info',
                    "Following are the identified clusters with more than one member"
                )

                for cluster_name, cluster_members in ordered_clusters.items():
                    # We include in the results only clusters with more than just
                    # one member.
                    if len(cluster_members) <= 1:
                        continue

                    self.log('info',
                             "Ssdeep cluster {0}".format(bold(cluster_name)))

                    self.log(
                        'table',
                        dict(header=['MD5', 'Name'], rows=cluster_members))

            # We're running against the already opened file.
            else:
                if not __sessions__.is_set():
                    self.log('error', "No session opened")
                    return

                if not __sessions__.current.file.ssdeep:
                    self.log('error',
                             "No ssdeep hash available for opened file")
                    return

                matches = []
                for sample in samples:
                    if sample.sha256 == __sessions__.current.file.sha256:
                        continue

                    if not sample.ssdeep:
                        continue

                    score = pydeep.compare(__sessions__.current.file.ssdeep,
                                           sample.ssdeep)

                    if score > 40:
                        matches.append(
                            ['{0}%'.format(score), sample.name, sample.sha256])

                    if arg_verbose:
                        self.log(
                            'info', "Match {0}%: {2} [{1}]".format(
                                score, sample.name, sample.sha256))

                self.log(
                    'info',
                    "{0} relevant matches found".format(bold(len(matches))))

                if len(matches) > 0:
                    self.log(
                        'table',
                        dict(header=['Score', 'Name', 'SHA256'], rows=matches))
Example #51
0
    def run(self):
        super(Fuzzy, self).run()

        if not HAVE_PYDEEP:
            self.log('error', "Missing dependency, install pydeep (`pip install pydeep`)")
            return

        arg_verbose = False
        arg_cluster = False
        if self.args:
            if self.args.verbose:
                arg_verbose = self.args.verbose
            if self.args.cluster:
                arg_cluster = self.args.cluster

            db = Database()
            samples = db.find(key='all')

            # Check if we're operating in cluster mode, otherwise we run on the
            # currently opened file.
            if arg_cluster:
                self.log('info', "Generating clusters, this might take a while...")

                clusters = dict()
                for sample in samples:
                    if not sample.ssdeep:
                        continue

                    if arg_verbose:
                        self.log('info', "Testing file {0} with ssdeep {1}".format(
                            sample.md5, sample.ssdeep))

                    clustered = False
                    for cluster_name, cluster_members in clusters.items():
                        # Check if sample is already in the cluster.
                        if sample.md5 in cluster_members:
                            continue

                        if arg_verbose:
                            self.log('info', "Testing {0} in cluser {1}".format(
                                sample.md5, cluster_name))
                        
                        for member in cluster_members:
                            if sample.md5 == member[0]:
                                continue

                            member_hash = member[0]
                            member_name = member[1]

                            member_ssdeep = db.find(key='md5', value=member_hash)[0].ssdeep
                            if pydeep.compare(sample.ssdeep, member_ssdeep) > 40:
                                if arg_verbose:
                                    self.log('info', "Found home for {0} in cluster {1}".format(
                                        sample.md5, cluster_name))

                                clusters[cluster_name].append([sample.md5, sample.name])
                                clustered = True
                                break

                    if not clustered:
                        cluster_id = len(clusters) + 1
                        clusters[cluster_id] = [[sample.md5, sample.name],]

                ordered_clusters = collections.OrderedDict(sorted(clusters.items()))

                self.log('info', "Following are the identified clusters with more than one member")

                for cluster_name, cluster_members in ordered_clusters.items():
                    # We include in the results only clusters with more than just
                    # one member.
                    if len(cluster_members) <= 1:
                        continue

                    self.log('info', "Ssdeep cluster {0}".format(bold(cluster_name)))

                    self.log('table', dict(header=['MD5', 'Name'],
                        rows=cluster_members))

            # We're running against the already opened file.
            else:
                if not __sessions__.is_set():
                    self.log('error', "No session opened")
                    return

                if not __sessions__.current.file.ssdeep:
                    self.log('error', "No ssdeep hash available for opened file")
                    return

                matches = []
                for sample in samples:
                    if sample.sha256 == __sessions__.current.file.sha256:
                        continue

                    if not sample.ssdeep:
                        continue

                    score = pydeep.compare(__sessions__.current.file.ssdeep,
                        sample.ssdeep)

                    if score > 40:
                        matches.append(['{0}%'.format(score), sample.name,
                            sample.sha256])

                    if arg_verbose:
                        self.log('info', "Match {0}%: {2} [{1}]".format(score,
                            sample.name, sample.sha256))

                self.log('info', "{0} relevant matches found".format(bold(len(matches))))

                if len(matches) > 0:
                    self.log('table', dict(header=['Score', 'Name', 'SHA256'],
                        rows=matches))
Example #52
0
    def add(self):
        if not __sessions__.is_set():
            self.log('error', "No session opened")
            return False
        if not __sessions__.current.misp_event:
            self.log('error', "Not attached to a MISP event")
            return False

        current_event = copy.deepcopy(__sessions__.current.misp_event.event)

        if self.args.add == 'hashes':
            if self.args.filename is None and self.args.md5 is None and self.args.sha1 is None and self.args.sha256 is None:
                if not __sessions__.current.file:
                    self.log('error', "Not attached to a file, please set the hashes manually.")
                    return False
                event = self.misp.add_hashes(current_event, filename=__sessions__.current.file.name,
                                             md5=__sessions__.current.file.md5, sha1=__sessions__.current.file.sha1,
                                             sha256=__sessions__.current.file.sha256,
                                             comment=__sessions__.current.file.tags)
            else:
                event = self.misp.add_hashes(current_event, filename=self.args.filename,
                                             md5=self.args.md5, sha1=self.args.sha1, sha256=self.args.sha256)
            self._check_add(event)
        elif self.args.add == 'regkey':
            if len(self.args.regkey) == 2:
                reg, val = self.args.regkey
            else:
                reg = self.args.regkey[0]
                val = None
            event = self.misp.add_regkey(current_event, reg, val)
            self._check_add(event)
        elif self.args.add == 'pipe':
            event = self.misp.add_pipe(current_event, self.args.pipe)
            self._check_add(event)
        elif self.args.add == 'mutex':
            event = self.misp.add_mutex(current_event, self.args.mutex)
            self._check_add(event)
        elif self.args.add == 'ipdst':
            event = self.misp.add_ipdst(current_event, self.args.ipdst)
            self._check_add(event)
        elif self.args.add == 'hostname':
            event = self.misp.add_hostname(current_event, self.args.hostname)
            self._check_add(event)
        elif self.args.add == 'domain':
            event = self.misp.add_domain(current_event, self.args.domain)
            self._check_add(event)
        elif self.args.add == 'url':
            event = self.misp.add_url(current_event, self.args.full_url)
            self._check_add(event)
        elif self.args.add == 'ua':
            event = self.misp.add_useragent(current_event, self.args.ua)
            self._check_add(event)
        elif self.args.add == 'pattern_file':
            event = self.misp.add_pattern(current_event, self.args.pfile, True, False)
            self._check_add(event)
        elif self.args.add == 'pattern_mem':
            event = self.misp.add_pattern(current_event, self.args.pmem, False, True)
            self._check_add(event)
        elif self.args.add == 'pattern_traffic':
            event = self.misp.add_traffic_pattern(current_event, self.args.ptraffic)
            self._check_add(event)