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 the bash shell.
  • echo a base64 encoded string and pipe to base64 -d to decode
  • pipe output of base64 -d to another bash 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
payload decoded with base64 -d

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 to zzhcht (installing e2fsprogs package if not found and possible)

  • set an environment variable for the new location of the renamed chattr binary

  • copies the ls binary to replace chattr

  • sets attributes on the replaced chattr (originally ls) to ensure the changes aren’t modified. As defined in the chattr 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

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
proxyip blurred because it may be another compromised device

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.

InitializationFunction 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() functionclean_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 and PATH
  • 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.tardebio.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() functionmakesshaxx() 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.