Cetus was discovered by Unit42 at Palo Alto Networks and it is a docker worm that spreads by exploiting insecure Docker daemons. Once the docker instance is infected it will launch XMRing, which will take advantage of the infected instance's CPU/GPU to mine cryptocurrencies. U42 wrote an analysis of the malware which was published their blog. This is going to be just me taking a look at the malware to avoid getting rusty :)
The binary is an position independent ELF, x86-64 bits, and not
stripped which makes everything easier. It calls
miner_start and then goes
into an infinite loop and calls
scan_start so I will focus on these two but
also going deeper in both. At no time exits so it will constantly scan for new
This function starts with MOVs that copy obfuscated data to the stack and
prepares a variable called
obfuscated_data. So, clearly, we are dealing with
what will be a deobfuscation routine... and there it is:
000011f3 lea rax, [rel miner_start()::'lambda'()::operator()() const::obfuscated_data] 000011fa lea rdx, [rax+0xb4] 00001201 nop dword [rax], eax 00001208 xor byte [rax], 0x2e // deobfuscation 0000120b add rax, 0x1 0000120f cmp rax, rdx 00001212 jne 0x1208
It will convert the data, obfuscated, to a readable state:
(gdb) dump memory data.bin 0x55b79bdde6a0 0x55b79bdde6a0+180 (gdb) !more data.bin docker-cache -B --donate-level 1 -o pool.minexmr.com:443 -u 85X7JcgPpwQdZXaK2TKJb8baQAXc3zBsnW7JuY7MLi9VYSamf4bFwa7SEAK9Hgp2P53npV19w1zuaK5bft5m2NN71CmNLoh -k --tls -t 1 --rig-id
This means that the malware drops the XMRing executable as docker-cache and executes it with those parameters. The wallet ID can also be seen there and a rig id that will be generated later. And then it comes the weirdest way to concatenate chars to form a command that will be finally executed:
0000130a movdqa xmm0, xmmword [rel data_2990] ; ' -l /var/log/utm' 00001312 mov edx, 0x67 ; 'g' 00001317 mov dword [rax+0x10], 0x6f6c2e70 ; 'p.lo' 0000131e mov word [rax+0x14], dx 00001322 mov rdi, rbp 00001325 movups xmmword [rax], xmm0 00001328 call system
Now that the cryptominer was executed as "docker-cache" then it will start the
scanning process to find more victims. One thing to notice is that you can check
the malware log at that log path mentioned previously and
The malware will utilize masscan
for this process.
scan_start is called with 2 arguments: 1) a randomly
generated number that will be used as an IP address and 2) the CIDR that is
/16. Then masscan will start scanning on port 2375/tcp.
00002950 char const data_2950 = "masscan %d.%d.%d.%d/%d -p 2375 -oL - --max-rate 360 2>/dev/null", 0
As the malware is not dropping masscan and not even checking if it exists, it
suggests that the point of entry was a machine with the proper tools already
installed or the attacker actively exploiting and injecting the malware.
Once it finds an open port the treat function is executed.
The treat function will basically try to infect and then prepare the victim
to start spreading. It executes docker
ps command against the remote docker
daemon in order to check if the daemon is insecure or not. If it was able to
execute the command sucessfully it will check if the instance was already
infected. If it is not the case then it will call install which will do the
heavy work of preparing the new container that will host the cryptominer. A new
name is created for this unwanted guest by calling new_name which will
randomly choose two words from a hardcoded list and concatenate them with a "_"
0000264e char const data_264e = "grommet", 0 00002656 char const data_2656 = "obelus", 0 0000265d char const data_265d = "agelast", 0 00002665 char const data_2665 = "amatorculist", 0 00002672 char const data_2672 = "peristeronic", 0 0000267f char const data_267f = "hirquiticke", 0 0000268b char const data_268b = "oxter", 0 00002691 char const data_2691 = "quire", 0 00002697 char const data_2697 = "baleful", 0 0000269f char const data_269f = "boorish", 0 000026a7 char const data_26a7 = "adroit", 0 000026ae char const data_26ae = "fecund", 0 000026b5 char const data_26b5 = "limpid", 0 000026bc char const data_26bc = "risible", 0 000026c4 char const data_26c4 = "verdant", 0 000026cc char const data_26cc = "zealous", 0
After a pretty name is chosen, the container is created by using a
ubuntu:18.04 image. Once that's done the update called to act against the
container. This function will prepare the instance by updating the apt cache
(what a nice touch), install
docker.io, copy itself to the
remote instance and then save the command to execute the binaries migrated in
/root/.bash_aliases. Last but not least, the container is restarted. This
same function is executed during the call to treat if the container found
during scanning is already infected to keep it up to date.
And that's it. This malware came after some days of reflecting and thinking how big companies already migrated it's whole infraestructure to run in kubernetes pods and how nowadays is common to see how code is already shipped with Dockerfiles which enables the possibility of users running containers on insecure instances. Luckily these daemon settings are not setup by default in common dockerd installations.
What will come next?