angular/angular.js

Expression execution within HTML entities

tomersela opened this issue · 3 comments

I'm submitting a ...

  • regression from 1.7.0
  • security issue
  • issue caused by a new browser version
  • other

Current behavior:
Including the following in an AngularJS template cause the inner expression (1+1) to be executed.

The following expression yields 2:<br>
&#123;&#123;1+1&#125;&#125;

image

Expected / new behavior:

I would expect the page to show:

The following expression yields 2:
{{1+1}}

While the above shows misbehavior, I marked this one as a security issue as many AngularJS based web-applications rely on escaping values as a strategy against XSS.
Consider the known expression bypass - {{constructor.constructor('alert(1)')()}} that fires an alert.

Even if one escapes {, }, (, ) and change to HTML entities, the expression will still fire an alert.

&#123;&#123;constructor.constructor&#40;'alert(1)'&#41;&#40;&#41;&#125;&#125;

image

Minimal reproduction of the problem with instructions:

Simple expression - http://plnkr.co/edit/tgLY35ttgxcNq0l3
Expression bypass - http://plnkr.co/edit/o4J1vyLfnrQAqAPt

AngularJS version: 1.7.9

Verified with the most recent version as of today (1.7.9)
This behavior appears from V1.6 and above
Not reproducible in V1.5

Browser: all

Anything else:

/

Since AngularJS compiles directly from the DOM nodes the strategy of "escaping" characters in an attempt to sanitize content that is then provided as a template is not valid.

At the point that AngularJS is triggered the original text of &#123;&#123;1+1&#125;&#125; has already been converted by the browser to {{1+1}}.

As described in the security documentation, templates are implicitly trusted by AngularJS:

If an attacker has access to control AngularJS templates or expressions, they can exploit an AngularJS application via an XSS attack, regardless of the version.

Moreover, the document suggests that any user provided content should be wrapped in an ngNonBindable directive:

If you must continue to allow user-provided content in an AngularJS template then the safest option is to ensure that it is only present in the part of the template that is made inert via the ngNonBindable directive.

Here are the above repros updated to use this directive:

http://plnkr.co/edit/urp9pzKzlUzL9yLc
http://plnkr.co/edit/YC8PKya5bKi43HLm

In addition to what Pete said, if you just want to escape interpolation markers , you have to prefix them with a backslash: https://docs.angularjs.org/api/ng/service/$interpolate#escaped-interpolation

@petebacondarwin @gkalpak thank you both for the detailed explanation!