Skip to content

Instantly share code, notes, and snippets.

@mrclay
Last active August 29, 2015 14:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mrclay/aea4042f60999cfed51c to your computer and use it in GitHub Desktop.
Save mrclay/aea4042f60999cfed51c to your computer and use it in GitHub Desktop.

More details on the WordPress XSS vulnerability found by Klikki. Both real exploits include a style attribute to widen the mouseover area to the whole viewport; I've left it out here to keep it simple.

The exploit comment is valid HTML and won't be altered by an HTML santizer:

<a title='x onmouseover=alert(unescape(/hello%20world/.source)) AAAAAAAAAAAA...[64 kb]..AAA'></a>

But once truncated by MySQL, the comment will become malformed HTML (note the attribute is left open):

<a title='x onmouseover=alert(unescape(/hello%20world/.source)) AAAAAAAAAAA

This actually creates 2 attacks:

The first one is caused by wptexturize() not having a way to deal with invalid HTML:

wptexturize expects that all tags start with < and end with >. Since our broken snippet does not, it processes it as text, converting ' to an escaped curly quote:

<a title=&#8220;x onmouseover=alert(unescape(/hello%20world/.source)) AAAAAAAAAAA

Now the browser sees 2 unquoted attributes instead of a single title, and mouseover contains the exploit (the real exploit includes style to make the element cover the whole viewport). You can see this in action at http://codepen.io/mrclay/pen/QbwzMq.

The second attack (similar to this old attack) can work even if wptexturize isn't used, as long as the attacker can submit two comments and the WordPress theme doesn't include a ' between them:

Consider a simple theme that outputs comments like this:

<li class="comment">
  Comment 1
</li>
<li class="comment">
  Comment 2
</li>

Now we place the truncated comments in. Note the first comment doesn't need the exploit code, all it does is leave the browser's HTML parser in the context of an attribute:

<li class="comment">
  <a title='AAAAAAAAAAAAAAAAAAA...
  <!-- this should be HTML context but is treated as attribute content -->
</li>
<li class="comment">
  <a title='x onmouseover=alert(unescape(/hello%20world/.source)) AAAAAAAAAAA
</li>

When this is parsed, a big block of HTML is now turned into a single title attribute, followed by an empty x attribute, and an unquoted onmouseover attribute! You can see this in action at http://codepen.io/mrclay/pen/EjaGvG.

@mrclay
Copy link
Author

mrclay commented Apr 28, 2015

Please let me know if I've missing something here. My reading is this is hardly a "WordPress" problem; any app that allows users to submit HTML that gets truncated is in danger, especially if the attacker can get 2 content fields displayed closely in the markup.

@beck24
Copy link

beck24 commented Apr 28, 2015

/sigh

Yep, looks right, and looks like it would affect any app not just wordpress

@mrclay
Copy link
Author

mrclay commented Apr 28, 2015

HTML sanitizers that reconstruct attributes with " quotes do mitigate this somewhat. At least Htmlawed does this. Also a round trip through PHP's DOMDocument HTML parser auto-closes the element.

@abdallahalsamman
Copy link

i tried to reproduce this at my localhost but there is no method working in here (version 4.1).
well when you said
'wptexturize expects that all tags start with < and end with >. Since our broken snippet does not, it processes it as text, converting ' to an escaped curly quote:

<a title=“x onmouseover=alert(unescape(/hello%20world/.source)) AAAAAAAAAAA'
it dont convert ' to an escaped curly only, it also converts the open tag "<" too so the output is like so:
&-l-t-;-a title=&-#-0-3-9-;x onmouseover=alert(unescape(/hello%20world/.source)) AAAAAAAAAAA
(with no dashes, github encodes decoded html)
is this being produced only from my end or what?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment