SEO Plugin by Squirrly SEO <= 12.4.16 - Missing Authorization to Authenticated (Contributor+) Privileged Cloud API Operations
Description
The SEO Plugin by Squirrly SEO plugin for WordPress is vulnerable to authorization bypass in all versions up to, and including, 12.4.16. This is due to the plugin not properly verifying that a user is authorized to perform an action. This makes it possible for authenticated attackers, with contributor-level access and above, to invoke privileged state-changing Squirrly cloud API operations, such as revoking the site's Google Search Console and Google Analytics integrations via `api/gsc/revoke` and `api/ga/revoke`, that are otherwise restricted to administrator-level users holding the `sq_manage_settings` capability.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:NTechnical Details
What Changed in the Fix
Changes introduced in v12.4.17
Source Code
WordPress.org SVN# Exploitation Research Plan: CVE-2026-7624 (Squirrly SEO) ## 1. Vulnerability Summary The **SEO Plugin by Squirrly SEO** (up to version 12.4.16) is vulnerable to a missing authorization check. While the plugin intends to restrict administrative settings and cloud API integrations to users with the…
Show full research plan
Exploitation Research Plan: CVE-2026-7624 (Squirrly SEO)
1. Vulnerability Summary
The SEO Plugin by Squirrly SEO (up to version 12.4.16) is vulnerable to a missing authorization check. While the plugin intends to restrict administrative settings and cloud API integrations to users with the sq_manage_settings capability (typically Administrators), certain internal handlers fail to validate this capability. This allows authenticated users with Contributor-level permissions and above to trigger state-changing operations on the site's Squirrly Cloud account, specifically revoking Google Search Console (GSC) and Google Analytics (GA) integrations.
2. Attack Vector Analysis
- Target Endpoint:
wp-admin/admin-ajax.php - Vulnerable Action:
sq_ajax_admin(inferred based on plugin architecture) or a related Squirrly AJAX dispatcher. - Payload Parameter:
sm(Squirrly Method) oractionparameter used to specify the API route (e.g.,api/gsc/revoke). - Authentication: Required (Contributor-level or higher).
- Authorization: Missing check for
sq_manage_settingscapability in the AJAX handler. - Nonce Requirement: Likely requires the
sq_noncegenerated for Squirrly admin operations.
3. Code Flow
- Entry Point: The user sends a POST request to
admin-ajax.php. - Hook Registration: The plugin (likely in
SQ_Classes_FrontController) registerswp_ajax_sq_ajax_admin(inferred). - Handler Execution: The handler (likely in a class like
SQ_Classes_RemoteController) receives the request. - The Flaw: The handler checks
is_user_logged_in()or relies on the fact thatwp_ajax_only fires for logged-in users, but it fails to callSQ_Classes_Helpers_Tools::userCan('sq_manage_settings')before processing thesmparameter. - Sink: The plugin invokes the Squirrly Cloud API via
wp_remote_postor a similar wrapper, passing therevokecommand to the remote Squirrly server.
4. Nonce Acquisition Strategy
Squirrly SEO localizes its configuration and nonces for the WordPress dashboard. Even Contributor-level users can access the dashboard (/wp-admin/), allowing them to extract the nonce from the page source.
- Trigger: The nonce is usually localized via
wp_localize_scriptand appears on most Squirrly-related admin pages, or even the main dashboard if the Squirrly "Dashboard" widget is active. - JS Variable:
sq_query(inferred) orsq_config. - Nonce Key:
nonce. - Extraction Steps:
- Login as a Contributor user.
- Navigate to
/wp-admin/index.php. - Use
browser_evalto extract the nonce:browser_eval("window.sq_query?.nonce || window.sq_config?.nonce").
5. Test Data Setup
- Install Plugin: Squirrly SEO version 12.4.16.
- Configure Plugin: As an Administrator, connect the plugin to a Squirrly account and integrate Google Search Console. (Note: For PoC purposes, the API call is the exploit; physical GSC connection may be mocked if the API returns a success/fail response).
- Create Attacker: Create a user with the
contributorrole.
6. Exploitation Strategy
The goal is to trigger the api/gsc/revoke operation using the Contributor's session.
HTTP Request (Playwright/http_request)
- URL:
http://[TARGET]/wp-admin/admin-ajax.php - Method:
POST - Headers:
Content-Type: application/x-www-form-urlencodedCookie: [Contributor Session Cookies]
- Body:
action=sq_ajax_admin &sm=api/gsc/revoke &sq_nonce=[EXTRACTED_NONCE]
(Note: sq_ajax_admin and sm are inferred based on the plugin's historical routing patterns and the "api/gsc/revoke" string mentioned in the vulnerability description.)
7. Expected Results
- HTTP Response: A
200 OKresponse, likely containing a JSON body such as{"success": true}or a message indicating the GSC integration has been revoked. - Side Effect: The site's connection to Google Search Console via Squirrly Cloud is terminated.
8. Verification Steps
- Check Plugin State: Use WP-CLI to check if the GSC token or connection status has changed in the Squirrly options.
wp option get sq_options
- Dashboard Check: Log in as an Administrator and navigate to the Squirrly "GSC" settings page. The integration should now appear as "Disconnected" or "Not Setup."
- Log Analysis: If the plugin logs API calls, check for a
revokecall initiated by the Contributor user ID.
9. Alternative Approaches
If sq_ajax_admin is not the correct action:
- Search the source code for the string
api/gsc/revoketo identify which controller handles this route. - Look for any
add_action('wp_ajax_...', ...)calls in theclassesdirectory that do not have accompanyingcurrent_user_canchecks. - Check the REST API routes via
wp-json/squirrly/v1/...(inferred) if the plugin has transitioned to REST handlers.
Summary
The SEO Plugin by Squirrly SEO fails to perform proper authorization checks in its AJAX administrative handler. This allows authenticated users with Contributor-level permissions or higher to execute privileged Squirrly Cloud API operations, such as revoking Google Search Console or Google Analytics integrations, which are intended only for administrators.
Vulnerable Code
// squirrly.php - Initializing the admin controller which registers the vulnerable AJAX handler if ( SQ_Classes_Helpers_Tools::isBackedAdmin() ) { SQ_Classes_ObjController::getClass( 'SQ_Classes_FrontController' )->runAdmin(); } --- // view/Assistant/Automation.php - Example of the correct check used in views but missing in the underlying AJAX handler if ( ! SQ_Classes_Helpers_Tools::userCan( 'sq_manage_settings' ) ) { echo '<div class="col-12 alert alert-success text-center m-0 p-3">' . esc_html__( "You do not have permission to access this page. You need Squirrly SEO Admin role.", "squirrly-seo" ) . '</div>'; return; }
Security Fix
@@ -25,6 +25,11 @@ public function action() { parent::action(); + // Check if the user has permission to manage Squirrly settings + if (!SQ_Classes_Helpers_Tools::userCan('sq_manage_settings')) { + wp_send_json_error(array('message' => esc_html__("You do not have permission to perform this action.", "squirrly-seo"))); + } + $sm = SQ_Classes_Helpers_Tools::getValue('sm'); switch ($sm) { case 'api/gsc/revoke':
Exploit Outline
1. Authenticate as a user with Contributor-level access. 2. Access the WordPress dashboard (/wp-admin/) and inspect the page source or use the browser console to extract the 'sq_nonce' from the localized 'sq_config' or 'sq_query' JavaScript objects. 3. Send a POST request to the '/wp-admin/admin-ajax.php' endpoint. 4. Include the following parameters in the body: 'action=sq_ajax_admin', 'sm=api/gsc/revoke' (to target the Google Search Console integration), and the extracted 'sq_nonce'. 5. The Squirrly Cloud API will process the request and revoke the site's integration because the plugin's internal dispatcher fails to verify the user's 'sq_manage_settings' capability before forwarding the command.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.