I found a bug that allows you to escape the AngularJS template sandbox. Angular is a mustache based template language. It allows you to put expressions that are evaluated in your html. For example, {{1+1}} renders at 2
The sandbox makes it so users can't access window / constructors from inside Angular within expressions. You can't do {{{}.constructor = ...}} for example. This is because those operations are considered unsafe should the site also be saving templates from user input. It opens up the site to XSS.
Going through the source code, I found that there is a check for obj === {}.constructor that can be bypassed.
Basically, it comes down to bypassing this:
if (obj) {
if (obj === (0).constructor || obj === (false).constructor || obj === ''.constructor ||
obj === {}.constructor || obj === [].constructor || obj === Function.constructor) {
throw $parseMinErr('isecaf',
'Assigning to a constructor is disallowed! Expression: {0}', fullExpression);
}
}
I was able to get past those checks by hiding my constructor calls inside another object (object literal or array literal).
As literal object:
{{x = {'y':''.constructor.prototype}; x['y'].charAt=[].join;$eval('x=alert(`Evaluated Object Literal`)');}}
Or as an array:
{{x = [''.constructor.prototype]; x[0].charAt=[].join; $eval('x=alert(`Evaluated Array`)');}}
How can the above checks be best improved to make it so these cases fail?
I tried changing the obj === {}.constructor checks to instanceof instead and that makes the checks work, but I think it may introduce its own security issues. I would like to submit a pull request, but I'm hoping other security researchers out there might contribute to the conversation in order to make the sandbox as strong as possible.
Here is my issue posted to Angular project for reference: https://github.com/angular/angular.js/issues/14939
Here is related material: http://blog.portswigger.net/2016/01/xss-without-html-client-side-template.html
Here is a jsFiddle: https://jsfiddle.net/ianhickey/5sb665we/