Пример #1
0
class blacklist(BaseModule):
    def __init__(self):
        BaseModule.__init__(self)
        self.uefi = UEFI(self.cs)
        self.cfg_name = 'blacklist.json'
        self.image = None
        self.efi_blacklist = None

    def is_supported(self):
        return True

    def blacklist_callback(self, efi_module):
        return check_match_criteria(efi_module, self.efi_blacklist,
                                    self.logger)

    def check_blacklist(self):
        res = ModuleResult.PASSED

        self.logger.log(
            "[*] searching for EFI binaries that match criteria from '{}':".
            format(self.cfg_name))
        for k in self.efi_blacklist.keys():
            entry = self.efi_blacklist[k]
            self.logger.log("    {:16} - {}".format(
                k, entry['description'] if 'description' in entry else ''))
            #if 'match' in entry:
            #    for c in entry['match'].keys(): self.logger.log( "[*]   {}".format(entry['match'][c]) )
            #if 'exclude' in entry:
            #    self.logger.log( "[*]   excluding binaries:" )
            #    for c in entry['exclude']: self.logger.log( "[*]   {}".format(entry['exclude'][c]) )

        # parse the UEFI firmware image and look for EFI modules matching the balck-list
        efi_tree = build_efi_model(self.uefi, self.image, None)
        #match_types = (spi_uefi.EFIModuleType.SECTION_EXE|spi_uefi.EFIModuleType.FILE)
        match_types = EFIModuleType.SECTION_EXE
        matching_modules = search_efi_tree(efi_tree, self.blacklist_callback,
                                           match_types)
        found = len(matching_modules) > 0
        self.logger.log('')
        if found:
            res = ModuleResult.WARNING
            self.logger.log_warn_check(
                "Black-listed EFI binary found in the UEFI firmware image")
        else:
            self.logger.log_passed_check(
                "Didn't find any black-listed EFI binary")
        return res

    def usage(self):
        self.logger.log(USAGE_TEXT)

    # --------------------------------------------------------------------------
    # run( module_argv )
    # Required function: run here all tests from this module
    # --------------------------------------------------------------------------
    def run(self, module_argv):
        self.logger.start_test(
            "Check for black-listed EFI binaries in UEFI firmware")

        self.usage()

        image_file = DEF_FWIMAGE_FILE
        if len(module_argv) == 0:
            # Read firmware image directly from SPI flash memory
            self.spi = SPI(self.cs)
            (base, limit, freg) = self.spi.get_SPI_region(BIOS)
            image_size = limit + 1 - base
            self.logger.log(
                "[*] dumping FW image from ROM to {}: 0x{:08X} bytes at [0x{:08X}:0x{:08X}]"
                .format(image_file, base, limit, image_size))
            self.logger.log(
                "[*] this may take a few minutes (instead, use 'chipsec_util spi dump')..."
            )
            self.spi.read_spi_to_file(base, image_size, image_file)
        elif len(module_argv) > 0:
            # Use provided firmware image
            image_file = module_argv[0]
            self.logger.log(
                "[*] reading FW image from file: {}".format(image_file))

        self.image = read_file(image_file)

        # Load JSON config with black-listed EFI modules
        if len(module_argv) > 1: self.cfg_name = module_argv[1]
        cfg_pth = os.path.join(get_main_dir(), "chipsec/modules/tools/uefi",
                               self.cfg_name)
        with open(cfg_pth, 'r') as blacklist_json:
            self.efi_blacklist = json.load(blacklist_json)

        return self.check_blacklist()
Пример #2
0
class scan_image(BaseModule):
    def __init__(self):
        BaseModule.__init__(self)
        self.uefi = UEFI(self.cs)
        self.image = None
        self.efi_list = None
        self.suspect_modules = {}

    def is_supported(self):
        return True

    #
    # callbacks to uefi_search.check_match_criteria
    #
    def genlist_callback(self, efi_module):
        md = {}
        if type(efi_module) == EFI_SECTION:
            #if efi_module.MD5:        md["md5"]     = efi_module.MD5
            if efi_module.SHA256: md["sha1"] = efi_module.SHA1
            if efi_module.parentGuid: md["guid"] = efi_module.parentGuid
            if efi_module.ui_string: md["name"] = efi_module.ui_string
            if efi_module.Name and efi_module.Name != SECTION_NAMES[
                    EFI_SECTION_PE32]:
                md["type"] = efi_module.Name
            self.efi_list[efi_module.SHA256] = md
        else:
            pass

    #
    # Generates new list of EFI executable binaries
    #
    def generate_efilist(self, json_pth):
        self.efi_list = {}
        self.logger.log(
            "[*] generating a list of EFI executables from firmware image...")
        efi_tree = build_efi_model(self.uefi, self.image, None)
        matching_modules = search_efi_tree(efi_tree, self.genlist_callback,
                                           EFIModuleType.SECTION_EXE, True)
        self.logger.log(
            "[*] found {:d} EFI executables in UEFI firmware image '{}'".
            format(len(self.efi_list), self.image_file))
        self.logger.log("[*] creating JSON file '{}'...".format(json_pth))
        write_file(
            "{}".format(json_pth),
            json.dumps(self.efi_list,
                       indent=2,
                       separators=(',', ': '),
                       cls=UUIDEncoder))
        return ModuleResult.PASSED

    #
    # Checks EFI executable binaries against allowed list
    #
    def check_list(self, json_pth):
        self.efi_list = {}
        with open(json_pth) as data_file:
            self.efilist = json.load(data_file)

        self.logger.log(
            "[*] checking EFI executables against the list '{}'".format(
                json_pth))

        # parse the UEFI firmware image and look for EFI modules matching list
        # - match only executable EFI sections (PE/COFF, TE)
        # - find all occurrences of matching EFI modules
        efi_tree = build_efi_model(self.uefi, self.image, None)
        matching_modules = search_efi_tree(efi_tree, self.genlist_callback,
                                           EFIModuleType.SECTION_EXE, True)
        self.logger.log(
            "[*] found {:d} EFI executables in UEFI firmware image '{}'".
            format(len(self.efi_list), self.image_file))

        for m in self.efi_list:
            if not (m in self.efilist):
                self.suspect_modules[m] = self.efi_list[m]
                guid = self.efi_list[m]["guid"] if 'guid' in self.efi_list[
                    m] else '?'
                name = self.efi_list[m]["name"] if 'name' in self.efi_list[
                    m] else '<unknown>'
                sha1 = self.efi_list[m]["sha1"] if 'sha1' in self.efi_list[
                    m] else ''
                self.logger.log_important(
                    "found EFI executable not in the list:\n    {} (sha256)\n    {} (sha1)\n    {{{}}}\n    {}"
                    .format(m, sha1, guid, name))

        if len(self.suspect_modules) > 0:
            self.logger.log_warn_check(
                "found {:d} EFI executables not in the list '{}'".format(
                    len(self.suspect_modules), json_pth))
            return ModuleResult.WARNING
        else:
            self.logger.log_passed_check(
                "all EFI executables match the list '{}'".format(json_pth))
            return ModuleResult.PASSED

    def usage(self):
        self.logger.log(USAGE_TEXT)

    # --------------------------------------------------------------------------
    # run( module_argv )
    # Required function: run here all tests from this module
    # --------------------------------------------------------------------------
    def run(self, module_argv):
        self.logger.start_test(
            "simple list generation/checking for (U)EFI firmware")

        self.res = ModuleResult.SKIPPED

        op = module_argv[0] if len(module_argv) > 0 else 'generate'

        if op in ['generate', 'check']:

            if len(module_argv) > 1:
                json_file = module_argv[1]
                image_file = module_argv[2]
                self.logger.log(
                    "[*] reading firmware from '{}'...".format(image_file))
            else:
                image_file = DEF_FWIMAGE_FILE
                json_file = DEF_EFILIST_FILE
                self.spi = SPI(self.cs)
                (base, limit, freg) = self.spi.get_SPI_region(BIOS)
                image_size = limit + 1 - base
                self.logger.log(
                    "[*] dumping firmware image from ROM to '{}': 0x{:08X} bytes at [0x{:08X}:0x{:08X}]"
                    .format(image_file, image_size, base, limit))
                self.spi.read_spi_to_file(base, image_size, image_file)

            self.image_file = image_file
            self.image = read_file(image_file)
            json_pth = os.path.abspath(json_file)

            if op == 'generate':
                if os.path.exists(json_pth):
                    self.logger.error(
                        "JSON file '{}' already exists. Exiting...".format(
                            json_file))
                    self.res = ModuleResult.ERROR
                else:
                    self.res = self.generate_efilist(json_pth)
            elif op == 'check':
                if not os.path.exists(json_pth):
                    self.logger.error(
                        "JSON file '{}' doesn't exists. Exiting...".format(
                            json_file))
                    self.res = ModuleResult.ERROR
                else:
                    self.res = self.check_list(json_pth)

        elif op == 'help':
            self.usage()
        else:
            self.logger.error(
                "unrecognized command-line argument to the module")
            self.usage()

        return self.res
Пример #3
0
class reputation(BaseModule):
    def __init__(self):
        BaseModule.__init__(self)
        self.uefi = UEFI(self.cs)
        self.image = None
        self.vt_threshold = 10
        self.vt = None

    def is_supported(self):
        if has_virus_total_apis:
            return True
        else:
            self.logger.log_important(
                """Can't import module 'virus_total_apis'.
Please run 'pip install virustotal-api' and try again.""")
            return False

    def reputation_callback(self, efi_module):
        vt_report = self.vt.get_file_report(efi_module.SHA256)

        while vt_report["response_code"] == 204:
            # The Public API is limited to 4 requests per minute.
            if self.logger.DEBUG:
                self.logger.log(
                    "VT API quota exceeded, sleeping for 1 minute... (-.-)zzZZ"
                )

            time.sleep(60)
            # Retry.
            vt_report = self.vt.get_file_report(efi_module.SHA256)

        if vt_report["results"]["response_code"] == 0:
            # Hash is unknown to VT.
            self.logger.log_warn_check(
                "Unfamiliar EFI binary found in the UEFI firmware image\n{}".
                format(efi_module))
            return False

        if vt_report["results"]["positives"] >= self.vt_threshold:
            self.logger.log_bad(
                "Suspicious EFI binary found in the UEFI firmware image\n{}".
                format(efi_module))
            return True

        if self.logger.VERBOSE:
            self.logger.log(
                "Benign EFI binary found in the UEFI firmware image\n{}".
                format(efi_module))

        return False

    def check_reputation(self):
        res = ModuleResult.PASSED

        # parse the UEFI firmware image and look for EFI modules matching the balck-list
        efi_tree = build_efi_model(self.uefi, self.image, None)
        match_types = EFIModuleType.SECTION_EXE
        matching_modules = search_efi_tree(efi_tree, self.reputation_callback,
                                           match_types)
        found = len(matching_modules) > 0
        self.logger.log('')
        if found:
            res = ModuleResult.WARNING
            self.logger.log_warn_check(
                "Suspicious EFI binary found in the UEFI firmware image")
        else:
            self.logger.log_passed_check(
                "Didn't find any suspicious EFI binary")
        return res

    def usage(self):
        self.logger.log(USAGE_TEXT)

    # --------------------------------------------------------------------------
    # run( module_argv )
    # Required function: run here all tests from this module
    # --------------------------------------------------------------------------
    def run(self, module_argv):
        self.logger.start_test(
            "Check for suspicious EFI binaries in UEFI firmware")

        self.usage()

        if len(module_argv) > 0:
            self.vt = VirusTotalPublicApi(module_argv[0])
        if len(module_argv) > 1:
            self.vt_threshold = int(module_argv[1])

        image_file = DEF_FWIMAGE_FILE
        if len(module_argv) > 2:
            # Use provided firmware image
            image_file = module_argv[2]
            self.logger.log(
                "[*] reading FW image from file: {}".format(image_file))
        else:
            # Read firmware image directly from SPI flash memory
            self.spi = SPI(self.cs)
            (base, limit, freg) = self.spi.get_SPI_region(BIOS)
            image_size = limit + 1 - base
            self.logger.log(
                "[*] dumping FW image from ROM to {}: 0x{:08X} bytes at [0x{:08X}:0x{:08X}]"
                .format(image_file, base, limit, image_size))
            self.logger.log(
                "[*] this may take a few minutes (instead, use 'chipsec_util spi dump')..."
            )
            self.spi.read_spi_to_file(base, image_size, image_file)

        self.image = read_file(image_file)

        return self.check_reputation()