CVE-2026-3892

Motors – Car Dealer, Classifieds & Listing <= 1.4.107 - Authenticated (Subscriber+) Arbitrary File Deletion via 'stm_dealer_logo_path' Parameter

highExternal Control of File Name or Path
8.1
CVSS Score
8.1
CVSS Score
high
Severity
1.4.108
Patched in
1d
Time to patch

Description

The Motors – Car Dealership & Classified Listings Plugin plugin for WordPress is vulnerable to arbitrary file deletion in all versions up to, and including, 1.4.107. This is due to insufficient file path validation in the become-dealer logo upload flow. The plugin allows any authenticated user to set an arbitrary filesystem path via the profile update handler. This makes it possible for authenticated attackers, with subscriber level access and above, to delete arbitrary files on the server.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=1.4.107
PublishedMay 13, 2026
Last updatedMay 14, 2026

What Changed in the Fix

Changes introduced in v1.4.108

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

This research plan outlines the steps required to demonstrate the arbitrary file deletion vulnerability in the **Motors – Car Dealership & Classified Listings** plugin (CVE-2026-3892). ### 1. Vulnerability Summary The Motors plugin fails to validate the `stm_dealer_logo_path` parameter when updatin…

Show full research plan

This research plan outlines the steps required to demonstrate the arbitrary file deletion vulnerability in the Motors – Car Dealership & Classified Listings plugin (CVE-2026-3892).

1. Vulnerability Summary

The Motors plugin fails to validate the stm_dealer_logo_path parameter when updating a user's profile meta. An authenticated attacker with Subscriber-level privileges can set this meta field to an arbitrary filesystem path (e.g., wp-config.php). When the attacker subsequently triggers a logo update or deletion via the "Become Dealer" flow, the plugin's logic attempts to "clean up" the previous logo by calling unlink() on the path stored in the stm_dealer_logo_path meta, resulting in the deletion of the target file.

2. Attack Vector Analysis

  • Vulnerable Parameter: stm_dealer_logo_path
  • Injection Point: User Profile Update (wp-admin/profile.php or a frontend equivalent).
  • Trigger Point: The "Become Dealer" submission handler (templates/user/private/become-dealer.php).
  • Authentication Level: Subscriber or higher.
  • Preconditions: The attacker must be able to update their own user meta and then access the dealer registration form.

3. Code Flow

  1. Injection Phase:
    • The plugin registers fields in includes/user-extra.php using hooks like show_user_profile.
    • While the snippet shows stm_user_avatar_path, the vulnerability exists in the handling of stm_dealer_logo_path.
    • When a user updates their profile, the plugin (via truncated logic) saves POST parameters starting with stm_ directly to user meta without path validation.
  2. Trigger Phase:
    • The attacker navigates to the "Become Dealer" page (handled by templates/user/private/become-dealer.php).
    • The attacker submits the form with a new stm-avatar file upload.
    • SINK: Inside the file upload handler (referenced around line 141), the plugin retrieves the existing path from the stm_dealer_logo_path user meta.
    • EXECUTION: The plugin calls unlink($old_path) to remove the previous logo before saving the new one. Because $old_path is controlled by the attacker, any file can be deleted.

4. Nonce Acquisition Strategy

This vulnerability involves two steps: updating profile meta and triggering the deletion.

Phase 1: Profile Update (Admin Context)

To update the profile meta via the standard WordPress admin panel:

  1. Action: update-user_{ID}
  2. Nonce Key: _wpnonce
  3. Acquisition:
    • Navigate to wp-admin/profile.php.
    • Use browser_eval to extract the nonce from the form: document.querySelector('#your-profile input[name="_wpnonce"]').value.

Phase 2: Dealer Registration (Frontend Context)

If the deletion is triggered via the frontend "Become Dealer" form:

  1. The template become-dealer.php does not appear to use a dedicated nonce for the file upload (it relies on is_user_logged_in()).
  2. If a nonce is required, it is likely localized as part of the theme/plugin's frontend scripts.

5. Exploitation Strategy

Step 1: Preparation

  • Create a Subscriber user.
  • Create a "canary" file to delete: /var/www/html/wp-content/uploads/canary.php.

Step 2: Path Injection

Inject the target file path into the user's meta.

  • Request Type: POST
  • URL: /wp-admin/profile.php
  • Headers: Content-Type: application/x-www-form-urlencoded
  • Payload:
    _wpnonce=[NONCE]&action=update&user_id=[ID]&stm_dealer_logo_path=/var/www/html/wp-content/uploads/canary.php&email=[USER_EMAIL]...
    
    (Note: Include other required fields like email and nicknames to satisfy WordPress profile update requirements).

Step 3: Trigger Deletion

Submit the "Become Dealer" form with a dummy image to trigger the cleanup logic.

  • Request Type: POST (Multipart Form Data)
  • URL: The URL of the page using the become-dealer.php template (e.g., /become-dealer/ or /account/become-dealer/).
  • Payload:
    • stm_company_name: Test Dealer
    • stm_licence: 12345
    • stm_location: London
    • stm-avatar: (A valid small .png or .jpg file)
    • Other required fields as defined in required_fields (line 74 of become-dealer.php).

6. Test Data Setup

  1. Canary File: echo "<?php // canary ?>" > /var/www/html/wp-content/uploads/canary.php
  2. User: Create a subscriber user attacker.
  3. Page Setup: Ensure a page exists with the "Become Dealer" functionality. This might require setting the page template to become-dealer via WP-CLI:
    wp post create --post_type=page --post_title="Become Dealer" --post_status=publish --post_content="[stm_become_dealer]"
    
    (Note: Verify the exact shortcode or template name in the plugin settings or files).

7. Expected Results

  1. The profile update request (Step 2) succeeds, and the database now stores the path to canary.php in the attacker's user meta.
  2. The "Become Dealer" request (Step 3) processes the file upload.
  3. The plugin identifies the "old" logo path from meta and unlinks it.
  4. The file /var/www/html/wp-content/uploads/canary.php is deleted from the filesystem.

8. Verification Steps

After the HTTP requests, use WP-CLI to verify the deletion:

# Check if the file still exists
ls /var/www/html/wp-content/uploads/canary.php

# Check the user meta to see the new path (optional)
wp user meta get [ID] stm_dealer_logo_path

9. Alternative Approaches

If stm_dealer_logo_path is not directly editable via profile.php, check for an AJAX handler that updates dealer settings:

  • Action: stm_ajax_update_user or stm_ajax_save_settings.
  • If the plugin uses a frontend dashboard, the meta might be updated via a POST to the profile dashboard page with the same parameter name.
  • Target File: If successful with the canary, the same method can target wp-config.php, leading to a site-wide Denial of Service or allowing for a fresh reinstall.
Research Findings
Static analysis — not yet PoC-verified

Summary

The Motors – Car Dealership & Classified Listings plugin for WordPress is vulnerable to authenticated arbitrary file deletion due to insufficient path validation in the 'Become Dealer' logo upload flow. An attacker with subscriber-level access can set a target filesystem path in their user metadata and then trigger a file deletion by performing a logo update, which executes an unvalidated unlink() call on the stored path.

Vulnerable Code

// includes/user-extra.php around line 145
// The plugin exposes raw path fields in the user profile which are saved to user meta.
<input type="text" name="stm_user_avatar_path" id="stm_user_avatar_path"
    value="<?php echo esc_attr( get_the_author_meta( 'stm_user_avatar_path', $user->ID ) ); ?>"
    class="regular-text"/>

---

// templates/user/private/become-dealer.php (logic starting around line 141)
// When a new logo is uploaded via the Become Dealer form, the plugin cleans up the old logo.
$file = $_FILES['stm-avatar'];
if ( is_array( $file ) && ! empty( $file['name'] ) ) {
    // ... logic to handle upload ...
    // The plugin retrieves the 'old' path from user meta (e.g., stm_dealer_logo_path)
    // SINK: The plugin calls unlink($old_path) without verifying it is within an allowed directory.
    unlink($old_path); 
}

Security Fix

--- /home/deploy/wp-safety.org/data/plugin-versions/motors-car-dealership-classified-listings/1.4.107/includes/helpers.php
+++ /home/deploy/wp-safety.org/data/plugin-versions/motors-car-dealership-classified-listings/1.4.108/includes/helpers.php
@@ -1305,6 +1305,33 @@
 	add_filter( 'motors_vl_dealer_logo_placeholder', 'motors_vl_dealer_logo_placeholder' );
 }
 
+if ( ! function_exists( 'stm_mvl_is_path_within_uploads' ) ) {
+	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 );
+	}
+}
+
+if ( ! function_exists( 'stm_mvl_filter_path_within_uploads' ) ) {
+	function stm_mvl_filter_path_within_uploads( $default, $path ) {
+		return stm_mvl_is_path_within_uploads( $path );
+	}
+	add_filter( 'stm_mvl_is_path_within_uploads', 'stm_mvl_filter_path_within_uploads', 10, 2 );
+}
+

Exploit Outline

1. Login as an authenticated user (Subscriber or higher). 2. Inject the target file path into the user's metadata (e.g., 'stm_dealer_logo_path') by submitting a POST request to /wp-admin/profile.php with the 'stm_dealer_logo_path' parameter set to the absolute path of the file you wish to delete (e.g., /var/www/html/wp-config.php). 3. Navigate to the 'Become Dealer' registration page. 4. Submit the 'Become Dealer' form and include an image file upload for the dealer logo ('stm-avatar'). 5. The plugin's submission handler identifies that a 'previous' logo path exists in the user's meta and attempts to delete it using unlink(). Because the path is attacker-controlled, the target file is deleted from the server.

Check if your site is affected.

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