Angular Template Injection

Posted by Alex, Comments

As part of our work at Coverity, the Security Research Laboratory (SRL) performs security reviews of our own software. Last week I had a look at some of the new code in our reporting webapp. It turned out that we were using some technologies and vulnerable code patterns I had not seen before, so I thought I would share what I saw with the world in case anyone sees the same pattern I did.

As I began to review this code for the first time, I identified a series of Closure Templates that were being used to generate HTML output, with the usual {$} syntax being used to insert model values into the output. After a little bit of looking around, I saw that they hadn't changed Closure's default escaping away from HTML escaping, and they were also not putting these properties into any contexts where HTML escaping was incorrect.

However, I also noticed that there was a lot of syntax similar to {literal}{{}}{/literal} and many HTML attributes such as ng-repeat that I hadn't seen before.

Somewhat confused by this, I dug a bit further and discovered that rather than being directly displayed, the output of these views was being passed to a JavaScript framework called Angular.

If you are like me and have never heard of Angular before, it seems to be a complete MVC framework for JavaScript and it turned out that our HTML output was actually being used as the views for Angular.

Like most server-side MVC, most of the view in Angular is plain old HTML. Angular augments this by a series of directives that are specified as attributes (e.g., the ng-repeat attribute I had seen) and the ability to bind data into attributes or right into HTML as Angular expressions. So the {literal}{{}}{/literal} I was seeing in our Closure Templates was being output as {{}} and being consumed by Angular, which was binding the values from its own model in there.

So, in my eternal quest for more XSS, I went to investigate what would happen if a user was able to inject {{}} expressions into an Angular template. I was convinced that this would let me XSS our application somehow.

After digging a bit, it turns out that Angular doesn't simply eval() these expressions, but rather has its own expression tokenizer/evaluator written in JavaScript. The things that this evaluator supports are:

  • Model access: {{modelVar}}
  • Field access: {{modelVar.field}}
  • Function calls: {{modelVar.function(1)}}
  • Built-in filter functions: {{ modelVar | json }}
  • Arithmetic and logical operators: {{1+2}}, {{true&&false}}, etc.
  • Object and Array constructors: {{ {name: 'test'} }}

The first thing I tried was to find a filter function that would allow me to somehow execute JavaScript, but after reviewing the built-in filter functions as well as the ones our application injected, I determined this wasn't going to work.

One of the filters our application created was called autoescape, which did some HTML escaping and was being applied to some data like {{tts.get('id')|autoescape}} which made me wonder if I could have my XSS by creating an expression that would return <script>alert(1)</script> without using any characters that would be encoded on the server side. Using this JsFiddle to test what would happen if I could I could get an expression that returned an XSS string, I realized that this too was a dead end for my XSS dreams.

So I decided to review the expression parser to see what was going on in there, and if anything looked sketchy. I discovered that the underlying objects that Angular was using in its expressions were native JavaScript objects (rather than providing their own custom object model, which some view technologies do); so field access was implemented by simply accessing the native fields of an object. This was interesting because besides properties you assign to an object, native objects will have several other fields attached.

After staring at the MDN for a bit, I discovered the same thing that many before me had discovered:

{}.toString.constructor('alert(1)') creates a new function from a string by invoking the Function constructor.

So to wrap this all up, if you can inject {{}} into an angular template, the following will execute JavaScript when data values are bound into the template:


And now our developers have one more XSS to fix before the code ships to our customers. If you'd like to play around with it yourself, I've set-up another jsFiddle with the payload already working.