CVE-2026-0688

Webmention <= 5.6.2 - Authenticated (Subscriber+) Server-Side Request Forgery

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

Description

The Webmention plugin for WordPress is vulnerable to Server-Side Request Forgery in all versions up to, and including, 5.6.2 via the 'Tools::read' function. This makes it possible for authenticated attackers, with Subscriber-level access and above, to make web requests to arbitrary locations originating from the web application and 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: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<=5.6.2
PublishedApril 1, 2026
Last updatedApril 2, 2026
Affected pluginwebmention

What Changed in the Fix

Changes introduced in v5.7.0

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan: CVE-2026-0688 (Webmention SSRF) ## 1. Vulnerability Summary The **Webmention** plugin for WordPress is vulnerable to an authenticated Server-Side Request Forgery (SSRF) via the `Tools::read` function. The vulnerability exists because the plugin registers a REST API end…

Show full research plan

Exploitation Research Plan: CVE-2026-0688 (Webmention SSRF)

1. Vulnerability Summary

The Webmention plugin for WordPress is vulnerable to an authenticated Server-Side Request Forgery (SSRF) via the Tools::read function. The vulnerability exists because the plugin registers a REST API endpoint that accepts a user-provided source URL and fetches it without sufficient validation or restriction to external addresses. This allows a user with at least Subscriber permissions (the read capability) to cause the server to make requests to internal services or arbitrary external locations.

2. Attack Vector Analysis

  • REST API Endpoint: wp-json/webmention/1.0/parse
  • HTTP Method: GET (as defined by WP_REST_Server::READABLE in Tools::register_routes)
  • Vulnerable Parameter: source
  • Required Parameter: target (must be a valid URL, but is not the primary SSRF target)
  • Authentication: Required (Subscriber-level access).
  • Capability Check: current_user_can( 'read' ) in the permission_callback.
  • Encryption/Nonce: Requires a standard WordPress REST API nonce (_wpnonce) for authenticated requests.

3. Code Flow

  1. Route Registration: In includes/class-tools.php, the Tools::register_routes function registers the /parse route. It sets the permission_callback to current_user_can( 'read' ), which is granted to all logged-in users, including Subscribers.
  2. Request Handling: When a GET request is made to /wp-json/webmention/1.0/parse, the Tools::read function is executed.
  3. Parameter Extraction: Tools::read extracts the source and target parameters from the $request object.
  4. The Sink: The code calls Request::get( $source, false ).
    • Based on the plugin architecture, Webmention\Request::get performs a remote HTTP request (likely using wp_remote_get) to the provided $source URL.
  5. Processing (Post-Request): The response is then passed to a Handler which attempts to parse it (e.g., using Microformats or the WordPress API handler). Even if parsing fails, the initial request to the internal/arbitrary IP has already been made.

4. Nonce Acquisition Strategy

The REST API requires a wp_rest nonce for authenticated sessions.

  1. Setup: Create a Subscriber user and log in.
  2. Page Navigation: Navigate to any standard WordPress page (like the Dashboard or Homepage).
  3. Extraction: Use browser_eval to extract the REST nonce from the wpApiSettings object, which is standard in WordPress for localized REST data.
    • JS Expression: window.wpApiSettings?.nonce
  4. Alternative: If wpApiSettings is not available, the agent can check the page source for _wpnonce in scripts or the X-WP-Nonce header in link tags.

5. Exploitation Strategy

The goal is to demonstrate that the server can be forced to reach an internal resource (e.g., http://127.0.0.1 or a metadata service).

Step-by-Step Plan:

  1. User Creation: Create a user with the subscriber role using WP-CLI.
  2. Authentication: Log in as the Subscriber.
  3. Nonce Retrieval: Navigate to /wp-admin/ and execute browser_eval("window.wpApiSettings.nonce") to get the REST nonce.
  4. Request Injection:
    • Target: http://localhost:8080/wp-json/webmention/1.0/parse
    • Method: GET
    • Query Parameters:
      • source: http://127.0.0.1:80 (or another internal IP/port)
      • target: https://example.com (required for validation but secondary)
      • _wpnonce: [EXTRACTED_NONCE]
  5. Request Execution: Use the http_request tool to perform the GET request.

Expected Payload (HTTP Request):

GET /wp-json/webmention/1.0/parse?source=http://127.0.0.1:80&target=https://example.com&_wpnonce=a1b2c3d4e5 HTTP/1.1
Host: localhost:8080
Cookie: [SUBSCRIBER_COOKIES]

6. Test Data Setup

  1. Target Internal Content: Ensure there is something to "hit" internally. The easiest target is the WordPress instance itself on 127.0.0.1:80.
  2. User:
    wp user create attacker attacker@example.com --role=subscriber --user_pass=password123
    
  3. Plugin State: Ensure the Webmention plugin is active.
    wp plugin activate webmention
    

7. Expected Results

  • Success Condition: The server attempts to fetch the source URL. If targeting a running service (like the local WP instance), the REST API might return a structured response from the parsing handler (e.g., in MF2 or WP mode).
  • JSON Response: A successful (though perhaps empty) JSON object containing parsed fields.
  • Error Response: If the internal service returns something non-HTML, the Handler might return a WP_Error with no_api_link (from class-wp.php), but the Request::get call will have already completed the SSRF.

8. Verification Steps

  1. Log Observation: If possible, check the access logs of the service at the source IP to see a request originating from the WordPress server's IP.
  2. Response Analysis:
    • If source is an internal WordPress site, look for responses that include details from that site's REST API, indicating the WP handler (includes/handler/class-wp.php) successfully navigated from the initial URL to the site's API root.
  3. Blind Verification: If no output is returned, use an OOB (Out-Of-Band) interaction service (like Webhook.site) as the source and verify the hit.

9. Alternative Approaches

  • Aggregated Mode: Set mode=aggregated in the query string to trigger the parse_aggregated path in Tools::read.
  • Targeting Metadata: On cloud environments, target http://169.254.169.254/latest/meta-data/ to demonstrate high-impact data exposure.
  • Protocol Smuggling: Attempt gopher:// or file:// if the underlying Request::get wrapper doesn't strictly enforce http(s). (Though esc_url_raw usually limits this).

Check if your site is affected.

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