Backdooring Embedded Devices
Discussing various ways to apply persistence to embedded devices
Introduction
If you have ever gotten a shell on an embedded device, you may have noticed that it is a wildly different experience when compared to your more common flavors of Linux distributions. Embedded devices while commonly run on a version of Linux, is more than likely significantly different than any Linux operating system you have used before.
Embedded devices commonly run a light-weight version of Linux that only contains the absolute most core packages needed to function. You may be thinking well how stripped down are we talking? Well in my experience, you are lucky if the operating system even recognizes the famous "whoami" command.

Introducing Busybox
However, embedded Linux systems commonly have what is referred to as "Busybox" installed on the system. Busybox is described as "combining tiny versions of many common UNIX utilities into a single small executable". Busybox is often thought of as The Swiss Army Knife of Embedded Linux.
Typically when running commands on an embedded device, it is actually just a soft link to the Busybox binary.

Busybox has a number of different versions that have been compiled for a variety of different architectures including MIPS and ARM. This comes in handy after getting an initial shell on a embedded device, as sometimes it may not even have Busybox binary installed or maybe it will only have a version of Busybox with limited commands.
This makes it possible to easily download a compatible version online and uploading it to the target device to give us more control.

After uploading a compatible Busybox binary to the target one could script a Netcat bind shell to initialize on startup.

While this may work in a crunch, it is definitely not the most reliable way to maintain persistence remote access. Additionally, this is not a full TTY shell, which is much more powerful then this "dumb" Netcat shell.
A TTY shell is basically a shell that gives us access to the terminal in addition to displaying STDOUT, it will also display streams such as STDERR. Some commands produce error messages that is outputted through a stream other than STDOUT. It benefits attackers greatly to have a shell that will display these streams of output.
Cross-Compiling for ARM
This brings me to the main topic of this blog, which is cross-compiling binaries for embedded devices. Because embedded devices frequently run on ARM architecture, you can't just use your standard GCC on Ubuntu to compile your C program. This requires a cross-compiler to compile a ARM compatible binary.
Prerequisites
In my example I used a fully updated Ubuntu 22.04 virtual machine to cross-compile on. Before starting there are some packages that need to be installed.
The GCC cross-compiler supporting programs can be installed like below.
Afterwards, the ARM cross-compiler needs to be installed like so.
sudo apt install gcc-arm-linux-gnueabi
Payload Source Code
For the bind shell, I will be using a slightly modified version of this program. In order to make the backdoor more persistent and allow me to be able to connect back multiple times, the following changes were implemented.
Compiling
Cross-compiling the program for the embedded device is easy as the following code snippet.
arm-linux-gnueabi-gcc backdoor.c -static -o backdoor
Because every embedded device is different, I found it imperative that the -static flag be used to compile the needed libraries within the binary. While this makes the payload significantly larger, it makes it much more versatile and less dependent on the environment.
After successfully compiling the binary, we upload it to the target device to test the compatibility.

No errors upon executing. This is looking really good. Let's try to connect to it from our attacking machine.

Looks like it is working as intended.
Persistence
Creating a Backdoor as a Service
Assuming the target embedded device is utilizing systemd for services, you should be able to setup the backdoor binary to run as a service. This creates a cushion of redundancy in the chance that the backdoor dies.
Add the following script to the following directory as a file called backdoor.service:
/etc/systemd/system/backdoor.service
Afterwards run the following commands to enabel and start the service.
You can check the status of your service by either running netstat to see if port 9999 is being exposed or systemctl status backdoor to see if the service is active.
Last updated