Building a backdoor in Node.js with 50 lines of code

Have you ever wondered how attackers gain high-level access to a computer system and how they manage to steal personal and financial data, install additional malware, and hijack devices?

Well, often this happens with the help of a backdoor.

 

In simple terms, to carry out such an attack, the attacker must inject the malware on your system and then open a communication channel that allows him to send commands and control the host remotely.

 

A hacker performing this attack manages to gain access similar to that of the application he attacked. If your application runs in sudo mode, then the attacker will gain superuser access (root-access).

 

Therefore, it is usually safe to run the application in a closed environment and give them as little access as possible.

 

Let’s see how this problem relates to NodeJS. 

I’ve chosen this language because it is one of the most widely used in web development and software development in general. 

Even though it is still debatable, it is almost safe to say that JavaScript, due to its vast ecosystem, has become the language we refer to as One language to rule them all.

 

Nowadays, npm which is, as I suppose everyone knows, the package manager for the JavaScript programming language, has reached over 1M public packages. According to this trivia, it has approximately 8TB in size.

 

These conditions, plus some key problems related to the implicit issues that NodeJS has, make this ecosystem the perfect choice for attackers.

Where is a backdoor hidden?

 

By far the hardest thing to do is to hide your backdoor so no one can find it. Usually, you will find such vulnerabilities being injected in two places: 

  • old legacy code which runs based on the first rule of programming: If it works don’t touch it 
  • inside hidden dependencies.

The problem with the first case is that it runs on a lot of redundant code. Code that no one can maintain but often can’t erase for fear of a bad implementation in which that piece of code is tied to all the other parts of the program.

The problem with the second case is that even your dependencies have dependencies. 

 

As it was the case for this incident, it is easy for a hacker to create a good library, but at the same time to create a dependency link and hide malware somewhere on that path.

 

For example, you use a popular npm package, let’s call it package A, and this package has a dependency B which also has a dependency C which also has a dependency D, which almost no one has heard of, but it has a backdoor in it.

How to code your own backdoor?

 

The best way to understand this security issue is by creating your own backdoor in a system.

 

What we want to achieve is the ability to execute arbitrary commands on a foreign computer system. 

 

In the end, the entire hack will look like this:

  • On the left is the hacker’s server, which acts as a receiver for the output of the commands.
  • On the right is the victim server.
  • In the middle is where the attack takes place.

 

If things are clear, let’s build our own simple backdoor in a NodeJS application.

 

Because we don’t have a big legacy code to hide our vulnerability in, we’ll go with the second case and we’ll pretend that we have a popular npm package for logging messages.

 

The victim’s application is a simple express based web app that uses our package.

 

To get what we set out to do, we need to bind our backdoor to the request object. In this way, we can intercept all the incoming requests with our malware. Even just like this and we already have great leverage and we can find out all the user credential or all the secret tokens when they authenticate against an application which uses our package.

 

To bind to the request object, our backdoor needs to be part of a middleware. Therefore I’ve created an npm package called logger whose purpose is to prettyprint all the incoming requests. 

 

This way, everyone who will use our module will have to connect it to the server as a middleware.

 

 

This line of code will trigger our backdoor.

 

Now by default, these are the parameters:

  • req – request is the incoming request to the server
  • res – is the response, but this is not interset for us
  • next – will call the next middleware in the chain (aka callback)

 

console.log() – is used as a decoy, because we said our package is used to prettyprint incoming requests, so it has to do something to look legit.

 

The workflow is the following: we send a request to the victim’s web server with a command and our backdoor will execute that command on the victim’s system. 

 

To do so, we’ll make use of the exec method from child_process which is a default module in NodeJS.

 

 

We’ll send the command we want to run on the foreign host as a query parameter along with a password. This way we protect our backdoor from other hackers and we’ll inform the script to omit this request and don’t print it. The developer will never see this attack coming.

 

To exploit this, we can send a get request formatted like this:

 

https://victim-url?pwd=secret-pwd&cmd=ls

 

In this way, our password will be validated and the ls (list files) command will be executed against the host.

 

All we need to do now is to somehow capture the output of the command. There is almost no point to run a command without seeing the output of it.

 

To see the result of our hack, we’ll make a request to one of our personal servers with the output of the command.

 

Our personal server, which acts as a receiver, will have the following code:

 

 

And to complete our backdoor, we’ll send the output of the commands in the body of a POST request to our server:

 

 

There is one last problem: how do we know the address of a victim? To create an attack, we need to know the web address of the victim.

 

To do so, we add one more handler in our receiver:

 

 

Now in our backdoor, we send a request when a new application uses our library. 

 

To do so, complete the previous code with the following snippet:

 

 

 

This way, each time a foreign application uses your npm package and someone accesses that application for the first time, a request will be sent to our receiver which informs us about the URL of the target.

 

How to protect yourself?

 

You might be asking yourself, if it is so easy to do it, then why don’t we hear more about this type of attack?

 

As I said before, the hard part is to hide or in this case to inject the vulnerability. Most of the time, these packages are scanned for vulnerabilities using third-party applications or are manually verified.

To protect yourself against this type of attacks, try to do your best in the following aspects:

  • encapsulate your code and give it as little access to the system as you can,
  • use well-known and maintained open source components,
  • use a good monitoring tool (preferably your own) to keep track of all the request are made to your server.

 

Guy Bar-Gil

Guy Bar-Gil / About Author

Guy is a product manager at WhiteSource, where we enable software development teams to integrate open source fearlessly and without compromising agility. Before WhiteSource, Guy worked for the IDF's intelligence division, where he spent time as a combat operator and project manager. Outside of work, you can find Guy reading (everything from fiction to physics), playing and watching sports, traveling the world, and spending time with friends and family. LinkedIn