What is 💉 SQL injection and How to prevent WordPress SQLi attacks

SQL (Structured Query Language) is a language that allows code to interact with databases. Injecting SQL commands into a vulnerable user input section on your WordPress website such as a contact form or search box is considered an SQL Injection attack. Depending on the actual SQL command, this could purge the database, send the data to unauthorized party, inject additional malware in the database, etc.


Types of SQL Injection Attacks

There are two main kinds of SQL injection:

  • classic SQL injection vulnerability where unfiltered user input lets an attacker send commands to the database and the output is sent back to the attacker.
  • blind SQL injection vulnerability is when the attacker can send commands to the database but don’t receive the database output.

These can further be divided into:

Out of band SQL injectionWhen an attacker uses SQL injection to gain access to a Web application, the database may send error messages, indicating that the SQL query syntax is incorrect. An attacker then runs SQL queries with a series of true or false questions.
Union-based attacksThe SQL UNION statement is used to send requests, and the results are returned as a partial HTTP response. Binding SQL injection is a type of SQL injection attack used by a UNION SQL operator to quickly retrieve required information from a connected database.
Error-Based SQL attacksError-Based SQL Injection is a technique that allows hackers to obtain information about the structure of the attacked server by exploiting error messages supplied by the server. In order to generate error messages and locate vulnerable information such as versions, paths, permissions, etc. hackers make invalid requests on purpose.

Boolean Blind SQL Injection
SQL requests are used to list the databases and based on the response hackers determine if the information was sent correctly. Instead of measuring request time like in Time-Based Blind SQL Injection, instead they combine the expressions TRUE and FALSE.
Time-Based Blind SQL InjectionBlind SQL Time Injection is a technique for calculating response times for various SQL calls to the database. The system will be forced to wait before returning a response, which will be TRUE or FALSE in this case. The hackers can determine whether the input was sent correctly based on the waiting time as well as the response itself.

Example WordPress SQLi

🔴 CVE-2021-36916 The security vulnerabilities in the Hide My WP plugin up to version 6.2.3 allowed anyone to add get resent admin tokens because of how the IP address is retrieved and how it is used inside of a SQL query.

By supplying a malicious payload in one of the IP address headers, it would be directly inserted into the SQL query which makes SQL injection possible.

For technical details regarding the SQLi vulnerabilities in Hide My WP plugin by wpWave please visit Pathstack blog.


Check if your website is vulnerable to SQLi

Q: How to check if your WordPress website is vulnerable to SQL injections?

A: The best way to check if your website is vulnerable to SQL injections is to test it, either manually by inserting code into forms and examining the results, or using tools such as sqlmap or Burp Suite.

Test your website with SQL code

Here are some basic stuff that you can manually insert into forms on your WordPress website to check if it is vulnerable to SQLi:

  • The single quote character ' and look for errors
  • Boolean conditions such as OR 1=1 and OR 1=2, and look for differences in the responses
  • Some SQL-specific syntax that evaluates to the base (original) value of the entry point, and to a different value
  • Payloads designed to trigger time delays when executed within an SQL query, and look for differences in the response time.
  • OAST payloads designed to trigger an out-of-band network interaction when executed within an SQL query, and monitor for errors

Test WordPress with SQLi tools

Use penetration testing tools such as sqlmap or Burp Suite’s web vulnerability scanner to check if your WordPress website is vulnerable to SQLi.

sqlmap

sqlmap is an open source penetration testing tool that automates the process of detecting and exploiting SQL injection flaws and taking over of database servers.

Here are some guides on using sqlmap on WordPress:

Burp Suite

There are many WAVS (Web Application Vulnerability Scanners) tools such as OWASP ZAP, Wapiti, Arachni, etc.

But according to a study published by ICAICST the Burp Suite Professional had the best results.

Burp also has a BApp Store where you can get the Burp Extender tool and install additional applications such as WordPress Scanner.


How to prevent SQL injections in WordPress

Because the harm that SQLi attacks may cause is so great, it’s best to stay watchful and take steps to prevent such an attack from happening in the first place.

Keep WordPress Updated

Update not just WordPress core, theme and plugin, but also make sure you are using latest PHP and MySQL versions.

WordPress

WordPress Core, theme and Plugins can be updated from wp-admin dashboard. When a new version of WordPress core, a plugin or theme is available, an alert bubble is displayed in your WordPress Admin Menu and the corresponding theme or plugin is highlighted on Themes and Plugins Screens.

PHP and MySQL

Depending on the hosting panel of your choice (Plesk, cPanel, Aapanel, CWP..) there is an option inside the hosting panel to change PHP version.

To update MySQL you must ask your hosting provider to update it server-wide.


Always have backups

Website backups are critical for WordPress users who want to build a website on their own.
Backing up the site on a regular basis can help us not only save the prior data, but also restore it quickly in the event of technical issues, website attacks, and other problems.

In the past I’ve covered several WordPress backup plugins and methods:


.htaccess Rules to protect WordPress from SQLi

If you are using a dedicated or virtual server I recommend hardening server security with proper mod_security rules, but if this is not a case and you use shared hosting then you can use the .htaccess files.

Add the following code inside your .htaccess file to block MySQL injections, RFI, base64, and other exploits from reaching the database.

<IfModule mod_rewrite.c>
# Enable rewrite engine
RewriteEngine On

RewriteCond %{HTTP_USER_AGENT} (libwww-perl|wget|python|nikto|curl|scan|java|winhttp|clshttp|loader) [NC,OR]
RewriteCond %{HTTP_USER_AGENT} (<|>|'|%0A|%0D|%27|%3C|%3E|%00) [NC,OR]
RewriteCond %{HTTP_USER_AGENT} (;|<|>|'|"|\)|\(|%0A|%0D|%22|%27|%28|%3C|%3E|%00).*(libwww-perl|wget|python|nikto|curl|scan|java|winhttp|HTTrack|clshttp|archiver|loader|email|harvest|extract|grab|miner) [NC,OR]
RewriteCond %{THE_REQUEST} \?\ HTTP/ [NC,OR]
RewriteCond %{THE_REQUEST} \/\*\ HTTP/ [NC,OR]
RewriteCond %{THE_REQUEST} etc/passwd [NC,OR]
RewriteCond %{THE_REQUEST} cgi-bin [NC,OR]
RewriteCond %{THE_REQUEST} (%0A|%0D) [NC,OR]
RewriteCond %{QUERY_STRING} [a-zA-Z0-9_]=http:// [OR]
RewriteCond %{QUERY_STRING} [a-zA-Z0-9_]=http%3A%2F%2F [OR]
RewriteCond %{QUERY_STRING} [a-zA-Z0-9_]=(\.\.//?)+ [OR]
RewriteCond %{QUERY_STRING} [a-zA-Z0-9_]=/([a-z0-9_.]//?)+ [NC,OR]
RewriteCond %{QUERY_STRING} \=PHP[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} [NC,OR]
RewriteCond %{QUERY_STRING} (\.\./|\.\.) [OR]
RewriteCond %{QUERY_STRING} ftp\: [NC,OR]
RewriteCond %{QUERY_STRING} http\: [NC,OR]
RewriteCond %{QUERY_STRING} https\: [NC,OR]
RewriteCond %{QUERY_STRING} \=\|w\| [NC,OR]
RewriteCond %{QUERY_STRING} ^(.*)/self/(.*)$ [NC,OR]
RewriteCond %{QUERY_STRING} ^(.*)cPath=http://(.*)$ [NC,OR]
RewriteCond %{QUERY_STRING} (\<|%3C).*script.*(\>|%3E) [NC,OR]
RewriteCond %{QUERY_STRING} (<|%3C)([^s]*s)+cript.*(>|%3E) [NC,OR]
RewriteCond %{QUERY_STRING} (\<|%3C).*iframe.*(\>|%3E) [NC,OR]
RewriteCond %{QUERY_STRING} (<|%3C)([^i]*i)+frame.*(>|%3E) [NC,OR]
RewriteCond %{QUERY_STRING} base64_encode.*\(.*\) [NC,OR]
RewriteCond %{QUERY_STRING} base64_(en|de)code[^(]*\([^)]*\) [NC,OR]
RewriteCond %{QUERY_STRING} GLOBALS(=|\[|\%[0-9A-Z]{0,2}) [OR]
RewriteCond %{QUERY_STRING} _REQUEST(=|\[|\%[0-9A-Z]{0,2}) [OR]
RewriteCond %{QUERY_STRING} ^.*(\[|\]|\(|\)|<|>).* [NC,OR]
RewriteCond %{QUERY_STRING} (NULL|OUTFILE|LOAD_FILE) [OR]
RewriteCond %{QUERY_STRING} (\./|\../|\.../)+(motd|etc|bin) [NC,OR]
RewriteCond %{QUERY_STRING} (localhost|loopback|127\.0\.0\.1) [NC,OR]
RewriteCond %{QUERY_STRING} (<|>|'|%0A|%0D|%27|%3C|%3E|%00) [NC,OR]
RewriteCond %{QUERY_STRING} concat[^\(]*\( [NC,OR]
RewriteCond %{QUERY_STRING} union([^s]*s)+elect [NC,OR]
RewriteCond %{QUERY_STRING} union([^a]*a)+ll([^s]*s)+elect [NC,OR]
RewriteCond %{QUERY_STRING} (;|<|>|'|"|\)|%0A|%0D|%22|%27|%3C|%3E|%00).*(/\*|union|select|insert|drop|delete|update|cast|create|char|convert|alter|declare|order|script|set|md5|benchmark|encode) [NC,OR]
RewriteCond %{QUERY_STRING} ^(%2d|\-)[^=]+$ [NC,OR]
RewriteCond %{QUERY_STRING} proc\/self\/environ [NC,OR]
RewriteCond %{QUERY_STRING} (sp_executesql) [NC]

RewriteRule ^(.*)$ - [F,L]
</IfModule>


⚠️ Improve MySQL Security

  • Avoid using the root user for WordPress database
  • Change table prefix from the default wp_
  • Limit accesses of the SQL user to sensitive directories
  • Block SQL keywords server-wide
  • Disable remote mysql access and block port 3306

Change database user and table prefix

Never use the MySQL root user as a database user for your WordPress website. Also, change the default WordPress table prefix from wp_ to something stronger like b223vvy_


Drop the Test Database

During the mysql_install_db process a test database is created and can be fully accessed by all users by default, making it a common target for hackers. It should therefore be removed during post-installation hardening.


Disable remote access

Remote access to the server should be disabled and the MySQL instance should be configured to only allow access to permitted hosts. This can be done by editing hosts.deny and hosts.allow files.

Inside the /etc/my.cnf file add a skip-networking entry under the [mysqld] section, MySQL can be configured to listen only to MySQL socket-based communications.


Set proper File Permissions

Make sure that my.cnf is only root writable and hat the default location for data at /usr/local/mysql/data is properly secured with the appropriate permissions.


For developers: use Parameterized Queries

Most instances of SQLi can be prevented by using parameterized queries (also known as prepared statements) instead of string concatenation within the query.

WordPress has a wpdb::prepare function that prepares a SQL query for safe execution.

The following code is vulnerable to SQL injection because the user input is put directly into the query:

if(isset($_POST['submit'])) {
global $wpdb;
$ordernumber = $_POST['ordernmbr'];
$orderfirstname = $_POST['firstname'];
$orderpostnumber = $_POST['postnmbr'];
$ordernumber = stripslashes_deep($ordernumber);
$orderfirstname = stripslashes_deep($orderfirstname);
$orderpostnumber = stripslashes_deep($orderpostnumber);
$result = $wpdb->get_results($wpdb->prepare( "SELECT * FROM         $wpdb->wp_postmeta
WHERE post_id = '$ordernumber' AND meta_value = '$orderfirstname'"));

This code can be easily rewritten in a way that prevents the user input from interfering with the query structure:

global $wpdb;
$tablename = $wpdb->prefix . "my_custom_table";
$sql = $wpdb->prepare( "SELECT * FROM %s ORDER BY date_created DESC",$tablename );
$results = $wpdb->get_results( $sql , ARRAY_A );

How to clean a WordPress SQL injection

Q: What to do when a WordPress website is hacked using SQL Injection?

A: Perform a detailed analysis of the hack, clean files and database, then take further steps to increase security.

Was this post helpful?

Leave a Comment