critical · 9.8Jul 24, 2026

Velocityjs Remote Code Execution via Property-Read to Function Constructor

Rohit Hatagale
AI Security Researcher, SecureLayer7

A critical flaw in the velocityjs template engine lets an attacker run arbitrary shell commands on the server by walking the JavaScript prototype chain inside a Velocity template, bypassing the…

Packagevelocityjs
Ecosystemnpm
Affected<= 2.1.6
Fixed in2.1.7
Velocityjs Remote Code Execution via Property-Read to Function Constructor

The problem

Velocityjs v2.1.6 evaluates property-read expressions in references.cjs without any blocklist. The getAttributes() function returns baseRef[property.id] with no check on keys like constructor or prototype.

An attacker who controls a Velocity template can chain two property reads to reach Object.constructor (the Function constructor), pass an arbitrary string of JavaScript, and execute it on the server with full Node.js privileges. The prior fix (GHSA-j658-c2gf-x6pq) only guarded the #set assignment target in set.cjs, leaving the right-hand expression path completely unfiltered.

Proof of concept

A working proof-of-concept for this issue in velocityjs, with the exact payload below.

javascript
const velocity = require('velocityjs');

const template = "#set($f=$x.constructor.constructor(\"return process.mainModule.require('child_process').execSync('whoami').toString()\"))#set($r=$f())$r";

console.log(velocity.render(template, { x: {} }));
// Output: <current OS username>

The exploit walks two hops up the prototype chain: {}.constructor resolves to Object, then Object.constructor resolves to Function. Passing a command string to Function(...) creates a callable that execSync runs under the server process.

The root cause is CWE-94 (Code Injection): references.cjs line 88 performs a raw bracket property access with zero key filtering, so prototype-chain properties are reachable by design. The patch in commit f8e47a6 extends the isBlockedPathKey check (previously only in set.cjs) to also cover property-read resolution in references.cjs, blocking constructor, __proto__, and prototype traversal in both code paths.

The fix

Upgrade to velocityjs 2.1.7. The fix was merged in pull request #192 (commit f8e47a6) and extends the blocked-key check to cover property-read expressions, not just #set assignment targets. If an immediate upgrade is not possible, do not render attacker-controlled templates under any circumstances.

Reporter not attributed.

References: [1][2][3][4][5]

Related research