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
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
Static Analysis means to analyse the file, while it's on the disk, not being run
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
Dynamic Analysis means to examine a process while it's running
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!