Introduction
Front-end development has undergone rapid transformation over the past decade. Most modern web applications are built using JavaScript frameworks like AngularJS, React, Vue, and others. According to Google, AngularJS is “a JavaScript-based open-source front-end web framework mainly maintained by Google and by a community of individuals and corporations to address many of the challenges encountered in developing single-page applications.”
Many people mistakenly believe these sophisticated frameworks are inherently immune to traditional vulnerabilities like Cross-Site Scripting (XSS). While many frameworks offer built-in defensive mechanisms, this is not the case. It’s just that the exploitation process is slightly different.
Angular Basics: Templates, Expressions, and Scope
Understanding a few core concepts of Angular is vital for understanding Client-Side Template Injection (CSTI).
Templates
When you view an Angular application in your browser, you are actually looking at a template. A template is an HTML snippet that dictates to Angular how to render the component. The main advantage of templates is their ability to accept and process data, allowing the application to dynamically generate HTML code based on the arguments passed to it.
- Example Template:
Welcome {{Username}}!
If the username is "ghostlulz", the application would dynamically display: Welcome ghostlulz!
Expressions
Angular expressions are JavaScript-like code snippets enclosed within double curly braces ({{...}}). Like standard JavaScript expressions, Angular expressions can contain:
- Literals:
{{1 + 1}} - Operators:
{{A + B}} - Variables:
{{User.name}} - Indexing:
{{Items[index]}}
Scope
The critical difference is how expressions are evaluated. Unlike standard JavaScript expressions, which are evaluated against the global window object, Angular expressions are evaluated against the Scope object ($scope).
- If you try to evaluate
{{alert(1)}}, it will typically fail because the scope object does not have analertfunction defined (unless a developer intentionally added one). - The Scope is simply an object where variables and functions can be defined:
$scope.username = "Ghostlulz";
$scope.greetings = function() {
return 'Welcome ' + $scope.username + '!';
};
When you use {{greetings()}}, Angular internally looks for greetings within the $scope object.
Client-Side Template Injection (CSTI) leading to XSS
According to Google, Client-Side Template Injection vulnerabilities “arise when applications using a client-side template framework dynamically embed user input in web pages.” Since Angular is a client-side template framework and allows user input to be embedded into its templates, it is a perfect target for this type of vulnerability.
Bypassing Traditional XSS Filters
If a security tester is unaware of Angular’s expression syntax, they might try a traditional XSS payload:
The simple payload fails, often because the server is encoding the input (e.g., using a function like htmlspecialchars in PHP) before it is passed to the template. This is a very popular and effective method of preventing classic XSS for most applications.
However, Angular is different. Because Angular uses expressions, the attacker does not need to use special characters like <, >, or quotes, which are the characters typically targeted by the encoding functions.
The tester can try an expression payload:
As shown, the injection of the expression {{1 + 1}} gets evaluated to 2. This result is a very strong indicator that the application is vulnerable to Client-Side Template Injection.
Executing JavaScript Code (XSS Payload)
Forcing an application to add two numbers together is not exciting. The next step is to inject JavaScript code to achieve XSS.
We know that simply inserting alert(1) will fail because the alert function is not defined in the scope object (it implicitly translates to $scope.alert(1)).
To bypass this, we need a way to access functions outside the limited scope. The scope object, by default, contains a reference to its own constructor object, which, in turn, contains another function also called constructor. This function can be used to dynamically generate and execute code—the exact capability needed for an XSS payload.
The common Angular CSTI payload to achieve XSS is:
{{constructor.constructor('alert(1)')()}}
This malicious Angular expression is injected into the page, causing the application to dynamically generate and execute the payload, resulting in the desired XSS alert box.
Angular Sandbox Bypass
To help prevent this type of attack, older versions of Angular (specifically 1.2 – 1.5) included a sandbox designed to limit the operations that could be performed within expressions. However, this sandbox was later removed in version 1.6 and above because it provided no real security; there were numerous publicly known sandbox bypasses for every version it was implemented in.
If the application you are testing is specifically between versions 1.2 – 1.5, you will need to research and implement the specific sandbox bypass payload for that version to get your XSS to execute.
Summary
With new technologies come new vulnerabilities. Any client-side template framework that accepts user input can be vulnerable to Client-Side Template Injection. This vulnerability is primarily used to trigger XSS payloads.
Since Angular uses expressions, attackers can often bypass traditional XSS preventions such as encoding special characters in the user’s input. Many developers rely heavily on this encoding method, which works fine in most applications but fails against those that utilize client-side templates and expressions, leading to high-impact vulnerabilities.