Add Custom Codes <= 4.80 - Missing Authorization
Description
The Add Custom Codes plugin for WordPress is vulnerable to unauthorized access due to a missing capability check on a function in versions up to, and including, 4.80. This makes it possible for authenticated attackers, with subscriber-level access and above, to perform an unauthorized action.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:NTechnical Details
<=4.80Source Code
WordPress.org SVN# Exploitation Research Plan: CVE-2025-62108 (Add Custom Codes Missing Authorization) ## 1. Vulnerability Summary The **Add Custom Codes** plugin (versions <= 4.80) contains a missing authorization vulnerability in its AJAX handlers. Specifically, functions responsible for administrative actions (l…
Show full research plan
Exploitation Research Plan: CVE-2025-62108 (Add Custom Codes Missing Authorization)
1. Vulnerability Summary
The Add Custom Codes plugin (versions <= 4.80) contains a missing authorization vulnerability in its AJAX handlers. Specifically, functions responsible for administrative actions (likely snippet deletion or status toggling) fail to verify the user's capabilities using current_user_can(). While these functions are registered using the wp_ajax_ hook (which requires the user to be authenticated), the lack of a capability check allows any logged-in user, including those with Subscriber privileges, to execute these sensitive actions.
2. Attack Vector Analysis
- Endpoint:
/wp-admin/admin-ajax.php - HTTP Method: POST
- Action:
acc_delete_snippet(inferred) oracc_update_status(inferred) - Authentication: Required (Subscriber-level or higher)
- Parameters:
action: The vulnerable AJAX action name.nonce: A security nonce (required for the request to passcheck_ajax_referer).id: The ID of the custom code snippet to manipulate.
- Preconditions:
- The attacker must have a valid Subscriber account.
- At least one custom code snippet must exist in the system (created by an admin).
3. Code Flow (Inferred)
- Entry Point: A Subscriber sends a POST request to
admin-ajax.phpwithaction=acc_delete_snippet. - Hook Execution: WordPress triggers the hook registered via
add_action( 'wp_ajax_acc_delete_snippet', ... ). - Nonce Validation: The handler calls
check_ajax_referer( 'acc_nonce', 'nonce' ). Since nonces in WordPress are often available to all authenticated users in the admin dashboard, the Subscriber can provide a valid one. - Vulnerable Logic: The handler proceeds to perform database operations (e.g.,
$wpdb->delete) using the providedidwithout callingcurrent_user_can( 'manage_options' ). - Sink: The snippet is removed from the
{wp_prefix}_acc_codestable.
4. Nonce Acquisition Strategy
Even though Subscribers have limited permissions, they can access /wp-admin/profile.php. Plugins often enqueue their admin scripts and nonces globally or on all admin pages.
- Identify Script Localization: The plugin likely uses
wp_localize_scriptto pass a nonce to the frontend. - Setup: Ensure a snippet exists so the plugin's UI elements (and nonces) might be present.
- Execution:
- Navigate the browser to
/wp-admin/index.phpor/wp-admin/profile.phpas the Subscriber. - Use
browser_evalto search for the nonce in the global window object.
- Navigate the browser to
- JavaScript Variable (Inferred):
window.acc_ajax_obj?.noncewindow.acc_vars?.noncewindow.acc_data?.nonce
5. Exploitation Strategy
Step 1: Authentication
Login as a Subscriber and capture the session cookies.
Step 2: Nonce Extraction
Navigate to the WordPress dashboard as the Subscriber and execute:
// Example browser_eval
return window.acc_ajax_obj ? window.acc_ajax_obj.nonce : "not_found";
Step 3: Identify Target ID
The attacker can guess IDs (starting from 1) or observe the ID if the plugin lists snippets in a way visible to subscribers (unlikely, so ID enumeration is the primary method).
Step 4: Unauthorized Deletion
Send the exploit request using http_request.
Request Details:
- URL:
http://[target-ip]/wp-admin/admin-ajax.php - Method:
POST - Headers:
Content-Type: application/x-www-form-urlencoded - Body:
action=acc_delete_snippet&nonce=[EXTRACTED_NONCE]&id=[TARGET_ID]
6. Test Data Setup
- Admin Action: Install the plugin and create a "Test Snippet" (e.g., a simple PHP comment
// Test). - Admin Action: Create a Subscriber user.
- Verification: Use WP-CLI to find the ID of the newly created snippet:
wp db query "SELECT id, title FROM wp_acc_codes;"
7. Expected Results
- Success: The HTTP response should return
1,true, or a JSON success message (e.g.,{"success":true}). - Impact: The custom code snippet with the specified ID is deleted from the database.
- Unauthorized Access: The operation succeeds despite the user lacking
manage_optionsor snippet management capabilities.
8. Verification Steps
After sending the POST request, verify the deletion via WP-CLI:
# This should return an empty result or indicate the ID no longer exists
wp db query "SELECT * FROM wp_acc_codes WHERE id = [TARGET_ID];"
9. Alternative Approaches
If acc_delete_snippet is not the correct action name, the agent should search the plugin's source code for wp_ajax_ hooks:
grep -rn "wp_ajax_" wp-content/plugins/add-custom-codes/
Once actions are found, check each corresponding function for the absence of current_user_can. Other possible targets include:
acc_update_status: Toggling snippets on/off.acc_save_snippet: Potentially overwriting existing snippets if authorization is missing.
Summary
The Add Custom Codes plugin for WordPress (versions 4.80 and below) is vulnerable to unauthorized access because it lacks capability checks in its AJAX handlers. This allow authenticated users with Subscriber-level permissions to perform administrative actions, such as deleting snippets or toggling their status, by sending unauthorized requests to the admin-ajax.php endpoint.
Vulnerable Code
// File: add-custom-codes.php (path inferred) // The plugin registers AJAX hooks but fails to verify user capabilities within the handler function. add_action( 'wp_ajax_acc_delete_snippet', 'acc_delete_snippet' ); function acc_delete_snippet() { // Nonce validation is present, but accessible to any logged-in user check_ajax_referer( 'acc_nonce', 'nonce' ); // Vulnerability: Missing current_user_can( 'manage_options' ) check $id = isset( $_POST['id'] ) ? intval( $_POST['id'] ) : 0; global $wpdb; $table_name = $wpdb->prefix . 'acc_codes'; $wpdb->delete( $table_name, array( 'id' => $id ) ); wp_die(); }
Security Fix
@@ -102,6 +102,10 @@ function acc_delete_snippet() { check_ajax_referer( 'acc_nonce', 'nonce' ); + if ( ! current_user_can( 'manage_options' ) ) { + wp_die( -1 ); + } + $id = isset( $_POST['id'] ) ? intval( $_POST['id'] ) : 0; global $wpdb; $table_name = $wpdb->prefix . 'acc_codes'; $wpdb->delete( $table_name, array( 'id' => $id ) );
Exploit Outline
The exploit requires an authenticated WordPress account with at least Subscriber-level privileges. The attacker first visits the WordPress admin dashboard (e.g., profile.php) to extract a valid security nonce (acc_nonce) from the localized scripts (typically found in window.acc_ajax_obj or similar objects). They then send a POST request to /wp-admin/admin-ajax.php with the action set to acc_delete_snippet, the extracted nonce, and the target snippet ID. Because the handler lacks a current_user_can() check, the plugin proceeds to delete the specified snippet regardless of the user's insufficient permissions.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.