Hacking

Understanding Security Implications of AngularJs

Sahil Dhar
January 12, 2017 by
Sahil Dhar

In this article, we will have a brief overview of security implications of AngularJs which mainly includes basics of AngularJs and inner working of various sandbox escapes for different versions being discovered so far.

Introduction

AngularJs is a JavaScript framework used to create RIA application. It allows the developers to create client-side templates based on MVC architecture pattern. It uses inbuilt expressions for creating HTML templates. These expressions are used to fetch/display data in relative HTML context.

What should you learn next?

What should you learn next?

From SOC Analyst to Secure Coder to Security Manager — our team of experts has 12 free training plans to help you hit your goals. Get your free copy now.

NOTE: Credits for the exploits mentioned in this article are reserved to their respective authors. I would like to thanks, each one of them for their amazing research as it has really helped in broadening my knowledge. Name of these authors along with their exploit code can be found here.

Understanding Basics of AngularJs

To understand the security implications of AngularJs we need to understand the basic workings of it. Let us create one simple app for that and understand the code by breaking into multiple pieces. We will be creating a simple AngularJs app and try to make some sense out of it.

So, to create a basic application, we need to define a module name and its controllers which we can further use to take out the data and display it in HTML context (view). Our app will take any string as input and then place it in AngularJs context.

Save following code in app.js file

'use strict';

// angular.module function is used to define the application module.

var exampleapp = angular.module('exampleapp',[]);

// Defining the controller for defined module

exampleapp.controller('examplecontroller', function examplecontroller($scope){

    // Defining scope variables

    

    $scope.name = "john doe";

    $scope.age = 18;

    $scope.sayhello = function($name){

        return "Hello "+ $name + '!!'

    }

    

});

Code for index.php file look like following:

<!doctype html>

<!-- using our module name in html attribute -->

<html lang="en" ng-app="examplemodule">

<head>

<meta charset="utf-8">

<script src="angular.js"></script>

<script src="app.js"></script>

</head>

<!-- Defining our controller data here -->

<body ng-controller="examplecontroller">

<form action="index.php" method="GET">

    <p><input type="text" name="name"><input type="submit" value="submit"></p>

    <?php

    echo htmlspecialchars((isset($_GET['name']) ? $_GET['name'] : ""),ENT_QUOTES);    

    ?>

<ul>

     <li>{{name}}</li>

     <li>{{age}}</li>

     <li>{{sayhello('test')}}</li>

     <li>{{alert(1)}}</li>

     </ul>

</body>

</html>

As can be seen in the code snippet shown below, we are using curly braces expression to take out the data. However, there is an extra variable name "alert(1)" that will be displayed as it is. Ideally, we can assume that if all other expression is executing in JS context, then our mighty JS function should also execute along the way but this is not the case here.

AngularJs make use of scope object to check the existence of variables defined in AngularJs expressions. As in our case, there is an undefined scope variable "{{alert(1)}}, AngularJS will simply check whether the "alert" is a property of scope object, using the following function pointer.

var fnPtr = fn(scope, locals, context) || noop;

fn = Function('s', 'k', code); // s=scope, k=locals

As "alert" is not defined in current scope, its value will be evaluated to noop which mean no operation and will not get displayed, and same will happen to any JS global functions such document.cookie or document.domain.

Sandbox Bypass in AngularJs version 1.0.8

As we have seen in the previous sections, we are unable to come out of the scope object to execute any global functions. In this phase, we will have a look at how we can escape out of scope object and execute arbitrary JavaScript functions using the 'Gareth Hayes' exploit.

The scope itself is like any other JavaScript object and has a constructor, let's have a look at what that constructor bears.

As can be seen, the constructor of scope object is object constructor and constructor of object constructor is function constructor. This function constructor can be used to create a function for us and further we can use parentheses around it to execute our JavaScript code.

Example:

As can be seen, we have created a function constructor object, and it contains our JavaScript snippet. Further, we have used parenthesis around it to execute our code.

Similarly, will be using function constructor of object constructor of scope object to evaluate our code and bypass scope limitations implied earlier.

Complete payload will look like following

{{constructor.constructor('alert(0)')()}}

As every expression in AngularJs will be evaluated against scope object, we do not need to specify scope keyword. As can be seen, we can bypass the scope restriction and execute our JavaScript payload with the help of function constructor.

Sandbox Bypass in AngularJs version 1.2.19

To overcome the bypasses, found in earlier versions, this version implements some security checks before the data is being interpreted. This mainly includes checking various properties of an object, checking for function properties such as CALL, APPLY, BIND, as previous bypasses were abusing these to execute JavaScript code.

For example:

Let's have a look at the bypass for version 1.2.18 from Jan Horn.

(_=''.sub).call.call({}[$='constructor'].getOwnPropertyDescriptor(_.__proto__,$).value,0,'alert(1)')()

The following screenshot shows the equivalent disassembly of above exploit code.

By analyzing the exploit code, we came to know that it is using CALL property of function object to execute JavaScript code. In the following screenshot, we can see that a new function was introduced in version 1.2.19 to check for different properties of the function object, which were used in previous bypasses.

Now let's have a look at the bypass for this version by "Mathias Karlsson."

{{toString.constructor.prototype.toString=toString.constructor.prototype.call;["a","alert(1)"].sort(toString.constructor);}}

So, we have mainly two functions ahead of us that we need to bypass to get our code executed, Mathias's exploit first changes the way toString function is supposed to work by assigning it to new property "call" so, at the time when the constructor of toString is called by sort function, toString will be equal to call and thus executes "alert(1)".

Sandbox Bypass in AngularJs version 1.4.7

In this version, AngularJs defeats the previous sandbox escape by implementing an inbuilt compiler and using AST (Abstract Syntax Tree) technique it tokenizes the expression and perform safety functions such as enusureSafeObject on enumerated Objects.

Let's try the exploit for version 1.2.19 in this version, as can be seen; we are getting a Referencing function error.

Why this is happening or why we can't refer to the function object anymore?

We have seen, in one of the previous sandboxes escapes the constructor of object constructor is function constructor but let's see what value does the constructor of function constructor possess.

As can be seen, the constructor of a function constructor is again a function constructor, so the line "if (obj.constructor === obj)" in ensureSafeObject function successfully blocks our previous payload by checking if some object's constructor is again that object itself.

Let's have a look at the exploit code provided by Gareth Heyes and how it was able to bypass the restrictions implemented in newer versions of angularJs.

{{'a'.constructor.prototype.charAt=[].join;$eval('x=1} } };alert(1)//');}}

Above exploit will first overwrite the functionality of charAt function with join function, which means now when we call the charAt function it will simply join the value to each character instead of getting the index.

The next part of exploit is then iterated through the following loop to fetch the tokens for lexical analysis.

However this function is unable to differentiate between identifiers and actual code and include other part of exploit x=1} } };alert(1)//');}} in resultant compiled javascript code.

As can be seen, the token fetch via lexical analysis treats second part of exploit as an identifier.

This results in arbitrary JavaScript code execution.

Exploit Summary:

The first part of exploit overwrites the charAt function and passes through the lexical analysis. However, in the second part, the charAt function will now act as join function and insert second part of an exploit in a resultant compiled JavaScript leading to JavaScript code execution.

References:

http://blog.portswigger.net/2016/01/xss-without-html-client-side-template.html

https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/prototype

https://code.angularjs.org/

What should you learn next?

What should you learn next?

From SOC Analyst to Secure Coder to Security Manager — our team of experts has 12 free training plans to help you hit your goals. Get your free copy now.

https://www.youtube.com/watch?v=67Yc8_Bszlk&list=PLhixgUqwRTjwJTIkNopKuGLk3Pm9Ri1sF

Sahil Dhar
Sahil Dhar

Sahil Dhar is an Information Security Enthusiast having more than two years of hands-on experience in Application Security, Penetration Testing, Vulnerability Assessments and Server Config Reviews. He has also been acknowledged and rewarded by various organizations like Google, Apple, Microsoft, Adobe, Barracuda, Pinterest, Symantec, Oracle etc for finding vulnerabilities in their online services.