untrusted JSON → lodash importsKeys → Function(paramList) → comma-split signature → missing args → default param executes
If you don’t understand these, the exploit will look like black magic.
new Function(arg1, arg2, body) builds a function (arguments before the body are parameter names).["a","b"] → "a,b").undefined.curl, basic Node/Express reading, and understanding JSON object keys.Function, eval, template compilation), think structure injection, not string injection.POST /renderreq.body.The entire bug reduces to one bad trust decision:
Fatal assumption: importsKeys are trusted variable names.
The bug exists because Lodash treats imports keys as variable names, while JavaScript treats function parameter lists as raw strings. When a comma-containing key crosses this boundary, a single JSON key expands into multiple parameters. Lodash supplies values positionally, not semantically, so excess parameters become undefined. In ES6, undefined is not a failure state, it is an execution trigger. so Function() parameter list is just a string
But arrays are coerced:
The JS engine only sees commas.
Lodash sees:
importsKeys = ["left, right"];
importsValues = ["VALUE"];
Compiled function
Mapping:
| Parameter | Value | Result |
|---|---|---|
| left | VALUE | filled |
| right | undefined | gap |
ES6 default parameters execute on undefined
We’re not injecting into the template body. We’re executing code during argument initialization.
Let injected values get consumed:
Mapping:
| Param | Value | Result |
|---|---|---|
| a | X | filled |
| b | lodash object | swallowed |
| c | undefined | executes |
No quote breaking
No template body injection
No escaping
Pure structure abuse
That’s why the name “Gap” is perfect.
Cause:
Effect:
Express parses JSON → req.body
Renderer forwards attacker-controlled imports
Lodash extracts keys (comma-containing string)
Function() builds a multi-parameter signature
Values run out → gap
Default parameter executes → RCE
Any system that forwards user-controlled strings into function signatures, argument lists, or compiler-like constructs becomes vulnerable to alignment attacks. The exploit does not rely on JavaScript, it relies on positional binding and default evaluation semantics.
Never pass user input as template options
Allowlist locals explicitly
Define helper imports server-side only
One JSON key becomes many JS parameters, the values run out, and undefined turns into execution.
In this writeup I just explained the one approach I used to solve the challenge, there is another approach to solve it so check the author writeup for more details here: Mushroom