WU_DOJO35_YesWeHack(SSTI with NodeJS)

- 4 mins read

Description

In the case of SSTI (Server-Side Template Injection) in a Node.js environment, a vulnerability is exploited when uncontrolled user data is injected into server-side templates. This type of flaw allows an attacker to execute malicious JavaScript code on the server, which can lead to complete system compromise, including reading or modifying sensitive files and executing remote commands. In this vulnerability, the identified entry point was the _render(template, data) function, where unfiltered data was passed directly into the template, allowing the attacker to manipulate template logic and gain control over the server.

Exploitation

Code Analysis

Pasted image 20240921154430.png

Initially, a messaging application is available, allowing messages to be sent to a recipient via Node.js code.

The first step is to examine the Node.js application. The initial lines show a Message class, with a constructor taking two parameters, to and msg, and assigning them to this.to = to; and this.msg = msg, respectively.

The send() method in this class then displays a string in the console indicating the message was sent to the recipient (this.to).

Next, the use of path.basename allows extracting the last segment of a path. For example, with /tmp/flag.txt, the function would return flag.txt. If this.to is controlled by a malicious user and contains something like ../../tmp/flag.txt, the application might create or overwrite critical files on the system without using path.basename.

The following line is of interest: var data = {"to":"", "msg":""}, which shows two possible payloads, one for the to field (representing the destination, here index.ejs) and one for the msg field, which will be exploited for SSTI injection.

Lastly, for detection purposes, the final line of code provides insights into the studied vulnerability:

Pasted image 20240921154737.png

This line shows that ejs.render is used to render an EJS template with the file index.ejs and the value of msg, indicating that template injection via EJS is possible through the msg value.

Detection Phase

Initially, the input of interest is found in the Message class in the application code. This class uses two parameters, this.to and this.msg, representing the two payloads, structured as {"to":"/files", "msg":"SSTI"}. These fields allow specification of the message destination (to) and message content (msg), which can be exploited to attempt an SSTI injection.

Pasted image 20240921121644.png

For detection, a format (render JS) is used in the form “<%= ….. %>”.

Pasted image 20240921121507.png

After submitting the form, our injection is executed during the request, resulting in the evaluation of the injected expression, for example 7*7, which gives the result 49.

Pasted image 20240921122142.png

After this detection phase, we can focus on the final line of code, which shows the following instruction:

Pasted image 20240921154737.png

In this line, fs.readFileSync reads an existing file. Knowing the file of interest is in /tmp/flag.txt, we could attempt to exploit this. However, direct use of fs is disabled, preventing us from directly reading the file with a payload such as:

Pasted image 20240921155356.png

Given this restriction, I focused on using the constructor, a common technique in SSTI cases. By bypassing direct access to fs, I crafted a payload using the JavaScript constructor to access process.mainModule and read the target file:

Pasted image 20240921155509.png
Pasted image 20240921155549.png

FLAG{W1th_Cr34t1vity_C0m3s_RCE!!}

Risks

The SSTI vulnerability allows an attacker to inject and execute malicious code on the server by manipulating insecure templates, like EJS here. This provides the ability to read sensitive files, execute system commands, or exfiltrate data. For example, I was able to read the /tmp/flag.txt file, demonstrating unauthorized access to server files, compromising data integrity and confidentiality.

Remediation

To protect an application against SSTI (Server-Side Template Injection) attacks, it is crucial to validate and escape all user inputs before including them in templates. This can be achieved by using escape methods specific to the template engine in use (like the escape option in EJS) to prevent the execution of injected code. Additionally, disabling any functionality that allows code execution within templates and restricting access to global objects or system modules like require is recommended. These practices, combined with strict user data filtering, provide effective protection against SSTI injections.

References

https://portswigger.net/web-security/server-side-template-injection
https://book.hacktricks.xyz/pentesting-web/ssti-server-side-template-injection
https://www.vaadata.com/blog/fr/vulnerabilite-web-server-side-template-injection-ssti-qu-est-ce-que-c-est-comment-s-en-proteger/
https://owasp.org/www-project-web-security-testing-guide/v41/4-Web_Application_Security_Testing/07-Input_Validation_Testing/18-Testing_for_Server_Side_Template_Injection