WEBINAR
GigaOm Radar Report for PTaaS: How to Make a Smarter Investment in Pentesting
WEBINAR
GigaOm Radar Report for PTaaS: How to Make a Smarter Investment in Pentesting

Common Vulnerabilities in NodeJS Applications

Node.js is an open-source and cross-platform JavaScript runtime environment. Today we are going to look at 3 different vulnerabilities by analyzing the source code of an application and how you can detect and exploit them. 

 

Node.js is an open-source and cross-platform JavaScript runtime environment. It is a popular tool for almost any kind of project! Node.js runs the V8 JavaScript engine, Google Chrome's core, outside the browser. This allows Node.js to be very performant.

That said, NodeJS has become very popular in recent years for deploying enterprise-level applications and beyond, but at the same time, many vulnerabilities have surfaced.

Today we are going to look at 3 different vulnerabilities by analyzing the source code of an application and how you can detect and exploit them. 

As always, we will rely on PWNX Labs. You can find the following lab in the retired machines. 

The Lab presents an application that shows a simple store from PWNX and Pentesting Made Simple. 

common-vulns-nodejs-applications
Let's analyze the code of the application in order to find the first vulnerability. 

JS (In)security - First Vulnerability: Eval function()

JS-first-vulnerability

The application implements a redirect function on any user login. The parameter used to redirect the user is then included in an eval function which leads to remote code execution allowing any user to execute code on the machine and potentially obtain a reverse shell. 

Javascript has two unsafe functions that execute code provided in a variable, eval and the function constructor. Let's look at the application's source code and search for unsafe functions. 

Here the eval function is used to parse the JSON document passed in the ReturnTo query parameter. The URL field from this JSON document is used after to redirect the user back to the page. In this scenario, the query string can be manipulated by an attacker. 

JSON-document

Let's try to pass a simple expression that uses a global process object to crash the application. 

global-process-object

We can notice that we didn't receive any response. The application log shows that the process was terminated because we passed the exit method. 

Looking back at the source code, the called eval function is a direct invocation meaning that the injected code has access to the current scope of the application, including all the local variables. 

eval-function

On the login function, the res object(response) from ExpressJS allows us to manipulate the content sent back to the response.

Let's try to inject some code that leaks all the user databases in a JSON response.

`res.send(JSON.stringify(users))`

JSON-response

As we can see is a JSON array with the info about all the users of the application.

Of course, when we have an eval function, we can perform code execution on the application and perform server takeover. Let's upload a webshell to execute arbitrary commands.

nodejs-webshell

This classic JS webshell starts an HTTP server listening on port 8000. The shell commands send the command query string parameter and are executed using the child_process module. 

nodejs-process-module

Injecting our webshell now, we can execute arbitrary commands connecting to port 8000.

webshell-port-8000-1

JS (In)security -Second Vulnerability: Loose Comparison

The NodeJS application is affected by a loose comparison vulnerability, the issue allows an attacker to trigger a condition always to be true and return always true values. In the case of this application, triggering a condition to be always true, an attacker can enumerate all the users(aka user enumeration attack) on the application.

JS-second-vuln

On utis.js, the loose comparison is used in the filter function and is called from the readProfile function. The loose comparison tries to compare a field of an object, provided as an input parameter, to a specific value and provided as an input parameter.

If it is possible to trick the filter condition into always being true, we would be able to retrieve information about all the users in the database. 

If the field did not exist, the property retrieval and the bracket notation would return undefined. If the value is null, the loose comparison will always be true. 

nodejs-value-is-null

How How to trick loose comparison:

  •     Mixed data types
  •     Arrays and objects
  •     Missing properties

Let’s login into the application and intercept the profile request.

nodejs-intercept-profile

Here we can see the field name, and we need to make it null as the loose comparison will be true and we will be able to retrieve the data. 

Delete the value param and insert a non-existing field parameter. 

nodejs-non-existent-field-parameter

The function is returning undefined. If the value is null, the loose comparison will always be true, and we can retrieve all the data.  

JS (In)security - Third Vulnerability: Prototype Pollution 

In the case of the Prototype Pollution vulnerability in the NodeJS application, the issue allows an attacker to control the default values of an object's properties. This allows the attacker to tamper with the application's logic and can lead to denial of service or remote code execution in extreme cases.

JS-third-vuln

The saveProfile function is the handler for the user profile management form. In the first step, we search the user database to find the user based on the user’s email.

If the user was found in the database, we clone fields into a clean temporary object named updatedUser.

After that is used, the Object.assign method is to update the user object retrieved from the user database. 

object-assign

The merge function in utils.js uses the for-in loop to iterate over the source object's properties.

Then it uses the bracket notation to add or write the properties based on their name.

The value comes also from the source object. The source object comes from the html form and could be manipulated by an attacker. 

What can you achieve with this type of attack?:

  • Denial of service 
  • Session fixation

Once again, we log in to the application and change the address via JSON request.

JSON-request

On the server side, the document is parsed, and the parsed object is cloned, as we saw from the source code before.

Here we can add an object overwriting the built-in object method to String.

object-overwriting

We caused a DOS on the web application.  The error message comes from the Express framework and is caused by the built-in object .toString is no longer a method but a string. 

A simple modification of the object prototype chain caused a denial of service attack.
denial-of-service-attack

Prototype pollution allows us to add properties to objects when they are not expected. 

In the login.js file, we notice that getting user credentials breeds them from the request body property.

What happens if these fields are missing from the request? 

Let's take the POST request on the profile again, and this time we use prototype pollution to add two properties, email and password. 

POST-request

Log out with the browser dev tool & then remove the email and password fields to cause both values to be missing.

remove-email-and-password

Now we can, for example, trick the user into sending a POST request with no values, and the user is logged as h4t4way@pms.com. This bug is called Session Fixation.

session-fixation

Even though the email and password fields were missing from the POST request, they were inherited by the request body object in the login function.

 body-object-login-function

Conclusion 

As with any other human-made technology, programming languages and environments present advantages and threats. Most technologies can be made as secure as possible with the proper use of certain principles, and Node.js is no exception. 

In this article, we saw a few of the many vulnerabilities that could affect Node.js applications. 

Resources: 

Back to Blog
About Reando Veshi
I am a penetration tester and occasional bug hunter. As a lover of WebSec, I always like to find something wrong and fix it. I have been passionate about computer science since childhood and have been in the security world for five and a half years. I started studying IT many years ago in school and university, where I learned JAVA and C and PHP, Python, and Javascript during my career. I’m the founder of Pentesting Made Simple, an Italian community where we speak about Pentesting, Bug Bounty, and Ethical Hacking in general. More By Reando Veshi
Azure AD: Pentesting Fundamentals
Core member Orhan Yildirim walks us through how to use Azure AD when pentesting.
Blog
May 23, 2022
The Guide to Understanding Content Security Policy (CSP) and Bypass Exploits
This blog post aims to demonstrate what CSP is and why CSP is implemented. And how attackers can bypass CSP. In this article, I will include how you can bypass some directives to achieve XSS on the target application.
Blog
Oct 12, 2022
Web Socket Vulnerabilites
WebSockets are an exciting technology that has been gaining traction in the industry. Many companies are using the technology, especially in their real-time services
Blog
Sep 27, 2022