Embed Any Document <= 2.7.10 - Authenticated (Contributor+) Stored Cross-Site Scripting
Description
The Embed Any Document – Embed PDF, Word, PowerPoint and Excel Files plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the sanitize_pdf_src function regex bypass in all versions up to, and including, 2.7.10 due to insufficient input sanitization and output escaping. This makes it possible for authenticated attackers, with Contributor-level access and above, to inject arbitrary web scripts in pages that will execute whenever a user accesses an injected page.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:L/I:L/A:NTechnical Details
<=2.7.10Source Code
WordPress.org SVNPatched version not available.
# Exploitation Research Plan: CVE-2025-12885 (Embed Any Document) ## 1. Vulnerability Summary The **Embed Any Document** plugin (<= 2.7.10) contains a Stored Cross-Site Scripting (XSS) vulnerability. The vulnerability exists within the `sanitize_pdf_src` function, which is intended to sanitize the …
Show full research plan
Exploitation Research Plan: CVE-2025-12885 (Embed Any Document)
1. Vulnerability Summary
The Embed Any Document plugin (<= 2.7.10) contains a Stored Cross-Site Scripting (XSS) vulnerability. The vulnerability exists within the sanitize_pdf_src function, which is intended to sanitize the source URL of a document before it is rendered in a viewer. A flaw in the regular expression used for sanitization allows an attacker to bypass the check and inject malicious payloads (such as javascript: URIs or attribute breakouts). Because this input is stored within a WordPress post/page via a shortcode and later rendered without sufficient output escaping, any user (including administrators) viewing the page will execute the injected script.
2. Attack Vector Analysis
- Endpoint:
wp-admin/post.php(Standard post creation/editing). - Vulnerable Component: The
[embeddoc]shortcode. - Vulnerable Parameter: The
urlattribute of the shortcode. - Authentication Level: Contributor or higher (any role capable of creating/editing posts and using shortcodes).
- Preconditions: The plugin must be active. The attacker needs to be able to publish or submit a post for review that contains the malicious shortcode.
3. Code Flow (Inferred)
- Entry Point: A user with Contributor+ privileges creates a post containing a shortcode:
[embeddoc url="MALICIOUS_PAYLOAD"]. - Storage: WordPress saves the shortcode string in the
post_contentcolumn of thewp_poststable. - Rendering: When the post is viewed, the
shortcode_attsand the registered callback forembeddoc(likely inEmbed_Any_Document_Publicclass) are triggered. - Processing: The plugin retrieves the
urlattribute and passes it to thesanitize_pdf_srcfunction (likely located ininc/embed-any-document-public.phpor similar). - Vulnerable Sink: The
sanitize_pdf_srcfunction applies a flawed regex. If the regex is bypassed, the raw or insufficiently sanitized string is returned. - Output: The plugin generates HTML (e.g., an
<embed>,<iframe>, or<object>tag) using the returned value as thesrcattribute. If theurlwasjavascript:alert(1), the resulting HTML might be<embed src="javascript:alert(1)" ...>.
4. Nonce Acquisition Strategy
While the final XSS execution is unauthenticated (anyone viewing the post), the injection requires an authenticated session.
- Post Creation Nonce: To save a post as a Contributor, the agent must obtain the
_wpnoncefrom thewp-admin/post-new.phppage. - Plugin-Specific Nonces: Based on the vulnerability description, the XSS is stored via the standard post editor. No specific plugin AJAX nonce is required for the primary attack vector.
- Browser Tooling: The agent should use
browser_navigateto login andbrowser_evalto extract the_wpnoncefrom the post editor form if performing the injection via raw HTTP requests.
5. Exploitation Strategy
The goal is to bypass sanitize_pdf_src and trigger XSS when an Admin views the post.
Step 1: Authentication
Login as a Contributor user using the browser_navigate tool.
Step 2: Payload Identification (Bypass Attempts)
We will test three common regex bypasses for URL sanitizers:
- Protocol Bypass:
[embeddoc url="javascript:alert(document.domain)//.pdf"](Appending.pdfto satisfy extension checks). - Attribute Breakout:
[embeddoc url='https://example.com/v.pdf" onmouseover="alert(1)"'](Testing if quotes are allowed). - Data URI:
[embeddoc url="data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg=="]
Step 3: Injection Request
Using the http_request tool, send a POST request to wp-admin/post.php to create a new post.
- URL:
http://localhost:8080/wp-admin/post.php - Method:
POST - Headers:
Content-Type: application/x-www-form-urlencoded - Body:
(Note:action=editpost &post_ID=[POST_ID] &_wpnonce=[NONCE] &post_title=XSS+Test &content=[embeddoc+url="javascript:alert(document.domain)//.pdf"+viewer="browser"] &publish=Publishviewer="browser"is often used to force the plugin to use an<embed>tag rather than a Google/Microsoft proxy).
Step 4: Verification
The agent will then navigate to the public URL of the created post as an Administrator and check if the payload is rendered in the HTML source or if the script executes.
6. Test Data Setup
- User: Create a user with the
contributorrole. - Post: Identify a valid
post_IDby navigating towp-admin/post-new.php. - Shortcode: The primary shortcode is
[embeddoc].
7. Expected Results
- When the Admin views the post, the HTML source should contain a tag similar to:
<embed src="javascript:alert(document.domain)//.pdf" ...> - The browser should execute the JavaScript (in the case of the
javascript:URI) or trigger the event handler (in the case of attribute breakout).
8. Verification Steps
- CLI Check: Use
wp post get [POST_ID] --field=post_contentto verify the shortcode was saved correctly. - HTML Inspection: Use
http_request(GET) on the post's permalink and search the response body for the payload string:grep "javascript:alert" response.html
9. Alternative Approaches
If the javascript: protocol is successfully blocked:
- Attribute Breakout: Attempt to use double quotes to close the
srcattribute and add a new event handler:url='https://example.com/test.pdf" onload="alert(1)"' - Case Sensitivity: Test if the regex is case-sensitive:
JaVaScRiPt:alert(1). - Null Byte/Encoded Chars: Test
%00javascript:orjavascript:. - Specific Viewers: The plugin supports multiple viewers (
google,microsoft,browser,box,pdfjs). If one is sanitized correctly, check the others by changing theviewerattribute in the shortcode. Thebrowserandpdfjsviewers are the most likely to be vulnerable to direct URI injection.
Summary
The Embed Any Document plugin for WordPress is vulnerable to Stored Cross-Site Scripting (XSS) in versions up to 2.7.10 due to an insufficient regular expression in the sanitize_pdf_src function. Authenticated attackers with Contributor-level access can bypass this sanitization by crafting a javascript: URI that ends in a .pdf extension within the [embeddoc] shortcode, leading to script execution when users view the post.
Exploit Outline
To exploit this vulnerability, an attacker with Contributor-level access or higher logs into the WordPress dashboard and creates or edits a post. They insert the [embeddoc] shortcode with a malicious url attribute, such as [embeddoc url="javascript:alert(document.domain)//.pdf" viewer="browser"]. The trailing '.pdf' bypasses the plugin's regex-based sanitization in the sanitize_pdf_src function. When a user, such as an administrator, views the published post, the plugin renders the malicious URL in the src attribute of an HTML tag (like <embed>), causing the browser to execute the injected JavaScript.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.