Esempio n. 1
0
class EicarPlugin(AntivirusPluginInterface, metaclass=PluginMetaClass):

    # =================
    #  plugin metadata
    # =================

    _plugin_name_ = "Eicar"
    _plugin_display_name_ = Eicar.name
    _plugin_author_ = "IRMA (c) Quarkslab"
    _plugin_version_ = "1.0.0"
    _plugin_category_ = IrmaProbeType.antivirus
    _plugin_description_ = "Plugin for Eicar test on Linux"
    _plugin_dependencies_ = [
        PlatformDependency('linux'),
        FileDependency(
            '/opt/Eicar_probe_activated',
            help='Make sure you have activated the probe',
        ),
    ]

    # ================
    #  interface data
    # ================

    module_cls = Eicar

    # `AntivirusPluginInterface`'s verify method check that the AV executable is
    # available.
    # We are bypassing this verification, because this probe doesn't have any
    # executable.
    @classmethod
    def verify(cls):
        return True
Esempio n. 2
0
class TrIDPlugin(PluginBase):

    class TrIDResults:
        ERROR = -1
        FAILURE = 0
        SUCCESS = 1

    _plugin_name_ = "TrID"
    _plugin_display_name_ = "TrID File Identifier"
    _plugin_author_ = "IRMA (c) Quarkslab"
    _plugin_version_ = "1.0.0"
    _plugin_category_ = "metadata"
    _plugin_description_ = "Plugin to determine file type"
    _plugin_dependencies_ = [
        PlatformDependency('linux'),
        FileDependency(
            os.path.join('/opt/trid/', 'trid'),
            help='Make sure you have downloaded trid binary'
        ),
        FileDependency(
            os.path.join('/opt/trid/', 'triddefs.trd'),
            help='Make sure to have downloaded trid definitions'
        ),
    ]

    def __init__(self):
        module = sys.modules['modules.metadata.trid.trid'].TrID
        self.module = module()

    def run(self, paths):
        results = PluginResult(name=type(self).plugin_display_name,
                               type=type(self).plugin_category,
                               version=None)
        # launch file analysis
        try:
            started = timestamp(datetime.utcnow())
            results.status, results.results = self.module.analyze(paths)
            stopped = timestamp(datetime.utcnow())
            results.duration = stopped - started
        except Exception as e:
            results.status = self.TrIDResults.ERROR
            results.error = type(e).__name__ + " : " + str(e)
        return results
Esempio n. 3
0
class DummyPlugin(PluginBase):
    class DummyResult:
        ERROR = -1
        FAILURE = 0
        SUCCESS = 1

    # =================
    #  plugin metadata
    # =================
    _plugin_name_ = "Dummy"
    _plugin_display_name_ = "Dummy"
    _plugin_author_ = "IRMA (c) Quarkslab"
    _plugin_version_ = "0.0.1"
    _plugin_category_ = IrmaProbeType.metadata
    _plugin_description_ = "Plugin to return SHA256 of a file"
    _plugin_dependencies_ = [
        PlatformDependency('linux'),
        FileDependency('/opt/Dummy_probe_activated',
                       help='Make sure you have activated the Dummy probe'),
    ]

    # =============
    #  constructor
    # =============

    def __init__(self):
        pass

    # ==================
    #  probe interfaces
    # ==================
    def run(self, paths):
        response = PluginResult(name=type(self).plugin_display_name,
                                type=type(self).plugin_category,
                                version=None)
        try:
            started = timestamp(datetime.utcnow())
            response.results = sha256sum(open(paths, 'rb'))
            stopped = timestamp(datetime.utcnow())
            response.duration = stopped - started
            response.status = self.DummyResult.SUCCESS
        except Exception as e:
            response.status = self.DummyResult.ERROR
            response.results = type(e).__name__ + " : " + str(e)
        return response
Esempio n. 4
0
class ICAPPlugin(PluginBase):

    class ICAPResult:
        ERROR = -1
        INFECTED = 0
        CLEAN = 1

    # =================
    #  plugin metadata
    # =================

    _plugin_name_ = "ICAP"
    _plugin_display_name_ = "ICAP"
    _plugin_author_ = "Vincent Rasneur <*****@*****.**>"
    _plugin_version_ = "1.0.0"
    _plugin_category_ = IrmaProbeType.external
    _plugin_description_ = "Plugin to query an ICAP antivirus server"
    _plugin_dependencies_ = [
        ModuleDependency(
            'icapclient',
            help='See requirements.txt for needed dependencies'
        ),
        FileDependency(
            os.path.join(os.path.dirname(__file__), 'config.ini')
        )
    ]

    # =============
    #  constructor
    # =============

    def __init__(self, **kwargs):
        # load default configuration file
        config = ConfigParser()
        config.read(os.path.join(os.path.dirname(__file__), 'config.ini'))

        self.conn_kwargs = self.retrieve_options(config, kwargs,
                                                 (('host', str),
                                                  ('port', int)))
        self.req_kwargs = self.retrieve_options(config, kwargs,
                                                (('service', str),
                                                 ('url', str),
                                                 ('timeout', int)))
        self.module = sys.modules['icapclient']

    @staticmethod
    def retrieve_options(config, kwargs, keys):
        options = {}

        # override default values if specified
        for option, type_ in keys:
            value = kwargs.get(option)
            if value is None:
                try:
                    value = config.get('ICAP', option)
                except NoOptionError:
                    pass
            if value is not None:
                options[option] = type_(value)

        return options

    def query_server(self, filename):
        conn = self.module.ICAPConnection(**self.conn_kwargs)
        conn.request('REQMOD', filename, read_content=False, **self.req_kwargs)
        resp = conn.getresponse()
        conn.close()

        # look for the headers defined inside
        # the RFC Draft for ICAP Extensions
        threat = resp.get_icap_header('X-Violations-Found')
        # multiple threats? try to parse the header values
        if threat is not None:
            try:
                values = threat.split('\n')
                # only read the human readable descriptions
                threats = [s.strip() for idx, s
                           in enumerate(values[1:]) if idx % 4 == 1]
                threat = '%s threat(s) found: %s' % \
                         (threats[0].strip(), ', '.join(threats))
            except:
                threat = 'Multiple threats found: %s' % threat
        if threat is None:
            # only a description
            threat = resp.get_icap_header('X-Virus-ID')
            if threat is not None:
                threat = 'Threat found: %s' % threat
        if threat is None:
            threat = resp.get_icap_header('X-Infection-Found')
            if threat is not None:
                # only return the human readable threat name
                cookie = SimpleCookie(threat)
                kv = cookie.get('Threat')
                if kv is not None:
                    threat = kv.value
            if threat is not None:
                threat = 'Threat found: %s' % threat

        return threat

    # ==================
    #  probe interfaces
    # ==================

    def run(self, paths):
        results = PluginResult(name=type(self).plugin_display_name,
                               type=type(self).plugin_category,
                               version=None)
        try:
            # query the ICAP server: issue a REQMOD request
            started = timestamp(datetime.utcnow())
            response = self.query_server(paths)
            stopped = timestamp(datetime.utcnow())
            results.duration = stopped - started
            if response is None:
                results.status = self.ICAPResult.CLEAN
                results.results = 'No threat found'
            else:
                results.status = self.ICAPResult.INFECTED
                results.results = response
        except Exception as e:
            results.status = self.ICAPResult.ERROR
            results.error = type(e).__name__ + " : " + str(e)
        return results
Esempio n. 5
0
class VirusTotalPlugin(PluginBase):
    class VirusTotalResult:
        ERROR = -1
        FOUND = 1
        NOT_FOUND = 0

    # =================
    #  plugin metadata
    # =================

    _plugin_name_ = "VirusTotal"
    _plugin_display_name_ = "VirusTotal"
    _plugin_author_ = "IRMA (c) Quarkslab"
    _plugin_version_ = "1.0.0"
    _plugin_category_ = IrmaProbeType.external
    _plugin_description_ = "Plugin to query VirusTotal API"
    _plugin_dependencies_ = [
        ModuleDependency('virus_total_apis',
                         help='See requirements.txt for needed dependencies'),
        FileDependency(os.path.join(os.path.dirname(__file__), 'config.ini'))
    ]

    # =============
    #  constructor
    # =============

    def __init__(self, apikey=None, private=None):
        # load default configuration file
        config = ConfigParser()
        config.read(os.path.join(os.path.dirname(__file__), 'config.ini'))

        # override default values if specified
        if apikey is None:
            self.apikey = config.get('VirusTotal', 'apikey')
        else:
            self.apikey = apikey

        if private is None:
            self.private = bool(config.get('VirusTotal', 'private'))
        else:
            self.private = private

        # choose either public or private API for requests
        if private:
            module = sys.modules['virus_total_apis'].PrivateApi
        else:
            module = sys.modules['virus_total_apis'].PublicApi
        self.module = module(self.apikey)

    def get_file_report(self, filename):
        with open(filename, 'rb') as filedesc:
            digest = md5sum(filedesc)
        return self.module.get_file_report(digest)

    # ==================
    #  probe interfaces
    # ==================

    def run(self, paths):
        results = PluginResult(name=type(self).plugin_display_name,
                               type=type(self).plugin_category,
                               version=None)
        try:
            # get the report, automatically append results
            started = timestamp(datetime.utcnow())
            response = self.get_file_report(paths)
            stopped = timestamp(datetime.utcnow())
            results.duration = stopped - started
            # check eventually for errors
            if 'error' in response:
                results.status = self.VirusTotalResult.ERROR
                results.error = str(response['error'])
            elif response['response_code'] == 204:
                results.status = self.VirusTotalResult.ERROR
                results.error = "Public API request rate limit exceeded"
            elif response['response_code'] == 403:
                results.status = self.VirusTotalResult.ERROR
                results.error = "Access forbidden (wrong key value or type)"
            elif response['response_code'] == 200 and \
                 response['results']['response_code'] != 1:
                results.status = self.VirusTotalResult.NOT_FOUND
            else:
                results.status = self.VirusTotalResult.FOUND
            results.results = response if 'error' not in response else None
        except Exception as e:
            results.status = self.VirusTotalResult.ERROR
            results.results = str(e)
        return results
Esempio n. 6
0
class YaraPlugin(PluginBase):

    class YaraResult:
        ERROR = -1
        FOUND = 1
        NOT_FOUND = 0

    # =================
    #  plugin metadata
    # =================

    _plugin_name_ = "Yara"
    _plugin_display_name_ = "Yara"
    _plugin_author_ = "Bryan Nolen @BryanNolen"
    _plugin_version_ = "1.0.0"
    _plugin_category_ = IrmaProbeType.metadata
    _plugin_description_ = "Plugin to run files against yara rules"
    _plugin_dependencies_ = [
        ModuleDependency(
            'yara',
            help='Requires yara 3 or greater and matching yara-python'
        ),
        FileDependency(
            os.path.join(os.path.dirname(__file__), 'config.ini')
        )
    ]

    # =============
    #  constructor
    # =============

    def __init__(self, rule_path=None):
        # load default configuration file
        config = ConfigParser()
        config.read(os.path.join(os.path.dirname(__file__), 'config.ini'))

        # override default values if specified
        if rule_path is None:
            self.rule_path = config.get('Yara', 'rule_path')
        else:
            self.rule_path = rule_path

        self.rules = sys.modules['yara'].compile(filepath=self.rule_path)

    def get_file_report(self, filename):
        try:
            results = (False, self.rules.match(filename, timeout=60))
        except Exception as e:
            results = (True, str(e))
        finally:
            return results

    # ==================
    #  probe interfaces
    # ==================

    def run(self, paths):
        results = PluginResult(name=type(self).plugin_display_name,
                               type=type(self).plugin_category,
                               version=None)
        try:
            # get the report, automatically append results
            started = timestamp(datetime.utcnow())
            (error_raised, response) = self.get_file_report(paths)
            stopped = timestamp(datetime.utcnow())
            results.duration = stopped - started
            # check eventually for errors
            if error_raised:
                results.status = self.YaraResult.ERROR
                results.error = response
            elif response.__len__() == 0:
                results.status = self.YaraResult.NOT_FOUND
            else:
                results.status = self.YaraResult.FOUND
            match_string = ""
            matches = []
            if results.status is self.YaraResult.FOUND:
                for match in response:
                    match_string = "{0}, {1}".format(match_string, match)
                    matches.append("{0!s}".format(match))
            results.results = None
            if not error_raised:
                # results.results = {'Matches': "{0}".format(match_string)}
                results.results = {'Matches': matches}
        except Exception as e:
            results.status = self.YaraResult.ERROR
            results.results = str(e)
        return results
Esempio n. 7
0
class NSRLPlugin(PluginBase):

    class NSRLPluginResult:
        ERROR = -1
        FOUND = 1
        NOT_FOUND = 0

    # =================
    #  plugin metadata
    # =================

    _plugin_name_ = "NSRL"
    _plugin_display_name_ = "National Software Reference Library"
    _plugin_author_ = "IRMA (c) Quarkslab"
    _plugin_version_ = "1.0.0"
    _plugin_category_ = IrmaProbeType.database
    _plugin_description_ = "Information plugin to query hashes on " \
                           "NRSL database"
    _plugin_dependencies_ = [
        ModuleDependency(
            'leveldict',
            help='See requirements.txt for needed dependencies'
        ),
        ModuleDependency(
            'modules.database.nsrl.nsrl'
        ),
        FileDependency(
            os.path.join(os.path.dirname(__file__), 'config.ini')
        )
    ]
    _plugin_mimetype_regexp = 'PE32'

    @classmethod
    def verify(cls):
        # load default configuration file
        config = ConfigParser()
        config.read(os.path.join(os.path.dirname(__file__), 'config.ini'))

        os_db = config.get('NSRL', 'nsrl_os_db')
        mfg_db = config.get('NSRL', 'nsrl_mfg_db')
        file_db = config.get('NSRL', 'nsrl_file_db')
        prod_db = config.get('NSRL', 'nsrl_prod_db')
        databases = [os_db, mfg_db, file_db, prod_db]

        # check for configured database path
        results = list(map(os.path.exists, databases))
        dbs_available = reduce(lambda x, y: x or y, results, False)
        if not dbs_available:
            raise PluginLoadError("{0}: verify() failed because "
                                  "databases are not available."
                                  "".format(cls.__name__))

        # check for LOCK file and remove it
        dbs_locks = [os.path.join(x, "LOCK") for x in databases]
        for lock in dbs_locks:
            try:
                if os.path.exists(lock):
                    os.unlink(lock)
            except:
                raise PluginLoadError("unable to remove lock {0}".format(lock))

    # ==================================
    #  constructor and destructor stuff
    # ==================================

    def __init__(self):
        # load default configuration file
        config = ConfigParser()
        config.read(os.path.join(os.path.dirname(__file__), 'config.ini'))

        # get configuration values
        nsrl_os_db = config.get('NSRL', 'nsrl_os_db')
        nsrl_mfg_db = config.get('NSRL', 'nsrl_mfg_db')
        nsrl_file_db = config.get('NSRL', 'nsrl_file_db')
        nsrl_prod_db = config.get('NSRL', 'nsrl_prod_db')

        # lookup module
        module = sys.modules['modules.database.nsrl.nsrl'].NSRL

        self.module = module(nsrl_file_db, nsrl_prod_db,
                             nsrl_os_db, nsrl_mfg_db)

    # ==================
    #  probe interfaces
    # ==================

    def run(self, paths):
        results = PluginResult(name=type(self).display_name,
                               type=type(self).plugin_category,
                               version=None)
        try:
            # lookup the specified sha1
            started = timestamp(datetime.utcnow())
            response = self.module.lookup_by_sha1(sha1sum(paths).upper())
            stopped = timestamp(datetime.utcnow())
            results.duration = stopped - started
            # check for errors
            if isinstance(response, dict) and \
                (not response.get('MfgCode', None) or
                 not response.get('OpSystemCode', None) or
                 not response.get('ProductCode', None) or
                 not response.get('SHA-1', None)):
                results.status = self.NSRLPluginResult.NOT_FOUND
                response = None
            else:
                results.status = self.NSRLPluginResult.FOUND
            results.results = response
        except Exception as e:
            results.status = self.NSRLPluginResult.ERROR
            results.error = str(e)
        return results
Esempio n. 8
0
class PEiDPlugin(PluginBase):
    class PEiDResult:
        ERROR = -1
        FOUND = 1
        NOT_FOUND = 0

    # =================
    #  plugin metadata
    # =================

    _plugin_name_ = "PEiD"
    _plugin_display_name_ = "PEiD PE Packer Identifier"
    _plugin_author_ = "Quarkslab"
    _plugin_version_ = "1.0.0"
    _plugin_category_ = IrmaProbeType.metadata
    _plugin_description_ = "Plugin to run files against PEiD signatures"
    _plugin_dependencies_ = [
        ModuleDependency('pefile',
                         help='See requirements.txt for needed dependencies'),
        ModuleDependency('peutils',
                         help='See requirements.txt for needed dependencies'),
        FileDependency(os.path.join(os.path.dirname(__file__), 'config.ini'))
    ]
    _plugin_mimetype_regexp = 'PE32'

    @classmethod
    def verify(cls):
        # load default configuration file
        config = ConfigParser()
        config.read(os.path.join(os.path.dirname(__file__), 'config.ini'))
        sign_path = config.get('PEiD', 'sign_path')

        # check for configured signatures path
        if not os.path.exists(sign_path):
            raise PluginLoadError("{0}: verify() failed because "
                                  "signatures file not found."
                                  "".format(cls.__name__))

    # =============
    #  constructor
    # =============

    def __init__(self):
        config = ConfigParser()
        config.read(os.path.join(os.path.dirname(__file__), 'config.ini'))
        sign_path = config.get('PEiD', 'sign_path')
        peutils = sys.modules['peutils']
        data = open(sign_path, "r", encoding="utf8", errors="ignore").read()
        self.signatures = peutils.SignatureDatabase(data=data)

    def analyze(self, filename):
        pefile = sys.modules['pefile']
        try:
            pe = pefile.PE(filename)
            results = self.signatures.match(pe)
            if results is None:
                return self.PEiDResult.NOT_FOUND, "No match found"
            else:
                return self.PEiDResult.FOUND, results[0]
        except pefile.PEFormatError:
            return self.PEiDResult.NOT_FOUND, "Not a PE"

    # ==================
    #  probe interfaces
    # ==================

    def run(self, paths):
        results = PluginResult(name=type(self).plugin_display_name,
                               type=type(self).plugin_category,
                               version=None)
        try:
            started = timestamp(datetime.utcnow())
            (status, response) = self.analyze(paths)
            stopped = timestamp(datetime.utcnow())
            results.duration = stopped - started
            results.status = status
            results.results = response
        except Exception as e:
            results.status = self.PEiDResult.ERROR
            results.error = type(e).__name__ + " : " + str(e)
        return results