
I thought I knew all about how to call functions without parentheses.
alert`1337`
throw onerror=alert,1337
Function`x${'alert\x281337\x29'}x```
'alert\x281337\x29'instanceof{[Symbol['hasInstance']]:eval}
valueOf=alert;window+''
x=new DOMMatrix;matrix=alert;x.a=1337;location='javascript'+':'+x
// or any DOMXSS sink such as location=name
This post presents yet another surprising method to help you understand how tagged template strings work. , which can be used to deepen your understanding of the JavaScript language and provides a foundation for avoiding JavaScript sandboxes and WAFs. It all started with my post about running non-alphanumeric JavaScript without parentheses. I found that you can pass a string to the tagged template. A tagged template means using the prefixed function in front of the template string literal.for example alert`123`
A tagged template that calls alert
I noticed in a previous post that you can pass multiple arguments to these functions with just strings, as the following code shows.
function x(){
alert(arguments[0]);
alert(arguments[1]);
}
x`x${'ale'+'rt(1)'}x`
What happens here is that all the strings are added to the first argument as an array and the second argument gets the string. alert(1)
But why is the string alert(1)
Passed as the second argument to a function? Strings are treated differently than placeholders. A normal string without placeholders is added as an array to the first argument, but the placeholders are added as a new argument of that type. This last point is important. What I didn’t realize at the time was that the placeholders were added as type arguments instead of strings! The following code shows this.
function x(){
alert(arguments[0]);
arguments[1]('hello')
}
function y(str){
alert(str);
}
x`x${y}x`
This works great. This means you can call a function and pass multiple arguments of any type. But there is a problem. If you use strings in tagged templates, they will always be added as the first argument, breaking functions that use the first argument. The goal here is to call the function with the arguments of your choice. For example, you might want to call: setTimeout
Because the first argument accepts a function or a string, and the third argument calls that function with that value.
setTimeout(alert, 0, 'I get passed to alert')
let’s call setTimeout
:
setTimeout`${alert}${0}${1}`//Uncaught SyntaxError: Unexpected token ','
Using your custom function again, you can see what’s going on.
function x(){
console.log(arguments);
}
x`${alert}${0}${1}`
So we know that the first argument contains an array of empty strings, and the last one contains another array full of empty strings.when setTimeout
When I convert these arrays to strings, I get a series of commas that cause syntax errors. somehow, setTimeout
A function that ignores the first argument, how would you do that? setTimeout.call
because the first argument will be an array that will be assigned to “this” in setTimeout
The function and alert are now passed as the first argument to the function, but…
setTimeout.call`${alert}${0}${1}`//Illegal invocation
Since we no longer call the function directly, JavaScript throws an exception and we can no longer call the function because “this” is no longer a window object.I thought it was game over, but realized I’d done some JS hacking in the past [].sort
others. These allow the function to be called without the illegal call error.
[].sort.call`${alert}1337`
Of course, other features are also available, such as: eval
and other array methods map
:
[].map.call`${eval}\\u{61}lert\x281337\x29`
I later discovered that you can use Reflect
that too:
above is new navigation.navigate
Chrome’s method that causes a redirect with a payload from window.name
. To call navigate
You have to provide the correct “thisObject” to the function. This is done in the function’s second argument. Reflect.apply
. of window.name
is used with the third argument, which must be an array of arguments sent to the function.use Reflect
The methods set and apply can assign to almost any object or call any function! Note the use of “window.name” to hide the payload. The payload is usually retrieved from another page or domain by passing it within the “name” property of a window that is passed between domains.
Conclusion
It’s pretty amazing that template strings support this behavior, allowing browsers to use sorting and other features this way. By hacking JavaScript, you can learn new and interesting ways to exploit its capabilities to produce unexpected results.
I hope someone finds an eighth way to run JavaScript without parentheses!
back to all articles