Crypto-Miner Malware Delivery Analysis
Discovery
A few weeks ago I was reworking a proof-of-concept exploit for a specific IoT device and while looking through the web service error log on the device, I noticed a few garbled mumbo-jumbo entries like in the following screenshot. These weren’t requests I had made to the device, but from another device (more than likely compromised) attempting to exploit a vulnerability and compromise the device I was working on. Seeing as the request is probably being made from a compromised victim device, I masked the specific requestor IP address just in case.
Error log entry for invalid request |
---|
While at first glance this appears to make no sense, we can break down the request, see what it does, and enumerate the delivery mechanism of this specific malware.
Breaking it Down
The first few segments of the log entry are just the request timestamp, entry type, process, etc. Basically nothing of relevance for what we’re interested in. Next we can see that the log entry appears to have two almost identical requests. The first copy is the access error, and the second is the reason for access error (filesystem path).
After trimming the fat and duplicate of the request we are left with the following: |
---|
Compromising the Machine
Vulnerability and Initial Access
The first segment to look at is this part:
Initial Exploit |
---|
Doing a quick Google search with that part returns information about CVE-2022-26134 which NIST describes it as:
In affected versions of Confluence Server and Data Center, an OGNL injection vulnerability exists that would allow an unauthenticated attacker to execute arbitrary code on a Confluence Server or Data Center instance. The affected versions are from 1.3.0 before 7.4.17, from 7.13.0 before 7.13.7, from 7.14.0 before 7.14.3, from 7.15.0 before 7.15.2, from 7.16.0 before 7.16.4, from 7.17.0 before 7.17.4, and from 7.18.0 before 7.18.1.
“allow an unauthenticated attacker to execute arbitrary code” - This means with this the request could run commands on the target system if vulnerable. The device I was working with doesn’t use Atlassian Confluence, so it hit a dead-end here. But given a server that is vulnerable, and a carefully tailored request, anyone could make the system run system commands.
Explanation of the specific vulnerability being exploited is beyond the scope of this writing, but there are articles and examples such as here and here, and even a room at TryHackMe that go more in depth for those interested.
Exploiting and Execution
Next let’s take a look and see exatly what commands it was trying to execute by analyzing the payload for command()
:
Exploit Payload |
---|
Wow, what is going on here?
What this contains is a list for the java.lang.ProcessBuilder().command()
to process.
bash
and-c
tells it to interpret the following with thebash
shell.echo
a base64 encoded string and pipe tobase64 -d
to decode- pipe output of
base64 -d
to anotherbash
to execute.
We will need to decode this string and see what it says in order to understand more. At this point all we can see is that it echo
a seemingly random string of characters to be read by base64
and executed after decoding.
Using either base64 -d
command or CyberChef and decoding the string we get:
Decoded Exploit Payload |
---|
Now this is starting to look interesting.
We have found what appears to be a script inteneded for execution on a victim system. Not all devices have the curl
program, so relying on that binary to be on the system would potentially hinder the ability for this payload to function. Instead, what this script does is create its own function to use /dev/tcp
for file transfer.
Staging
Once the initial payload is executed it downloads this new script w.sh
:
Stager Script w.sh |
---|
Boiled down, this new script starts by assigning some variables for reference later in the script such as the path to ls
and the domain and URL path for future downloads.
It then does the following:
find the
chattr
binary and move it tozzhcht
(installing e2fsprogs package if not found and possible)set an environment variable for the new location of the renamed
chattr
binarycopies the
ls
binary to replacechattr
sets attributes on the replaced
chattr
(originallyls
) to ensure the changes aren’t modified. As defined in thechattr
manpage:a - A file with the ‘a’ attribute set can only be opened in append mode for writing. Only the superuser or a process possessing the CAP_LINUX_IMMUTABLE capability can set or clear this attribute.
i - A file with the ‘i’ attribute cannot be modified: it cannot be deleted or renamed, no link can be created to this file, most of the file’s metadata can not be modified, and the file can not be opened in write mode. Only the superuser or a process possessing the CAP_LINUX_IMMUTABLE capability can set or clear this attribute.
check if the current user ID is root and retrieve another script depending on result:
- if user is root download
ar.sh
- if user is not root download
ai.sh
- if user is root download
Setting up Shop
At this point in the exploit we’re at a fork in the road. If the script has access as system administrator or root
it has free reign of the system. But what if it only has normal user or unprivileged access? We will start by looking at it from a low-privileged user basis first and analyze the script.
Unprivileged System Access
Unprivileged Script ai.sh |
---|
This script starts with setting some variables like proxyip
and media_url
as can be seen in the screenshot above. It then sets up a check_exists
function that checks that the system is not already communicating with the proxyip
which would signal that that the miner is probably already running on this system.
After results from the check_exists
function, the script continues to download the media_url
and output to a file named xm.jpg
. But if this file is a JPEG, why is it following up the download by running tar
, chmod
, and ./start
?
To get to the bottom of this we’ll download and examine the file with file
and see what it does contain.
Reported File Type of xm.jpg |
---|
Well, the file
output explains the script running tar
on the file after the download. It is a compressed TAR archive, not an actual JPEG.
For the next step we will list the contents of this tarball and see what it contains.
List of Contents xm.jpg |
---|
xm.jpg contents file information |
---|
BINGO! we’ve reached the plant.
The ‘unprivileged’ branch of the script downloads the plant as a compressed TAR archive masking itself as a JPEG image, then unpacks the archive and makes the start
binary found in the archive executable to continue by running ./start
.
The remaining files in the archive are for setting up the miner, the XMRig miner binaries, and the hide
for hiding the processes.
Privileged System Access
This script is much longer than the unprivileged script (486 lines compared to 29) and has a lot going on, so we will dissect and analyze.
The first section to look at is the function calls at the bottom of the script. From there we will examine what the functions being called perform.
In the initialization there are variable assignments for domain
, mainurl
, miner_url
, and rshell_url
to be visited later in the functions. The function calls at the bottom of the script outlines the order the functions will be called in. We will follow this map to determine what the script is doing.
Initialization | Function Calls |
---|---|
Starting in the function call section of the script there is the m_command()
function that checks the work from the initial stager script w.sh
regarding chattr
.
m_command() function |
---|
The next functions called are env_set()
and clean_logs()
.
env_set() function | clean_logs() function |
---|---|
The env_set()
function performs the following:
- disable firewall
firewalld
- set maximum
ulimit
for resources (YOLO SEND IT!! mode) - sets some environment variables to take care of
history
andPATH
- disable SELINUX and the kernel watchdog (is the system taking to long to respond?)
- set DNS resolver hosts to Google (8.8.8.8) and a Chinese DNS resolver 114DNS (114.114.114.114)
- clear and takeover
crontab
just in case a system cron job tries to periodically interfere with the new operations
Then the clean_logs()
function does just as it says and iterates through the log files truncating each as an empty file.
Continuing through the function calls we find the download_f()
function:
download_f() function |
---|
This function creates the directory /var/tmp/.11
if it doesn’t already exist (MOHOME
from the initialization) and looks for the sshd
binary at that location. If the sshd
binary exists it will delete and download a new copy by retrieving the enbash.tar
file from the URL stored in miner_url
, storing it as debash.tar
and extracting the contents. It then will check for a bioset
binary in the same location and downloads that if needed by retrieving the enbio.tar
file, storing it as debio.tar
and extracting the contents.
debash.tar | debio.tar |
---|---|
debash.tar & debio.tar file information |
---|
Digging around I was able to find the bash.sh script used for hiding processes as part of the DDexec Github repository.
Looking at the size and md5sum of the two miner binary files aarch64
and x86_64
from the ‘unprivileged’ download file xm.jpg
and comparing to the sshd
binary from enbash.tar
it is probably safe to assume the sshd
binary is the miner binary.
Binary Comparisons |
---|
The binary bioset
from the enbio.tar
appears to be the reverse shell utility Platypus.
The next functions called, setup_s()
and makesshaxx()
, proceed to set up the miner to appear as an SSH daemon and add keys to the root
user for future access by the attacker.
setup_s() function | makesshaxx() function |
---|---|
Following the SSH daemon setup and access we will find the ins_package()
function which installs build tools for compiling some future tooling and kernel module rootkit Diamorphine from source as well as net-tools
and masscan
for providing network analysis and host discovery.
ins_package() function |
---|
The next function exec_hide2()
contains another large encoded string.
exec_hide2() function |
---|
Decoding this string with base64 -d
reveals the source for another process-hider, libprocesshider. The string is decoded in this function and binary compiled.
This was as far as I continued through my own dissection and analysis because during research into the fkoths
binary contained in the enbash.tar
file, I found an article written just over one month prior by researchers at Cado Security regarding this exact novel malware tooling. They do a much better job of explaining the remainder of the delivery and function in their article posted March 6, 2024 Spinning YARN - A New Linux Malware Campaign Targets Docker, Apache Hadoop, Redis and Confluence.
I have emailed Cado Security offering an archive of my findings that contain samples they mentioned not being able to retrieve, and hope to assist in what capacity I may.
Hopefully what I was able to cover in the delivery of this malware was informative, as I learned quite a bit through the researching of the scripts and locating the origins of some of the code and utilities used.