CVE-2026-2144

Magic Login Mail or QR Code <= 2.05 - Unauthenticated Privilege Escalation via Insecure QR Code File Storage

highImproper Privilege Management
8.1
CVSS Score
8.1
CVSS Score
high
Severity
2.06
Patched in
3d
Time to patch

Description

The Magic Login Mail or QR Code plugin for WordPress is vulnerable to Privilege Escalation in all versions up to, and including, 2.05. This is due to the plugin storing the magic login QR code image with a predictable, static filename (QR_Code.png) in the publicly accessible WordPress uploads directory during the email sending process. The file is only deleted after wp_mail() completes, creating an exploitable race condition window. This makes it possible for unauthenticated attackers to trigger a login link request for any user, including administrators, and then exploit the race condition between QR code file creation and deletion to obtain the login URL encoded in the QR code, thereby gaining unauthorized access to the targeted user's account.

CVSS Vector Breakdown

CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H
Attack Vector
Network
Attack Complexity
High
Privileges Required
None
User Interaction
None
Scope
Unchanged
High
Confidentiality
High
Integrity
High
Availability

Technical Details

Affected versions<=2.05
PublishedFebruary 13, 2026
Last updatedFebruary 16, 2026
Affected pluginmagic-login-mail
Research Plan
Unverified

This research plan focuses on exploiting a race condition in the **Magic Login Mail or QR Code** plugin (<= 2.05). The vulnerability stems from the use of a static filename for a sensitive QR code image in a public directory. --- ### 1. Vulnerability Summary The "Magic Login Mail or QR Code" plugi…

Show full research plan

This research plan focuses on exploiting a race condition in the Magic Login Mail or QR Code plugin (<= 2.05). The vulnerability stems from the use of a static filename for a sensitive QR code image in a public directory.


1. Vulnerability Summary

The "Magic Login Mail or QR Code" plugin allows users to log in via a link sent to their email or by scanning a QR code. When a magic login is requested, the plugin generates a QR code image containing a sensitive login token/URL.

The plugin saves this image to a predictable path: wp-content/uploads/QR_Code.png. Crucially, this file is created just before wp_mail() is called and is deleted immediately after wp_mail() returns. Because wp_mail() (especially when using SMTP or external handlers) is a relatively slow operation, a race condition window exists where an unauthenticated attacker can download the QR code, decode the login URL, and gain access to the targeted account.

2. Attack Vector Analysis

  • Endpoint: The magic login request trigger. This is typically a wp-admin/admin-ajax.php action or a POST request to the login page.
  • Vulnerable Action: magic_login_request or similar (inferred).
  • Target User: Typically an administrator (User ID 1).
  • Payload: The username or email of the target user sent to the request endpoint.
  • Authentication: Unauthenticated.
  • Preconditions:
    1. The "Magic Login" and "QR Code" features must be enabled in plugin settings.
    2. The attacker knows the username or email of an administrator.

3. Code Flow (Inferred)

  1. Entry Point: MagicLogin::trigger_magic_login() (Action: wp_ajax_nopriv_magic_login_request or triggered via login_form).
  2. QR Generation: The plugin calls a QR generation library.
  3. File Write: The plugin calls file_put_contents( $upload_dir['basedir'] . '/QR_Code.png', $qr_data ).
  4. Email Sending: The plugin calls wp_mail(). <-- Race window starts here.
  5. Cleanup: The plugin calls unlink( $upload_dir['basedir'] . '/QR_Code.png' ). <-- Race window ends here.

4. Nonce Acquisition Strategy

Login-related actions often require a nonce if triggered via AJAX.

  1. Locate Nonce: Check the WordPress login page (/wp-login.php) for enqueued scripts from the plugin.
  2. Identify JS Variable: Look for wp_localize_script output.
    • Likely Variable: window.magic_login_vars or window.magic_login_data.
    • Likely Key: nonce or magic_nonce.
  3. Extraction Command:
    // Example extraction via browser_eval
    browser_eval("window.magic_login_vars?.nonce")
    
  4. Creation of Test Page: If the login page doesn't expose it, the plugin might use a shortcode like [magic_login_form].
    wp post create --post_type=page --post_status=publish --post_title="Login" --post_content='[magic_login_form]'
    

5. Exploitation Strategy

Step 1: Preparation
Identify the target admin username (defaulting to 'admin'). Determine the upload directory path (usually /wp-content/uploads/).

Step 2: Trigger and Capture (The Race)
Since wp_mail is the bottleneck, we need to send the trigger and immediately poll the static file location.

Request A (Trigger):

  • Method: POST
  • URL: http://vulnerable-site.tld/wp-admin/admin-ajax.php
  • Body: action=magic_login_request&user_login=admin&nonce=[NONCE_OBTAINED_PREVIOUSLY]
  • Content-Type: application/x-www-form-urlencoded

Request B (Capture - Concurrent):

  • Method: GET
  • URL: http://vulnerable-site.tld/wp-content/uploads/QR_Code.png

Execution Logic:

  1. Start a loop or multiple concurrent requests for Request B.
  2. Fire Request A.
  3. As soon as Request B returns 200 OK, download the image.

Step 3: Decoding
The captured QR_Code.png will contain a URL like:
http://vulnerable-site.tld/?magic-login=TOKEN_VALUE

Step 4: Escalation
Navigate to the extracted URL in the browser to be automatically logged in as the administrator.

6. Test Data Setup

  1. Plugin Config: Ensure "QR Code" is enabled in Magic Login settings.
  2. User: Ensure an admin user exists (e.g., username admin).
  3. Mail Lag (Optional): To make the race condition easier to hit in a test environment, one could use a plugin like "WP Mail Logging" or configure a slow SMTP server, which increases the execution time of wp_mail().

7. Expected Results

  • Trigger Response: A success message indicating a magic link was sent.
  • Capture Response: A binary image file (image/png) found at /wp-content/uploads/QR_Code.png.
  • Final Result: The decoded URL redirects the attacker to the WordPress Dashboard with administrator privileges.

8. Verification Steps

  1. File Existence Check: Use ls -l /var/www/html/wp-content/uploads/QR_Code.png during the request to prove the file is written.
  2. Session Verification: After navigating to the magic link, use wp-cli to verify the session:
    wp user get admin --field=user_login
    # Check if the browser session cookies correspond to the admin user
    

9. Alternative Approaches

  • Inferred Action Names: If magic_login_request is incorrect, grep the plugin source for wp_ajax_nopriv to find the correct trigger:
    grep -r "wp_ajax_nopriv" /var/www/html/wp-content/plugins/magic-login-mail/
    
  • Filename Variations: If QR_Code.png is not found, check if the plugin uses the user ID in the name (e.g., QR_Code_1.png - inferred). Grep the source for .png:
    grep -r ".png" /var/www/html/wp-content/plugins/magic-login-mail/ | grep "uploads"
    
  • Direct Pathing: If /wp-content/uploads/ is protected, check for wp-content/uploads/magic-login/QR_Code.png (inferred).
Research Findings
Static analysis — not yet PoC-verified

Summary

The Magic Login Mail or QR Code plugin (<= 2.05) stores sensitive login QR codes in a publicly accessible directory using a static, predictable filename (QR_Code.png). Because these files exist for the duration of the wp_mail() function call, an unauthenticated attacker can exploit a race condition to download the QR code, decode the magic login URL, and gain administrative access.

Vulnerable Code

// magic-login-mail/includes/class-magic-login-request.php (Inferred location)

$upload_dir = wp_upload_dir();
$file_path = $upload_dir['basedir'] . '/QR_Code.png';

// Generates the QR code and writes it to a predictable static path
$renderer->render($qr_code)->save($file_path);

$attachments = array($file_path);
$sent = wp_mail($user_email, $subject, $message, $headers, $attachments);

// The file remains accessible until wp_mail completes, which can be delayed by SMTP or external APIs
if (file_exists($file_path)) {
    unlink($file_path);
}

Security Fix

--- a/includes/class-magic-login-request.php
+++ b/includes/class-magic-login-request.php
@@ -1,6 +1,7 @@
 $upload_dir = wp_upload_dir();
-$file_path = $upload_dir['basedir'] . '/QR_Code.png';
+$random_suffix = wp_generate_password(20, false);
+$file_path = $upload_dir['basedir'] . '/QR_Code_' . $random_suffix . '.png';
 
 $renderer->render($qr_code)->save($file_path);
 
 $attachments = array($file_path);

Exploit Outline

The exploit targets the predictable file path and the delay inherent in the WordPress mail-sending process. An attacker first determines the target username (e.g., 'admin') and begins a high-frequency polling loop of GET requests to the known path 'wp-content/uploads/QR_Code.png'. While polling, the attacker triggers the plugin's magic login request for the target user (via wp-admin/admin-ajax.php or the login form). If the attacker's GET request occurs after the file is written but before wp_mail() finishes and unlinks the file, the attacker successfully downloads the image. The image is then decoded using any QR reader to extract the unique login token URL, which provides immediate authenticated access to the target account.

Check if your site is affected.

Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.