Disclaimer: This article is meant to educate developers and security practitioners of the current and present dangers of node.js development. Ethically, I cannot make the majority of my code publicly available on GitHub. However, I will show a few snippets and concepts for security awareness. If you are a past or present security colleague, please reach out for a live demonstration of the code.
This article will demonstrate the current dangers of embedded malware within node.js development pipelines. At the conclusion, I’ll provide a simple shell script to help you grep for malicious ASCII and BASE64 encoded strings within your node_modules sub-directories to assist you in the hunt.
Lucky for us, Node.js provides both an all-in-one local development environment as well as a server and middle ware runtime environment. Meaning an insecure version of node.js is likely on every “full-stack” developer workstation. Most notably, node.js extends a class called child_process which exports exec() and spawn() methods. The child_process class effectively means node.js can execute shell commands and executable code locally and possibly remotely if the API so allows.
Consider that serverless.com and many other frameworks encourage users to deploy their node.js into AWS and GCP. Ultimately to deploy into GCP and AWS you’ll need AWS Access Keys likely persisted locally on the users disk after they authenticate with their CLI. I suspected that embedded node.js malware could spawn a child process under the context of the user which executes a some code that “gets all the cloud keys”. From that point, you can move laterally from the Desktop to gaining persistence in the cloud. Let’s test the theory.
In a few minutes, I whipped up come code based on the public node.js documentation
I wrote multi-staged piece of code which does the following
WARNING: ALL ACCESS KEYS HAVE BEEN REVOKED. DON’T TRY (-;
Finding the Node.js malware
---- JavaScrypt local command execution ---- child_process exec() spawn() ---- Executables from child_process ---- bash zsh ... ... ----- HTTP tunnels --- req.connect()
The shell script can be found below
The shell script recursively greps for the use of child_process embedded malware in the node_modules and finds the malware deeply nested inside of /express/lib/express.js.
The tool does generate some “noise” but it provides an easy way to start the hunting. In addition to checking for regular ACII strings the script also looks for base64 encoded commands strings as well. Of course, the script can’t account for all the potential encodings and encryption used to obfuscate and morph malicious static content. In reality, you must also monitor your runtime for suspicious activity.
Going beyond searching for the static code, Node.js executes completely in memory unless you install additional package modules that make use of the child_process class. Node.js also does not have “persistent logging” by default but instead can be enabled with verbose and debugging to log to stdout and stderr within the console or you can pipe it into a local log file yourself.
Although no silver bullet I used the following during this exercise do monitor runtime activity inside an isolated virtual machine..
##### monitor network connections for backdoors during node.js runs netstat -c ##### dump network pcap to analyze node.js traffic with wireshark tcpdump -i <interface> -s 65535 -w <file> ##### look for unusual process execution under node ##### ps aux | grep node ##### Restrict the user environment while analyzing the source code No SUDO, No User with access to local cloud keys or SSH keys folders ##### No local secrets, please ... just say no Do not run new node_modules from Desktop with any local passwords, logged in browser, cloud access keys, ssh keys etc until you feel the code is safe