Exemple #1
0
    def __init__(self):
        BaseModule.__init__(self)
        self._uefi = UEFI(self.cs)

        nv = EFI_VARIABLE_NON_VOLATILE
        bs = EFI_VARIABLE_BOOTSERVICE_ACCESS
        rt = EFI_VARIABLE_RUNTIME_ACCESS
        ta = EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS

        self.uefispec_vars = {
            #### From UEFI Spec Table 11 "Global Variables"
            "LangCodes": bs | rt,
            "Lang": nv | bs | rt,
            "Timeout": nv | bs | rt,
            "PlatformLangCodes": bs | rt,
            "PlatformLang": nv | bs | rt,
            "ConIn": nv | bs | rt,
            "ConOut": nv | bs | rt,
            "ErrOut": nv | bs | rt,
            "ConInDev": bs | rt,
            "ConOutDev": bs | rt,
            "ErrOutDev": bs | rt,
            "Boot0001": nv | bs | rt,
            "Boot0002": nv | bs | rt,
            "BootOrder": nv | bs | rt,
            "BootNext": nv | bs | rt,
            "BootCurrent": bs | rt,
            "BootOptionSupport": bs | rt,
            "Driver0001": nv | bs | rt,
            "DriverOrder": nv | bs | rt,
            "Key0001": nv | bs | rt,
            "HwErrRecSupport": nv | bs | rt,  # HwErrRecSupport should be RO
            "SetupMode": bs | rt,  # SetupMode should be RO
            "KEK": nv | bs | rt | ta,
            "PK": nv | bs | rt | ta,
            "SignatureSupport": bs | rt,  # RO
            "SecureBoot": bs | rt,  # RO
            "KEKDefault": bs | rt,  # RO
            "PKDefault": bs | rt,  # RO
            "dbDefault": bs | rt,  # RO
            "dbxDefault": bs | rt,  # RO
            "dbtDefault": bs | rt,  # RO
            "OsIndicationsSupported": bs | rt,  # RO
            "OsIndications": nv | bs | rt,
            "VendorKeys": bs | rt  # RO
        }

        self.uefispec_ro_vars = ("HwErrRecSupport", "SetupMode",
                                 "SignatureSupport", "SecureBoot",
                                 "KEKDefault", "PKDefault", "dbDefault",
                                 "dbxDefault", "dbtDefault",
                                 "OsIndicationsSupported", "VendorKeys")
Exemple #2
0
    def decode_rom(self):
        _uefi = UEFI(self.cs)
        self.logger.log(
            "[CHIPSEC] Decoding SPI ROM image from a file '{}'".format(
                self._rom))
        f = read_file(self._rom)
        if not f:
            return False
        (fd_off, fd) = get_spi_flash_descriptor(f)
        if (-1 == fd_off) or (fd is None):
            self.logger.error(
                "Could not find SPI Flash descriptor in the binary '{}'".
                format(self._rom))
            self.logger.log_information(
                "To decode an image without a flash decriptor try chipsec_util uefi decode"
            )
            return False

        self.logger.log(
            "[CHIPSEC] Found SPI Flash descriptor at offset 0x{:X} in the binary '{}'"
            .format(fd_off, self._rom))
        rom = f[fd_off:]

        # Decoding SPI Flash Regions
        flregs = get_spi_regions(fd)
        if flregs is None:
            self.logger.error("SPI Flash descriptor region is not valid")
            self.logger.log_information(
                "To decode an image with an invalid flash decriptor try chipsec_util uefi decode"
            )
            return False

        _orig_logname = self.logger.LOG_FILE_NAME

        pth = os.path.join(self.cs.helper.getcwd(), self._rom + ".dir")
        if not os.path.exists(pth):
            os.makedirs(pth)

        for r in flregs:
            idx = r[0]
            name = r[1]
            base = r[3]
            limit = r[4]
            notused = r[5]
            if not notused:
                region_data = rom[base:limit + 1]
                fname = os.path.join(
                    pth,
                    '{:d}_{:04X}-{:04X}_{}.bin'.format(idx, base, limit, name))
                write_file(fname, region_data)
                if FLASH_DESCRIPTOR == idx:
                    # Decoding Flash Descriptor
                    self.logger.set_log_file(os.path.join(pth, fname + '.log'))
                    parse_spi_flash_descriptor(self.cs, region_data)
                elif BIOS == idx:
                    # Decoding EFI Firmware Volumes
                    self.logger.set_log_file(os.path.join(pth, fname + '.log'))
                    decode_uefi_region(_uefi, pth, fname, self._fwtype)

        self.logger.set_log_file(_orig_logname)
Exemple #3
0
 def run(self):
     t = time.time()
     self._uefi = UEFI(self.cs)
     self.func()
     self.logger.log(
         "[CHIPSEC] (uefi) time elapsed {:.3f}".format(time.time() - t))
     return
Exemple #4
0
class access_uefispec(BaseModule):
    def __init__(self):
        BaseModule.__init__(self)
        self._uefi = UEFI(self.cs)

        nv = EFI_VARIABLE_NON_VOLATILE
        bs = EFI_VARIABLE_BOOTSERVICE_ACCESS
        rt = EFI_VARIABLE_RUNTIME_ACCESS
        ta = EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS

        self.uefispec_vars = {
            #### From UEFI Spec Table 11 "Global Variables"
            "LangCodes": bs | rt,
            "Lang": nv | bs | rt,
            "Timeout": nv | bs | rt,
            "PlatformLangCodes": bs | rt,
            "PlatformLang": nv | bs | rt,
            "ConIn": nv | bs | rt,
            "ConOut": nv | bs | rt,
            "ErrOut": nv | bs | rt,
            "ConInDev": bs | rt,
            "ConOutDev": bs | rt,
            "ErrOutDev": bs | rt,
            "Boot0001": nv | bs | rt,
            "Boot0002": nv | bs | rt,
            "BootOrder": nv | bs | rt,
            "BootNext": nv | bs | rt,
            "BootCurrent": bs | rt,
            "BootOptionSupport": bs | rt,
            "Driver0001": nv | bs | rt,
            "DriverOrder": nv | bs | rt,
            "Key0001": nv | bs | rt,
            "HwErrRecSupport": nv | bs | rt,  # HwErrRecSupport should be RO
            "SetupMode": bs | rt,  # SetupMode should be RO
            "KEK": nv | bs | rt | ta,
            "PK": nv | bs | rt | ta,
            "SignatureSupport": bs | rt,  # RO
            "SecureBoot": bs | rt,  # RO
            "KEKDefault": bs | rt,  # RO
            "PKDefault": bs | rt,  # RO
            "dbDefault": bs | rt,  # RO
            "dbxDefault": bs | rt,  # RO
            "dbtDefault": bs | rt,  # RO
            "OsIndicationsSupported": bs | rt,  # RO
            "OsIndications": nv | bs | rt,
            "VendorKeys": bs | rt  # RO
        }

        self.uefispec_ro_vars = ("HwErrRecSupport", "SetupMode",
                                 "SignatureSupport", "SecureBoot",
                                 "KEKDefault", "PKDefault", "dbDefault",
                                 "dbxDefault", "dbtDefault",
                                 "OsIndicationsSupported", "VendorKeys")

    def is_supported(self):
        supported = self.cs.helper.EFI_supported()
        if not supported:
            self.logger.log("OS does not support UEFI Runtime API")
            self.res = ModuleResult.NOTAPPLICABLE
        return supported

    def diff_var(self, data1, data2):
        if data1 is None or data2 is None:
            return data1 != data2

        oldstr = ":".join("{:02x}".format(c) for c in data1)
        newstr = ":".join("{:02x}".format(c) for c in data2)

        if oldstr != newstr:
            print(oldstr)
            print(newstr)
            return True
        else:
            return False

    def can_modify(self, name, guid, data):
        ret = False

        #origdata = _uefi.get_EFI_variable(name, guid)
        origdata = data
        datalen = len(bytearray(data))
        baddata = 'Z' * datalen  #0x5A is ASCII 'Z'
        if baddata == origdata:
            baddata = 'A' * datalen  #in case we failed to restore previously
        status = self._uefi.set_EFI_variable(name, guid, baddata)
        if status != StatusCode.EFI_SUCCESS:
            self.logger.log_good(
                'Writing EFI variable {} did not succeed.'.format(name))
        newdata = self._uefi.get_EFI_variable(name, guid)
        if self.diff_var(newdata, origdata):
            self.logger.log_bad(
                'Corruption of EFI variable of concern {}. Trying to recover.'.
                format(name))
            ret = True
            self._uefi.set_EFI_variable(name, guid, origdata)
            if self.diff_var(self._uefi.get_EFI_variable(name, guid),
                             origdata):
                nameguid = name + ' (' + guid + ')'
                self.logger.log_bad(
                    'RECOVERY FAILED. Variable {} remains corrupted. Original data value: {}'
                    .format(nameguid, origdata))
        return ret

    def check_vars(self, do_modify):
        res = ModuleResult.PASSED
        vars = self._uefi.list_EFI_variables()
        if vars is None:
            self.logger.log_warning(
                'Could not enumerate UEFI Variables from runtime.')
            self.logger.log_important(
                "Note that UEFI variables may still exist, OS just did not expose runtime UEFI Variable API to read them.\nYou can extract variables directly from ROM file via 'chipsec_util.py uefi nvram bios.bin' command and verify their attributes manually."
            )
            return ModuleResult.SKIPPED

        uefispec_concern = []
        ro_concern = []
        rw_variables = []

        self.logger.log('[*] Testing UEFI variables ..')
        for name in vars.keys():
            if name is None: pass
            if vars[name] is None:
                pass

            if len(vars[name]) > 1:
                self.logger.log_important(
                    'Found two instances of the variable {}.'.format(name))
            for (off, buf, hdr, data, guid, attrs) in vars[name]:
                self.logger.log('[*] Variable {} ({})'.format(
                    name, get_attr_string(attrs)))
                perms = self.uefispec_vars.get(name)
                if perms is not None:
                    if perms != attrs:
                        attr_diffs = (perms ^ attrs)
                        extra_attr = attr_diffs & attrs
                        missing_attr = attr_diffs & ~extra_attr
                        uefispec_concern.append(name)
                        if extra_attr != 0:
                            self.logger.log_important(
                                '  Extra attributes:' +
                                get_attr_string(extra_attr))
                            if (extra_attr & ~(
                                    EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS |
                                    EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS
                                    | EFI_VARIABLE_APPEND_WRITE) != 0):
                                res = ModuleResult.FAILED
                        if missing_attr != 0:
                            self.logger.log_important(
                                '  Missing attributes:' +
                                get_attr_string(missing_attr))
                        if res != ModuleResult.FAILED:
                            res = ModuleResult.WARNING

                if do_modify:
                    self.logger.log(
                        "[*] Testing modification of {} ..".format(name))
                    if name in self.uefispec_ro_vars:
                        if self.can_modify(name, guid, data):
                            ro_concern.append(name)
                            self.logger.log_bad(
                                "Variable {} should be read only.".format(
                                    name))
                            res = ModuleResult.FAILED
                    else:
                        if self.can_modify(name, guid, data):
                            rw_variables.append(name)

        if uefispec_concern:
            self.logger.log('')
            self.logger.log_bad(
                'Variables with attributes that differ from UEFI spec:')
            for name in uefispec_concern:
                self.logger.log('    {}'.format(name))

        if do_modify:
            if ro_concern:
                self.logger.log('')
                self.logger.log_bad(
                    'Variables that should have been read-only and were not:')
                for name in ro_concern:
                    self.logger.log('    {}'.format(name))

            if rw_variables:
                self.logger.log('')
                self.logger.log_unknown(
                    'Variables that are read-write (manual investigation is required):'
                )
                for name in rw_variables:
                    self.logger.log('    {}'.format(name))

        self.logger.log('')

        if ModuleResult.PASSED == res:
            self.logger.log_passed(
                'All checked EFI variables are protected according to spec.')
        elif ModuleResult.FAILED == res:
            self.logger.log_failed(
                'Some EFI variables were not protected according to spec.')
        return res

    def run(self, module_argv):
        self.logger.start_test("Access Control of EFI Variables")

        do_modify = (len(module_argv) > 0 and module_argv[0] == OPT_MODIFY)
        self.res = self.check_vars(do_modify)
        return self.res
Exemple #5
0
 def __init__(self):
     BaseModule.__init__(self)
     self._uefi = UEFI( self.cs )
Exemple #6
0
 def __init__(self):
     BaseModule.__init__(self)
     self.uefi = UEFI(self.cs)
     self.image = None
     self.efi_list = None
     self.suspect_modules = {}
Exemple #7
0
 def __init__(self):
     BaseModule.__init__(self)
     self.uefi = UEFI(self.cs)
     self.cfg_name = 'blacklist.json'
     self.image = None
     self.efi_blacklist = None
Exemple #8
0
class s3bootscript(BaseModule):

    def __init__(self):
        BaseModule.__init__(self)
        self._uefi = UEFI( self.cs )

    def is_supported(self):
        supported = self.cs.helper.EFI_supported()
        if not supported: self.logger.log_skipped_check( "OS does not support UEFI Runtime API" )
        return supported

    def is_inside_SMRAM( self, pa ):
        #return ( (self.smrrbase & self.smrrmask) == ( pa & self.smrrmask ) )
        return ( pa >= self.smrambase and pa < self.smramlimit)

    def is_inside_SPI( self, pa ):
        return ( pa >= (BOUNDARY_4GB - HIGH_BIOS_RANGE_SIZE) and pa < BOUNDARY_4GB )

    def check_dispatch_opcodes( self, bootscript_entries ):
        self.logger.log( '[*] Checking entry-points of Dispatch opcodes..' )
        dispatch_ep_ok = True
        n_dispatch = 0
        for e in bootscript_entries:
            if e.decoded_opcode is None: continue
            if S3BootScriptOpcode.EFI_BOOT_SCRIPT_DISPATCH_OPCODE   == e.decoded_opcode.opcode:
               #chipsec.hal.uefi_common.S3BootScriptOpcode.EFI_BOOT_SCRIPT_DISPATCH_2_OPCODE == e.decoded_opcode.opcode:
                n_dispatch += 1
                dispatchstr = "Dispatch opcode (off 0x{:04X}) with entry-point 0x{:016X}".format(e.offset_in_script, e.decoded_opcode.entrypoint)
                #self.logger.log( e )
                if not self.is_inside_SMRAM( e.decoded_opcode.entrypoint ) and not self.is_inside_SPI( e.decoded_opcode.entrypoint ):
                    dispatch_ep_ok = False
                    self.logger.log_bad( dispatchstr + " > UNPROTECTED"  )
                else:
                    self.logger.log_good( dispatchstr + " > PROTECTED" )
        self.logger.log( "[*] Found {:d} Dispatch opcodes".format(n_dispatch) )
        return dispatch_ep_ok

    def check_s3_bootscript( self, bootscript_pa ):
        res = BOOTSCRIPT_OK
        self.logger.log( "[*] Checking S3 boot-script at 0x{:016X}".format(bootscript_pa) )
        # Checking if it's in SMRAM
        scriptInsideSMRAM = self.is_inside_SMRAM( bootscript_pa )
        if scriptInsideSMRAM:
            res |= BOOTSCRIPT_INSIDE_SMRAM
            self.logger.log_good( 'S3 boot-script is in SMRAM' )
            self.logger.log_important( "Note: the test could not verify Dispatch opcodes because the script is in SMRAM. Entry-points of Dispatch opcodes also need to be protected." )
        else:
            res |= BOOTSCRIPT_OUTSIDE_SMRAM
            self.logger.log_bad( 'S3 boot-script is not in SMRAM' )
            self.logger.log( '[*] Reading S3 boot-script from memory..' )
            script_all = self.cs.mem.read_physical_mem( bootscript_pa, 0x100000 )
            self.logger.log( '[*] Decoding S3 boot-script opcodes..' )
            script_entries = parse_script( script_all, False )
            dispatch_opcodes_ok = self.check_dispatch_opcodes( script_entries )
            if dispatch_opcodes_ok:
                res |= DISPATCH_OPCODES_PROTECTED
                self.logger.log_important( "S3 boot-script is not in protected memory but didn't find unprotected Dispatch entry-points" )
            else:
                res |= DISPATCH_OPCODES_UNPROTECTED
                self.logger.log_bad( 'Entry-points of Dispatch opcodes in S3 boot-script are not in protected memory' )
        return res

    def check_s3_bootscripts( self, bsaddress = None ):
        res    = 0
        status = ModuleResult.ERROR
        scriptInsideSMRAM = False

        if bsaddress:
            bootscript_PAs = [ bsaddress ]
        else:
            found,bootscript_PAs = self._uefi.find_s3_bootscript()
            if not found:
                self.logger.log_good( "Didn't find any S3 boot-scripts in EFI variables" )
                self.logger.log_warn_check( "S3 Boot-Script was not found. Firmware may be using other ways to store/locate it, or OS might be blocking access." )
                return ModuleResult.WARNING

            self.logger.log_important( 'Found {:d} S3 boot-script(s) in EFI variables'.format(len(bootscript_PAs)) )

        for bootscript_pa in bootscript_PAs:
            if 0 == bootscript_pa: continue
            res |= self.check_s3_bootscript( bootscript_pa )

        self.logger.log('')
        #if BOOTSCRIPT_OK == res:
        #    status = ModuleResult.PASSED
        #    self.logger.log_passed_check( 'S3 Boot-Scripts including Dispatch entry-points seem to be stored in protected locations' )

        if (res & BOOTSCRIPT_OUTSIDE_SMRAM) != 0:
            # BOOTSCRIPT_OUTSIDE_SMRAM
            if (res & DISPATCH_OPCODES_UNPROTECTED) != 0:
                # DISPATCH_OPCODES_UNPROTECTED
                status = ModuleResult.FAILED
                self.logger.log_failed_check( 'S3 Boot-Script and Dispatch entry-points do not appear to be protected' )
            else:
                # DISPATCH_OPCODES_PROTECTED
                status = ModuleResult.WARNING
                self.logger.log_warn_check( 'S3 Boot-Script is not in SMRAM but Dispatch entry-points appear to be protected. Recommend further testing' )
        else:
            # BOOTSCRIPT_INSIDE_SMRAM
            status = ModuleResult.WARNING
            self.logger.log_warn_check( "S3 Boot-Script is inside SMRAM. The script is protected but Dispatch opcodes cannot be inspected" )

        self.logger.log_important( "Additional testing of the S3 boot-script can be done using tools.uefi.s3script_modify" )

        return status

    def run( self, module_argv ):
        if len(module_argv) > 2:
            self.logger.error( 'Expected module options: -a <bootscript_address>' )
            return ModuleResult.ERROR

        self.logger.start_test( "S3 Resume Boot-Script Protections" )
        script_pa = None

        if len(module_argv) > 0:
            script_pa = int(module_argv[0],16)
            self.logger.log( '[*] Using manually assigned S3 Boot-Script table base: 0x{:016X}'.format(script_pa) )
        (self.smrambase, self.smramlimit, self.smramsize) = self.cs.cpu.get_SMRAM()
        if self.smrambase is not None and self.smramlimit is not None:
            self.logger.log( '[*] SMRAM: Base = 0x{:016X}, Limit = 0x{:016X}, Size = 0x{:08X}'.format(self.smrambase, self.smramlimit, self.smramsize) )

        try:
            if script_pa is not None: return self.check_s3_bootscripts( script_pa )
            else:                     return self.check_s3_bootscripts( )
        except:
            self.logger.error("The module was not able to recognize the S3 resume boot script on this platform.")
            if self.logger.VERBOSE: raise
            return ModuleResult.ERROR
Exemple #9
0
class variables(BaseModule):
    def __init__(self):
        BaseModule.__init__(self)
        self._uefi = UEFI(self.cs)

    def is_supported(self):
        supported = self.cs.helper.EFI_supported()
        if not supported:
            self.logger.log_skipped_check(
                "OS does not support UEFI Runtime API")
        return supported

    def can_modify(self, name, guid, data, attrs):
        self.logger.log("    > attempting to modify variable {}:{}".format(
            guid, name))
        datalen = len(data)
        #print_buffer( data )

        baddata = chr(ord(data[0]) ^ 0xFF) + data[1:]
        #if datalen > 1: baddata = baddata[:datalen-1] + chr( ord(baddata[datalen-1]) ^ 0xFF )
        status = self._uefi.set_EFI_variable(name, guid, baddata)
        if StatusCode.EFI_SUCCESS != status:
            self.logger.log(
                '    < modification of {} returned error 0x{:X}'.format(
                    name, status))
        else:
            self.logger.log(
                '    < modification of {} returned succees'.format(name))

        self.logger.log(
            '    > checking variable {} contents after modification..'.format(
                name))
        newdata = self._uefi.get_EFI_variable(name, guid)

        #print_buffer( newdata )
        #chipsec.file.write_file( name+'_'+guid+'.bin', data )
        #chipsec.file.write_file( name+'_'+guid+'.bin.bad', baddata )
        #chipsec.file.write_file( name+'_'+guid+'.bin.new', newdata )

        _changed = (data != newdata)
        if _changed:
            self.logger.log_bad(
                "EFI variable {} has been modified. Restoring original contents.."
                .format(name))
            self._uefi.set_EFI_variable(name, guid, data)
            # checking if restored correctly
            restoreddata = self._uefi.get_EFI_variable(name, guid)
            #print_buffer( restoreddata )
            if (restoreddata != data):
                self.logger.error(
                    "Failed to restore contents of variable {} failed!".format(
                        name))
            else:
                self.logger.log(
                    "    contents of variable {} have been restored".format(
                        name))
        else:
            self.logger.log_good("Could not modify UEFI variable {}:{}".format(
                guid, name))
        return _changed

    ## check_secureboot_variable_attributes
    # checks authentication attributes of Secure Boot EFI variables
    def check_secureboot_variable_attributes(self, do_modify):
        res = ModuleResult.ERROR
        not_found = 0
        not_auth = 0
        not_wp = 0
        is_secureboot_enabled = False

        sbvars = self._uefi.list_EFI_variables()
        if sbvars is None:
            self.logger.log_warn_check('Could not enumerate UEFI variables.')
            return ModuleResult.SKIPPED

        for name in SECURE_BOOT_VARIABLES:

            if name in sbvars.keys() and sbvars[name] is not None:
                if len(sbvars[name]) > 1:
                    self.logger.log_failed_check(
                        'There should only be one instance of variable {}'.
                        format(name))
                    return ModuleResult.FAILED
                for (off, buf, hdr, data, guid, attrs) in sbvars[name]:
                    self.logger.log(
                        "[*] Checking protections of UEFI variable {}:{}".
                        format(guid, name))

                    # check the status of Secure Boot
                    if EFI_VAR_NAME_SecureBoot == name:
                        is_secureboot_enabled = (data is not None
                                                 and len(data) == 1
                                                 and ord(data) == 0x1)

                    #
                    # Verify if the Secure Boot key/database variable is authenticated
                    #
                    if name in SECURE_BOOT_KEY_VARIABLES:
                        if IS_VARIABLE_ATTRIBUTE(
                                attrs,
                                EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS):
                            self.logger.log_good(
                                'Variable {}:{} is authenticated (AUTHENTICATED_WRITE_ACCESS)'
                                .format(guid, name))
                        elif IS_VARIABLE_ATTRIBUTE(
                                attrs,
                                EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS
                        ):
                            self.logger.log_good(
                                'Variable {}:{} is authenticated (TIME_BASED_AUTHENTICATED_WRITE_ACCESS)'
                                .format(guid, name))
                        else:
                            not_auth += 1
                            self.logger.log_bad(
                                'Variable {}:{} is not authenticated'.format(
                                    guid, name))

                    #
                    # Attempt to modify contents of the variables
                    #
                    if do_modify:
                        if self.can_modify(name, guid, data, attrs):
                            not_wp += 1

            else:
                not_found += 1
                self.logger.log_important(
                    'Secure Boot variable {} is not found'.format(name))
                continue

        self.logger.log('')
        self.logger.log('[*] Secure Boot appears to be {}abled'.format(
            'en' if is_secureboot_enabled else 'dis'))

        if len(SECURE_BOOT_VARIABLES) == not_found:
            # None of Secure Boot variables were not found
            self.logger.log_skipped_check(
                'None of required Secure Boot variables found. Secure Boot is not enabled'
            )
            return ModuleResult.SKIPPED
        else:
            # Some Secure Boot variables exist
            sb_vars_failed = (not_found > 0) or (not_auth > 0) or (not_wp > 0)
            if sb_vars_failed:
                if not_found > 0:
                    self.logger.log_bad(
                        "Some required Secure Boot variables are missing")
                if not_auth > 0:
                    self.logger.log_bad(
                        'Some Secure Boot keying variables are not authenticated'
                    )
                if not_wp > 0:
                    self.logger.log_bad(
                        'Some Secure Boot variables can be modified')

                if is_secureboot_enabled:
                    self.logger.log_failed_check(
                        'Not all Secure Boot UEFI variables are protected')
                    return ModuleResult.FAILED
                else:
                    self.logger.log_warn_check(
                        'Not all Secure Boot UEFI variables are protected')
                    return ModuleResult.WARNING

            else:
                self.logger.log_passed_check(
                    'All Secure Boot UEFI variables are protected')
                return ModuleResult.PASSED

    # --------------------------------------------------------------------------
    # run( module_argv )
    # Required function: run here all tests from this module
    # --------------------------------------------------------------------------
    def run(self, module_argv):
        self.logger.start_test("Attributes of Secure Boot EFI Variables")
        do_modify = (len(module_argv) > 0 and module_argv[0] == OPT_MODIFY)
        return self.check_secureboot_variable_attributes(do_modify)
Exemple #10
0
class uefivar_fuzz(BaseModule):
    def __init__(self):
        BaseModule.__init__(self)
        self._uefi = UEFI(self.cs)

    def is_supported(self):
        supported = self.cs.helper.EFI_supported()
        if not supported:
            self.logger.log_skipped_check(
                "OS does not support UEFI Runtime API")
        return supported

    def rnd(self, n=1):
        rnum = b''
        for j in range(n):
            rnum += struct.pack("B", random.randint(0, 255))
        return rnum

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

    def run(self, module_argv):
        self.logger.start_test("Fuzz UEFI Variable Interface")

        self.logger.warn(
            "Are you sure you want to continue fuzzing UEFI variable interface?"
        )
        s = cs_input("Type 'yes' to continue > ")
        if s != 'yes': return

        # Default options
        _NAME = 'FuzzerVarName'
        _GUID = UUID('414C4694-F4CF-0525-69AF-C99C8596530F')
        _ATTRIB = 0x07
        _SIZE = 0x08
        _DATA = struct.pack("B", 0x41) * _SIZE

        ITERATIONS = 1000
        SEED = int(time())
        CASE = 1
        BOUND_STR = 255  #tested value that can be increased or decreased to fit the limit bounds
        BOUND_INT = 1000

        FUZZ_NAME = True
        FUZZ_GUID = True
        FUZZ_ATTRIB = True
        FUZZ_DATA = True
        FUZZ_SIZE = True

        # Init fuzzing primitives
        name_prim = prim.string(value=_NAME, max_len=BOUND_STR)
        attrib_prim = prim.dword(
            value=_ATTRIB)  # i think the attrib field is 4 bytes large?
        data_prim = prim.random_data(value=_DATA,
                                     min_length=0,
                                     max_length=BOUND_INT)

        help_text = False

        if len(module_argv):
            fz_cli = module_argv[0].lower()
            if ('all' != fz_cli):
                FUZZ_NAME = False
                FUZZ_GUID = False
                FUZZ_ATTRIB = False
                FUZZ_DATA = False
                FUZZ_SIZE = False

                if ('name' == fz_cli): FUZZ_NAME = True
                elif ('guid' == fz_cli): FUZZ_GUID = True
                elif ('attrib' == fz_cli): FUZZ_ATTRIB = True
                elif ('data' == fz_cli): FUZZ_DATA = True
                elif ('size' == fz_cli): FUZZ_SIZE = True
                else: help_text = self.usage()

            if len(module_argv) > 1:
                if (module_argv[1].isdigit()): ITERATIONS = int(module_argv[1])
                else: help_text = self.usage()

            if len(module_argv) > 2:
                if (module_argv[2].isdigit()): SEED = int(module_argv[2])
                else: help_text = self.usage()

            if len(module_argv) > 3:
                if (module_argv[3].isdigit()): CASE = int(module_argv[3])
                else: help_text = self.usage()

        if not help_text:
            random.seed(SEED)
            write_file('SEED.txt', str(SEED))

            if not len(module_argv): fz_cli = 'all'
            self.logger.log('Test      : {}'.format(fz_cli))
            self.logger.log('Iterations: {:d}'.format(ITERATIONS))
            self.logger.log('Seed      : {:d}'.format(SEED))
            self.logger.log('Test case : {:d}'.format(CASE))
            self.logger.log('')
            for count in range(1, ITERATIONS + CASE):
                if FUZZ_NAME:
                    _NAME = ''
                    if name_prim.mutate():
                        _NAME = name_prim.render()
                    else:  # if mutate() returns false, we need to reload the primitive
                        name_prim = prim.string(value=_NAME, max_len=BOUND_STR)
                        _NAME = name_prim.render()

                if FUZZ_GUID: _GUID = uuid4()

                if FUZZ_ATTRIB:
                    if attrib_prim.mutate():
                        _ATTRIB = attrib_prim.render()
                    else:
                        attrib_prim = prim.dword(value=_ATTRIB)
                        _ATTRIB = attrib_prim.render()

                if FUZZ_DATA:
                    if data_prim.mutate():
                        _DATA = data_prim.render()
                    else:
                        data_prim = prim.random_data(value=_DATA,
                                                     min_length=0,
                                                     max_length=BOUND_INT)
                        data_prim.mutate()
                        _DATA = data_prim.render()

                if FUZZ_SIZE:
                    if _DATA:
                        _SIZE = random.randrange(len(_DATA))
                    else:
                        _SIZE = random.randrange(1024)

                if (count < CASE): continue

                self.logger.log('  Running test #{:d}:'.format(count))
                self.logger.flush()
                status = self._uefi.set_EFI_variable(_NAME, str(_GUID), _DATA,
                                                     _SIZE, _ATTRIB)
                self.logger.log(status)
                status = self._uefi.delete_EFI_variable(_NAME, str(_GUID))
                self.logger.log(status)

        return ModuleResult.PASSED
Exemple #11
0
 def __init__(self):
     BaseModule.__init__(self)
     self.uefi = UEFI(self.cs)
     self.image = None
     self.vt_threshold = 10
     self.vt = None
Exemple #12
0
 def __init__(self):
     BaseModule.__init__(self)
     self.logger.HAL = True
     self._uefi = UEFI(self.cs)
     self.bootscript_PAs = None
     self.parsed_scripts = None
Exemple #13
0
class s3script_modify(BaseModule):

    DISPATCH_ENTRYPOINT_INSTR = '\x90\x90\xF4\xF4'

    def __init__(self):
        BaseModule.__init__(self)
        self.logger.HAL = True
        self._uefi = UEFI(self.cs)
        self.bootscript_PAs = None
        self.parsed_scripts = None

    def get_bootscript(self):
        if self.bootscript_PAs is None or self.parsed_scripts is None:
            (self.bootscript_PAs,
             self.parsed_scripts) = self._uefi.get_s3_bootscript(False)
        return (self.bootscript_PAs, self.parsed_scripts)

    def is_supported(self):
        supported = self.cs.helper.EFI_supported()
        if not supported:
            self.logger.log("OS does not support UEFI Runtime API")
            self.res = ModuleResult.NOTAPPLICABLE
        else:
            _, ps = self.get_bootscript()
            if not ps:
                self.res = ModuleResult.NOTAPPLICABLE
                self.logger.log("Unable to locate boot script")
                supported = False
        return supported

    def modify_s3_reg(self, opcode, address, new_value):
        (bootscript_PAs, parsed_scripts) = self.get_bootscript()
        if parsed_scripts is None:
            self.logger.log_bad("Did not find boot script.")
            return False
        for bootscript_pa in bootscript_PAs:
            if (bootscript_pa == 0): continue
            self.logger.log(
                "[*] Looking for 0x{:X} opcode in the script at 0x{:016X}..".
                format(opcode, bootscript_pa))
            for e in parsed_scripts[bootscript_pa]:
                if e.decoded_opcode is not None       and \
                   opcode  == e.decoded_opcode.opcode and \
                   address == e.decoded_opcode.address:

                    self.logger.log_good(
                        "Found opcode at offset 0x{:04X}".format(
                            e.offset_in_script))
                    self.logger.log(e)
                    pa = bootscript_pa + e.offset_in_script
                    self.logger.log(
                        "[*] Modifying S3 boot script entry at address 0x{:016X}.."
                        .format(pa))

                    orig_entry_buf = self.cs.mem.read_physical_mem(
                        pa, e.length)
                    self.logger.log("[*] Original entry:")
                    print_buffer(orig_entry_buf)

                    if S3BootScriptOpcode.EFI_BOOT_SCRIPT_PCI_CONFIG_WRITE_OPCODE == opcode or \
                       S3BootScriptOpcode.EFI_BOOT_SCRIPT_MEM_WRITE_OPCODE        == opcode or \
                       S3BootScriptOpcode.EFI_BOOT_SCRIPT_IO_WRITE_OPCODE         == opcode:
                        e.decoded_opcode.values[0] = new_value
                    else:
                        e.decoded_opcode.value = new_value

                    entry_buf = encode_s3bootscript_entry(e)
                    self.cs.mem.write_physical_mem(pa, e.length, entry_buf)

                    new_entry_buf = self.cs.mem.read_physical_mem(pa, e.length)
                    self.logger.log("[*] Modified entry:")
                    print_buffer(new_entry_buf)
                    return True

        self.logger.log_bad(
            "Did not find required 0x{:X} opcode in the script".format(opcode))
        return False

    def modify_s3_dispatch(self):
        ep_size = len(self.DISPATCH_ENTRYPOINT_INSTR)
        (smram_base, smram_limit, smram_size) = self.cs.cpu.get_SMRAM()
        (ep_va,
         new_entrypoint) = self.cs.mem.alloc_physical_mem(ep_size, smram_base)
        self.cs.mem.write_physical_mem(new_entrypoint, ep_size,
                                       self.DISPATCH_ENTRYPOINT_INSTR)
        new_ep = self.cs.mem.read_physical_mem(new_entrypoint, ep_size)
        self.logger.log_good(
            "Allocated new DISPATCH entry-point at 0x{:016X} (size = 0x{:X}):".
            format(new_entrypoint, ep_size))
        print_buffer(new_ep)

        (bootscript_PAs, parsed_scripts) = self.get_bootscript()
        if parsed_scripts is None:
            self.logger.log_bad("Did not find boot script.")
            return False
        for bootscript_pa in bootscript_PAs:
            if (bootscript_pa == 0): continue
            self.logger.log(
                "[*] Searching the script at 0x{:016X} for DISPATCH opcodes..".
                format(bootscript_pa))
            for e in parsed_scripts[bootscript_pa]:
                if e.decoded_opcode is not None and \
                   S3BootScriptOpcode.EFI_BOOT_SCRIPT_DISPATCH_OPCODE == e.decoded_opcode.opcode:

                    self.logger.log_good(
                        "Found DISPATCH opcode at offset 0x{:04X}".format(
                            e.offset_in_script))
                    self.logger.log(e)
                    pa = bootscript_pa + e.offset_in_script
                    self.logger.log(
                        "[*] Modifying S3 boot script entry at address 0x{:016X}.."
                        .format(pa))

                    orig_entry_buf = self.cs.mem.read_physical_mem(
                        pa, e.length)
                    self.logger.log("[*] Original entry:")
                    print_buffer(orig_entry_buf)

                    e.decoded_opcode.entrypoint = new_entrypoint
                    entry_buf = encode_s3bootscript_entry(e)
                    self.cs.mem.write_physical_mem(pa, e.length, entry_buf)

                    new_entry_buf = self.cs.mem.read_physical_mem(pa, e.length)
                    self.logger.log("[*] Modified entry:")
                    print_buffer(new_entry_buf)
                    self.logger.log(
                        'After sleep/resume, the system should hang')
                    return True

        self.logger.log_bad("Did not find any suitable DISPATCH opcodes")
        return False

    def modify_s3_dispatch_ep(self):
        ep_pa = None
        (bootscript_PAs, parsed_scripts) = self.get_bootscript()
        if parsed_scripts is None:
            self.logger.log_bad("Did not find boot script.")
            return False
        for script_pa in bootscript_PAs:
            if (script_pa == 0): continue
            self.logger.log(
                "[*] Looking for DISPATCH opcode in the script at 0x{:016X}..".
                format(script_pa))
            for e in parsed_scripts[script_pa]:
                if e.decoded_opcode is not None and \
                   S3BootScriptOpcode.EFI_BOOT_SCRIPT_DISPATCH_OPCODE == e.decoded_opcode.opcode:
                    ep_pa = e.decoded_opcode.entrypoint
                    self.logger.log_good(
                        "Found DISPATCH opcode at offset 0x{:04X} with entry-point 0x{:016X}"
                        .format(e.offset_in_script, ep_pa))
                    self.logger.log(e)
                    break
            if ep_pa is not None: break

        if ep_pa is None:
            self.logger.log_bad("Didn't find any DISPATCH opcodes")
            return False

        ep_size = len(self.DISPATCH_ENTRYPOINT_INSTR)
        self.cs.mem.write_physical_mem(ep_pa, ep_size,
                                       self.DISPATCH_ENTRYPOINT_INSTR)
        new_ep = self.cs.mem.read_physical_mem(ep_pa, ep_size)
        self.logger.log(
            "[*] New DISPATCH entry-point at 0x{:016X} (size = 0x{:X}):".
            format(ep_pa, ep_size))
        print_buffer(new_ep)
        return True

    def modify_s3_mem(self, address, new_value):
        if address is None:
            (smram_base, smram_limit, smram_size) = self.cs.cpu.get_SMRAM()
            (va, address) = self.cs.mem.alloc_physical_mem(0x1000, smram_base)
            self.logger.log(
                "[*] Allocated memory at 0x{:016X} as a target of MEM_WRITE opcode"
                .format(address))

        val = self.cs.mem.read_physical_mem_dword(address)
        self.logger.log("[*] Original value at 0x{:016X}: 0x{:08X}".format(
            address, val))

        (bootscript_PAs, parsed_scripts) = self.get_bootscript()
        if parsed_scripts is None:
            self.logger.log_bad("Did not find boot script.")
            return False
        for bootscript_pa in bootscript_PAs:
            if (bootscript_pa == 0): continue
            self.logger.log(
                "[*] Looking for MEM_WRITE opcode in the script at 0x{:016X}.."
                .format(bootscript_pa))
            for e in parsed_scripts[bootscript_pa]:
                if e.decoded_opcode is not None and \
                   S3BootScriptOpcode.EFI_BOOT_SCRIPT_MEM_WRITE_OPCODE == e.decoded_opcode.opcode:

                    self.logger.log_good(
                        "Found opcode at offset 0x{:X}".format(
                            e.offset_in_script))
                    self.logger.log(e)
                    pa = bootscript_pa + e.offset_in_script
                    self.logger.log(
                        "[*] Modifying S3 boot script entry at address 0x{:016X}.."
                        .format(pa))

                    orig_entry_buf = self.cs.mem.read_physical_mem(
                        pa, e.length)
                    self.logger.log("[*] Original entry:")
                    print_buffer(orig_entry_buf)

                    e.decoded_opcode.address = address
                    e.decoded_opcode.values[0] = new_value
                    entry_buf = encode_s3bootscript_entry(e)
                    self.cs.mem.write_physical_mem(pa, e.length, entry_buf)

                    new_entry_buf = self.cs.mem.read_physical_mem(pa, e.length)
                    self.logger.log("[*] Modified entry:")
                    print_buffer(new_entry_buf)
                    self.logger.log(
                        'After sleep/resume, read address 0x{:08X} and look for value 0x{:08X}'
                        .format(address, new_value))
                    return True

        self.logger.log_bad(
            "Did not find required 0x{:X} opcode in the script".format(
                S3BootScriptOpcode.EFI_BOOT_SCRIPT_MEM_WRITE_OPCODE))
        return False

    def modify_s3_add(self, new_opcode):
        e_index = None
        (bootscript_PAs, parsed_scripts) = self.get_bootscript()
        if parsed_scripts is None:
            self.logger.log_bad("Did not find boot script.")
            return False
        for bootscript_pa in bootscript_PAs:
            if (bootscript_pa == 0): continue
            script_buffer = self.cs.mem.read_physical_mem(bootscript_pa, 4)
            script_type, hdr_len = id_s3bootscript_type(script_buffer, False)
            self.logger.log(
                "[*] S3 boot script type: 0x{:0X}".format(script_type))

            self.logger.log(
                "[*] Looking for TERMINATE opcode in the script at 0x{:016X}.."
                .format(bootscript_pa))
            for e in parsed_scripts[bootscript_pa]:
                if e.index is not None and e.index != -1: e_index = e.index + 1

                if e.decoded_opcode is not None and S3BootScriptOpcode.EFI_BOOT_SCRIPT_TERMINATE_OPCODE == e.decoded_opcode.opcode:
                    self.logger.log_good(
                        "Found TERMINATE opcode at offset 0x{:X}".format(
                            e.offset_in_script))
                    self.logger.log(e)
                    pa = bootscript_pa + e.offset_in_script
                    orig_entry_buf = self.cs.mem.read_physical_mem(
                        pa, e.length)
                    #print_buffer( orig_entry_buf )

                    self.logger.log("[*] New S3 boot script opcode:")
                    self.logger.log(new_opcode)
                    self.logger.log(
                        "[*] Adding new opcode entry at address 0x{:016X}..".
                        format(pa))
                    new_entry = create_s3bootscript_entry_buffer(
                        script_type, new_opcode, e_index)
                    print_buffer(new_entry)

                    self.cs.mem.write_physical_mem(pa, len(new_entry),
                                                   new_entry)
                    last_entry_pa = pa + len(new_entry)
                    self.logger.log(
                        "[*] Moving TERMINATE opcode to the last entry at 0x{:016X}.."
                        .format(last_entry_pa))
                    self.cs.mem.write_physical_mem(last_entry_pa,
                                                   len(orig_entry_buf),
                                                   orig_entry_buf)
                    return True

        self.logger.log_bad("Did not find TERMINATE opcode")
        return False

    def run(self, module_argv):
        self.logger.start_test('S3 Resume Boot-Script Testing')
        sts = False
        op = module_argv[0].lower() if len(module_argv) > 0 else 'add_op'
        if (op == 'replace_op'):
            scmd = module_argv[1].lower(
            ) if len(module_argv) > 1 else 'dispatch_ep'
            if scmd in cmd2opcode:
                if len(module_argv) < 4:
                    self.logger.error(
                        'Expected module options: -a replace_op,{},<reg_address>,<value>'
                        .format(scmd))
                    return ModuleResult.ERROR
                reg_address = int(module_argv[2], 16)
                value = int(module_argv[3], 16)
                sts = self.modify_s3_reg(cmd2opcode[scmd], reg_address, value)
                if sts:
                    self.logger.log(
                        '[*] After sleep/resume, check the value of register 0x{:X} is 0x{:X}'
                        .format(reg_address, value))
            elif 'dispatch' == scmd:
                sts = self.modify_s3_dispatch()
            elif 'dispatch_ep' == scmd:
                sts = self.modify_s3_dispatch_ep()
            elif 'mem' == scmd:
                new_value = int(module_argv[2],
                                16) if len(module_argv) >= 3 else 0xB007B007
                address = int(module_argv[3],
                              16) if len(module_argv) == 4 else None
                sts = self.modify_s3_mem(address, new_value)
            else:
                self.logger.error(
                    "Unrecognized module command-line argument: {}".format(
                        scmd))
                self.logger.log(examples_str)
                return ModuleResult.ERROR
        elif (op == 'add_op'):
            scmd = module_argv[1].lower(
            ) if len(module_argv) > 1 else 'dispatch'
            new_opcode = None
            if scmd in cmd2opcode:
                if len(module_argv) < 5:
                    self.logger.error(
                        'Expected module options: -a add_op,{},<reg_address>,<value>,<width>'
                        .format(scmd))
                    return ModuleResult.ERROR
                address = int(module_argv[2], 16)
                value = int(module_argv[3], 16)
                width = int(module_argv[4], 16)
                width_val = script_width_values[width]
                value_buff = struct.pack(
                    "<{}".format(script_width_formats[width_val]), value)
                if (S3BootScriptOpcode.EFI_BOOT_SCRIPT_MEM_WRITE_OPCODE
                        == cmd2opcode[scmd] or S3BootScriptOpcode.
                        EFI_BOOT_SCRIPT_PCI_CONFIG_WRITE_OPCODE
                        == cmd2opcode[scmd]
                        or S3BootScriptOpcode.EFI_BOOT_SCRIPT_IO_WRITE_OPCODE
                        == cmd2opcode[scmd]):
                    new_opcode = op_io_pci_mem(cmd2opcode[scmd], None,
                                               width_val, address, 0, 1,
                                               value_buff, None, None)
                else:
                    self.logger.error("Unsupported opcode: {}".format(scmd))
                    self.logger.log(examples_str)
                    return ModuleResult.ERROR
            elif 'dispatch' == scmd:
                if len(module_argv) < 3:
                    (smram_base, smram_limit,
                     smram_size) = self.cs.cpu.get_SMRAM()
                    (va, entrypoint) = self.cs.mem.alloc_physical_mem(
                        0x1000, smram_base)
                    self.cs.mem.write_physical_mem(
                        entrypoint, len(self.DISPATCH_ENTRYPOINT_INSTR),
                        self.DISPATCH_ENTRYPOINT_INSTR)
                else:
                    entrypoint = int(module_argv[2], 16)
                new_opcode = op_dispatch(
                    S3BootScriptOpcode.EFI_BOOT_SCRIPT_DISPATCH_OPCODE, None,
                    entrypoint)
            else:
                self.logger.error("Unrecognized opcode: {}".format(scmd))
                self.logger.log(examples_str)
                return ModuleResult.ERROR

            sts = self.modify_s3_add(new_opcode)
        else:
            self.logger.error(
                "Unrecognized module command-line argument: {}".format(op))
            self.logger.log(examples_str)
            return ModuleResult.ERROR

        if sts:
            self.logger.log_passed_check(
                'The script has been modified. Go to sleep..')
            return ModuleResult.PASSED
        else:
            return ModuleResult.FAILED