So if you didn't know, ES6 added template strings, which are these really awesome things because they have multiline and string interpolation capabilities (and they're safely far away from JSON, which in my opinion shouldn't have such capabilities). They are very pretty and incredibly handy; you write
console.log(`
I'm so ${scared} in case ${ I.fall.off() } my chair
And I'm wonderin' how I'll get down the stair
`);
and this gets converted into console.log("\nI'm so " + scared + " in case " + I.fall.off() +
" my chair\nAnd I'm wonderin' how I'll get down the stair\n");
Except for one thing: they're called "template strings" because actually this is a sort of "default behavior" which can be metaprogrammed. There is a default interpreter which could be written: function interpret(text_segments, ...split_segments) {
var out = "";
for (var i = 0; i < split_segments.length; i++) {
out += text_segments[i] + split_segments[i];
}
return out + text_segments[i];
}
but... you can write one of your own, if you want, and put it on the beginning. Therefore: > console.log(`abc ${[1,2,3]} def`)
abc 1,2,3 def
undefined
> console.log `abc ${[1,2,3]} def`
[ 'abc ', ' def' ] [ 1, 2, 3 ]
undefined
Notice that the side effect of console.log has happened with the arguments given to it, allowing for code execution.As for mitigation... add detection of backticks to whatever code was detecting parentheses. It's not a very widely used symbol in any context other than shell scripting and LaTeX anyways, so you're probably good to go if you just outlaw that character before calling eval() on the whole.
No, stop trying to "detect" whether something is code and just properly encode things in the first place. If this allows someone to get code into your website, that is not a minor flaw to be fixed with a quick tweak, it is indicative of a fundamental flaw in the understanding of whoever wrote that code. When things are properly encoded, browsers won't execute them. (Give or take some issues around content type detection.)
Still no support on IE (desktop|mobile), Android, ios<9
!+[]`$
The parent link above is ES5 and uses 8 characters: !+[](){}
The jsfuck.com ES6 dialect uses 6 characters: !+[]()
jsfuck was previously ES5 but they now rely on ES6isms to shorten their code.That last bit isn't snark; it's my personal experience.
As mentioned in the article, there are filters in the real world that will be penetrated by this.
$foo = "file_get_contents";
$bar = "http://example.com/";
$baz = $foo($bar);
In PHP 7 this doesn't even require you to give the function name its own variable: ("file_get_contents")("http://example.com/");[[]+{}][+[]][++[[]][+[]]+[++[[]][+[]]][+[]]]+[!![]+[]][+[]][++[[]][+[]]+[++[[]][+[]]][+[]]]+[!![]+[]][+[]][+[]]+[!![]+[]][+[]][+[]]+[![]+[]][+[]][++[[]][+[]]+[++[[]][+[]]][+[]]+[++[[]][+[]]][+[]]]