Velocity.js Remote Code Execution via Function Constructor Bypass
Velocity.js versions up to 2.1.6 are vulnerable to Remote Code Execution (RCE) through an incomplete fix for a previous prototype pollution vulnerability, enabling attackers to craft malicious Velocity templates to leverage unfiltered property-read expressions and execute arbitrary JavaScript code on the server, leading to full server compromise.
A critical Remote Code Execution (RCE) vulnerability (GHSA-7gfh-x38p-prh3) has been identified in velocityjs versions up to 2.1.6, impacting applications that render attacker-controlled Velocity templates. This flaw represents a bypass of the previously issued fix for GHSA-j658-c2gf-x6pq, which addressed prototype pollution but incompletely mitigated the underlying issue. The vulnerability allows an attacker to manipulate unfiltered property-read expressions to invoke the JavaScript Function constructor with arbitrary code. This can lead to the execution of arbitrary shell commands on the server, enabling full system compromise, data exfiltration, and lateral movement. This exploit is particularly dangerous because it stems from a fix that was incomplete, leaving a critical attack vector open.
Attack Chain
- An attacker crafts and submits a malicious Velocity template containing an RCE payload to a vulnerable application.
- The vulnerable
velocityjstemplate engine begins processing the malicious template, specifically evaluating the expression$x.constructor.constructor(...). - During template evaluation, the
getAttributes()function inreferences.cjsis called for$x.constructor, which, due to a lack of filtering, resolves to theObjectconstructor. - Subsequently, the second
.constructorlookup on theObjectconstructor is processed, also viagetAttributes(), resolving to the JavaScriptFunctionconstructor. - The attacker's arbitrary JavaScript string payload, such as
"return process.mainModule.require('child_process').execSync('whoami').toString()"from the PoC, is passed as an argument to theFunctionconstructor. - This action dynamically creates a new JavaScript function object containing the attacker's supplied code, which includes a call to
child_process.execSyncfor command execution. - The newly created malicious function is then assigned to a Velocity template variable (e.g.,
$f) using the#setdirective. - The Velocity template subsequently invokes this variable (
$f()), triggering the execution of the embedded system command (whoamiin the PoC) on the underlying operating system and allowing the attacker to receive its output.
Impact
Applications using velocityjs versions <= 2.1.6 are at severe risk of full server compromise. A successful exploitation allows an attacker to execute arbitrary shell commands with the privileges of the vulnerable application, potentially leading to the theft of sensitive data, access to cloud credentials, and the ability to pivot to other systems within the internal network. The vulnerability's nature as an RCE means direct control over the host system, making it a critical threat to any organization deploying affected velocityjs applications. The previous fix for prototype pollution (GHSA-j658-c2gf-x6pq) was bypassed, making this a more severe RCE rather than just data manipulation.
Recommendation
- Upgrade all instances of
velocityjsto version 2.1.7 or later immediately to apply the patch mentioned in the references. - Deploy the provided Sigma rule to detect suspicious process creation patterns indicative of RCE attempts originating from Node.js applications.
- Enable comprehensive
process_creationlogging for all Node.js application servers to capture details such as parent process, image path, and command line arguments. - Block the malicious Velocity template syntax identified in the IOCs at application input layers if immediate patching is not feasible.
Detection coverage 1
Detect Velocity.js RCE via child_process.execSync
highDetects the execution of child_process.execSync via a Node.js application, which can be indicative of Remote Code Execution stemming from the Velocity.js vulnerability (GHSA-7gfh-x38p-prh3).
Detection queries are available on the platform. Get full rules →
Indicators of compromise
1
other
| Type | Value |
|---|---|
| other | process.mainModule.require('child_process').execSync('whoami') |