I recently found a popular website which echoed back user input. It correctly sanitised
< to prevent any HTML injection.
It let through
<h2> elements unaltered! Why? I suspect because the output was:
<h2>Your search for ... returned no results</h2>
And, somehow, the parser was getting confused. OK, what can we do with this little vector?
onmouseover() - but they were (sensibly) blocked. The
<h2> element only has access to the Global Attributes. So we could inject content which use Right-To-Left text, or add some metadata attributes - but that's not particularly useful.
The most useful Global Attribute is
style="". Yup! Good old CSS!
The limitation of the style attribute is that it only applies to the specific element it is attached to. So you can't rewrite the entire page. You also can't use the
content: property - as that only applies to
Using normal CSS, we can change the colour and size of our newly injected
<h2>, faff around with the background colour, change the font, and move it about the page. Good for a bit of digital graffiti, but not much else.
What about using
background-image? Using that, we can pull in an external resource.
<h2 style="background-image:url('https://evil.site/whatever.png'); width:512px; height:512px;"> ... </h2>
That will load an external picture on the site. It could be an animated GIF saying "You're a winner! Visit www.... to claim your prize!". It wouldn't be clickable, but might catch a few people out.
It is possible to load an SVG. And SVG can contain JS. But - alas and alack - the JS doesn't run in background mode - even if the JS is bundled as Base64.
content-disposition of the image won't force the browser to download it, either.
While fuzzing around with the input, I made an interesting mistake. I mistyped
<sgv>. That invalid element was added to the page's HTML! That means there's a parser somewhere which is stripping out only the elements it knows about. Browsers typically ignore elements they don't understand - so there's no danger to users there. But it points to the idea that there may be some elements the sanitiser doesn't know about.
And, indeed, there were! For example, it happily took the obsolete
<plaintext> element, and dumped it into the markup. Which caused the page to break.
It also, delightfully, took the
OMG…!— Terence Eden (@edent) June 4, 2022
I legit did not expect that to work… pic.twitter.com/5Q4ymMzJDG
I reported the issue immediately, and got an acknowledgement. But, sadly, after a few months the website was still not fixed.
Take a bow, ACM Digital Library!