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 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)
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
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
def __init__(self): BaseModule.__init__(self) self._uefi = UEFI( self.cs )
def __init__(self): BaseModule.__init__(self) self.uefi = UEFI(self.cs) self.image = None self.efi_list = None self.suspect_modules = {}
def __init__(self): BaseModule.__init__(self) self.uefi = UEFI(self.cs) self.cfg_name = 'blacklist.json' self.image = None self.efi_blacklist = None
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
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)
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
def __init__(self): BaseModule.__init__(self) self.uefi = UEFI(self.cs) self.image = None self.vt_threshold = 10 self.vt = None
def __init__(self): BaseModule.__init__(self) self.logger.HAL = True self._uefi = UEFI(self.cs) self.bootscript_PAs = None self.parsed_scripts = None
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