iPXE
Threat model

Overview

iPXE is a network-capable bootloader, generally operating at ring 0 (or equivalent) with all memory protections disabled. All data originating from the network should be considered to be untrusted and potentially malicious.

Under UEFI, iPXE operates before ExitBootServices() is called. An exploit against iPXE can therefore potentially produce a Secure Boot compromise, requiring all existing binaries to be revoked (by incrementing the SBAT generation in include/ipxe/sbat.h for the official binaries that are signed via the iPXE shim).

Threats

The primary threat category is traffic originating from a network peer. All code on the receive datapath must carefully validate received lengths and contents during parsing.

iPXE includes its own cryptographic stack (covering TLS, CMS, X.509, etc). This stack is used for iPXE's own security (HTTPS connections and code-signing verification) but is entirely unrelated to UEFI Secure Boot since iPXE defers all Secure Boot decisions to the platform's own LoadImage() and StartImage() calls. A compromise within iPXE's cryptographic stack would be a compromise of iPXE's security, but would not be a Secure Boot exploit (unless it also allowed for e.g. arbitrary code execution or an out-of-bounds write).

UEFI Secure Boot

Files may be annotated with FILE_SECBOOT() declarations to indicate whether or not they are permitted to be included in a UEFI Secure Boot build. The absence of a marker is taken to prohibit inclusion.

Exclusion from a UEFI Secure Boot build does not indicate that a file is outside the scope of the threat model, but does generally indicate that it is a lower priority.

There are some files that are explicitly marked as FORBIDDEN, for various reasons:

  • Some files are intrinsically insecure by design. For example, the GDB debugging stub allows a remote device to read and write arbitrary host memory locations.
  • Some files are excluded by policy. For example, the direct kernel loading capability in image/lkrn.c is marked as FORBIDDEN to ensure that iPXE's policy of delegating Secure Boot decisions to the platform firmware's LoadImage() and StartImage() is respected.
  • Some obsolete cryptographic algorithms such as MD4 and MD5 are excluded to prevent their accidental inclusion via configuration changes.
  • Some third-party code such as the 802.11 stack has previously been reviewed and found to contain multiple security holes (mostly failing to check return values for memory allocation or other failures), and is excluded by agreement with Microsoft. Files in this category need not be reviewed for security issues: they are already known to contain holes and a full rewrite is required before it becomes worthwhile to raise individual issues.

Exclusions

Malicious locally attached hardware is explicitly outside the scope of iPXE's threat model. DMA-capable hardware is assumed to have write access to all of host memory (i.e. an IOMMU is not assumed to be present). Device drivers are therefore not required to exhaustively defend against incorrect or inconsistent values written by DMA-capable hardware, since it is assumed that any such hardware could already crash the system (or overwrite iPXE's code) without any further assistance.

DMA-incapable hardware (such as USB devices) are on the edge of the threat model. It is assumed that an attacker with physical access to attach new hardware also has the ability to take full control of the system anyway (e.g. by disabling UEFI Secure Boot). That said, USB device drivers should generally guard against misbehaving USB or other DMA-incapable hardware, since that hardware would not otherwise be able to crash the system.

Under UEFI, other boot services code running on the same system is similarly on the edge of the threat model. If UEFI Secure Boot is enabled, then any other code running on the same system must itself be part of a signed binary (or be part of the platform firmware), and so is by definition already at least as privileged as iPXE itself. That said, iPXE components that interact via UEFI protocols should be written defensively, since experience shows that UEFI firmware code quality is generally quite poor, especially in closed-source vendor firmware that does not simply use the upstream EDK2 components.

Infiniband local networks are generally regarded as being within the trust boundary, not least because iPXE includes support for remote DMA access. (For this reason, Infiniband device drivers are excluded from a Secure Boot build.)

iPXE generally transfers control to whatever payload it boots at the same privilege level held by iPXE itself (i.e. ring 0 or equivalent). There are therefore some classes of defect that are simply not interesting. For example, a malformed NBI image could potentially be able to exploit a bug in arch/x86/image/nbi.c, but this would be utterly pointless since the exploit cannot do anything that a well-formed NBI image could not do simply by allowing itself to be executed.

Notes

There are several recurring patterns and helper functions used in iPXE that any security reviewer should be familiar with:

  • Dynamic memory allocation via malloc() (or any other allocator such as realloc(), umalloc(), dma_alloc(), etc) is expected to be safe against malicious parameters (such as excessive allocation sizes), and all callers are expected to handle allocation failures gracefully. The comments in core/malloc.c provide some of the internal details.
  • Assertions via assert() are non-terminating and are no-ops in production builds. See the documentation for assert for more details.
  • Errors are generally expected to be caught (unless explicitly ignored) and propagated to the caller. Error handling code should use the composable structured goto pattern where relevant.
  • I/O buffers are used extensively to hold transmitted and received packet data. The documentation for struct io_buffer describes the semantics and caveats for manipulating these buffers safely.
  • Data transfer buffers are often used to provide a bounds-checking abstraction around long-term host storage. The comments in core/xferbuf.c describe the semantics.
  • ASN.1 parsing is handled by an extremely paranoid set of helper functions that should be safe to use on any untrusted input.
  • The non-standard ssnprintf() variation of the standard snprintf() C library function is often used to assemble content from a sequence of formatted strings, in a way that is designed to be safe against buffer size underflow. See the documentation for ssnprintf for more details.