PDF for WPForms <= 6.3.0 - Missing Authorization
Description
The PDF for WPForms + Drag and Drop Template Builder plugin for WordPress is vulnerable to unauthorized access due to a missing capability check on a function in all versions up to, and including, 6.3.0. This makes it possible for authenticated attackers, with Subscriber-level access and above, to perform an unauthorized action.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:NTechnical Details
<=6.3.0Source Code
WordPress.org SVNThis research plan targets **CVE-2025-68534**, a missing authorization vulnerability in the **PDF for WPForms** plugin (<= 6.3.0). The vulnerability allows Subscriber-level users to perform unauthorized actions, likely modifying plugin settings or PDF templates, because the plugin fails to perform a…
Show full research plan
This research plan targets CVE-2025-68534, a missing authorization vulnerability in the PDF for WPForms plugin (<= 6.3.0). The vulnerability allows Subscriber-level users to perform unauthorized actions, likely modifying plugin settings or PDF templates, because the plugin fails to perform a current_user_can() check in an AJAX handler.
1. Vulnerability Summary
- Vulnerability: Missing Authorization (Insecure Direct Object Reference / Privilege Escalation to Action).
- Location: An AJAX handler function registered via
wp_ajax_in the plugin's admin or AJAX controller. - Cause: The function verifies a nonce (CSRF protection) but lacks a capability check (e.g.,
current_user_can('manage_options')), allowing any authenticated user (including Subscribers) to trigger the logic. - Impact: Unauthorized modification of plugin configuration, PDF templates, or font settings.
2. Attack Vector Analysis
- Endpoint:
wp-admin/admin-ajax.php - Action (Inferred): Likely related to the Drag and Drop builder or settings. Potential candidates:
pdf_for_wpforms_save_settings,pdf_wpforms_update_template, orpdf_wpforms_save_font. - Authentication: Authenticated (Subscriber+).
- Payload: Sent via
POSTtoadmin-ajax.php. - Required Parameters:
action: The vulnerable AJAX action string._wpnonceor a custom nonce parameter (e.g.,security,nonce).- Data parameters (e.g.,
settings[],template_id,css).
3. Code Flow (Trace Path)
- Entry Point: The plugin registers an AJAX action using
add_action( 'wp_ajax_<action_name>', ... ). - Hook: WordPress core triggers the callback function when a logged-in user hits
admin-ajax.phpwith the matching action. - Verification (Insufficient): The callback function likely calls
check_ajax_referer( 'action_string', 'parameter_name' )orwp_verify_nonce(). - Authorization (Missing): The function proceeds to update database options (e.g.,
update_option()) or post meta without checkingcurrent_user_can(). - Sink: Modification of plugin state or templates.
4. Nonce Acquisition Strategy
To exploit this as a Subscriber, we must find where the plugin exposes the nonce to authenticated users.
- Identify Script Localization: The nonce is likely localized in the WordPress admin dashboard.
- Test Page: Even if the plugin doesn't have a dedicated Subscriber menu, it may enqueue its admin scripts globally or on the
profile.phppage. - Detection Script:
- Login as a Subscriber.
- Navigate to
wp-admin/profile.php. - Use
browser_evalto search for localized objects. - Inferred JS Object:
pdf_wpforms_adminorwpforms_pdf_builder. - Inferred Key:
nonceorsecurity.
Action Plan for Agent:
// Example browser_eval to find the nonce
JSON.stringify(Object.keys(window).filter(key => key.toLowerCase().includes('pdf') || key.toLowerCase().includes('wpforms')));
// Then drill down:
window.pdf_wpforms_admin?.nonce;
5. Exploitation Strategy
Step 1: Discover the Action and Nonce Key
Search the plugin source for AJAX registrations:
grep -rn "wp_ajax_" wp-content/plugins/pdf-for-wpforms/
Identify the callback function and check for current_user_can.
Step 2: Identify Nonce Action and Variable
Locate wp_create_nonce or check_ajax_referer in the identified function. Note the action string used (e.g., pdf-for-wpforms-admin). Search for where this nonce is passed to the frontend:
grep -rn "wp_localize_script" wp-content/plugins/pdf-for-wpforms/
Step 3: Extract Nonce as Subscriber
- Create a Subscriber user via WP-CLI.
- Use the
browser_navigatetool to log in as the Subscriber. - Navigate to
wp-admin/index.php. - Execute
browser_evalto extract the nonce from the localized JS variable identified in Step 2.
Step 4: Execute Unauthorized Action
Using the http_request tool, send a POST request to admin-ajax.php.
Example Payload (Inferred):
- URL:
http://localhost:8080/wp-admin/admin-ajax.php - Method:
POST - Headers:
Content-Type: application/x-www-form-urlencoded,Cookie: [Subscriber Cookies] - Body:
action=pdf_for_wpforms_save_settings&security=[EXTRACTED_NONCE]&pdf_settings[test_mode]=1&pdf_settings[unauthorized_access]=pwned
6. Test Data Setup
- Install Plugin: Ensure
pdf-for-wpformsversion 6.3.0 is installed. - Create User:
wp user create attacker attacker@example.com --role=subscriber --user_pass=password - Configure Plugin: Ensure the plugin is active and has at least one default setting or template existing.
7. Expected Results
- HTTP Response: A
200 OKor{"success":true}response. - System State: The plugin settings or a specific PDF template is modified in the
wp_optionstable orwp_postmetatable.
8. Verification Steps
After the http_request, verify the change via WP-CLI:
# If it modifies options:
wp option get pdf_for_wpforms_settings
# If it modifies template metadata:
wp post list --post_type=pdf_template --fields=ID,post_title
wp post meta list [ID]
9. Alternative Approaches
- Action Discrepancy: If
wp_ajax_is not found, check foradmin_inithooks that handle$_POSTor$_GETdirectly without capability checks. - Generic Nonce: Check if the plugin uses
wp_create_nonce( -1 )or a very common action string likewpforms-admin. - REST API: Check if the plugin registers any REST routes via
register_rest_routewithout apermission_callback.grep -rn "register_rest_route" wp-content/plugins/pdf-for-wpforms/
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.