WPDeeply
Download free plugin
Security Research

Loginizer before 1.6.4 — Unauthenticated SQL Injection

An unauthenticated SQL injection in Loginizer’s brute-force protection, reachable from any WordPress login form. Fixed in 1.6.4 — and pushed to over a million sites by a rare WordPress forced update.

This page preserves a historical WPDeeply disclosure at its original URL. The research is credited to its author; remediation information has been kept current by the site’s present operators, who did not discover the vulnerability.

Loginizer is a brute-force protection plugin installed on well over a million WordPress sites. Its core job is to watch failed logins and block the addresses that produce too many of them. To do that it writes every failed attempt to the database — and in versions before 1.6.4, it wrote the submitted username into an SQL query without properly neutralising it first.

The result was an unauthenticated SQL injection reachable from the one page every WordPress installation exposes to the internet: wp-login.php. No account was needed. No setting had to be changed. Brute-force protection is enabled by default on installation, which meant the vulnerable code path was live on a typical install from the moment the plugin was activated.

The vulnerability

  • Identifier: CVE-2020-27615
  • Affected: Loginizer < 1.6.4
  • Fixed in: 1.6.4
  • Type: Unauthenticated (time-based blind) SQL injection, with resultant stored cross-site scripting
  • Privileges required: none

The defect sat in the failed-login handling — the loginizer_login_failed function — and in the address-validation helper lz_valid_ip. Both took values that arrive from the request and placed them into queries against Loginizer’s own logging tables without sufficient escaping. The log parameter, which WordPress uses to carry the submitted username, was the practical entry point.

Because the injection landed in a logging routine rather than a page that returns results, exploitation was blind: an attacker inferred data by measuring how long the response took, rather than reading it directly from the page. Blind does not mean harmless. Blind SQL injection against the WordPress users table yields password hashes and email addresses, one conditional query at a time, and it can be automated. A public Metasploit auxiliary module (contributed by h00die, with work from red0xff) demonstrated exactly that, extracting stored credential hashes from a vulnerable installation.

The patch also tightened input sanitisation elsewhere in the plugin, closing the stored XSS possibility that came with logging unescaped usernames into an admin-facing table. That second issue mattered because the injected value was rendered back inside the WordPress dashboard: an administrator viewing the failed-login log was the delivery mechanism.

Why this one was different: WordPress forced the update

The WordPress security team has been able to push a plugin update to sites automatically since WordPress 3.7 introduced the capability in 2013. It is used sparingly and deliberately, because silently changing code on someone else’s server is a serious step. This was one of the occasions where it was judged proportionate.

On 15 and 16 October 2020 the Loginizer plugin recorded roughly 1.13 million downloads — the signature of a directory-wide forced update rather than ordinary adoption. The plugin author subsequently reported that around 89% of installations had been moved to the patched version. For a plugin vulnerability, that is an unusually high patch rate, and it happened in about two days rather than the months such fixes normally take to propagate.

The forced update is the most instructive part of this disclosure. It tells you what the WordPress security team considered the worst case: an unauthenticated, automatable database read on a plugin whose vulnerable code was enabled by default on a million-plus sites.

Impact

An attacker who could reach the login page of an affected site could, without credentials:

  • Extract usernames, email addresses and password hashes from the WordPress users table.
  • Read arbitrary other database contents accessible to the WordPress database user.
  • Store crafted values that were later rendered in the administrative interface.

Password hashes are not passwords, but they are a starting point — particularly on sites where an administrator has reused a weak password elsewhere. Combined with the harvested email addresses, this is enough material for a credible follow-up campaign against the site’s own operators.

What to do now

If you still run Loginizer, you are almost certainly already patched — the forced update handled most of the ecosystem in October 2020, and many versions have shipped since. Confirm rather than assume:

  • Check the installed version under Plugins. Anything below 1.6.4 is affected and should be updated immediately.
  • If a site has been offline, restored from an old backup, or had automatic updates disabled since 2020, treat it as unpatched until you have looked.
  • On any site that ran a vulnerable version while exposed, rotate administrator passwords and review the user list for accounts you do not recognise.
  • Review the current Loginizer security profile for its present maintenance status and any open issues.

More broadly: this vulnerability is a good argument for checking what a security plugin actually does to your database. Software installed to protect a site is still software, and brute-force loggers write attacker-controlled strings by design.

Related reading

Research and original disclosure by Slavco Mihajloski (mslavco), published on WPDeeply.