WU_DOJO35_YesWeHack(SSTI with NodeJS)
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
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:
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.
For detection, a format (render JS) is used in the form “<%= ….. %>”.
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
.
After this detection phase, we can focus on the final line of code, which shows the following instruction:
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:
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:
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