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.

image

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 Step

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 (sanitize_* and 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.

Plugins, Plugins everywhere

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:

image

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.

Optimizing Our Process

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:

  • Interaction with the WordPress database: references to $wpdb->
  • String interpolation in SQL-like strings:
    • ['"].+?\s(WHERE|where)\s*\w+\s*=\s*\$\w+.+?['"]
    • ["'](SELECT|select|DELETE|delete)\s+.*?(FROM|from)\s+.+?(WHERE|where).+?(\$\w+).+(['"]|;)
    • ...
  • Optional: Security measures relating to sanitization attempts: esc_sql
  • Exposure of unauthenticated endpoints: references to add_action("wp_ajax_nopriv_

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:

image

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:

Findings & Conclusion

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.

Submission CVE ID Vulnerability Plugin
2022-02-16 CVE-2022-0656 LFI udraw
2022-02-16 CVE-2022-0657 SQL Injection 5-stars-rating-funnel
2022-02-16 CVE-2022-0658 SQL Injection commonsbooking
2022-02-17 CVE-2022-0679 LFI / RCE narnoo-distributor
2022-02-18 CVE-2022-0693 SQL Injection master-elements
2022-02-18 CVE-2022-0694 SQL Injection advanced-booking-calendar
2022-02-23 CVE-2022-0739 SQL Injection bookingpress-appointment-booking
2022-02-23 CVE-2022-0747 SQL Injection infographic-and-list-builder-ilist
2022-02-24 CVE-2022-0760 SQL Injection simple-link-directory
2022-02-26 CVE-2022-0769 SQL Injection users-ultra
2022-02-26 CVE-2022-0771 SQL Injection sitesupercharger
2022-02-26 CVE-2022-0773 Stored XSS searchiq
2022-02-26 CVE-2022-0780 SQL Injection documentor-lite
2022-02-27 CVE-2022-0781 SQL Injection nirweb-support
2022-02-27 CVE-2022-0782 SQL Injection nd-donations
2022-02-27 CVE-2022-0783 SQL Injection multiple-shipping-address-woocommerce
2022-02-27 CVE-2022-0784 SQL Injection wp-experiments-free
2022-02-27 CVE-2022-0785 SQL Injection daily-prayer-time-for-mosques
2022-02-27 CVE-2022-0786 SQL Injection kivicare-clinic-management-system
2022-02-28 CVE-2022-0787 SQL Injection wp-limit-failed-login-attempts
2022-02-28 CVE-2022-0788 SQL Injection wp-fundraising-donation
2022-03-01 CVE-2022-0814 SQL Injection ubigeo-peru
2022-03-01 CVE-2022-0817 SQL Injection badgeos
2022-03-01 CVE-2022-0818 Stored XSS woo-coupon-usage
2022-03-01 CVE-2022-0826 LFI (Plugin's Backup) church-admin
2022-03-02 CVE-2022-0827 SQL Injection wp-video-gallery-free
2022-03-02 CVE-2022-0833 SQL Injection bestbooks
2022-03-02 CVE-2022-0836 SQL Injection sema-api
2022-03-03 CVE-2022-0846 SQL Injection speakout
2022-03-04 CVE-2022-0867 SQL Injection arprice-responsive-pricing-table
2022-03-04 CVE-2022-0948 SQL Injection personal-dictionary
2022-03-04 CVE-2022-0949 SQL Injection wp-contacts-manager
2022-03-11 CVE-2022-0952 SQL Injection woc-order-alert
2022-03-11 CVE-2022-1013 SQL Injection stopbadbots
2022-03-11 CVE-2022-1014 Privilege Escalation sitemap-by-click5


Further reading