In July 2019, Fortinet’s FortiGuard Labs discovered and reported nine SQL injection vulnerabilities in nine different popular WordPress plugins across a variety of categories, including advertisement, donation, gallery, forms, newsletter, and video player. These plugins are being actively used by hundreds of thousands of WordPress websites, with some of them ranked in the top position for their corresponding category.
All discovered vulnerabilities have been assigned a FortiGuard Labs identity that includes details and corresponding CVE ID: FG-VD-19-092, FG-VD-19-094, FG-VD-19-095, FG-VD-19-096, FG-VD-19-097, FG-VD-19-098, FG-VD-19-099, FG-VD-19-101 and FG-VD-19-102.
An interesting point is eight out of nine vulnerabilities were found with the same simple code pattern that made them vulnerable to a SQL injection. In spite of the potential for exploit, many developers simply do not carefully filter user-supplied data. And in this case, this happened despite WordPress Core’s efforts, since they support various built-in methods to ensure that any user-supplied data is well-sanitized.
In this blog post, we will go through the set of WordPress security mechanisms, analyze some of the discovered vulnerabilities, and explain the ways an attacker could exploit these vulnerabilities and how to prevent them – all from a developer’s perspective.
At the time of writing, all of the issues identified in this blog have been fixed and published by their respective vendors. Fortinet’s FortiGuard Labs appreciates their quick responses and timely fixes.
A SQL injection occurs when user input is used to construct a SQL query without being properly sanitized. Consider the following example:
At first glance, one would say this code is vulnerable to a SQL injection attack, since $id is retrieved from $_GET and passed into the SQL query without any sanitization. The good news is, in the latest version of WordPress, magic quotes are always added to $_POST/$_GET/$_REQUEST/$_COOKIE by default . This helps WordPress maintain consistency and provide the best possible security capabilities. Therefore, the above code is not actually vulnerable.
Beside enforcing the adding of slashes to all input values, WordPress also provides several built-in sanitization and escape functions for cleaning user input and/or securing output . Developers can use sanitize_email() to sanitize email addresses, or sanitize_text_field() to clean text field values, or sanitize_sql_orderby() to validate the SQL ORDER BY clauses, and more. Most common types of user input are covered by the WordPress sanitize_*() class of helper functions.
Although WordPress Core is doing its best to help developers prevent common attacks caused by malformed user-input, bad coding practices and misusing escaping functions still lead to simple but critical vulnerabilities.
This vulnerability is a classic SQL Injection which exists in the AdRotate plugin through v5.2, in both the FREE and PRO versions. The flaw can be spotted in dashboard/publisher/adverts-edit.php, at line 25.
The variable $ad_edit_id is used to construct a SQL query. This variable is retrieved from $_GET, in the adrotate_manage function:
Since esc_attr only escapes HTML attributes and $ad_edit_id is not escaped with double quotes in the SQL query, we can execute an arbitrary SQL statement by injecting payloads into $ad_edit_id.
Although the management interface is only available for the Administrator role, a lack of a CSRF token makes it possible for unauthenticated attackers to remotely steal information, including session tokens, with a minimum user interaction by using this SQL injection vulnerability to cause a XSS:
The patch from the developers is simply adding quotes to $ad_edit_id in the query.
NextGEN Gallery has been the industry’s standard WordPress gallery plugin since 2007 and continues to enjoy over 1.5 million new downloads per year. It's appeal is that it is easy to use for simple photo galleries, but powerful enough for the most demanding photographers, visual artists, and imaging professionals. At the time of writing, NextGEN Gallery has over 900,000 active installations and is the most popular plugin in the Gallery category .
The flaw happens in an AJAX API, which allows users to attach photos from galleries while writing posts.
The function get_displayed_gallery_entities_action in modules/attach_to_post/package.module.attach_to_post.php is responsible for showing photos in selected galleries. A keyed array param displayed_gallery is retrieved via the POST method to create a gallery object in line 119. Attributes of the object are escaped by the esc_sql helper function. Then get_entities is called in line 130, which leads us to the get_entities function in modules/nextgen_gallery_display/package.module.nextgen_gallery_display.php.
Since the return request has a value of both, the _get_image_entities in line 832 is called.
This function basically prepares a query that returns all images in the displayed gallery. As you may have noticed in line 1041, it applies a sorting order procedure based on $sort_by and $sort_direction, which are retrieved from the created gallery object. Any authenticated user with permission to use NextGEN Gallery can manipulate the params to create this gallery object. Although all attributes of the object are escaped with esc_sql, in fact, attackers do not need to escape quotes to perform an SQL injection attack within the ORDER BY clause. Hence, esc_sql can’t prevent the NextGEN Gallery from being exploited in this case.
The patch from the Imagely team works fine. By only allowing intended values in the ORDER BY clause, the query is safe.
Using the same pattern, I was able to discover seven more plugins that were also vulnerable to an SQL injection attack. One of them actually tried to whitelist the sort values, but failed to accomplish their goal because of a small coding mistake.
Give is the highest rated, most downloaded, and best supported donation plugin for WordPress .
The vulnerable code exists in the get_order_query function in includes/donors/class-give-donors-query.php.
As the comment points out, the get_order_query tries to remove non-existing columns for the ORDER BY clause, and filter sort values by applying the esc_sql helper function. The removal does not work as expected because after a non-existing column is unset in line 467, an escaped value is reinserted in line 470. Many of us already know that esc_sql is not useful for preventing a SQL injection attack within the ORDER BY clause. Therefore, the constructed query is still vulnerable. The same Blind-SQL injection technique can be used to exploit this vulnerability.
A simple 1-line patch from Impress team allows the filter to work as expected, which makes the query completely safe.
Other vulnerabilities had the same vulnerable code pattern, and their patches are quite similar.
In this section, we will provide some recommendations to use when developing a WordPress plugin that should help prevent a SQL injection attack. The WordPress community has a useful and complete Handbook for plugin development . As a developer, you should always follow WordPress coding standards and secure coding best practices. In addition, the following recommendations will not only help to prevent a SQL injection attack, but other vulnerabilities caused by malformed user-input as well:
WordPress is the most dominant CMS, with 61.0% of market share, which means that it is used in 34.3% of all websites . According to Wordfence, 52% of related vulnerabilities reported were WordPress plugins . These numbers indicate that targeting WordPress plugins is one of the most practical attack vectors for cybercriminals. SQL injection is not a new technique, but it always poses serious security threats to web applications and web servers. To avoid it, developers should always follow coding standards and secure coding best practices.
For WordPress users, you should always keep WordPress and plugins up to date as well.
If you are interested in this kind of assessment for your software or application, FortiGuard Labs provides a tailor-made vulnerability assessment and penetration testing service that can help you improve the security of your products. Check out https://fortiguard.com/services/pentesting for more information.
-== FortiGuard Lion Team ==-
Organizations using FortiWeb - Fortinet’s Web Application Firewall, are already protected from these vulnerabilities with generic SQL injection detections. FortiGuard Labs also released the following IPS signatures, which cover all the vulnerabilities mentioned:
FG-VD-19-092: Base Score 9.0, Critical severity
FG-VD-19-094: Base Score 9.0, Critical severity
FG-VD-19-095: Base Score 9.0, Critical severity
FG-VD-19-096: Base Score 9.0, Critical severity
FG-VD-19-097: Base Score 9.0, Critical severity
FG-VD-19-098: Base Score 9.0, Critical severity
FG-VD-19-099: Base Score 9.0, Critical severity
FG-VD-19-101: Base Score 9.0, Critical severity
FG-VD-19-102: Base Score 9.0, Critical severity
 WordPress, “Function Reference/stripslashes_deep”, https://codex.wordpress.org/Function_Reference/stripslashes_deep (27th Aug 2019)
 WordPress, “Validating Sanitizing and Escaping User Data”, https://codex.wordpress.org/Validating_Sanitizing_and_Escaping_User_Data (27th Aug 2019)
 Imagely, “WordPress Gallery Plugin – NextGEN Gallery”, https://wordpress.org/plugins/nextgen-gallery/ (27th Aug 2019)
 Impress, “Give - Donation Plugin and Fundraising Platform”, https://wordpress.org/plugins/give/ (27th Aug 2019)
 WordPress, “Plugin Handbook”, https://developer.wordpress.org/plugins/ (27th Aug 2019)
 W3Techs, “Usage statistics and market share of WordPress”, https://w3techs.com/technologies/details/cm-wordpress/all/all (01 Aug 2019)
 Wordfence, “How Attackers Gain Access to WordPress Sites” (2016), https://www.wordfence.com/blog/2016/03/attackers-gain-access-wordpress-sites/ (27th Aug 2019)
Read about the FortiGuard Security Rating Service, which provides security audits and best practices.