Crypto Miner Malware Analysis

Catching, and taking apart a crypto miner redtail malware


0. Introduction

Hello!
This was my first time doing a malware analysis, and I wanted to document the entire process from setting the honeypot trap to taking apart the malware. I tried to make this page understandable to everyone to the best of my abilities. I hope you will find this deep dive as interesting as it was for me. It's about a 10-15 minute read.

1. The Honeypot

A honeypot is a service, which catches incoming connections, login attempts, file uploads etc. on a specified port (port 22, SSH in this case). It emulates a complete system, pretending to be an actual computer, responding to commands, while being completely fake.
I chose Cowrie for this task, as it can handle SSH (and several others, but I didn't use them). I wanted it to be completely secure, so I used docker, while isolated from the other dockers running on my home server.
The docker-compose setup is the following (open a new yaml file using nano docker-compose.yml):

  • restart: "no" - This disables automatic restart of the docker, in case of a crash, I didn't want the docker to be vulnerable.
  • security_opt: -no-new-privileges: true - The docker cannot create higher privileged users than itself.
  • ports - Both SSH and TELNET ports are open, but I only used SSH here. Cowrie listens to SSH on the internal 2222 port, where the outside 22 port is routed to
  • volumes - The first noteworthy mount here is the fake_userdb.txt, which is a text file,
    containing only *:*:*. This allows any username:password combination to log in.
    (cowrie.cfg is also important, we will get to it in a second)
  • networks - The docker is on an isolated network, called isolated_honey, which has internal: false, because we want to receive information from the outside world, and driver: bridge is the default docker setting, it just lets this docker talk to other dockers on the same network. (But currently there is none)
Before launching, we have to set some very important flags in the cowrie.cfg file.
The most important settings here are these: outbound_enabled allow_direct_tcpip allow_local_txpip because these stop the "intruders" from using our server as a proxy.
We can set download_external to true, if we want to receive files (mostly malware).

We can now launch the docker, using sudo docker compose up -d (This launches all dockers defined in the yaml file, this time cowrie is all alone).


2. Cowrie

By default Cowrie's logs are basically unreadable, in their default form:

So I made a bash script, which turns it into beautiful one-liners: Here it's clearly visible, what time did something try to log in, with what SSH version, what credentials it tried to use, what command it "ran", and how much time they spent in the honeypot.
Bash script

Coming soon


I left the honeyput running 2 times overnight, and there were some interesting results:

Something uploaded a file from China with the hash d2ae2d2203786d3a1ef722b614801d4d7ef826ea4cf22e2148e4771c36305139, and when uploading it to VirusTotal, it will tell you, that it's a Linux based crypo miner.
This bot tried to steal many types of data from the server. First it got information about the server itself (uname, /proc/cpuinfo), info about other miner malwares running (grep | '[Mm]iner'), and a huge list of directories, where to bot looked for files, Telegram data, SMS data etc. Of course, all information it got, is completely fake, thanks to Cowrie.
Lastly a proxy attempt: Here, a bot tried to contact bing.com from my server, probably to check if the proxy works, and if it can later send Ddos attacks, or malicious data posing as me. These are what got stopped by the config flags we set earlier. My IP would get blacklisted pretty quickly, if the outgoing packets weren't dropped.

Finally, here is today's subject: Here, I got a huge chunk of malware from an infected computer in Germany, we will be focusing on one of them.

3. Static Analysis

First, we have to see what type of file this is.
After running file 59c29436755b0778e968d49feeae20ed65f5fa5e35f9f7965b8ed93420db91e5, we get the following result: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, no section header
Let's dissect this:

  • ELF 64-bit LSB executable - This just means the code is written for Linux systems, and is an executable binary (compiled machine code)
  • x86-64 - written for modern 64 bit systems (This is why I chose this file, the others were written for different architectures)
  • statically linked - This means that the executable can run without any external dependencies, everything is built into the executable to be able to run.
  • no section header - This is the most painful one to see, it means that the data that labels parts of the code (function names, locations), has been removed. This makes reverse engineering much more difficult.

I decided I'll try to decompile it with Ghidra anyway, just to see if I could get any information. After loading the file, I tried to get the strings out of the binary. This amount of strings in a binary file is very unusual, it often has thousands, not six.
But still, this result tells us a very important detail: This file is packed with the UPX executable packer
But just in case, I checked the binary itself: And there it is! In the top right: #UPX!
I was definitely not going to unpack the file on my server, in case anything bad happens, so I created a Debian Bookworm 12 virtual machine on my host computer. I installed some tools, and got the malware on the VM.

There was a possibility of the malware author modifying the UPX header, just enough for the binary to still be able to run, but it wouldn't be possible to unpack it. Thankfully, this wasn't the case: After giving it back to Ghidra in its unpacked form, we have many more strings: We can already see the word CRYPTO, which is the first sign it's a crypto miner.
When scrolling down a little, we can find actual coin names: It uses XMRIG, which is a popular open source Monero miner. Monero coin is favorable for hackers, because it's nearly impossible to track where the mined coins are being sent, thus the attacker remains anonymous.
There are multiple coins listed, probably backup coins, if there is anything wrong with the Monero pool, or the CPU can't mine efficiently.

4. Dynamic Analysis

I couldn't get any more meaningful information from reading the insides of the binary, so I decided to switch to dynamic analysis.

First of all, let's get the malware ready:
chmod 777 malware - Sets read, write, execute permissions to every user.

Since the section headers are stripped, we can't just tell the debugger to put a breakpoint where 'main' is.
We can only tell GDB (the debugger I'm using), to start at a memory address we specify.

We can start the debugger with gdb ./malware

Now the debugger console pops up, we can set a breakpoint at *0x400000, which is 'usually' somewhere around where the actual code starts in a statically linked binary.

Now that we have everything ready, let's start the debugger: run

We can see, that after starting the malware, it immediately started a new process, and terminated the previous. We can follow the child processes with set follow-fork-mode child and set stop-on-solib-events 1: The process opened a third process (554), which executed /usr/bin/dash, essentially overwriting itself. And since gdb is attached to the newest child, it couldn't find the breakpoint anymore, and resulted in a memory access error.

While trying to work out what to do, I checked top to see what was slowing the VM down. We can see that the kworker+ is using 26.7% CPU, which is high for an empty install, which isn't doing any actual system processes.

Additional information

The malware started with around 20-30% CPU usage, but many times peaked at 100-160% (using more than one core), and was constantly switching between sshd, kworker+ and systemd, system processes each one using close to max CPU. My theory is that the actual miner would use less on a normal setup, but since the network is disconnected, the malware continuously tries to connect, and gets in a wait-connect-wait loop.

Since I could never catch the current PID (process ID) the malware was running as (switching very frequently), we needed a different approach.

Thankfully there is a process strace which can track a process, even if it constantly opens and closes new ones.
Running sudo strace -f -s 1000 -e trace=execve,write,connect ./malware we can see every execution, file writing and network connection attempt from the malware: A summary on what can be seen on this image:

  • The malware is writing a crontab entry, which makes the malware start every time the system is restarted.
  • It's trying to open a backdoor on port 38901 using iptables, but is failing, because iptables didn't come preinstalled with the system. It's goal is to be able to remotely access the system later.
  • Now the most interesting part. At the bottom part of the screen, we can see a hardcoded IP address. The malware is trying to connect to 95.215.19.53 on port 853, which is DNS-over-TLS, which is an encrypted version of DNS.

Interestingly, when running nslookup 95.215.19.53 we can find an actual URL:

And when visiting the site, we get a DNS server site, which claims to be non logging, and uncensored, which is favorable for someone who wants to stay hidden.
Why is the malware using this?

Because traditional DNS servers work in plaintext, meaning that if a computer wants to go to google.com, the router, internet provider, or anyone who catches that network packet, can see that URL. But since this DNS site uses DNS-over-TLS, the packets go on a different port (853), and are encrypted. So when the malware wants to connects to a site (now dns.njal.la), any packet capture software (like Wireshark) cannot see inside the request, and cannot find the URL. This is an extra layer of security for the hacker.

Now that we know where the malware is trying to send data to, we can actually pretend to be that server!

Running sudo ip addr add 95.215.19.53/32 dev lo reroutes that IP address to our loopback address, which means that any data sent to 95.215.19.53, just gets "sent" to us instead.

We can use netcat to catch every piece of data: sudo ncat -lvp 853
When running the malware, we get this: This looks like encrypted data. We can "decrypt" this, using sudo ncat -lvp 853 --ssl -v -k
This will generate a temporary, fake security certificate. With this, netcat can open the encrypted tunnel, and read the data inside: If we ignore the netcat debug closing message, we can make out something: ml2137gangst
After running this several times, the first few characters (now ml in ml2137gangst) were 'random', there was V, H, L on a different line, @ even, which makes me think that it's just an encoding error, but I may be wrong here. So the complete string is probably 2137gangst.
This string is likely a some kind of password the malware sends to the server, in order for the server to do something else, or redirect traffic elsewhere. Also could just be a phrase to signal the hacker that a new computer has been infected.
Probable meaning behind 2137gangst

This is where things turn speculative, because I don't know anything 100%. A simple google search shows that the number 2137 appears to be a 'meme' in polish culture. There is also a Polish Youtube channel named '2137 gang', that may or may not be connected. Or maybe it's really just a meme, and both the musicians, and the hackers are fond of it.

Anyway I couldn't find anything else interesting in the network logs. Since in the binary, we didn't find any string predefined, with strace we figured out the malware's persistence tries, and the IP it's trying to talk to, but nothing else, there is one last thing to try.

We could dump the entire memory of one of the processes run by the malware.

This was not easy to do, as we know, the malware is constantly switching processes, but after some precise timing, I managed to dump the memory.

Firstly, I tried running strings on the log, which displays all text in a binary (or any other type) file. Since this is a statically linked binary, most of strings output will be system messages and error strings, which are not relevant, but I decided to include it anyway.

Looking some more, we can find keywords in the binary data section (marked by ^@ as empty space):
  • stratum+tcp:// - Stratum is a protocol used in crypo mining
  • selfrep - Usually means "self-replication", as in infecting other PCs, like a worm
  • redtail - This is a cryptomining malware family
  • masscan and zmap - These are mass scanning tools, to find other PCs
But finally, after searching for a long time, I found something interesting: There are a number of IP addresses here. This is probably an embedded configuration file for the miner. NiceHash being there, suggests that the hashing power the infected are producing, may be sold on Nicehash, which is a marketplace for bitcoin and hashing power.
Using nslookup on any of those proxies reveal these 5 IP addresses:
  • 45.148.10.68 - Based in Netherlands, Amsterdam
  • 45.148.10.112 - This one, and the ones below are all from Andorra according to IP lookup sites, but a Censys (world wide IP and port scanner) search confirms they are all from Netherlands
  • 45.148.10.144
  • 45.148.10.113
  • 45.148.10.145
  • 45.148.10.208

These IP addresses are proxies, which means they act as middlemen between the infected computers and the actual mining pools. All of the infected machines use one of these proxies, and the hacker can "collect" the hashing power in a single place.

When trying to ping the first address, it works, but when trying to traceroute it, it times out, which is weird. Basic ping uses ICMP Type 8, which is the Echo Request/Ping, while traceroute uses Type 11 (Time Exceeded), which is likely being blocked for some reason.
Anyway, confirming with nmap -p 853 --reason 45.148.10.68, it sends back syn-ack which means the server is alive and responsive. The server also responds to port 2137, which confirms, that the malware is configured to access the server through that port.

The Censys search also confirmed, that these 6 IP addresses are the only ones open on port 2137, so they are part of the hackers network. They are also open on 21370, but the service is unknown. Probably a backup.

Upon searching a little bit more, I uncovered more urls: All of the p.2137gang.* urls lead to the same IP: 130.12.180.51

This address (according to Censys) has ports 22, 80 and 443 open, which is a completely normal setup for a remote access web server. When visiting this address, it's just a nginx configuration landing page, likely deliberately, to fool automated scanners.
This is probably the "center of operations", but I can't dig any further, even though the server is responsive, it sends no data, and the admin panel (if there is one) is probably deep in an unguessable subdirectory.

Interestingly, the address leads to Germany which is weird, since there are many mentions of the 2137 Poland meme, and one of the addresses ends with .pl, once again meaning Poland. Unless this is another proxy, or if they have their main server in Germany.

I had one last idea, to dump the entire virtual machine memory into a single file on my host PC, and scan through there, but unfortunately it yielded no results. I could only find what we already knew.

4. Conclusion

As soon as I realised this malware is a miner, my self defined goal was to find the wallet address of the hacker. This, unfortunately, didn't happen, as the wallet address is probably on one of the proxies, or on the main server, to avoid people finding it like this. It also could have been encoded somewhere, but unless I want to spend days filtering through the entire virtual machine's memory dump, I will never find it that way.

This was my first time really digging myself into these kind of investigations, and even though I couldn't find everything I wanted, it was a really satisfying process to catch a malware with a honeypot, and take it apart as much as I could, and find interesting things.

Thank you for reading!

5. IOC (Indicators of Compromise)

Type Indicator Description
SHA-256 Hash 59c29436755b0778e968d49feeae20ed65f5fa5e35f9f7965b8ed93420db91e5 Primary Dropper (Packed ELF Executable)
SHA-256 Hash d2ae2d2203786d3a1ef722b614801d4d7ef826ea4cf22e2148e4771c36305139 Secondary Linux Crypto Miner (China)
IPv4 Address 130.12.180.51 Suspected Master Node / C2 Panel (Germany)
IPv4 Address 95.215.19.53 Njalla DNS-over-TLS Provider
IPv4 Address 45.148.10.68 Proxy / Stratum Node (Netherlands)
IPv4 Address 45.148.10.112 Proxy / Stratum Node (Netherlands)
IPv4 Address 45.148.10.113 Proxy / Stratum Node (Netherlands)
IPv4 Address 45.148.10.144 Proxy / Stratum Node (Netherlands)
IPv4 Address 45.148.10.145 Proxy / Stratum Node (Netherlands)
IPv4 Address 45.148.10.208 Proxy / Stratum Node (Netherlands)
Domain p.2137gang.pl Master C2 Domain
Domain p.2137gang.st Master C2 Domain
Domain p.2137gang.store Master C2 Domain
Domain p.2137gang.net Master C2 Domain
Domain p.2137gang.shop Master C2 Domain
Domain proxies.identities.network Stratum Proxy URL
Domain proxies.internetshadow.org Stratum Proxy URL
Domain proxies.internetshadow.link Stratum Proxy URL
Domain proxies.insanitycpp.cx Stratum Proxy URL
Domain proxies.insanecppdev.com Stratum Proxy URL
Domain dns.njal.la DoT Resolution Domain
TCP Port 2137 Primary Stratum / C2 Port
TCP Port 21370 Backup Stratum Port
TCP Port 853 Encrypted DNS-over-TLS Port
TCP Port 38901 Iptables Backdoor Target Port
String 2137gangst Malware C2 Handshake / Authorization