20. May 2022
We at cyllective always strive to improve and expand our knowledge in our field of expertise. It was during a recent audit where we (once more) came into contact with WordPress. Even after the audit had passed, it did peak the interest of one of our security engineers (@_cydave). As part of our work also involves giving back to the community, we decided to dedicate a fair amount of time into the research on WordPress plugins and we are pleased to present an insight into our research. -- "@cyllective"
By now you've most likely heard about the pleasure of building a small website or cool blog, but it should be quickly and without hassle, oodles and oodles of beautiful designs, powerful features and the freedom to "build anything you want" - to most people, WordPress comes to mind.
But not everybody has heard about the associated security nightmares that the vast plugin and theme ecosystem entails. As the individual plugin maintainers usage of secure code practices highly varies.
Wether you've experienced the horrors of copy-pasta'd vulnerable components before (yes, we are looking at you TimThumb ;), uploader ;)) or had to explain to that one friend of yours, not to play Pokémon trainer with plugins and themes on the WordPress marketplace - we all have been touched by the grace of WordPress in some shape or form.
This blog post summarizes our explorational WordPress plugin security research, which we've been working on for around three months. Our initial goals for this research were not set in stone, beyond getting in some practice for improving our secure code review / auditing routine and to learn more about WordPress plugins in the process.
The first few hours of our research were spent reading through WordPress's Plugin Handbook
in order to get a basic understanding of their structure (hook all the things - Actions and Filters)
and available security measures (
esc_* functions, nonce
creation and validation, etc.).
Aside from the "official" resources, we dug around exploit-db for additional insight into attacking / auditing WordPress plugins. The following papers provided us with valuable pointers:
Equipped with an ever growing scratchpad of notes, references and rabbit holes to chase, we decided to start digging.
The oodles and oodles of plugins, merely a few clicks away to get lost in reading, tracing function calls and keeping a keen eye out for sources and sinks - a sheer bottomless ball pit of good fun - we just couldn't decide on where to start...
We had been scraping and indexing plugin metadata as well as the plugin's files, version numbers, relative filepaths and checksums for quite a while. The data harvesting process was painfully slow, due to the transfer speeds of WordPress' SVN server, but provided us with valuable information that we were then able to incorperated in our research in the form of our own database.
Initially, we started picking a fixed set of randomly selected plugins from our database, without any regards to the release date, popularity of the plugin or it's use case. We also limited ourselves to a maximum of thirty minutes per plugin to audit, so we could expose ourselves to varying levels of code quality, structure and security awareness of the plugin developers.
After around one week of downloading, installing, auditing, uninstalling of random plugins, we uncovered an unauthenticated SQL injection vulnerability. We've also uncovered a series of local file inclusion and remote code execution vulnerabilities in severely outdated plugins, which just happened to be part of our random pool. The fact that we invested quite a bit of time in auditing plugins that had not received updates in the past 5 years didn't sit well with us. We wouldn't benefit all too much if we would continue on with our current plugin selection criteria. By auditing severly outdated plugins the WordPress ecosystem woulnd't benefit that much either, as they weren't relevant and supported anymore anyways.
Fortunately, we've been scraping plugin metadata for quite a while and could utilize the release- and last updated date fields to filter out severly outdated plugins. Out of curiosity, we plotted the number of plugins by the date they were last updated on and grouped them by year:
We decided to exclude plugins that did not receive updates in the last two years, so we wouldn't be investing time in auditing "dead" software. This brought the capacity of our random pool down to roughly 20'000 plugins.
We continued auditing plugins left and right for a few more days and ended up finding two additional unauthenticated SQL injection vulnerabilities in two separate plugins. After having audited roughly one thousand plugins at this point, we developed a slight obsession (or hyper focus) on SQL injection vulnerabilities. Because SQL injection vulnerabilities are rather easy to spot, we began experimenting with a bunch of RegEx patterns, which would help us in discovering SQL injections more rapidly. But as expected, searching for vulnerabilities purely based on RegEx patterns, turned out to be suboptimal. Especially the time loss, caused by the high rate of false positives (and false negatives), motivated us to come up with a better approach.
Recalling that most plugins on the marketplace have a set of tags associated with them, inspired us to experiment with a similar approach, with the goal of finding more "relevant" plugins. Instead of using tags, which describe the plugin's use cases, we chose to search the plugin's source code for WordPress specific library calls and then enrich our database with this information in the form of tags.
Because we were mostly interested in finding SQL injection vulnerabilities (ideally, unauthenticated ones), we would look out for a combination of specific behavior:
With our new approach in mind, some more RegEx patterns and yet another Python script at the ready, we re-lived the radical internet speeds of the 90s, by downloading all of the WordPress plugins via their SVN mirror once again. For each downloaded plugin we recursively grepped for specific patterns in the source code and once a match was found, we would attach a tag, describing the pattern that was used, to the plugin.
After days of downloading and grepping we ended up with tagged plugins:
Using the tags (stored in the "notes" column in the screenshot above) as an additional filter, we were able to reduce the pool of targets to audit down to roughly 5'000 plugins.
In the first few days of analysing a subset of the new targets, we encountered 2 more SQL injection vulnerabilities and as the audit-spree continued, we would occasionally find up to 4 or 5 vulnerabilities a day. To us, the fact that we were finding vulnerabilities more frequently, compared to our initial approach, felt like a clear indicator that we were on the right track. :smile:
On each day that we discovered one or more vulnerabilities, we would responsibly disclose them with the help of the fine folks at WPScan (WPScan being a CNA of WordPress CVE numbers). Instead of keeping track of all the back-and-forth communication across two or more parties (the plugin developers, the WordPress plugin team and a selected CNA) all while correctly timing, escalating and taking further steps by yourself, one can simply use their vulnerability submission form and let the WPScan team take care of all the necessary steps for you.
Thank you, WPScan and team, for providing us with such a smooth process! :+1:
Having exhausted the pool of 5'000 plugins after around two months, we decided to conclude our explorational research. We ended up finding and reporting a total of 35 vulnerabilities, all of which could have been exploited by unauthenticated attackers.
|LFI / RCE
|LFI (Plugin's Backup)