Wordpress is a Content Management Service (CMS) that powers 43% of websites on the internet. Since it is written in PHP and has the ability to add extensions, once you are an admin, it is simply game over. By adding an extension, you have code execution on the box.
To prevent XSS on the admin user turning into code execution, there are
hardening guide. In this guide, the admin user is put into a sandbox, not allowing for the adding of extensions. This means that code execution is no longer possible.
Wordpress has a hundreds of options for configuring a site. In the source code for options, they are added via update_option($key, $value) and retrieved via get_option($key). An admin user is able to modify any and all options in this database.
These options can take objects, arrays, integers and all datatypes that can be serialized. To ensure that no deserialization attacks can be performed, there is a check to make sure that if the string is already serialized, then it is double serialized via the maybe_serialize($data) function and deserialized via maybe_unserialize($data).
In a database update script within Wordpress core, there is a situation where the function unserialize is used directly on a value instead of maybe_unserialize. Although this seems like an obvious vulnerability, we still have a problem: the data is double serialized.
The function maybe_unserialize checks for the start of the object to have specific characters. These characters are directly correlated with the types that can be deserialized from the unserialize function. Can we find an issue with this identification function?
It turned out that the serialization type "Object with custom deserialization function" was NOT accounted for. This means that the data could be serialized but NOT be double serialized! To me, this feels like a classic verification vs. usage problem!
To actually exploit this, an attacker needs to insert a PHP serialized string carrying a gadget in the database with the type that will result in the data not being double serialized. Once the payload is in the database, trigger the vulnerable code by changing the database version option. When the upgrade script calls unserialize, it triggers the pop chain to get code execution on the server.
Overall, this is a very deep yet interesting bug! A nice writeup with a clear explanation.