# PortSwigger XSS Lab: DOM XSS in AngularJS

### 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
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1763921141790/611515d2-09fc-4d40-a4c4-0320d3ea7d63.png align="center")
    
* As seen in the above image, the website is running AngularJS 1.7.7
    
* AngularJS below 1.7.8 and above 1+ use the `$scope` method 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**.
    
* `$scope` follows a **prototypal inheritance** model.
    

> **Note: AngularJS evaluates expressions in the context of** `$rootScope` if 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 `$scope` object 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
    
    ```jsx
    {{ $on.constructor('alert(1)')() }}
    ```
    
    * `$on` is a **function** (defined on the prototype of `$rootScope`).
        
    * In JavaScript, **all functions are objects**, and all function objects have a `.constructor` property.
        
    * So when you do:
        
        ```jsx
        $scope.$on.constructor
        ```
        
    * You're accessing the `.constructor` property of the `$on` function object
        
    * This returns the **native** `Function` constructor:
        
        ```jsx
        console.log($scope.$on.constructor === Function); // ✅ true
        ```
        
    * `$on.constructor('alert(1)')` — Evaluates to a function equivalent to `new Function('alert(1)')`
        
    * `()`: Immediately invokes that function
        
    * Because, In JavaScript, functions are first-class objects, and you can:
        
        * Create a function dynamically (e.g., via `Function` constructor)
            
        * Call it immediately using `()` — even in the **same line**
            
* Let’s execute the payload in the search bar of the website
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1763921177305/9469a694-d761-40aa-a3b5-5331a60a0b54.png align="center")
    
* After successful execution of the JS exploit, the lab will be solved
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1763921199809/1e9e8cbf-a86a-4aad-a46c-486af5802b67.png align="center")
    
* Using the above payload, we were able to **leverage prototypal inheritance in JavaScript** to access the `$on` method, retrieve its `.constructor` (which points to the native `Function` constructor), and **dynamically create a new global function**.
    
* By appending `()` at the end, we **immediately invoked** the generated function, which executed the `alert()` 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 `Function` constructor 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:**

```javascript
<p>Hello {{ username }}!</p>
```

If `$scope.username = "Logan"` in the controller, AngularJS replaces the placeholder dynamically with:

```javascript
Hello Logan!
```

It can also evaluate JavaScript expressions:

```javascript
{{ 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:

```javascript
{{ 1 + 1 }}  ✔
{{ user.name }} ✔
```

But block:

```javascript
{{ 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:

```jsx
{{ $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:

```javascript
{{ 7*7 }} → CSTI detection
```

From there, chaining prototype abuse with sandbox bypass techniques allows escalation to full script execution:

```javascript
{{ $on.constructor('alert(1)')() }} → XSS
```
