WP Import – Ultimate CSV XML Importer for WordPress <= 7.35 - Authenticated (Contributor+) Server-Side Request Forgery via Bitly Shortlink Bypass
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:NTechnical Details
<=7.35What Changed in the Fix
Changes introduced in v7.36
Source Code
WordPress.org SVNThis 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 inUrlUpload::__construct) - Vulnerable Parameter:
url - Required Nonce:
securekey(Validated viacheck_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
- Entry Point: The AJAX action
wp_ajax_get_csv_urltriggersUrlUpload::upload_function(). - Nonce Check:
check_ajax_referer('smack-ultimate-csv-importer', 'securekey')verifies thesecurekeyparameter from$_POST. - 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. - Bypass Logic:
// uploadModules/UrlUpload.php:92 if(strstr($file_url, 'https://bit.ly/')){ $file_url = $this->unshorten_bitly_url($file_url); } - Sink: The unvalidated
$file_urlis then passed to functions likeget_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.
- Preparation: Log in as a Contributor.
- 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).
- Variable Extraction: The nonce is stored in a JavaScript object named
smack_nonce_object. - Agent Instruction:
- Use
browser_navigatetohttp://localhost:8080/wp-admin/. - Use
browser_evalto execute:window.smack_nonce_object?.nonce. - Verify if the key exists. If not, the
singleimportsetting might be false. You may need to enable it viawp option update sm_uci_pro_settings '{"singleimport":"true"}' --format=json.
- Use
5. Exploitation Strategy
The goal is to force the server to request an internal resource.
- Setup Target: Use an external URL shortener (like Bitly) to create a link pointing to
http://127.0.0.1:80/wp-admin/orhttp://169.254.169.254/latest/meta-data/.
Note: In a controlled lab, you can spoof Bitly by adding127.0.0.1 bit.lyto/etc/hostsand running a simple redirect server. - Craft Request:
- URL:
http://localhost:8080/wp-admin/admin-ajax.php - Method:
POST - Body (URL-encoded):
action=get_csv_urlurl=https://bit.ly/[SHORT_PATH]securekey=[EXTRACTED_NONCE]
- URL:
- Execute: Use
http_requestwith the above parameters. - Observe Response: A successful SSRF will often return a response indicating a file type mismatch or "Download Failed," but the
get_headerscall or the subsequent cURL attempt will have already reached the internal target.
6. Test Data Setup
- User Creation:
wp user create attacker attacker@example.com --role=contributor --user_pass=password - Plugin Activation: Ensure
wp-ultimate-csv-importeris active. - 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 theContent-Typedetection logic (Lines 143-167), potentially leaking internal server information in the process.
8. Verification Steps
- Monitor Outbound Traffic: Use a tool like
tcpdumpor an internal listener to confirm the WordPress server initiated a connection to the IP address resolved from the Bitly link. - 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.phpthat sends aLocation: http://127.0.0.1:80/header. Point theurlparameter tohttp://[MOCK_IP]/redirect.php?ignore=https://bit.ly/to satisfy thestrstrcheck (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.
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
@@ -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.