Further attack surface of WordPress PHAR injection
Trung Nguyen

Summary
In August 2018, Sam Thomas presented a new vulnerability of WordPress at Black Hat USA 2018. The PHP object injection vulnerability is not new, but the way attacker can trigger this error is worth mentioning. In this article, I will go over the detail of this exploit and inspect further impact of this vulnerability to the WordPress community. A list of more than 300 WordPress plugins that could be used to exploit this bug is also included.
Object injection
Let’s talk about object injection real quick. This vulnerability was first revealed in 2010, and has since then plagued the world of software security greatly.
Object injection vulnerability comes from the misuse of data serialization. This is the process of converting program data, such as complex objects, into readable strings or binary data. Almost all modern programming languages provide some mechanism of serialization. Applications typically use serialization and deserialization, which is the inverse process, to handle communication of complex data in memory between different processes or hosts.
Data serialization is not dangerous in itself. But when unvalidated user input is directly passed on to application to perform deserialization, this quickly becomes a total mess.
Phar meta data
Traditionally, in order to trigger an object injection vulnerability, an attacker needs to trigger an unserialize()
function call on input that he has control over. Then, the pop chain is executed, and depending on the application’s loaded class, remote code execution can be obtained.
However, as Sam Thomas has demonstrated, unserialize()
function call is no longer the requirement to exploit this vulnerability. This is due to the quirks of how PHP interpreter handle Phar file. According to PHP manual : “Phar archives are similar in concept to Java JAR archives, but are tailored to the needs and to the flexibility of PHP applications.”. The problem is that Phar archives also contain meta-data field, which is serialized PHP data.
Let’s consider this piece of code, which creates a Phar file.
<?php class TestObject {
}
@unlink("phar.phar");
$phar = new Phar("phar.phar");
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>");
$o = new TestObject();
$phar->setMetadata($o);
$phar->addFromString("test.txt", "test");
$phar->stopBuffering();
?>
Suppose we have just created this Phar file, and now we want to do some file operation on it. Let’s try file_exist()
<?php
class TestObject {
public function __destruct() {
echo 'Destruct called';
}
}
$filename = 'phar://phar.phar/a_random_string';
file_exists($filename);
?>
Running above code, we will get Destruct called
output to console. This code triggers the old php deserialization vulnerability that we are so familiar with. Notice that we didn’t call any unserialize()
, just plain file_exist()
call. In fact, any file operations on Phar file will trigger deserialization of meta data field.
WordPress exploit
WordPress version 4.9 has been shown to be vulnerable to this kind of attack. An attacker with an Author
privilege can upload a Phar archive with the malicious payload to the WordPress site, and later trigger the deserialization vulnerability through an xmlrpc call.
The exploit works by logging into the authenticated account, uploading the Phar file, which is disguised as a valid gif image. Then the attacker change the file name of the uploaded file so that it includes phar://
prefix. The final request will trigger a file operation on this Phar archive. Output of attacker’s commands is directly embedded in WordPress’s HTTP response.
To ease the task of sending multiple HTTP requests when exploiting this bug, I have written a short python script to automate the process. You can view the code here. However, you should use a proxy such as Burp Suite
to verify if the exploit still works as expected.
For the exploit to work, the WordPress application must have some classes that provide gadgets for the Pop chain. The full detail of pop chain are out of scope of this blog, but you can find many resources about this exploit on the Internet. The original paper only shows 1 plugin (Woocommerce
) as gadgets for the the pop chain. I have gone further and found roughly 300 WordPress plugins that provide classes to construct a valid pop chain for the exploit. 7 out of these 300 plugins have more than 100000 active installations, as reported by WordPress.org. The python exploit assumes that the targeted application has Woocommerce
installed. To exploit other plugins, you will have to manually construct a different payload.
Finding other plugins to construct payload was relatively easy. Because all WordPress plugins have their source code available online, I only need to download and scan their code for potential exploits. The vulnerable plugins must have classes that use foreach
in __destruct()
method. This is a fairly trivial task, except that I have to write a custom string matching function. It turns out that using regex to filter out function definitions from source code is not straightforward at all.
All in all, this is the list of plugins that I found.