CVE-2025-14627

WP Import – Ultimate CSV XML Importer for WordPress <= 7.35 - Authenticated (Contributor+) Server-Side Request Forgery via Bitly Shortlink Bypass

mediumServer-Side Request Forgery (SSRF)
6.4
CVSS Score
6.4
CVSS Score
medium
Severity
7.36
Patched in
1d
Time to patch

Description

The WP Import – Ultimate CSV XML Importer for WordPress plugin for WordPress is vulnerable to Server-Side Request Forgery in all versions up to, and including, 7.35. This is due to inadequate validation of the resolved URL after following Bitly shortlink redirects in the `upload_function()` method. While the initial URL is validated using `wp_http_validate_url()`, when a Bitly shortlink is detected, the `unshorten_bitly_url()` function follows redirects to the final destination URL without re-validating it. This makes it possible for authenticated attackers with Contributor-level access or higher to make the server perform HTTP requests to arbitrary internal endpoints, including localhost, private IP ranges, and cloud metadata services (e.g., 169.254.169.254), potentially exposing sensitive internal data.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=7.35
PublishedJanuary 1, 2026
Last updatedJanuary 1, 2026

What Changed in the Fix

Changes introduced in v7.36

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

This research plan outlines the steps to exploit a Server-Side Request Forgery (SSRF) vulnerability in the **WP Ultimate CSV Importer** plugin (<= 7.35). ### 1. Vulnerability Summary The vulnerability exists in the `UrlUpload::upload_function()` method located in `uploadModules/UrlUpload.php`. The …

Show full research plan

This research plan outlines the steps to exploit a Server-Side Request Forgery (SSRF) vulnerability in the WP Ultimate CSV Importer plugin (<= 7.35).

1. Vulnerability Summary

The vulnerability exists in the UrlUpload::upload_function() method located in uploadModules/UrlUpload.php. The plugin allows users to provide a URL to import CSV/XML data. While the plugin initially validates the URL using wp_http_validate_url(), it performs a special check for Bitly shortlinks. If a Bitly link is detected, it calls unshorten_bitly_url() to resolve the destination but fails to re-validate the resulting URL. This allows an attacker to bypass SSRF protections by providing a Bitly link that redirects to internal resources (e.g., localhost, 169.254.169.254).

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • AJAX Action: get_csv_url (Registered in UrlUpload::__construct)
  • Vulnerable Parameter: url
  • Required Nonce: securekey (Validated via check_ajax_referer('smack-ultimate-csv-importer', 'securekey'))
  • Authentication: Authenticated, Contributor-level or higher (via wp_ajax_ hook).
  • Precondition: The attacker must obtain a valid Bitly shortlink (or a link containing the string https://bit.ly/) that redirects to a sensitive internal target.

3. Code Flow

  1. Entry Point: The AJAX action wp_ajax_get_csv_url triggers UrlUpload::upload_function().
  2. Nonce Check: check_ajax_referer('smack-ultimate-csv-importer', 'securekey') verifies the securekey parameter from $_POST.
  3. Initial Validation: $file_url = wp_http_validate_url(esc_url_raw($_POST['url'])). A legitimate Bitly link (e.g., https://bit.ly/3xyz) passes this check.
  4. Bypass Logic:
    // uploadModules/UrlUpload.php:92
    if(strstr($file_url, 'https://bit.ly/')){
        $file_url = $this->unshorten_bitly_url($file_url);
    }
    
  5. Sink: The unvalidated $file_url is then passed to functions like get_headers($file_url, 1) (Line 112, 137) or used to initiate a download, causing the server to make a request to the resolved internal destination.

4. Nonce Acquisition Strategy

The nonce is localized in the admin dashboard for the react-js script.

  1. Preparation: Log in as a Contributor.
  2. Access Admin: Navigate to any admin page where the plugin might enqueue its scripts (e.g., the plugin's "Import/Update" page if accessible, or check if it loads on the main dashboard).
  3. Variable Extraction: The nonce is stored in a JavaScript object named smack_nonce_object.
  4. Agent Instruction:
    • Use browser_navigate to http://localhost:8080/wp-admin/.
    • Use browser_eval to execute: window.smack_nonce_object?.nonce.
    • Verify if the key exists. If not, the singleimport setting might be false. You may need to enable it via wp option update sm_uci_pro_settings '{"singleimport":"true"}' --format=json.

5. Exploitation Strategy

The goal is to force the server to request an internal resource.

  1. Setup Target: Use an external URL shortener (like Bitly) to create a link pointing to http://127.0.0.1:80/wp-admin/ or http://169.254.169.254/latest/meta-data/.
    Note: In a controlled lab, you can spoof Bitly by adding 127.0.0.1 bit.ly to /etc/hosts and running a simple redirect server.
  2. Craft Request:
    • URL: http://localhost:8080/wp-admin/admin-ajax.php
    • Method: POST
    • Body (URL-encoded):
      • action=get_csv_url
      • url=https://bit.ly/[SHORT_PATH]
      • securekey=[EXTRACTED_NONCE]
  3. Execute: Use http_request with the above parameters.
  4. Observe Response: A successful SSRF will often return a response indicating a file type mismatch or "Download Failed," but the get_headers call or the subsequent cURL attempt will have already reached the internal target.

6. Test Data Setup

  1. User Creation:
    wp user create attacker attacker@example.com --role=contributor --user_pass=password
  2. Plugin Activation: Ensure wp-ultimate-csv-importer is active.
  3. Setting Toggle:
    wp option update sm_uci_pro_settings '{"singleimport":"true"}' --format=json (to ensure scripts/nonces are enqueued).

7. Expected Results

  • If targeting a valid internal URL (like the WP login page), the plugin may return a JSON response where the error message or the logic reflects the headers of the internal target.
  • For example, if get_headers() succeeds on the internal target, the code continues to line 113:
    $get_file_headers = get_headers($file_url, 1);
  • If the internal URL doesn't look like a supported file (csv, xml, etc.), it may fall into the Content-Type detection logic (Lines 143-167), potentially leaking internal server information in the process.

8. Verification Steps

  1. Monitor Outbound Traffic: Use a tool like tcpdump or an internal listener to confirm the WordPress server initiated a connection to the IP address resolved from the Bitly link.
  2. Check Logs: Examine the plugin's internal logs or wp-content/uploads/smack_uci/ for any evidence of files attempted to be downloaded from the internal IP.

9. Alternative Approaches

  • Mock Bitly: If real Bitly links are blocked in the test environment, create a local PHP script redirect.php that sends a Location: http://127.0.0.1:80/ header. Point the url parameter to http://[MOCK_IP]/redirect.php?ignore=https://bit.ly/ to satisfy the strstr check (the check only looks for the presence of the string, not its position).
  • Metadata Services: Attempt to reach http://169.254.169.254/latest/meta-data/ to demonstrate cloud environment impact.
Research Findings
Static analysis — not yet PoC-verified

Summary

The WP Ultimate CSV Importer plugin is vulnerable to Server-Side Request Forgery (SSRF) via the upload_function in versions up to 7.35. While the plugin initially validates a user-provided URL, it fails to re-validate the final destination after resolving Bitly shortlinks, allowing authenticated attackers to target internal services and cloud metadata endpoints.

Vulnerable Code

// uploadModules/UrlUpload.php:84
    public function upload_function(){
		check_ajax_referer('smack-ultimate-csv-importer', 'securekey');
		$file_url = esc_url_raw($_POST['url']);
		$file_url = wp_http_validate_url($file_url);
		$media_type = '';
        if (isset($_POST['MediaType'])) {
            $media_type = sanitize_key($_POST['MediaType']);
        }
		if(!$file_url){					
		$response['success'] = false;
		$response['message'] = 'Download Failed.URL is not valid.';
		echo wp_json_encode($response); 
		die();
		}
		$response = [];
		global $wpdb;
		$file_table_name = $wpdb->prefix ."smackcsv_file_events";			
			if(strstr($file_url, 'https://bit.ly/')){
				$file_url = $this->unshorten_bitly_url($file_url);
			}

Security Fix

--- uploadModules/UrlUpload.php
+++ uploadModules/UrlUpload.php
@@ -101,4 +101,5 @@
 			if(strstr($file_url, 'https://bit.ly/')){
 				$file_url = $this->unshorten_bitly_url($file_url);
+				$file_url = wp_http_validate_url($file_url);
 			}

Exploit Outline

The exploit requires an authenticated user with Contributor-level access or higher. 1. Log in to the WordPress dashboard and obtain the 'securekey' nonce, typically found in the localized JavaScript object 'smack_nonce_object' on the plugin's import pages. 2. Create a redirect using Bitly (or a similar shortener that provides a URL containing 'https://bit.ly/') that points to an internal resource, such as 'http://169.254.169.254/latest/meta-data/' for AWS environments or 'http://localhost/wp-admin/'. 3. Send an AJAX request to the 'get_csv_url' action with the following parameters: 'action=get_csv_url', 'securekey=[NONCE]', and 'url=[BITLY_LINK]'. 4. The plugin will pass the initial validation (the Bitly domain is public), detect the Bitly string, resolve the redirect, and then use the resulting internal URL in sensitive sinks like get_headers() or cURL without further validation.

Check if your site is affected.

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