Critical Wordpress update fixes zero-day flaw unnoticed

An apparently unremarkable “point” release of the world’s most popular website software, WordPress, rolled out across the web at the end of last week.

Unlike its closest competitors, Drupal and Joomla, WordPress knows how to apply security updates to itself automatically so version 4.7.2 made its way on to millions of websites with their owners none the wiser:

On Thursday, January 26, we released WordPress 4.7.2 to the world. The release went out over our autoupdate system and, over a couple of hours, millions of WordPress 4.7.x users were protected without knowing about the issue or taking any action at all.

Any website operators who looked up from their cornflakes to read the security release in more detail will have seen that 4.7.2 dealt with three important but far from hair-raising security vulnerabilities.

What they won’t have seen is that is also fixed a fourth vulnerability: an unauthenticated privilege escalation vulnerability serious enough to warrant deployment in total silence.

Vulnerability #4

The unmentioned vulnerability affects version 4.7.0 and 4.7.1 of WordPress so if for some reason you’re running a WordPress site that hasn’t self-updated stop reading and get the update now yourself (it’s that serious).

The vulnerability, which affects the WordPress REST API added in the 4.7 release, allows attackers to modify the content on any affected website remotely.

A bug like this is a massive opportunity for criminals and when a serious vulnerability is found in a popular web Content Management System automated attacks can start within hours.

The problem was discovered by Marc-Alexandre Montpas from web security outfit Sucuri as part of a vulnerability research project.

He alerted the WordPress team on January 20 and they began work on the fix while monitoring for in-the-wild activity and alerting security vendors and web hosts about the problem.

When the update was ready the WordPress team took the decision to deploy the fix without disclosing details of it first, giving themselves an opportunity to stay ahead of the criminals who might exploit it:

By Wednesday afternoon, most of the hosts we worked with had protections in place. Data from all four WAFs and WordPress hosts showed no indication that the vulnerability had been exploited in the wild. As a result, we made the decision to delay disclosure of this particular issue to give time for automatic updates to run and ensure as many users as possible were protected before the issue was made public.

Once the automatic deployment process had run its course the silent fix was disclosed in an update to the the security release.

How the attack works

An API (Application Programming Interface) is an interface to a computer program that’s meant to be used by other computer programs rather than people. They’re what allow apps on your phone to update your Twitter account, your fridge to talk to your supermarket or your time-tracking software to tell your accounts software what you’ve been up to.

Unlike the graphical interfaces used by people, APIs are sets of commands. The modern vogue for web-based systems is to implement APIs using the web’s simple and stateless HTTP protocol, an idea known as REST (Representational state transfer).

The WordPress REST API allows authorised computer programs access to your site’s data so that they can do things like creating, updating or deleting WordPress posts.

It works by looking out for specially crafted HTTP requests sent to specific URLs or “endpoints”. The endpoint for creating, updating or deleting WordPress posts is /wp/v2/posts/.

Anyone in the world can request any public URL from any website (that’s the whole point of the web, after all) so websites have to check that the people or programs sending requests to their APIs are authenticated and authorised to perform those commands.

What Montpas discovered was a subtle but potentially devastating way to bypass those checks.

To demonstrate it, let’s imagine that we have a WordPress blog with a few thousand posts and pages. All WordPress posts and pages have numeric IDs and we’re going to use 1337 in our example.

To tell WordPress which post we want to change we add its ID to the end of the REST API endpoint and send the data we want to change to the URL that creates.

The URL or endpoint for updating post 1337 is therefore /wp/v2/posts/1337.

You can also tell WordPress the ID of the post you want to change by adding an id parameter to the URL, and Montpas’s first important discovery was that if he used an id parameter he could get WordPress to accept a non-numeric post ID.

You’ll see why this ability to pass in non-numeric IDs was important in a minute, but for now know that if we send our update request to /wp/v2/posts/1337?id=1337ISMYREALTARGET WordPress 4.7.0 and 4.7.1 will accept 1337ISMYREALTARGET as the post ID.

Of course WordPress doesn’t allow just anyone to edit a post, so having accepted your fake ID it checks to see if we’re allowed to edit it. Unfortunately a subtle flaw in that check allows our attack to progress a little further.

It turns out that if the post 1337ISMYREALTARGET exists and you don’t have permission to edit it you’ll be stopped in your tracks but if the post doesn’t exist (which it can’t because all WordPress posts have numeric IDs) you aren’t stopped.

Having fooled the gatekeeper your data progresses to the WordPress update_item method which, as you might expect from its name, is the code that actually updates the post. You might also expect that our attack will fail here because even though we’re allowed to update 1337ISMYREALTARGET we can’t change something that doesn’t exist.

It’s no accident that our fake ID starts with the ID of a post that does exist though: 1337.

Just before WordPress retrieves our fake post for editing it converts (or “casts”, in the vernacular) the ID it’s been handed into an integer.

WordPress is written in the PHP language and PHP’s (int) cast will convert our the non-existent ID 1337ISMYREALTARGET into the very-much-in-existence 1337 and our updates will be applied to it.

Of course we needn’t limit our attack to a single post. We could easily write a program to cycle through all the numbers from 1 to 9999999 and update every post and page on a site so it includes the SEO spam, advertising or malware of our choice.

We can also use the attack as a foothold to exploit other vulnerabilities, as Montpas explains:

From there, they can add plugin-specific shortcodes to exploit vulnerabilities (that would otherwise be restricted to contributor roles) … Depending on the plugins enabled on the site, even PHP code could be executed very easily.

What next?

If you’re running a WordPress website, check that you’re running the latest version of WordPress by logging in and going to Dashboard > Updates.

If you want to see how WordPress actually fixed the problem and you aren’t shy of reading code you can bask in the glory of open-source software by viewing the changes on the WordPress Trac site.

If that’s a bit much for you then take a look at How to avoid being one of the “73%” of WordPress sites vulnerable to attack.