PortSwigger XSS Lab: DOM XSS in AngularJS
DOM XSS in AngularJS expression with angle brackets and double quotes HTML-encoded

Description
This lab contains a DOM-based cross-site scripting vulnerability in a AngularJS expression within the search functionality.
AngularJS is a popular JavaScript library, which scans the contents of HTML nodes containing the ng-app attribute (also known as an AngularJS directive). When a directive is added to the HTML code, you can execute JavaScript expressions within double curly braces. This technique is useful when angle brackets are being encoded.
Task
To solve this lab, perform a cross-site scripting attack that executes an AngularJS expression and calls the alert function.
Methodology
Add the Target URL in Burpsuite Scope
Lets identify the framework and its version for the current website

As seen in the above image, the website is running AngularJS 1.7.7
AngularJS below 1.7.8 and above 1+ use the
$scopemethod which is used to bind data between controller and view (DOM)It plays a key role in the two-way data binding mechanism.
When a controller sets values on
$scope, all DOM elements that use that controller automatically get access to those properties and methods.$scopefollows a prototypal inheritance model.
Note: AngularJS evaluates expressions in the context of
$rootScopeif no controller binding exists.
Any property or methods defined in the controller is accessible to the DOM elements inside that controller’s scope.
The catch is
Even if no properties are explicitly bound in the DOM, the DOM nodes (via AngularJS directives like
ng-controller,ng-repeat, etc.) are still under the influence of the$scopeobject from the controller.So, even if a DOM node doesn’t use any
{{ expression }}or directive, the scope still applies and exists for it — it's just not visible until you tap into it.Because of JavaScript’s prototypal inheritance, any HTML node under an AngularJS controller:
Gets associated with a scope object, and
That scope object inherits from
$rootScope, which provides built-in methods like:$eval()$on()$watch()$emit()$broadcast()
Even if the DOM element doesn't use
{{ }}or bind any model, as long as it's within the controller's scope, it inherits those scope methods through the prototype chain.Based on this logic, let us construct our payload
{{ $on.constructor('alert(1)')() }}$onis a function (defined on the prototype of$rootScope).In JavaScript, all functions are objects, and all function objects have a
.constructorproperty.So when you do:
$scope.$on.constructorYou're accessing the
.constructorproperty of the$onfunction objectThis returns the native
Functionconstructor:console.log($scope.$on.constructor === Function); // ✅ true$on.constructor('alert(1)')— Evaluates to a function equivalent tonew Function('alert(1)')(): Immediately invokes that functionBecause, In JavaScript, functions are first-class objects, and you can:
Create a function dynamically (e.g., via
Functionconstructor)Call it immediately using
()— even in the same line
Let’s execute the payload in the search bar of the website

After successful execution of the JS exploit, the lab will be solved

Using the above payload, we were able to leverage prototypal inheritance in JavaScript to access the
$onmethod, retrieve its.constructor(which points to the nativeFunctionconstructor), and dynamically create a new global function.By appending
()at the end, we immediately invoked the generated function, which executed thealert()call — resulting in an alert popup, all within a single line of code.In summary, AngularJS expressions can be abused when user-controlled data is processed by the Angular template engine. By leveraging prototype inheritance and accessing the
Functionconstructor through$on.constructor(), an attacker can escape AngularJS sandboxing and execute arbitrary JavaScript.
Remediation
Use AngularJS 1.8.0+ patched sandbox
Disable expression evaluation (
strictContextualEscaping)Use Content Security Policy (CSP)
Sanitize user input before rendering
Sandboxing in AngularJS
Why {{ }} Is Used in AngularJS
AngularJS uses double curly braces ({{ }}) for expression binding, also known as interpolation.
This syntax allows dynamic values to be inserted into the DOM based on JavaScript expressions evaluated by AngularJS. It enables the view (HTML) to reactively display data managed by the controller.
Example:
<p>Hello {{ username }}!</p>
If $scope.username = "Logan" in the controller, AngularJS replaces the placeholder dynamically with:
Hello Logan!
It can also evaluate JavaScript expressions:
{{ 2 + 2 }} → 4
{{ username.toUpperCase() }} → LOGAN
Key points:
{{ }}acts like a safe, mini expression parser.It updates automatically when scope values change (two-way binding).
It avoids writing full
<script>tags inside HTML.
This mechanism improves templating — but also becomes dangerous when user input is parsed as an expression, leading to AngularJS-based XSS.
What Is AngularJS Sandboxing?
AngularJS includes a sandbox, which is a restricted execution environment designed to prevent template expressions from running arbitrary JavaScript.
In theory, this means expressions inside {{ }} should only access a limited safe subset of functions - NOT the entire DOM or global JavaScript environment.
For example, the sandbox is meant to allow:
{{ 1 + 1 }} ✔
{{ user.name }} ✔
But block:
{{ alert(1) }} ❌
{{ constructor.constructor('alert(1)')() }} ❌
However — earlier AngularJS versions (including 1.7.7, as in this lab) had sandbox escape vulnerabilities. By abusing prototype inheritance and accessing internal function constructors, attackers can bypass the sandbox and execute real JavaScript.
So the payload:
{{ $on.constructor('alert(1)')() }}
works because it breaks out of the sandbox and executes code in the page’s JavaScript context -effectively turning a harmless template expression into a stored or reflected XSS.
CSTI: The Origin of This Vulnerability Class
This behavior led to the emergence of a new type of security issue known as Client-Side Template Injection (CSTI).
In AngularJS and other JavaScript frameworks that support client-side templating, user-controlled input may be interpreted as executable template code rather than plain text. When untrusted data reaches the Angular interpolation engine ({{ }}), attackers can inject expressions and begin interacting with the framework:
{{ 7*7 }} → CSTI detection
From there, chaining prototype abuse with sandbox bypass techniques allows escalation to full script execution:
{{ $on.constructor('alert(1)')() }} → XSS


![Lecture 4 - Rediscovering Process Scheduling [Part - 1]](/_next/image?url=https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1765604682888%2F80e6cf20-aded-4aac-8c75-affdd35615b2.jpeg&w=3840&q=75)

![Lecture 3 - Reinventing MMU [Part - 2]](/_next/image?url=https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1763234243271%2F6bb1c737-9def-4975-a06d-7ca59791c881.png&w=3840&q=75)
![Lecture 2 - Reinventing MMU [Part - 1]](/_next/image?url=https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1762492010507%2F7db0bf79-265d-41bc-990f-0cb2c68e61f2.jpeg&w=3840&q=75)