Yet another way to invoke functions without parentheses
An Alternative Method for Parentheses-Free Function Invocation
I have discovered yet another technique to execute arbitrary JavaScript functions without the use of parentheses (). This specific payload functions by hijacking the Error.prepareStackTrace method to dynamically generate functions.
🛠️ The Mechanism: prepareStackTrace
Normally, prepareStackTrace is utilized to format stack traces. It is automatically triggered whenever the .stack property of an Error object is accessed.
The function accepts two primary arguments:
- The
Errorobject itself. - A structured representation of the stack (essentially an array of code positions).
Comparison of Signatures
The signature of prepareStackTrace mirrors that of the Function constructor. As long as the second argument is a string of valid JavaScript, we can create a function.
| Feature | prepareStackTrace | Function Constructor |
|---|---|---|
| Argument 1 | Error Object | Function Body (String) |
| Argument 2 | Stack Array | Function Name/Args |
⚠️ The Challenge: Data Types
If we simply assign Error.prepareStackTrace = Function, attempting to call the resulting function leads to a SyntaxError.
"This is expected behavior because a stack trace is an
Arrayof positions, not a valid string of JavaScript code."
To bypass this, we must manipulate Array.prototype.toString() so that it returns our specific payload instead of the default array representation.
Implementation Strategies
There are two primary ways to achieve this:
- Direct Overwrite: Replace the method with a constant function returning the payload.
- Prototype Pollution: Sneak the payload into another function and then redirect the
Arrayprototype.
I found a clever way to stay within the Error realm by using Error.prototype.toString, which can be influenced by polluting Object.prototype.message.
🚀 The Execution Flow
To summarize the logic:
Final Trigger: The valueOf Trick
Once the function is generated, we still need to invoke it without (). I utilized the valueOf technique, which triggers when an object is treated as a primitive. Alternatively, one could use toString via string concatenation.
Requirements Checklist:
- Overwrite
prepareStackTracewithFunction - Pollute
Object.prototype.message - Redirect
Array.prototype.toString - Coerce object to primitive via
+x
💻 The Final Payload
// 1. Set the payload via prototype pollution
Object.prototype.message='alert\x281337\x29';
// 2. Redirect Array string conversion to Error string conversion
Array.prototype.toString=Error.prototype.toString;
// 3. Use the Function constructor to generate the function
Error.prepareStackTrace = Function;
// 4. Create the error and link valueOf to the generated function
const x = new Error;
x.valueOf=x.stack;
// 5. Invoke via unary plus coercion
+x;