29

I was reading the famous Unix Recovery Legend, and it occurred to me to wonder:

If I had a BusyBox shell open, and the BusyBox binary were itself deleted, would I still be able to use all the commands included in the BusyBox binary?

Clearly I wouldn't be able to use the BB version of those commands from another running shell such as bash, since the BusyBox file itself would be unavailable for bash to open and run. But from within the running instance of BusyBox, it appears to me there could be two methods by which BB would run a command:

  1. It could fork and exec a new instance of BusyBox, calling it using the appropriate name—and reading the BusyBox file from disk to do so.
  2. It could fork and perform some internal logic to run the specified command (for example, by running it as a function call).

If (1) is the way BusyBox works, I would expect that certain BusyBox-provided commands would become unavailable from within a running instance of BB after the BB binary were deleted.

If (2) is how it works, BusyBox could be used even for recovery of a system where BB itself had been deleted—provided there were still a running instance of BusyBox accessible.

Is this documented anywhere? If not, is there a way to safely test it?

Wildcard
  • 36,499
  • 2
    is there a way to safely test it? Download the generic x86 openwrt image and attach the image to a new VirtualBox machine – basin Apr 04 '16 at 20:17
  • 2
    And this raises the question, how do Busybox commands continue to work after PATH is unset? Does it assume a default value of PATH? – muru Apr 05 '16 at 01:05
  • 2
    @muru: From the source code (at least for its ash clone) it looks like it treats an unset PATH the same as it would an empty string, so it searches the current directory, and only that. – hmakholm left over Monica Apr 05 '16 at 15:57
  • @HenningMakholm Well, my comment was answered by Gilles' answer. However, it's good to know that - I'd expected only builtins to work. – muru Apr 05 '16 at 16:40
  • 1
    Apart from some seldom accidents/legends, this is useful in real-life if you want to replace your operating system by a different unpacked root directory. In this case ld.so (ld-linux.so) and /bin/mv are crucial. – u_Ltd. Sep 30 '20 at 23:16

3 Answers3

34

By default, BusyBox doesn't do anything special regarding the applets that it has built in (the commands listed with busybox --help).

However, if the FEATURE_SH_STANDALONE and FEATURE_PREFER_APPLETS options are enabled at compile time, then when BusyBox sh¹ executes a command which is a known applet name, it doesn't do the normal PATH lookup, but instead runs its built-in applets through a shortcut:

  • Applets that are declared as “noexec” in the source code are executed as function calls in a forked process. As of BusyBox 1.22, the following applets are noexec: chgrp, chmod, chown, cksum, cp, cut, dd, dos2unix, env, fold, hd, head, hexdump, ln, ls, md5sum, mkfifo, mknod, sha1sum, sha256sum, sha3sum, sha512sum, sort, tac, unix2dos.
  • Applets that are declared as “nofork” in the source code are executed as function calls in the same process. As of BusyBox 1.22, the following applets are nofork: [[, [, basename, cat, dirname, echo, false, fsync, length, logname, mkdir, printenv, printf, pwd, rm, rmdir, seq, sync, test, true, usleep, whoami, yes.
  • Other applets are really executed (with fork and execve), but instead of doing a PATH lookup, BusyBox executes /proc/self/exe, if available (which is normally the case on Linux), and a path defined at compile time otherwise.

This is documented in a bit more detail in docs/nofork_noexec.txt. The applet declarations are in include/applets.src.h in the source code.

Most default configurations turn these features off, so that BusyBox executes external commands like any other shell. Debian turns these features on in both its busybox and busybox-static packages.

So if you have a BusyBox executable compiled with FEATURE_SH_STANDALONE and FEATURE_PREFER_APPLETS, then you can execute all BusyBox commands from a BusyBox shell even if the executable is deleted (except for the applets that are not listed above, if /proc/self/exe is not available).

¹ There are actually two implementations of "sh" in BusyBox — ash and hush — but they behave the same way in this respect.

  • @Wildcard I included a bit too much when I extracted the list from the source code. Fixed, thanks. – Gilles 'SO- stop being evil' Apr 05 '16 at 01:16
  • This is a great answer, but I'm having a little trouble following the conditionals. It seems there are four values/variables that could have an effect on the behavior of any given command: FEATURE_SH_STANDALONE, FEATURE_PREFER_APPLETS, nofork and noexec. Could you please clarify how these four factors interact? I only see two possible behaviors in the final outcome, as specified in my question; why the dual flags "nofork" and "noexec"? – Wildcard Apr 05 '16 at 01:32
  • 1
    @Wildcard FEATURE_PREFER_APPLETS and FEATURE_SH_STANDALONE are compile-time flags, enabling or disabling features. The applets are marked nofork and noexec irrespective of which flags were used. Whether or not such markings have any effect depends on FEATURE_PREFER_APPLETS being enabled. Hence, three possible behaviours: 1. FEATURE_PREFER_APPLETS disabled, 2. FEATURE_PREFER_APPLETS enabled and applet is nofork, 3. FEATURE_PREFER_APPLETS enabled and applet is noexec. The third para in the docs explain it nicely. And the last section shows the possible cases. – muru Apr 05 '16 at 02:04
  • Thanks, @muru. In summary then it looks like to get the behavior of "able to run all (or nearly all) BB commands from a running BB instance even if the binary is deleted," BB would need to be compiled with FEATURE_PREFER_APPLETS, FEATURE_SH_STANDALONE and FEATURE_SH_NOFORK? – Wildcard Apr 05 '16 at 02:23
  • 1
    @Wildcard FEATURE_SH_STANDALONE (which requires FEATURE_PREFER_APPLETS). nofork isn't needed. With FEATURE_SH_STANDALONE, /proc/self/exe is used where applicable, so it will work even if BB was deleted. You can test this out with fairly minimal risk on any Debian or Arch Linux systm, run busybox ash, unset PATH, do basin's commands. It works fine. – muru Apr 05 '16 at 02:38
  • 3
    On an Ubuntu 14.04.1 LTS system, Busybox is configured to prefer applets. Since neither cat nor chmod require exec-ing a pathname, you can recover the executable thusly: cat /proc/self/exe > busybox; chmod 755 busybox. – Barefoot IO Apr 05 '16 at 02:40
  • Why would cat be nofork but tac be noexec? I mean, there shouldn't be any fundamental difference between the two. – forest Apr 05 '16 at 05:57
  • 1
    @forest There's a huge difference: tac requires either a seekable input file which is not always available, or reading the entire input into memory. cat can read its input from start to finish, discarding what it's already processed. It's much easier to implement and it's also much more commonly used, so it makes more sense to optimise that one. – hvd Apr 05 '16 at 07:24
  • 1
    @Wildcard Nofork and noexec are indications set on each applet. FEATURE_xxx is a compile-time option for BusyBox as a whole. The nofork and noexec indications only matter if FEATURE_PREFER_APPLETS is active (at least for the purpose of executing a command in the shell, they're also used in some other contexts). – Gilles 'SO- stop being evil' Apr 05 '16 at 10:01
  • @Giles, the bullet points for noexec and nofork currently show the opposite examples than they should - Applets that are declared as “noexec” in the source code are executed as function calls in a forked process. As of BusyBox 1.22, the following applets are nofork – Keith Hall Apr 05 '16 at 10:48
  • The third bullet point now fully answers my question. So, given the availability of /proc/self/exe and given a BusyBox binary compiled with FEATURE_SH_STANDALONE and FEATURE_PREFER_APPLETS, all BusyBox commands would indeed be fully available so long as BB itself were running. Thanks very much; accepted! – Wildcard Apr 05 '16 at 23:05
  • busybox show the error applet not found when I try to use the echo command in a distroless docker image like gcr.io/distroless/java-debian10:11. Is there any repository to download a compiled binary of busybox with FEATURE_SH_STANDALONE and FEATURE_PREFER_APPLETS enabled? – august0490 Mar 04 '21 at 06:05
  • @august0490 You could download Debian's busybox-static binary. Extract /bin/busybox-static from the .deb package and it'll work on any system with a non-antique Linux kernel and the matching processor architecture. – Gilles 'SO- stop being evil' Mar 04 '21 at 12:37
10

is there a way to safely test it? With the generic x86 openwrt image:

vbox screenshot

Most commands are not built-in, but some are, like echo and printf. A binary file with arbitrary contents can be created using printf, but chmod +x will be a problem.

basin
  • 2,051
0
is there a way to safely test it?

You can check it with the error message of the command. If it's the same from within busybox and when invoked explicitely, then the "adventure" would fail.

On my system the sash shell would be still available, if I had it running.

user@ulmus-thomasii:~$ echo mv b | busybox sh
mv: Fehlender Zieldatei‐Operand hinter 'b'
„mv --help“ liefert weitere Informationen.
user@ulmus-thomasii:~$ /bin/mv b
/bin/mv: Fehlender Zieldatei‐Operand hinter 'b'
„/bin/mv --help“ liefert weitere Informationen.
user@ulmus-thomasii:~$ echo mv b | sash
mv: Fehlender Zieldatei‐Operand hinter 'b'
„mv --help“ liefert weitere Informationen.
user@ulmus-thomasii:~$ echo -mv b | sash
usage: -mv srcName ... destName
user@ulmus-thomasii:~$ 
u_Ltd.
  • 111