• Skip to primary navigation
  • Skip to content
  • Skip to primary sidebar
  • Skip to footer

Sucuri Blog

Website Security News

  • Products
    • Website Security Platform
    • Website Firewall (WAF)
    • Enterprise Website Security
    • Multisite Solutions
  • Features
    • Detection
    • Protection
    • Performance
    • Response
    • Backups
  • Partners
    • Agency Solutions
    • Partners
    • Referral Program
    • Ecommerce
  • Resources
    • Guides
    • Webinars
    • Infographics
    • SiteCheck
    • Reports
    • Email Courses
  • Immediate Help
  • Login

Security Advisory: Stored XSS in Akismet WordPress Plugin

October 14, 2015Marc-Alexandre Montpas

Security Risk: Dangerous

Exploitation Level: Easy/Remote

DREAD Score: 9/10

Vulnerability: Stored XSS

Patched Version: 3.1.5

777
SHARES
FacebookTwitterSubscribe

During a routine audit for our WAF, we discovered a critical stored XSS vulnerability affecting Akismet, a popular WordPress plugin deployed by millions of installs.

Vulnerability Disclosure Timeline:

  • October 2nd, 2015 – Bug discovered, initial report to Automattic security team
  • October 5th, 2015 – Automattic security team acks receipt of report, sets patch date for October 13th
  • October 13th, 2015 – Patch made public with the release of Akismet 3.1.5
  • October 14th, 2015 – Sucuri Public Disclosure of Vulnerability (After auto-updates from Automattic team)

Are you at risk?

This vulnerability affects everyone using Akismet version 3.1.4 and lower and have the WordPress “Convert emoticons like 🙂 and 😛 to graphics on display” option enabled which is the case by default on any new WordPress installation. The issue can be found in the way Akismet deals with hyperlinks present inside the website’s comments, which could allow an unauthenticated attacker with good knowledge of WordPress internals to insert malicious scripts in the Comment section of the administration panel. Doing this could lead to multiple exploitation scenarios using XSS in Akismet, including a full site compromise.

Technical Details

We started investigating this plugin after noticing the following snippets:

Sucuri-Akismet-XSS-v1

Sucuri-Akismet-XSS-v2

 

The text_add_link_class method is hooked to the comment_text WordPress filter, which is applied when displaying a user-provided comment inside the administration panel’s Comment section. The regular expression matches any <a> tags that contains a double-quoted href attribute and execute a callback method to modify the tag’s content. We found it interesting that it would only match a double-quoted href attribute because normally single quotes are allowed too. This led us to think about what type of bugs this behavior could produce. As part of our tests, we tried passing this payload:

Sucuri-Akismet-XSS-v3

 

Our objective here was to cause the double-quoted href to grab ‘>Test<abbr title=’ and put it in the title attribute of the initial preg_replace’s <span> tag. However, this is what it responded:

Sucuri-Akismet-XSS-v4

 

Interestingly, it appears our trick worked, but the double-quoted href stopped grabbing content too early when meeting the rel=” nofollow” attribute, which was apparently appended there by some other filter running before Akismet’s preg_replace. We needed to find where this snippet was located and what it did, in order to prevent this concatenation from happening.

Sucuri-Akismet-XSS-v5

 

We quickly found the culprit was the wp_rel_nofollow function. It used a regex that contained a lazy capturing group based on the dot character, which doesn’t match line break characters, so we could prevent the new attribute from being appended to our payload easily by inserting a newline character like this:

Sucuri-Akismet-XSS-v6

 

Which once in the administration panel, gave the following result:

Sucuri-Akismet-XSS-v7

 

This showed we could insert less-than and greater-than symbols inside the <span>‘s title attribute, something that WordPress’ wp_kses function doesn’t allow normally (meaning we somehow bypassed some of wp_kses’ restrictions using this bug). While this clearly indicated we made some progress, there was still a long way to go to get a full blown stored XSS.

One of the challenges we had was to find a way to get out of the double-quote sequence to insert new event handlers like onmouseover or onclick in order to demonstrate the possibility of executing malicious scripts in there. That’s when we had a look at what other filters were running when grabbing the content of a comment.

Sucuri-Akismet-XSS-v8

 

For those not familiar with WordPress filters, they are used by plugins to modify a certain values by passing them to custom hooked functions. As you can see, some of the call to add_filter above contains a third numeric argument: this is the priority parameter (note: when it’s not there, the default value of ’10’ is automatically used). It is there to specify the order in which each function should be executed when running the filter.

In this case, we could see that the force_balance_tags, convert_smilies and wpautop functions were hooked after Akismet’s  text_add_link_class (it is set to the default, 10). From these three, convert_smilies was the one we needed:

Sucuri-Akismet-XSS-v9

 

Quickly explained, this function does the following:

  1. Split the comment by separators matching this regex: ‘/<(.*)>/U’ (basically, a separator here would be anything starting with a less-than symbol and ending with a greater-than symbol, like an HTML tag)
  2. For each chunk of text we have split, check if we’re inside an ‘ignore block‘ like <code> or <pre> – places you usually don’t want smilies to be generated.
  3. If we’re not in such a block, check if there is some smilies in the text using the regular expression contained in $wp_smiliessearch and send the matching smiley to the translate_smiley function, where it’ll be replaced with an <img> tag.

So, what could possibly go wrong with this approach? Yes, some of you will have noticed, we managed to insert both a less-than and greater-than symbols inside an HTML tag earlier:

Sucuri-Akismet-XSS-v10

 

This implies that if we add a valid smiley just before the <abbr> tag, it should get generated inside the <span> title attribute and break the double-quote sequence. We quickly prepared the following test payload to ensure our new theory was right:

Sucuri-Akismet-XSS-v11

 

Which resulted in this very interesting output:
<span title="'> <img src="http://vulnerablesite.com/wp-includes/images/smilies/simple-smile.png" alt=":-)" class="wp-smiley" style="height: 1em; max-height: 1em;" /> <abbr title='" class="comment-link"><a href='
href="'> :-) <abbr title='" ' class="comment-link">x</abbr></a></abbr></span>

As you can see from the blue part, our new <img> tag broke the <span>‘s title attribute and inserted new parameters, exactly as we planned! One after effect of this, which wasn’t in our plan initially, was that it also closed the tag, injecting whatever followed our smiley into the DOM. In other words, the snippet that was put in the title attribute, <abbr title=’,  now acted as a complete tag, which changes how the browser interprets the rest of the tag’s content:

Sucuri-Akismet-XSS-v12

 

Notice how the hyperlink tag disappeared? How is it that some of it’s attributes are now part of the <abbr> tag? Let’s break the output into pieces to get a better view of what really happened:

First, <abbr title=’ is injected into the DOM, which starts a new abbr tag.

<abbr title='" class="comment-link"><a href='
href="'> :-) <abbr title='" ' class="comment-link">x</abbr></a>

As you may also notice, it’s title attribute is truncated just after an opening single-quote, which means everything after that is gonna be part of the title attribute, until another single quote is met.

<abbr title='" class="comment-link"><a href='
href="'> :-) <abbr title='" ' class="comment-link">x</abbr></a>

What happened here is that the closest single quote is located inside our payload’s <a> tag. More specifically, it is the first single quote of our hyperlink’s single-quoted href attribute.

<abbr title='" class="comment-link"><a href='
href="'> :-) <abbr title='" ' class="comment-link">x</abbr></a>

And our second nested href, the double-quoted one, is appended as a new attribute.

Using this discovery, all we had to do was to repeat the same steps, but add other attributes to the new <abbr> tag by writing these inside our hyperlink’s single-quoted href attribute.

Which worked just as expected:

Sucuri-Akismet-XSS-v13

 

Of course, other attributes could still be added to make the payload easier to trigger (adding a style attribute that resizes the <abbr> tag so it covers the whole page to ensure the administrator eventually moves his mouse over it, for example).

From this point, the attacker has a full blown stored XSS in Akismet to use as an attack vector and compromise the security of this website’s users even further, something you definitely don’t want to see happen.

Update as Soon as Possible

If you’re using a vulnerable version of this plugin, update as soon as possible! In the event where you can not do this, we strongly recommend leveraging our Website Firewall or equivalent technology to get it patched virtually.

777
SHARES
FacebookTwitterSubscribe

Categories: Security Advisory, Vulnerability Disclosure, WordPress SecurityTags: WordPress Plugins and Themes, XSS

About Marc-Alexandre Montpas

Marc-Alexandre Montpas is Sucuri’s Senior Security Analyst who joined the company in 2014. Marc’s main responsibilities include reversing security patches and scavenging vulnerabilities, old and new. His professional experience covers eight years of finding bugs in open-source software. When Marc isn’t breaking things, you might find him participating in a hacking CTF competition. Connect with him on Twitter.

Reader Interactions

Comments

  1. Christos Chatzaras

    October 14, 2015

    Websites that have Akismet installed but disabled are not infected, right?

    • Marc Montpas

      October 15, 2015

      Right. These aren’t affected. 🙂

  2. J.D.

    October 14, 2015

    The article mentions bypassing wp_kses with this bug. Just to clarify, that was because the wp_kses filter will run before the function in Akismet, even though they both have the same priority, because wp_kses() is registered first.

  3. Orun Bhuiyan

    October 14, 2015

    1338 indeed. Nice work and cool PoC.

  4. Marc Montpas

    October 15, 2015

    It certainly would prevent our sample exploit from working, but I wouldn’t count on it to protect my site. We found it was possible using WP emoticons conversion functionalities, but there may be other ways to achieve the same result.. Updating the plugin still remains the best option.

  5. Jasm

    October 27, 2015

    This won’t work if the comments are disabled? Right?

  6. Aaron Dear

    November 23, 2015

    I believe I had a network of sites slammed by this vulnerability across my hosting. It got through a single unused site’s un-updated plugins.

    Just another reason to keep EVERY site you own on an account updated, even if you aren’t using it. And if you’re not using it… you should probably delete it.

Primary Sidebar

Socialize With Sucuri

We're actively engaged across multiple platforms. Follow us and let's connect!

  • Facebook
  • Twitter
  • LinkedIn
  • YouTube
  • Instagram
  • RSS Feed

WordPress Security Course

How to Clean a Hacked Website Guide

WordPress Security Guide

How to know you can trust a plugin

Join Over 20,000 Subscribers!

Footer

Products

  • Website Firewall
  • Website AntiVirus
  • Website Backups
  • WordPress Security
  • Enterprise Services

Solutions

  • DDos Protection
  • Malware Detection
  • Malware Removal
  • Malware Prevention
  • Blacklist Removal

Support

  • Blog
  • Knowledge Base
  • SiteCheck
  • Research Labs
  • FAQ

Company

  • About
  • Media
  • Events
  • Employment
  • Contact
  • Testimonials
  • Facebook
  • Twitter
  • LinkedIn
  • Instagram

Customer Login

Sucuri Home

  • Terms of Use
  • Privacy Policy
  • Frequently Asked Questions

© 2022 Sucuri Inc. All rights reserved

Sucuri Cookie Policy
See our policy>>

Our website uses cookies, which help us to improve our site and enables us to deliver the best possible service and customer experience.