Current Offer: 80% Discount on All Our Pentesting Exams

A Deep Dive into Server-Side JavaScript Injection (SSJI) Vulnerabilities

A Deep Dive into Server-Side JavaScript Injection (SSJI) Vulnerabilities

Hello readers! In this blog post, our Principal Consultant Rohit Misuriya and our Senior Consultant Aditya Raj Singh have discussed the infamous Server-Side JavaScript Injection (SSJI) vulnerability. The blog covers everything from the basics of Server-Side injection to possible attack vectors. They have explained the vulnerability, any prerequisites, attack vectors, how the vulnerability works in the background, recommendations, practice labs for hands-on experience, and more.

By the end of this article, you’ll have a solid understanding of SSJI attacks and the tools & techniques required to detect and exploit SSJI vulnerabilities. So, let’s dive into the world of SSJI!

TL;DR

  • SSJI occurs when an attacker injects malicious JavaScript into a web application’s server-side code. 
  • SSJI can lead to unauthorized data and system access, as well as allow attackers to perform attacks such as Remote Command Execution (RCE) and Server-Side Request Forgery (SSRF) in severe cases.
  • To prevent SSJI attacks, web developers should always validate and sanitise all user input, use input filtering to remove non-essential characters, and keep their web applications and libraries up-to-date to ensure they are not vulnerable to known security flaws.

Client-Side JavaScript Injection vs. Server-Side JavaScript Injection

Client-side and server-side JavaScript injection are two different types of security vulnerabilities, and each poses different risks to a web application. Now let us understand the differences between the two.

Client-Side JavaScript Injection Vulnerabilities

Client-Side JavaScript Injection vulnerabilities occur when an attacker is able to successfully inject a malicious JavaScript code into a web application, which then gets executed in the victim user’s browser. These vulnerabilities typically arise due to insufficient input validations that are implemented by the developers and inadequate security measures that are implemented on the client side. The injected code is executed within the context of the victim user’s browser, allowing the attacker to manipulate the behaviour of the web page, steal user data, and much more on behalf of the victim user without its consent.

There are several types of client-side JavaScript injection vulnerabilities, some of which are as follows:

  • Cross-site scripting (XSS) is the most common form of client-side JavaScript injection vulnerability. It occurs when an attacker is able to inject malicious scripts into a website, which are then executed by other users who visit the affected page. XSS vulnerabilities can be categorized as stored, reflected, or DOM-based, depending on how the malicious script is injected and executed.
  • Another similar type of vulnerability would be DOM (Document Object Model) Manipulation. When attackers can manipulate the DOM, which represents the structure of a web page, using malicious JavaScript. It can lead to various security risks, such as changing the appearance of the page a.k.a defacing, adding misleading information, redirecting users to attacker-controlled malicious websites, or harvesting sensitive information such as credentials.

Server-Side JavaScript Injection Vulnerabilities

Server-Side JavaScript Injection vulnerabilities, on the other hand, occur when an attacker is able to inject malicious JavaScript code into the server-side components or scripts, which gets executed on the server before the response is sent back to the client’s browser. Similar to Client-Side JavaScript Injection vulnerabilities these vulnerabilities also occur due to insufficient input validation and in addition poor coding practices on the server side. Compared to Client-Side JavaScript Injection vulnerabilities the Server-Side JavaScript Injection Vulnerabilities have comparatively serious consequences, as they allow attackers to manipulate the server’s behaviour and potentially gain unauthorized access to sensitive data or perform actions that the server that they are not allowed to do. Some of these vulnerabilities are explained below.

  • One such vulnerability is Server-Side Template Injection (SSTI) which occurs when an attacker is able to inject code into server-side templates that are then dynamically rendered to create a response. If not properly sanitized, these templates can execute the injected code, leading to data exposure or remote code execution on the server.
  • The other type of vulnerability and the one which we will be covering extensively in this blog is the Server-Side JavaScript Injection vulnerability. In cases where JavaScript is executed on the server side, attackers can attempt to inject malicious JavaScript code that the server will execute. This could lead to unauthorised access, data leaks, or other security breaches.

Server-Side JavaScript Injection (SSJI)

SSJI is a type of security vulnerability that occurs when an attacker can inject malicious JavaScript code into a web application’s server-side code. This can happen when the web application does not properly validate or sanitize user input, or when it relies on untrusted data from an external source. Once an attacker has successfully injected their code, it can then be executed on the server to steal sensitive data, manipulate server-side resources, or even take control of the entire web application. There are several ways in which SSJI can occur, including but not limited to the following:

  • An attacker can manipulate user input to inject JavaScript code into server-side scripts or templates, which will be executed on the server.
  • An attacker can manipulate HTTP headers the client sends to inject JavaScript code that will be executed on the server.
  • An attacker can manipulate query parameters sent to a server to inject JavaScript code that will be executed on the server.
  • An attacker can manipulate cookies sent to a server to inject JavaScript code that will be executed on the server.

An attacker can use multiple JavaScript functions to run malicious JavaScript code on the server, some of which are mentioned below:

  • eval()
  • setTimeout()
  • setInterval()
  • Function()

They are exposed if the input is not properly validated. For instance, using eval() to perform DoS (Denial of Service) will consume the entire CPU power. In essence, an attacker can also carry out or perform anything virtually on the system (within user permission limits). Once the attacker has successfully injected malicious code, it can then be used to perform a range of attacks, including but not limited to the following:

  • Persistent Cross-site scripting (XSS) attacks: The attacker can use the injected malicious JavaScript code on the Server-Side to steal sensitive information from the server, such as sensitive information stored on the server.
  • Server-side request forgery (SSRF) attacks: The attacker can use the injected malicious JavaScript code on the server side to manipulate server-side resources, such as databases or APIs, by sending unauthorized requests.
  • Remote code execution (RCE) attacks: The attacker can also use the injected malicious JavaScript code on the server side to execute arbitrary code on the server, which can then be leveraged to perform a complete takeover of the web server.

That was just the tip of the iceberg as both these attacks can have severe consequences. Now that we have developed a basic understanding of what SSJI is, let’s see a few examples along with some code snippets to understand how this vulnerability can be carried out.

SSJI via Node.js

Let us consider the following Node.js code snippet, which uses the eval() function to execute the user-supplied JavaScript code on the application server. In this example, the eval() function is used to execute the userInput value as JavaScript code on the server. This means that an attacker could potentially inject a malicious JavaScript code into the userInput value to execute arbitrary commands on the server. 

For example, an attacker could supply the following value for userInput and in the background server, this payload will use the child_process module of Node.js to execute the rm -rf /* command that deletes all files that are present on the application server:

SSJI via JavaScript

Let us consider the following server-side JavaScript code, which takes a user-supplied value as input and uses it to construct a MongoDB query in the back end:

In this example, the userInput variable is not properly validated/sanitized, which means that an attacker could potentially inject JavaScript code into the userInput value which can then be used to modify the underlying MongoDB query and execute arbitrary commands on the application server. For example, an attacker could inject the following value as user input to modify the underlying MongoDB query on the server-side and extract all the records available in the products collection that is available on the server-side:

The above-mentioned value would modify the query to include a JavaScript condition that always evaluates to true, effectively returning all records in the collection.

Let us take another example, Let’s consider a situation where a web application allows users to submit feedback that is later displayed in an administrator’s dashboard.

In this example, an attacker could identify that the application processes user feedback without proper validation which they can leverage to provide the following input as the feedback parameter:

The attacker’s input includes JavaScript code that uses the fs module to write a file named pwned.txt with the content “Hacked!” to the server’s filesystem. When the attacker’s input is processed by the server, the malicious JavaScript code is executed on the server side, and the file pwned.txt is created with the content that was specified by the attacker.

SSJI to SSRF

SSJI and SSRF are two different types of attacks, but they can be related in some cases and in some special circumstances can be chained together to increase the impact. SSJI can be used to carry out SSRF attacks by injecting malicious JavaScript code that requests a specific URL, which can then be leveraged to exploit vulnerabilities in the targeted system. Below is an example of how SSJI can be used to carry out an SSRF attack in a Node.js application:

In the above code snippet, the url parameter is taken from the end user as input and is then directly concatenated to the backend JavaScript, the response of which is then returned to the end user after getting processed on the server-side in the response body. An attacker could use this vulnerability to inject a URL that points to a vulnerable server, such as a local server, and exploit it using the server’s credentials. Below is an example payload that can be used by an attacker to exploit this vulnerability and carry out an SSRF attack:

In this example, the attacker has injected a URL that points to a local server that is running on port 8080 internally, which is accessible from the server that is vulnerable to SSJI. If the local server has any vulnerabilities, such as a weak authentication mechanism, the attacker could exploit it to gain access to sensitive information.

It should also be noted that SSRF may not be possible in every case, and the attacker might not be presented with the details every single time as the server will process the attacker’s input locally on the available services running on the target server.

SSJI to RCE

As we have seen in the previous examples it must now be clear that SSJI can be used as part of a larger attack, such as remote command execution (RCE), in which an attacker can execute arbitrary commands on the server by injecting malicious code into the web application’s server-side code. RCE attacks are typically carried out by exploiting vulnerabilities in the server-side code, such as unvalidated user input or poorly secured APIs, to inject malicious code. The attacker can then use the injected code to execute arbitrary commands on the server, such as reading or modifying files, creating or deleting user accounts, or even installing backdoors to maintain persistence on the server. Below is an example of how SSJI can be used to carry out an RCE attack:

Let us try to see how SSJI can be used to achieve RCE on an application. Consider the following Node.js code, which takes user-supplied input and uses the exec() function from the child_process module in the backend to execute a shell command on the server:

In this example, the userInput variable is not properly validated or sanitized, which means an attacker could potentially inject a malicious shell command into the userInput value to execute arbitrary commands on the server. For example, an attacker could supply the ’; ls /’ value for userInput to execute a command that lists all files on the server. This value would append a semicolon to the end of the user input, effectively terminating the current command and allowing the attacker to execute any additional commands they choose. The second command in this example lists all files in the root directory of the server.

An attacker could also supply the following value for userInput to execute a command that downloads and executes a malicious script on the server:

This value would use the wget command to download a malicious script from the attacker’s server, and then pipe the output to the sh command, which would execute the script. This could allow the attacker to take control of the server or access sensitive information.

To prevent this type of attack, developers should properly validate and sanitize all user input to ensure that it does not contain any untrusted or malicious code. Additionally, developers should avoid using unsafe functions like exec() to execute shell commands on the server, and should instead use safer alternatives like the spawn() function from the child_process module, which can help prevent injection attacks by providing separate arguments for the command and its arguments.

Interesting Real-World Scenarios

There have been several CVEs (Common Vulnerabilities and Exposures) in various web frameworks and libraries related to SSJI. The following are a few interesting CVEs associated with SSJI, along with details on how the CVE can be exploited in a real-world scenario:

SSJI to RCE in Paypal

Recently, an SSJI vulnerability was identified in a subdomain owned by Paypal. The researcher observed that the demo.paypal.com server responds differently to certain types of input. Specifically, it reacts differently to backslash (‘\‘) and newline (‘%0a‘) requests by throwing a ‘syntax error‘ in the responses. However, it responds with HTTP 200 OK for characters like single quotes, double quotes, and others. The security researcher performed some reconnaissance and identified that the PayPal Node.js application uses the Dust.js JavaScript templating engine on the server-side.

Upon investigating the source code of Dust.js on GitHub, the security researcher identified that the issue is related to the use of the “if” Dust.js helpers. In older versions of Dust.js, the “if” helpers are used for conditional evaluations. These helpers internally use JavaScript’s eval() function to evaluate complex expressions. The security researcher identified that the “if” helper’s eval() function is vulnerable to SSJI. The application takes user-provided input and applies html encoding to certain characters like single quotes () and double quotes (), making direct exploitation challenging. However, the security researcher finds that there is a vulnerability when the input parameter is treated as an array instead of a string. 

The following code snippet indicates the use of the eval function which is known to cause the SSJI vulnerabilities and is often time a potential attack vector.

The security researcher crafted the below-mentioned payload that leverages the vulnerability to execute arbitrary commands. By sending specific input like /etc/passwd to the demo application, they managed to exfiltrate sensitive information. The payload uses Node.js’s child_process.exec() to run the curl command and send the contents of the /etc/passwd file to an external server.

SSJI to RCE in Fastify

A Server-Side JavaScript Injection vulnerability in Fastify was reported a while back, allowing an attacker with control over a single property name in the serialization schema to achieve Remote Command Execution in the context of the web server. The security researcher found that Fastify was using fast-json-stingify to serialize the data in the response. This library was found to be vulnerable to Server-Side Injection which was leveraged to achieve Remote Code Execution. The submitted PoC exploit contained the following code.

The security researcher was able to demonstrate, using the above-mentioned exploit code, that the vulnerable library fast-json-stringify, which incorrectly handled the input, could be used by an adversary to perform RCE, which he was able to achieve successfully, as shown in the screenshot below.

This vulnerability was marked as a High-risk issue by the team and was patched shortly after that and appropriate mitigations were put in place to effectively handle this weakness by Fastify.

SSJI in Bassmaster Node.JS Plugin

A while ago, an SSJI vulnerability was found in the internals.batch function of the bassmaster plugin for the hapi server framework for Node.js via lib/batch.js file which allowed unauthenticated remote attackers to execute arbitrary Javascript code on the server side using an eval. This vulnerability was leveraged by adversaries on a huge scale to perform RCE on web applications that supported the bassmaster plugin. Shortly after this vulnerability was identified and the PoC exploits were made public a commit was made to the existing bassmaster plugin in which the following changes were made to effectively mitigate the discovered vulnerability.

SSJI in MongoDB

Recently, an SSJI vulnerability was identified in a MongoDB due to inadequate validation of the requests sent to the nativeHelper function in SpiderMonkey, which allowed the remote authenticated adversaries to perform a denial of service attack (invalid memory access and server crash) or execution of arbitrary code using a specially crafted memory address in the first argument. According to the publicly available PoC exploit of this vulnerability, the NativeFunction func comes from the x javascript object which is then called without any appropriate validation checks and results in a denial of service attack or execution of arbitrary code. The publicly available exploit for this vulnerability is as follows:

Practice Labs

As a group of seasoned penetration testers and security researchers, we firmly advocate for a practical, hands-on approach to cyber security. In line with this philosophy, we have recently released a lab on Server-Side Javascript Injection on our platform, Vulnmachines. Through our labs, readers can gain valuable insights into this vulnerability and its exploitation by simulating real-life scenarios, allowing for a deeper understanding of its implications.

In our lab on SSJI, you will come across a web application that allows users to search for phone numbers and ages by providing a first name or last name. However, the application has a critical vulnerability that enables attackers to exploit Server-Side JavaScript Injection, potentially leading to unauthorized access to sensitive information, such as file listings and source code.

The application features a search functionality that sends a GET request to the server with two parameters: q and SearchBy. The q parameter holds the search string, while the SearchBy parameter specifies the function to call, either firstName or lastName:

The SearchBy function in the server-side code is vulnerable to SSJI, which allows malicious users to inject JavaScript code into the SearchBy parameter. Unsafely handling user input exposes the application to potential attacks. An attacker can exploit this vulnerability by injecting SSJI payloads into the q parameter.

Constructing Payload to Fetch the Listing of Current Directory: 

One SSJI payload to fetch the listing of the current directory would be as follows: res.end(require(‘fs’).readdirSync(‘.’).toString()) 

This payload leverages the fs module in Node.js, allowing the attacker to execute file system operations. readdirSync retrieves the contents of the current directory (denoted by the dot ‘.‘), and toString() converts the resulting array to a string. The res.end() method is commonly used to send a response back to the client, in this case, containing the directory listing:

Fetching “app.js” Source Code: 

To retrieve the source code of the app.js file, attackers can use the following SSJI payload: res.end(require(‘fs’).readFileSync(“<PATH>”)) 

In this payload, the <PATH> placeholder should be replaced with the appropriate path to the app.js file on the server. By executing this payload, the attacker can obtain the source code of app.js, which contains the source code of the application and the flag for this lab:

Mitigations and Best Practices

To prevent this type of attack, developers should avoid using the eval() function and instead use safer alternatives, such as the Function() constructor or JSON parsing functions, to execute dynamic JavaScript code on the server. Additionally, all user input should be properly validated and sanitised to ensure that it does not contain any untrusted or malicious code. Here are some best practices to consider:

  • Validate all user input and external data, such as data from APIs, before using it in your application. Also, remove any characters or strings that could be used to inject malicious code. This can help prevent malicious code from being injected into your server-side code.
  • A defence-in-depth approach would be to use prepared statements in conjunction with parameterized queries, ensuring that the applications are not vulnerable to SQL injection attacks and also eliminating the possibility of performing SSJI attacks via SQLI.
  • Use security libraries and frameworks that provide input validation and sanitization functions. For example, many web frameworks have built-in functionality to prevent code injection attacks.
  • When rendering data in your application, use output encoding or escaping to prevent malicious code from being executed in the browser. This can help prevent cross-site scripting (XSS) attacks, which can be used to inject and execute malicious JavaScript code.
  • Restrict access to sensitive resources, such as server-side scripts and databases, to authorised users only. This can help prevent unauthorised access and manipulation of your resources.
  • Keep your web application software and frameworks up to date to ensure that you have the latest security patches and features.

By following these best practices, you can help prevent server-side JavaScript injection attacks and protect your web application from malicious actors.

Web Frameworks and Libraries for Preventing SSJI

Web frameworks and libraries play an important role in preventing server-side JavaScript injection attacks by providing built-in security features and guidelines that help developers write secure code. Many modern web frameworks, such as Express.js, provide features for securely handling user input, such as input validation and sanitization. These frameworks often have built-in security features that help prevent injection attacks, such as parameterized queries that can help prevent SQL injection attacks and built-in sanitization functions that can help prevent cross-site scripting (XSS) attacks.

Below is an example of how you can prevent server-side JavaScript injection in a Node.js application:

In this example, the userInput variable is first validated using a regular expression to ensure that it only contains alphanumeric characters. If the input fails the validation check, the server returns an error response and does not perform any further processing. If the input is valid, the userInput variable is then sanitised using a regular expression to remove any potentially malicious characters, such as quotes or backticks. This helps prevent injection attacks by ensuring that the input does not contain any code that could be executed on the server.

Finally, the sanitised user input is used to perform a safe operation, such as querying a database, and the results are returned to the client.

References

Leave a Reply

Your email address will not be published. Required fields are marked *

Arrange a Callback