Wiki / Software

U-Boot

The second-stage bootloader on most embedded Linux devices. It brings up the board, loads the kernel, and offers an interactive shell that is frequently the fastest way to own the system.

U-Boot (Das U-Boot) is the second-stage bootloader used by the overwhelming majority of embedded Linux devices: routers, cameras, NAS boxes, industrial gateways, set-top boxes. It initialises RAM and the essential peripherals, locates a kernel in flash or over the network, and jumps to it. It also ships an interactive command shell, and that shell is often the single most productive target on the whole board.

Where it sits

The mask ROM inside the SoC runs first and is immutable. It finds U-Boot in flash and jumps to it. U-Boot then sets up the board and loads the kernel. Everything after the mask ROM lives in writable flash, which is precisely why the boot chain is an attack surface rather than a formality.

The interactive prompt

On boot U-Boot prints a banner and counts down an autoboot delay, usually one to three seconds. Interrupt it (any key, or a specific one the banner names) and you land at a prompt:

U-Boot 2016.01 (Mar 14 2019 - 09:22:41)
Hit any key to stop autoboot:  0
=>

That prompt is reached over the board's serial console, which is why UART discovery matters so much. From it:

printenv                 # dump the whole environment: bootargs, bootcmd, mtdparts
setenv bootargs '... init=/bin/sh'
saveenv                  # persist to flash (if the environment is writable)
boot                     # run bootcmd
md 0x80000000 100        # memory display
sf probe; sf read ...    # read raw SPI flash into RAM
tftpboot 0x80000000 zImage

Why it matters

printenv alone is a full reconnaissance report. It tells you the flash partition layout (mtdparts), where the kernel and rootfs live, the console device, and the exact kernel command line the vendor intends. mtdparts in particular is what lets you dump the right offsets afterwards.

Then there is the classic escalation: append init=/bin/sh (or single) to bootargs and boot. The kernel starts, mounts the rootfs, and runs your shell as PID 1 instead of the vendor init. No password, no login, root on the device. If the environment is writable the change survives a reboot; if not, it still lasts for this boot, which is usually enough to dump the filesystem or plant a key.

U-Boot can also read and write raw flash (sf, nand), so a board with an open prompt and no other exposed interface can still be fully imaged.

Hardening, and how it fails

  • Autoboot delay set to 0. Reduces the window rather than closing it; a key sent during reset sometimes still lands, and the delay is itself an environment variable.
  • CONFIG_AUTOBOOT_KEYED. Requires a specific string to interrupt. The string is in the U-Boot binary you can dump from flash.
  • Console disabled or silent=1. The UART pads are still there and the environment variable is still settable if flash is writable.
  • Verified boot. The real defence: U-Boot checks a signature on the kernel image (FIT with signed hashes) and refuses an unsigned one. This is what stops the init=/bin/sh trick from being useful, provided the signature covers bootargs too. Many deployments sign the kernel and leave the command line unauthenticated, which defeats the entire scheme.

From Linux

On a running device with a shell, fw_printenv and fw_setenv read and write the same environment from userspace, configured by /etc/fw_env.config. That file also reveals where the environment lives in flash.

Pitfalls

  • Saving a broken environment can brick the boot. Note the original values before saveenv, and prefer a one-shot setenv plus boot when you only need this boot.
  • uImage is U-Boot's own container format: a 64-byte header (load address, entry point, CRC, OS and architecture) wrapped around a usually compressed kernel. Editing the payload without recomputing the CRC makes the image unbootable.
  • Two boards of the same model can run different U-Boot builds with different commands compiled in. help on the target is the authority, not the upstream documentation.

What it pairs with

You reach the prompt over uart, usually through a usb-uart adapter after finding the header with a logic-analyzer or a multimetre. What you dump from it goes to binwalk. The wider boot chain and its trust model are in bootloader and secure-boot.

Further reading