Wiki / Software

BusyBox

One binary that provides several hundred Unix utilities. It is the entire userland on most embedded Linux devices, and the reason those devices behave subtly differently from a desktop.

The applet list (busybox | head -n 20)
$ busybox | head -n 20
BusyBox v1.35.0 (Debian 1:1.35.0-4+b7) multi-call binary.
BusyBox is copyrighted by many authors between 1998-2015.
Licensed under GPLv2. See source distribution for detailed
copyright notices.

Usage: busybox [function [arguments]...]
   or: busybox --list[-full]
   or: busybox --show SCRIPT
   or: busybox --install [-s] [DIR]
   or: function [arguments]...

	BusyBox is a multi-call binary that combines many common Unix
	utilities into a single executable.  Most people will create a
	link to busybox for each function they wish to use and BusyBox
	will act like whatever it was invoked as.

Currently defined functions:
	[, [[, acpid, adjtimex, ar, arch, arp, arping, ascii, ash, awk, base64,
	basename, bc, blkdiscard, blkid, blockdev, brctl, bunzip2, bzcat,
	bzip2, cal, cat, chgrp, chmod, chown, chroot, chvt, clear, cmp, cp,
Captured on the machine that builds this course. busybox --version and the exact applet set vary by build; trimmed to the first 20 lines with head, the full applet list runs to several hundred names.

BusyBox packs several hundred common Unix utilities into a single executable of roughly one megabyte. It calls itself the Swiss Army knife of embedded Linux, and on the majority of routers, cameras and gateways it is not one tool among many: it is the whole userland.

The multi-call design

There is one binary. Every utility is a symlink to it, and BusyBox decides which applet to run by looking at argv[0]:

/bin/ls     -> /bin/busybox
/bin/cat    -> /bin/busybox
/bin/sh     -> /bin/busybox
/sbin/init  -> /bin/busybox

busybox with no arguments lists everything compiled in, which is the fastest way to learn what a device can do:

$ busybox
BusyBox v1.31.1 (2020-06-03) multi-call binary.
Currently defined functions:
    [, [[, ash, awk, base64, cat, chmod, ... telnetd, tftp, top, ...

You can also invoke an applet explicitly, which is useful when the symlink is missing: busybox wget http://....

Why one binary

Size. Statically linking coreutils, util-linux, procps, net-tools and a shell separately costs tens of megabytes; BusyBox costs one, because all the applets share the same libc, the same startup code and the same helper functions. On a device with 8 MB of flash, that is the difference between shipping and not shipping.

The cost is functionality. Every applet is a reduced implementation. ls may not have --color, ps may not accept BSD-style options, awk handles less than GNU awk, and find supports fewer predicates. This is the single most common source of confusion when working on an embedded target: a command exists, has the right name, and does not accept the option you typed.

What matters for an assessment

  • busybox with no arguments is reconnaissance. The applet list tells you whether telnetd, tftp, nc, wget, dropbear or ftpget are available before you go looking for them. It tells you what an attacker on the device has to work with, and what you have to work with.
  • The version string is in the banner, and BusyBox has a real CVE history. Version alone is weak evidence, though: distributors patch selectively, and the applet in question may not even be compiled in.
  • The shell is ash, not bash. No arrays, no [[ ]], no process substitution, different echo behaviour. Scripts written on a desktop routinely fail on the target for this reason alone.
  • init is often BusyBox init, reading /etc/inittab. That file tells you what starts, on which console, and whether a shell is spawned on the serial line.
  • Applets can be suid-relevant. A single suid BusyBox binary with a permissive applet set has historically been a straightforward privilege escalation, though on most of these devices everything already runs as root.

In a firmware image

After unpacking a rootfs with binwalk, three things are worth checking immediately: bin/busybox --help output or the version string via strings, the set of symlinks in /bin and /sbin (which is the applet list without running anything), and /etc/inittab for what BusyBox init is told to start.

Pitfalls

  • Assuming GNU behaviour. Check busybox <applet> --help on the target rather than the man page on your machine.
  • Assuming an applet exists because the symlink does, or does not exist because it does not. The compiled-in set is the authority; symlinks can be stale.
  • Dropping your own statically linked binary onto the device is often easier than fighting a reduced applet, provided you build for the right architecture.

What it pairs with

embedded-linux is the wider environment, rootfs and squashfs-concept cover where it lives, binwalk is how you get to it, and u-boot is what starts the kernel that starts it.

Further reading