OFFSEC – Writing Node.js malware that steals your cloud access keys

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.

Vector

You may not realize it but the majority of node.js development is built upon thousands of JavaScript package dependencies from upstream source contributors called node modules. For example, serverless.com packages over 1,000+ node package modules as part of a development framework that thousands of people download daily.

Node Package Manager (NPM) allows developers to publish and download open-source JavaScript packages with each other. Much like open source repositories.

If you drill down into the node_module sub-directory within your application root folder then you’ll find the “node packages”. Open one up and you’ll find that each node package module has it’s own node_modules sub-directory creating a complex nested web of JavaScript executable dependencies. Hundreds of thousands of lines of code nested deeply within one another being shared with fresh developers.

Threat

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.

Weaponizing

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

  1. Asynchronous JavaScript which spawns a child_process and executes a wget request to the attacker machine and downloads the second stage malicious payload
  2. Asynchronous JavaScript call executes the newly downloaded piece of python malware which enumerates for Google Cloud and AWS access keys then POSTs the users cloud credentials to the attacker’s machine

Notice, that that I didn’t need to download the second stage python executable and persist a malicious file to disk and risk being caught by AV. At this point, I could have just as easily packaged the entire attack into JavaScript middleware which would run completely in memory. There was no need to persist the second stage on the Desktop because the goal was to elevate privileges to a cloud account.

I then downloaded a popular node.js module called “express” which can be found here. This is literally the most common node_module to host web-servers and APIs. Then I placed the node.js JavaScript down a few hundreds lines in the main() function which always get called when the user runs the web server. “Hello world”

Unknowingly, to the developer, the server turns on and says “Hello World” and the malicious JavaScript code executes asynchronously behind the scenes so there is little delay in process times. You can see below my attacking machine receives a GET for the get_cloud_tokens.py. The python script executes locally and packages all of developers cloud access keys inside of a JSON object and POST the cloud access keys back to my attacking server. I didn’t spend the time on setting up the command and control server to handle POST to the demo.

WARNING: ALL ACCESS KEYS HAVE BEEN REVOKED. DON’T TRY (-;

Finding the Node.js malware

Although quite simple, I’ve written a small shell script tool that recursively greps your node.js application root folder for a risky JavaScript strings that are worth investigating. For example …

---- 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

https://github.com/secSandman/realbadjs/blob/master/realbadjs.sh

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 

— s3cS&m@n

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s