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
However on front end you can see that the JavaScript execution takes place.
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"
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=’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 ’ causing ’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&d=mm&r=g' srcset='http://0.gravatar.com/avatar/f093859f703f565c9a183ba22eae46ee?s=64&d=mm&r=g 2x' class='avatar avatar-32 photo' height='32' width='32' /> root</strong><br /><a href='mailto:root@localhost.com'>root@localhost.com</a><br /><a href="edit-comments.php?s=172.28.128.1&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&d=mm&r=g' srcset='http://0.gravatar.com/avatar/f093859f703f565c9a183ba22eae46ee?s=64&d=mm&r=g 2x' class='avatar avatar-32 photo' height='32' width='32' /> root</strong><br /><a href='mailto:root@localhost.com'>root@localhost.com</a><br /><a href="edit-comments.php?s=172.28.128.1&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> <a title='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"> <a title='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 linkUpdate 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
Thanks for tutorial