Fix : WordPress Comment XSS 4.2 or below

This post discusses WordPress Comment XSS affecting version 4.2 or below. I have outlined the internal working of this specific XSS.

28-Apr-2015: UPDATE: WordPress has released an official fix. https://wordpress.org/news/2015/04/wordpress-4-2-1/
29-Apr-2015: UPDATE2: added detailed Response text for reference purposes and to showcase how it was exploited.
29-apr-2015: Update3: Some more details added
14-may-2015 : update 4 : added baidu link (chinese)
———————————————————————————————————

Today a post was made by Jouko Pynnönen of Klikki Oy Describing a XSS scenario which affects WordPress 4.2, 4.1.2, and 4.1.1, MySQL versions 5.1.53 and 5.5.41 to the least and might have affect over previous version’s also.

Understanding the bug

In short the bug is in the process of xss filtering and storage. The comment is first sent through various filters and at the end is stored in the database. The PoC code validates on all the checks however when it is suppose to be stored in DB the mysql db column for comment data has a hardlimit of 64kb and hence the data is truncated to store only 64kb. This causes the input to be stripped and hence the new payload is able to cause an XSS. This causes the issue when the comment is viewed in the front end. At this point as per my checks its not affecting backend and backend can see comment as is.

Refer to the end of post for a PoC code and response output.

This comment was seen as following at the backend.
In Recent activities section
comment_xss_1

In Comments page
comment_xss

However on front end you can see that the JavaScript execution takes place.
comment_xss2

SOLUTION

I have devised a simple fix for the above problem at a server level.
I have used Nginx server however a similar configuration should be possible at other servers also.
Here is a sample configuration of my test box. Refer to the section “FIX for WordPress Comment XSS”.

server {
listen 80 default_server;
listen [::]:80 default_server ipv6only=on;
client_max_body_size 100M;
root /wordpress;
index index.php;
server_name 172.28.128.11;
location / {
try_files $uri $uri/ /index.php?$args;
}

##
## FIX for WordPress Comment XSS
##
location ~ wp-comments-post.php$ {
client_max_body_size 64K;
try_files $uri =404;
fastcgi_split_path_info ^(.+?\.php)(/.*)$;
if (!-f $document_root$fastcgi_script_name) {
return 404;
}
include fastcgi_params;
fastcgi_pass unix:/var/run/php5-fpm.sock;
}
##
## FIX ends Here
##

location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+?\.php)(/.*)$;
if (!-f $document_root$fastcgi_script_name) {
return 404;
}
include fastcgi_params;
fastcgi_pass unix:/var/run/php5-fpm.sock;

}
}

This configuration basically applies a hard limit of 64KB over data sent to wp-comments-post.php which is responsible for posting comment. as we already know anything more than 64kb is not going to be retained hence there is no point allowing this request.

Just to be sure that this doesn’t affect any other section i tried uploading a large file and it all went smoothly.

Attack Detection via error_log

If you are concerned about the logs you will start seeing following style logs in your error_log when someone tries this attack.


2015/04/27 09:17:08 [error] 19317#0: *9 client intended to send too large body: 65835 bytes, client: 172.28.128.1, server: 172.28.128.11, request: "POST /wp-comments-post.php HTTP/1.1", host: "172.28.128.11", referrer: "http://172.28.128.11/?p=1"

comment_xss_error_log

Also just to emphasis on the fact this issue is already being exploited in wild now.

If you feel this might be wrong assumption or some workaround is possible for this feel free to suggest alternatives or criticise fix.

Sample PoC

For testing purposes following PoC was used.

<a title='x onmouseover=alert(unescape(/hello%20world/.source))
style=position:absolute;left:0;top:0;width:5000px;height:5000px
AAAAAAAAAAAA [64 kb] ...'></a>

This code results in following response

<p><a title=&#8217;x onmouseover=alert(unescape(/hello%20world/.source)) style=position:absolute;left:0;top:0;width:5000px;height:5000px AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA ..... <snipped for brivity> AAA</p> </div>

as you can see the input “‘” was converted to &#8217; causing &#8217;x to become title and onmouseover as an attribute to a tag causing the exploit.

However the same code when rendered at backend resulted in following output and hence not exploited.

<td class='author column-author'><strong><img alt='' src='http://0.gravatar.com/avatar/f093859f703f565c9a183ba22eae46ee?s=32&#038;d=mm&#038;r=g' srcset='http://0.gravatar.com/avatar/f093859f703f565c9a183ba22eae46ee?s=64&amp;d=mm&amp;r=g 2x' class='avatar avatar-32 photo' height='32' width='32' /> root</strong><br /><a href='mailto:ro&#111;t&#64;lo&#99;a&#108;hos&#116;&#46;&#99;o&#109;'>ro&#111;t&#64;lo&#99;a&#108;hos&#116;&#46;&#99;o&#109;</a><br /><a href="edit-comments.php?s=172.28.128.1&#038;mode=detail">172.28.128.1</a></td><td class='comment column-comment'><div class="comment-author"><strong><img alt='' src='http://0.gravatar.com/avatar/f093859f703f565c9a183ba22eae46ee?s=32&#038;d=mm&#038;r=g' srcset='http://0.gravatar.com/avatar/f093859f703f565c9a183ba22eae46ee?s=64&amp;d=mm&amp;r=g 2x' class='avatar avatar-32 photo' height='32' width='32' /> root</strong><br /><a href='mailto:ro&#111;t&#64;&#108;&#111;ca&#108;ho&#115;&#116;.&#99;&#111;m'>ro&#111;t&#64;&#108;&#111;ca&#108;ho&#115;&#116;.&#99;&#111;m</a><br /><a href="edit-comments.php?s=172.28.128.1&#038;mode=detail">172.28.128.1</a></div><div class="submitted-on">Submitted on <a href="http://172.28.128.11/?p=1#comment-10">2015/04/29 at 4:07 am</a></div><p>

&lt;a title=&#039;x onmouseover=alert(unescape(/hello%20world/.source)) style=position:absolute;left:0;top:0;width:5000px;height:5000px  AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
..... <snipped for brivity>
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA</p>

<div id="inline-10" class="hidden">
<textarea class="comment" rows="1" cols="1">
&lt;a title=&#039;x onmouseover=alert(unescape(/hello%20world/.source)) style=position:absolute;left:0;top:0;width:5000px;height:5000px  AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
..... <snipped for brivity>
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA</textarea>
		<div class="author-email">root@localhost.com</div>
		<div class="author">root</div>
		<div class="author-url"></div>
		<div class="comment_status">1</div>
		</div>

Update 3
——–
Couple of more links added for reference
1. Another explaination about the same attack
2. A real Life attack using the same vulnerability
3. Base64 Decoded payload of second link

Update 4
———

Baidu wrote multiple blog post analyzing this XSS.

xteam.baidu.com/?p=177
xteam.baidu.com/?p=198
xteam.baidu.com/?p=209
xteam.baidu.com/?p=225

1 thought on “Fix : WordPress Comment XSS 4.2 or below”

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top