Ejemplo n.º 1
0
    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()
Ejemplo n.º 2
0
    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()
Ejemplo n.º 3
0
    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) <= 2:
                self.usage()
                return self.res
            elif len(module_argv) > 2:
                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
Ejemplo n.º 4
0
    def run(self, module_argv):
        self.logger.start_test(
            "Check for blocked 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)

        if not self.image:
            if len(module_argv) == 0:
                self.logger.log_important(
                    'Unable to read SPI and generate FW image. Access may be blocked.'
                )
            self.logger.error('No FW image file to read.  Exiting!')
            self.res = ModuleResult.ERROR
            return self.res

        # Load JSON config with blocked 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 blockedlist_json:
            self.efi_blockedlist = json.load(blockedlist_json)

        self.res = self.check_blockedlist()
        return self.res
Ejemplo n.º 5
0
    def run(self):
        try:
            self._spi = SPI(self.cs)
        except SpiRuntimeError as msg:
            self.logger.error(msg)
            return

        self._msg = "it may take a few minutes (use DEBUG or VERBOSE logger options to see progress)"
        t = time.time()
        self.func()

        self.logger.log(
            "[CHIPSEC] (spi) time elapsed {:.3f}".format(time.time() - t))
Ejemplo n.º 6
0
 def __init__(self):
     BaseModule.__init__(self)
     self.spi    = SPI( self.cs )
Ejemplo n.º 7
0
class bios_wp(BaseModule):

    def __init__(self):
        BaseModule.__init__(self)
        self.spi    = SPI( self.cs )

    def is_supported(self):
        return True

    def check_BIOS_write_protection(self):
        self.logger.start_test( "BIOS Region Write Protection" )
        #
        # BIOS Control Register
        #
        #reg_value = chipsec.chipset.read_register(self.cs, 'BC')
        #chipsec.chipset.print_register(self.cs, 'BC', reg_value)

        #ble    = chipsec.chipset.get_register_field(self.cs, 'BC', reg_value, 'BLE')
        ble = self.cs.get_control('BiosLockEnable', with_print=True)
        #bioswe = self.cs.get_register_field('BC', reg_value, 'BIOSWE')
        bioswe = self.cs.get_control('BiosWriteEnable')
        #smmbwp = chipsec.chipset.get_register_field(self.cs, 'BC', reg_value, 'SMM_BWP')
        smmbwp = self.cs.get_control( 'SmmBiosWriteProtection' )

        # Is the BIOS flash region write protected?
        write_protected = 0
        if 1 == ble and 0 == bioswe:
            if 1 == smmbwp:
                self.logger.log_good( "BIOS region write protection is enabled (writes restricted to SMM)" )
                write_protected = 1
            else:
                self.logger.log_important( "Enhanced SMM BIOS region write protection has not been enabled (SMM_BWP is not used)" )
        else:
            self.logger.log_bad( "BIOS region write protection is disabled!" )

        return write_protected == 1

    def check_SPI_protected_ranges(self):
        (bios_base,bios_limit,bios_freg) = self.spi.get_SPI_region( BIOS )
        self.logger.log( "\n[*] BIOS Region: Base = 0x{:08X}, Limit = 0x{:08X}".format(bios_base,bios_limit) )
        self.spi.display_SPI_Protected_Ranges()

        pr_cover_bios = False
        pr_partial_cover_bios = False
    #    for j in range(5):
    #        (base,limit,wpe,rpe,pr_reg_off,pr_reg_value) = spi.get_SPI_Protected_Range( j )
    #        if (wpe == 1 and base < limit and base <= bios_base and limit >= bios_limit):
    #            pr_cover_bios = True
    #        if (wpe == 1 and base < limit and limit > bios_base):
    #            pr_partial_cover_bios = True

        areas_to_protect  = [(bios_base, bios_limit)]
        protected_areas = list()


        for j in range(5):
            (base,limit,wpe,rpe,pr_reg_off,pr_reg_value) = self.spi.get_SPI_Protected_Range( j )
            if base > limit: continue
            if wpe == 1:
                for area in areas_to_protect:
                    # overlap bottom
                    start,end = area
                    if base <= start and limit >= start:
                        if limit >= end:
                            areas_to_protect.remove(area)
                        else:
                            areas_to_protect.remove(area)
                            area = (limit+1,end)
                            areas_to_protect.append(area)

                    # overlap top
                    elif base <= end and limit >= end:
                        if base <= start:
                            areas_to_protect.remove(area)
                        else:
                            areas_to_protect.remove(area)
                            area = (start,base-1)
                            areas_to_protect.append(area)
                            start,end = area
                    # split
                    elif base > start and limit < end:
                        areas_to_protect.remove(area)
                        areas_to_protect.append((start,base-1))
                        areas_to_protect.append((limit+1, end))


        if (len(areas_to_protect)  == 0):
            pr_cover_bios = True
        else:
            if (len(areas_to_protect) != 1 or areas_to_protect[0] != (bios_base,bios_limit)):
                pr_partial_cover_bios = True

        if pr_partial_cover_bios:
            self.logger.log( '' )
            self.logger.log_important( "SPI protected ranges write-protect parts of BIOS region (other parts of BIOS can be modified)" )

        else:
            if not pr_cover_bios:
                self.logger.log( '' )
                self.logger.log_important( "None of the SPI protected ranges write-protect BIOS region" )

        return pr_cover_bios

    # --------------------------------------------------------------------------
    # run( module_argv )
    # Required function: run here all tests from this module
    # --------------------------------------------------------------------------
    def run(self, module_argv ):
        wp = self.check_BIOS_write_protection()
        spr = self.check_SPI_protected_ranges()

        self.logger.log('')
        if wp:
            if spr:  self.logger.log_passed_check( "BIOS is write protected (by SMM and SPI Protected Ranges)" )
            else:    self.logger.log_passed_check( "BIOS is write protected" )
        else:
            if spr:  self.logger.log_passed_check( "SPI Protected Ranges are configured to write protect BIOS" )
            else:
                self.logger.log_important( 'BIOS should enable all available SMM based write protection mechanisms or configure SPI protected ranges to protect the entire BIOS region' )
                self.logger.log_failed_check( "BIOS is NOT protected completely" )

        if wp or spr: return ModuleResult.PASSED
        else: return ModuleResult.FAILED
Ejemplo n.º 8
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
Ejemplo n.º 9
0
class spi_access(BaseModule):
    def __init__(self):
        BaseModule.__init__(self)
        self.spi = SPI(self.cs)

    def is_supported(self):
        return True

    ##
    # Displays the SPI Regions Access Permissions
    def check_flash_access_permissions(self):

        res = ModuleResult.PASSED
        fdv = self.cs.read_register_field('HSFS', 'FDV') == 1
        frap = self.cs.read_register('FRAP')
        brra = self.cs.get_register_field('FRAP', frap, 'BRRA')
        brwa = self.cs.get_register_field('FRAP', frap, 'BRWA')
        if self.cs.is_register_defined('FDOC') and self.cs.is_register_defined(
                'FDOD'):
            self.cs.write_register('FDOC', 0x3000)
            tmp_reg = self.cs.read_register('FDOD')
            brra |= ((tmp_reg >> 8) & 0xFFF)
            brwa |= ((tmp_reg >> 20) & 0xFFF)

        # Informational
        # State of Flash Descriptor Valid bit
        if not fdv:
            self.logger.log("[*] Flash Descriptor Valid bit is not set")

        # CPU/Software access to Platform Data region (platform specific)
        if brwa & (1 << PLATFORM_DATA):
            self.logger.log(
                "[*] Software has write access to Platform Data region in SPI flash (it's platform specific)"
            )

        # Warnings
        # CPU/Software access to GBe region
        if brwa & (1 << GBE):
            res = ModuleResult.WARNING
            self.logger.log_warning(
                "Software has write access to GBe region in SPI flash")

        # Failures
        # CPU/Software access to Flash Descriptor region (Read Only)
        if brwa & (1 << FLASH_DESCRIPTOR):
            res = ModuleResult.FAILED
            self.logger.log_bad(
                "Software has write access to SPI flash descriptor")

        # CPU/Software access to Intel ME region (Read Only)
        if brwa & (1 << ME):
            res = ModuleResult.FAILED
            self.logger.log_bad(
                "Software has write access to Management Engine (ME) region in SPI flash"
            )

        if fdv:
            if ModuleResult.PASSED == res:
                self.logger.log_passed_check(
                    "SPI Flash Region Access Permissions in flash descriptor look ok"
                )
            elif ModuleResult.FAILED == res:
                self.logger.log_failed_check(
                    "SPI Flash Region Access Permissions are not programmed securely in flash descriptor"
                )
            elif ModuleResult.WARNING == res:
                self.logger.log_warn_check(
                    "Certain SPI flash regions are writeable by software")
        else:
            res = ModuleResult.WARNING
            self.logger.log_warn_check(
                "Either flash descriptor is not valid or not present on this system"
            )

        return res

    # --------------------------------------------------------------------------
    # run( module_argv )
    # Required function: run here all tests from this module
    # --------------------------------------------------------------------------
    def run(self, module_argv):
        self.logger.start_test("SPI Flash Region Access Control")
        self.spi.display_SPI_Ranges_Access_Permissions()
        self.res = self.check_flash_access_permissions()
        return self.res
Ejemplo n.º 10
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()
Ejemplo n.º 11
0
class bios_wp(BaseModule):
    def __init__(self):
        BaseModule.__init__(self)
        self.spi = SPI(self.cs)

    def is_supported(self):
        return True

    def check_BIOS_write_protection(self):
        ble = self.cs.get_control('BiosLockEnable', with_print=True)
        bioswe = self.cs.get_control('BiosWriteEnable')
        smmbwp = self.cs.get_control('SmmBiosWriteProtection')

        # Is the BIOS flash region write protected?
        write_protected = 0
        if (1 == ble) and (0 == bioswe):
            if 1 == smmbwp:
                self.logger.log_good(
                    "BIOS region write protection is enabled (writes restricted to SMM)"
                )
                write_protected = 1
            else:
                self.logger.log_important(
                    "Enhanced SMM BIOS region write protection has not been enabled (SMM_BWP is not used)"
                )
        else:
            self.logger.log_bad("BIOS region write protection is disabled!")

        return write_protected == 1

    def check_SPI_protected_ranges(self):
        (bios_base, bios_limit, _) = self.spi.get_SPI_region(BIOS)
        self.logger.log(
            "\n[*] BIOS Region: Base = 0x{:08X}, Limit = 0x{:08X}".format(
                bios_base, bios_limit))
        self.spi.display_SPI_Protected_Ranges()

        pr_cover_bios = False
        pr_partial_cover_bios = False

        areas_to_protect = [(bios_base, bios_limit)]

        for j in range(5):
            (base, limit, wpe, _, _, _) = self.spi.get_SPI_Protected_Range(j)
            if base > limit:
                continue
            if wpe == 1:
                areas = areas_to_protect[:]
                for area in areas:
                    (start, end) = area
                    if (base <= start) and (limit >= start):  # overlap bottom
                        if limit >= end:
                            areas_to_protect.remove(area)
                        else:
                            areas_to_protect.remove(area)
                            area = (limit + 1, end)
                            areas_to_protect.append(area)
                    elif (base <= end) and (limit >= end):  # overlap top
                        if base <= start:
                            areas_to_protect.remove(area)
                        else:
                            areas_to_protect.remove(area)
                            area = (start, base - 1)
                            areas_to_protect.append(area)
                    elif (base > start) and (limit < end):  # split
                        areas_to_protect.remove(area)
                        areas_to_protect.append((start, base - 1))
                        areas_to_protect.append((limit + 1, end))

        if (len(areas_to_protect) == 0):
            pr_cover_bios = True
        else:
            if (len(areas_to_protect) != 1) or (areas_to_protect[0] !=
                                                (bios_base, bios_limit)):
                pr_partial_cover_bios = True

        if pr_partial_cover_bios:
            self.logger.log('')
            self.logger.log_important(
                "SPI protected ranges write-protect parts of BIOS region (other parts of BIOS can be modified)"
            )
        else:
            if not pr_cover_bios:
                self.logger.log('')
                self.logger.log_important(
                    "None of the SPI protected ranges write-protect BIOS region"
                )

        return pr_cover_bios

    # --------------------------------------------------------------------------
    # run( module_argv )
    # Required function: run here all tests from this module
    # --------------------------------------------------------------------------
    def run(self, module_argv):
        self.logger.start_test("BIOS Region Write Protection")
        wp = self.check_BIOS_write_protection()
        spr = self.check_SPI_protected_ranges()

        self.logger.log('')
        if wp:
            if spr:
                self.logger.log_passed(
                    "BIOS is write protected (by SMM and SPI Protected Ranges)"
                )
            else:
                self.logger.log_passed("BIOS is write protected")
        else:
            if spr:
                self.logger.log_passed(
                    "SPI Protected Ranges are configured to write protect BIOS"
                )
            else:
                self.logger.log_important(
                    'BIOS should enable all available SMM based write protection mechanisms.'
                )
                self.logger.log_important(
                    'Or configure SPI protected ranges to protect the entire BIOS region.'
                )
                self.logger.log_failed("BIOS is NOT protected completely")

        if wp or spr:
            self.res = ModuleResult.PASSED
        else:
            self.res = ModuleResult.FAILED

        return self.res
Ejemplo n.º 12
0
 def __init__(self):
     BaseModule.__init__(self)
     self.iobar = iobar( self.cs )
     self.spi   = SPI( self.cs )
Ejemplo n.º 13
0
class bios_smi(BaseModule):

    def __init__(self):
        BaseModule.__init__(self)
        self.iobar = iobar( self.cs )
        self.spi   = SPI( self.cs )
    
    def is_supported(self):
        return (self.cs.get_chipset_id() not in chipsec.chipset.CHIPSET_FAMILY_ATOM)

    def get_PMBASE(self):
        if self.iobar.is_IO_BAR_defined( 'PMBASE' ):
            (io_base,io_size) = self.iobar.get_IO_BAR_base_address( 'PMBASE' )
            return io_base
        else:
            return (self.cs.pci.read_dword( 0, 31, 0, self.cs.Cfg.CFG_REG_PCH_LPC_PMBASE ) & self.cs.Cfg.CFG_REG_PCH_LPC_PMBASE_MASK)
    
    def get_TCOBASE(self):
        return (self.get_PMBASE() + self.cs.Cfg.TCOBASE_ABASE_OFFSET)
    
    def check_SMI_locks(self):

        self.logger.start_test( "SMI Events Configuration" )
    
        #
        # First check SMM_BWP in BIOS control to warn if SMM write-protection of the BIOS is not enabled
        #
        (BcRegister, reg_value) = self.spi.get_BIOS_Control()
        #self.logger.log( BcRegister )
        if 0 == BcRegister.SMM_BWP: self.logger.log_bad( "SMM BIOS region write protection has not been enabled (SMM_BWP is not used)\n" )

        ok = True

        #
        # Check if global SMI is enabled and TCO SMI is enabled (GBL_SMI_EN and TCO_EN in PMBASE[SMI_EN])
        #
        pmbase = self.get_PMBASE()
        self.logger.log("[*] PMBASE (ACPI I/O Base) = 0x%04X" % pmbase )

        smi_en = self.cs.io.read_port_dword( pmbase + self.cs.Cfg.PMBASE_SMI_EN )
        self.logger.log("[*] SMI_EN (SMI Control and Enable) register [I/O port 0x%X] = 0x%08X" % (pmbase + self.cs.Cfg.PMBASE_SMI_EN,smi_en) )
        self.logger.log("    [13] TCO_EN (TCO Enable)            = %u" % ((smi_en & (1<<13)) >> 13) )
        self.logger.log("    [00] GBL_SMI_EN (Global SMI Enable) = %u" % (smi_en & 0x1) )

        if (smi_en & 0x1) != 1:
            ok = False
            self.logger.log_bad( "Global SMI is not enabled" )
        elif ((smi_en & (1<<13)) >> 13) != 1:
            self.logger.warn( "TCO SMI is not enabled. BIOS may not be using it" )
        else: self.logger.log_good( "All required SMI events are enabled" )
    
        #
        # TCO_LOCK TCOBASE I/O register
        #
        tcobase = pmbase + self.cs.Cfg.TCOBASE_ABASE_OFFSET
        self.logger.log("[*] TCOBASE (TCO I/O Base) = 0x%04X" % tcobase )

        tco1_cnt = self.cs.io.read_port_word( tcobase + 0x8 ) # TCO1_CNT (TCOBASE + 0x8 = (ACPIBASE + 0x60) + 0x8)
        self.logger.log("[*] TCO1_CNT (TCO1 Control) register [I/O port 0x%X] = 0x%04X" % (tcobase + 0x8, tco1_cnt) )
        self.logger.log("    [12] TCO_LOCK = %u" % ((tco1_cnt & (1<<12)) >> 12) )

        if ((tco1_cnt & (1<<12)) >> 12) != 1:
            ok = False
            self.logger.log_bad( "TCO SMI event configuration is not locked. TCO SMI events can be disabled" )
        else: self.logger.log_good( "TCO SMI configuration is locked" )
    
        #
        # SMI_LOCK 0:31:0 PCIe CFG register
        #
        gen_pmcon_1 = self.cs.pci.read_word( 0, 31, 0, self.cs.Cfg.GEN_PMCON ) # BDF 0:31:0 offset 0xA0 (GEN_PMCON_1), SMI_LOCK is bit 0
        self.logger.log("[*] GEN_PMCON_1 (General PM Config 1) register [BDF 0:31:0 + 0x%X] = 0x%04X" % (self.cs.Cfg.GEN_PMCON, gen_pmcon_1) )
        self.logger.log("    [04] SMI_LOCK = %u" % ((gen_pmcon_1 & (1<<4)) >> 4) )

        if ((gen_pmcon_1 & (1<<4)) >> 4) != 1:
            ok = False
            self.logger.log_bad( "SMI events global configuration is not locked. SMI events can be disabled" )
        else: self.logger.log_good( "SMI events global configuration is locked" )
    
        if ok:
            res = ModuleResult.PASSED
            self.logger.log_passed_check( "All required SMI sources seem to be enabled and locked!" )
        else:
            if BcRegister.SMM_BWP == 1:
                res = ModuleResult.WARNING
                self.logger.log_warn_check( "Not all required SMI sources are enabled and locked, but SPI flash writes are still restricted to SMM." )
            else:
                res = ModuleResult.FAILED
                self.logger.log_failed_check( "Not all required SMI sources are enabled and locked!" )
        return res
    
    
    
    # --------------------------------------------------------------------------
    # run( module_argv )
    # Required function: run here all tests from this module
    # --------------------------------------------------------------------------
    def run( self, module_argv ):
        return self.check_SMI_locks()
Ejemplo n.º 14
0
class bios_smi(BaseModule):
    def __init__(self):
        BaseModule.__init__(self)
        self.iobar = iobar(self.cs)
        self.spi = SPI(self.cs)

    def is_supported(self):
        return (self.cs.get_chipset_id()
                not in chipsec.chipset.CHIPSET_FAMILY_ATOM)

    def get_PMBASE(self):
        if self.iobar.is_IO_BAR_defined('PMBASE'):
            (io_base, io_size) = self.iobar.get_IO_BAR_base_address('PMBASE')
            return io_base
        else:
            return (self.cs.pci.read_dword(0, 31, 0,
                                           self.cs.Cfg.CFG_REG_PCH_LPC_PMBASE)
                    & self.cs.Cfg.CFG_REG_PCH_LPC_PMBASE_MASK)

    def get_TCOBASE(self):
        return (self.get_PMBASE() + self.cs.Cfg.TCOBASE_ABASE_OFFSET)

    def check_SMI_locks(self):

        self.logger.start_test("SMI Events Configuration")

        #
        # First check SMM_BWP in BIOS control to warn if SMM write-protection of the BIOS is not enabled
        #
        (BcRegister, reg_value) = self.spi.get_BIOS_Control()
        #self.logger.log( BcRegister )
        if 0 == BcRegister.SMM_BWP:
            self.logger.log_bad(
                "SMM BIOS region write protection has not been enabled (SMM_BWP is not used)\n"
            )

        ok = True

        #
        # Check if global SMI is enabled and TCO SMI is enabled (GBL_SMI_EN and TCO_EN in PMBASE[SMI_EN])
        #
        pmbase = self.get_PMBASE()
        self.logger.log("[*] PMBASE (ACPI I/O Base) = 0x%04X" % pmbase)

        smi_en = self.cs.io.read_port_dword(pmbase + self.cs.Cfg.PMBASE_SMI_EN)
        self.logger.log(
            "[*] SMI_EN (SMI Control and Enable) register [I/O port 0x%X] = 0x%08X"
            % (pmbase + self.cs.Cfg.PMBASE_SMI_EN, smi_en))
        self.logger.log("    [13] TCO_EN (TCO Enable)            = %u" %
                        ((smi_en & (1 << 13)) >> 13))
        self.logger.log("    [00] GBL_SMI_EN (Global SMI Enable) = %u" %
                        (smi_en & 0x1))

        if (smi_en & 0x1) != 1:
            ok = False
            self.logger.log_bad("Global SMI is not enabled")
        elif ((smi_en & (1 << 13)) >> 13) != 1:
            self.logger.warn(
                "TCO SMI is not enabled. BIOS may not be using it")
        else:
            self.logger.log_good("All required SMI events are enabled")

        #
        # TCO_LOCK TCOBASE I/O register
        #
        tcobase = pmbase + self.cs.Cfg.TCOBASE_ABASE_OFFSET
        self.logger.log("[*] TCOBASE (TCO I/O Base) = 0x%04X" % tcobase)

        tco1_cnt = self.cs.io.read_port_word(
            tcobase +
            0x8)  # TCO1_CNT (TCOBASE + 0x8 = (ACPIBASE + 0x60) + 0x8)
        self.logger.log(
            "[*] TCO1_CNT (TCO1 Control) register [I/O port 0x%X] = 0x%04X" %
            (tcobase + 0x8, tco1_cnt))
        self.logger.log("    [12] TCO_LOCK = %u" % ((tco1_cnt &
                                                     (1 << 12)) >> 12))

        if ((tco1_cnt & (1 << 12)) >> 12) != 1:
            ok = False
            self.logger.log_bad(
                "TCO SMI event configuration is not locked. TCO SMI events can be disabled"
            )
        else:
            self.logger.log_good("TCO SMI configuration is locked")

        #
        # SMI_LOCK 0:31:0 PCIe CFG register
        #
        gen_pmcon_1 = self.cs.pci.read_word(
            0, 31, 0, self.cs.Cfg.GEN_PMCON
        )  # BDF 0:31:0 offset 0xA0 (GEN_PMCON_1), SMI_LOCK is bit 0
        self.logger.log(
            "[*] GEN_PMCON_1 (General PM Config 1) register [BDF 0:31:0 + 0x%X] = 0x%04X"
            % (self.cs.Cfg.GEN_PMCON, gen_pmcon_1))
        self.logger.log("    [04] SMI_LOCK = %u" % ((gen_pmcon_1 &
                                                     (1 << 4)) >> 4))

        if ((gen_pmcon_1 & (1 << 4)) >> 4) != 1:
            ok = False
            self.logger.log_bad(
                "SMI events global configuration is not locked. SMI events can be disabled"
            )
        else:
            self.logger.log_good("SMI events global configuration is locked")

        if ok:
            res = ModuleResult.PASSED
            self.logger.log_passed_check(
                "All required SMI sources seem to be enabled and locked!")
        else:
            if BcRegister.SMM_BWP == 1:
                res = ModuleResult.WARNING
                self.logger.log_warn_check(
                    "Not all required SMI sources are enabled and locked, but SPI flash writes are still restricted to SMM."
                )
            else:
                res = ModuleResult.FAILED
                self.logger.log_failed_check(
                    "Not all required SMI sources are enabled and locked!")
        return res

    # --------------------------------------------------------------------------
    # run( module_argv )
    # Required function: run here all tests from this module
    # --------------------------------------------------------------------------
    def run(self, module_argv):
        return self.check_SMI_locks()
Ejemplo n.º 15
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()