OVERVIEW
========
A security flaw in WordPress 3 allows injection of JavaScript into certain
text fields. In particular, the problem affects comment boxes on WordPress
posts and pages. These don't require authentication by default.
The JavaScript injected into a comment is executed when the target user
views it, either on a blog post, a page, or in the Comments section of the
administrative Dashboard.
In the most obvious scenario the attacker leaves a comment containing the
JavaScript and some links in order to put the comment in the moderation
queue. The exploit is not then visible to normal users, search engines, etc.
When a blog administrator goes to the Dashboard/Comments section to review
new comments, the JavaScript gets executed. The script can then perform
operations with administrator privileges.
For instance, our PoC exploits first clean up traces of the injected script
from the database, then perform other administrative tasks such as changing
the current user's password, adding a new administrator account, or using
the plugin editor to write attacker-supplied PHP code on the server (this
impact applies to any WordPress XSS if triggered by an administrator).
These operations happen in the background without the user seeing anything
out of ordinary.
If the attacker writes new PHP code on the server via the plugin editor,
another AJAX request can be used to execute it instantaneously, whereby the
attacker gains operating system level access on the server.
The exploit will NOT be triggered directly at the Dashboard "root view"
because only snippets (20 first words) of the latest comments are shown
there with all HTML stripped.
If approved there, the exploit will be triggered by any user viewing the
targeted blog posting or page, with their corresponding privileges.
Plugins that let unprivileged users to enter HTML text may offer other
attack vectors.
DETAILS
=======
WordPress allows a few HTML tags in comments, such as the anchor <A>, bold
<B>, and code <CODE> tags. Certain white-listed attributes are allowed in
each tag. Obviously, the "href" attribute is important for anchor tags, but
e.g. the "onmouseover" attribute would be undesirable.
The problem occurs in a text formatting function called wptexturize() which
is normally executed for each comment and other blocks of text. The
function replaces certain simple characters with fancier HTML entities. For
instance, straight quote symbols are replaced with opening and closing
curly quotes, unicode 8220 and 8221.
In order to avoid interfering with HTML formatting, wptexturize() first
splits the text in segments. The splitting is expected to pick HTML tags
(which aren't texturized) apart from running text (which is texturized).
In addition to HTML tags, the code is supposed to recognize
square-bracketed shortcodes such as [CODE] and avoid texturizing them.
The splitting is implemented with a regular expression in
wp-includes/formatting.php:
$textarr = preg_split('/(<.*>|\[.*\])/Us', $text, -1,
PREG_SPLIT_DELIM_CAPTURE);
A text containing carefully mixed square and angle brackets confuses the
splitting process and results in HTML code getting partially texturized.
An attacker can exploit the bug to supply any attributes in the allowed
HTML tags. A style attribute can be used to create a transparent tag
covering the whole window, forcing the execution of its onmouseover handler.
In practical applications the script would probably first remove the
transparent tag to avoid interfering with UI events and re-triggering
the handler.
It could then insert a new <SCRIPT> tag to load a more complex JavaScript
file to execute from another web server. This script can use e.g. jQuery to
chain AJAX operations for posting HTML forms and retrieving the required
nonces.
AFFECTED VERSIONS
=================
We tested a few WordPress versions from 3.0 to the latest 3.9.2. All tested
versions were vulnerable. The problem seems to have gone uncorrected for
almost four years.
Version 4.0 uses a different kind of regular expression and is NOT
vulnerable to this problem.
WORKAROUNDS
===========
Texturizing can be easily disabled by adding a return statement in the
beginning of the function in wp-includes/formatting.php:
function wptexturize($text) {
return $text; // ADD THIS LINE
global $wp_cockneyreplace;
This changes how some punctuation marks look like but the difference is
quite minor.
We have also made a WordPress plugin available for disabling texturization. For
more information and an up-to-date version of this document, please refer
to our website http://klikki.fi
The preferred solution should be applying the official patch released by
WordPress.
VENDOR RESPONSE
===============
WordPress was notified on September 26 and has released patches correcting
the problem. The WordPress security advisory is available at
https://wordpress.org/news/2014/11/wordpress-4-0-1/
CREDITS
=======
The vulnerability was discovered and researched by Jouko Pynnonen, Klikki
Oy, Finland.
--
Jouko Pynnonen <jouko@iki.fi>
Klikki Oy - http://klikki.fi