One to one user Chat by WPGuppy <= 1.1.4 - Unauthenticated Information Disclosure via Chat Message Interception
Description
The One to one user Chat by WPGuppy plugin for WordPress is vulnerable to unauthorized access of data due to a missing capability check on the /wp-json/guppylite/v2/channel-authorize rest endpoint in all versions up to, and including, 1.1.4. This makes it possible for unauthenticated attackers to intercept and view private chat messages between users.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:NTechnical Details
<=1.1.4# Exploitation Research Plan - CVE-2025-6792 ## 1. Vulnerability Summary The **One to one user Chat by WPGuppy** plugin (lite version) up to 1.1.4 is vulnerable to **Unauthenticated Information Disclosure**. The vulnerability exists in the registration of the `/wp-json/guppylite/v2/channel-authoriz…
Show full research plan
Exploitation Research Plan - CVE-2025-6792
1. Vulnerability Summary
The One to one user Chat by WPGuppy plugin (lite version) up to 1.1.4 is vulnerable to Unauthenticated Information Disclosure. The vulnerability exists in the registration of the /wp-json/guppylite/v2/channel-authorize REST API endpoint. The plugin fails to implement a proper permission_callback (or implements one that always returns true), allowing unauthenticated users to obtain authorization signatures for private chat channels. These signatures are typically used to subscribe to real-time message streams (like Pusher), enabling an attacker to intercept and view private communications between any two users.
2. Attack Vector Analysis
- Endpoint:
/wp-json/guppylite/v2/channel-authorize - Method:
POST(typically for authorization endpoints in this plugin) - Vulnerable Parameter:
channel_name - Authentication: None required (Unauthenticated).
- Preconditions:
- The plugin must be active.
- The attacker needs to know or guess the IDs of the two users whose chat they wish to intercept.
- A chat channel must follow a predictable naming convention (e.g.,
private-guppy-chat-{ID1}-{ID2}).
3. Code Flow (Inferred)
- Route Registration: The plugin registers REST routes during the
rest_api_inithook. - Missing Check: In the
register_rest_routecall forguppylite/v2/channel-authorize, thepermission_callbackis either missing, set to__return_true, or lacks acurrent_user_can()check that validates if the requesting user is actually one of the participants in thechannel_name. - Authorization Logic:
- The handler receives a
channel_name(e.g.,private-guppy-chat-1-2) and asocket_id. - It uses the plugin's internal secret key/Pusher credentials to generate an HMAC signature for that
socket_idandchannel_name. - It returns this signature to the unauthenticated requester.
- The handler receives a
- Information Disclosure: With this signature, the attacker can connect to the chat websocket and receive all messages sent between User 1 and User 2.
4. Nonce Acquisition Strategy
While REST API endpoints with permission_callback => '__return_true' often don't strictly require a nonce for GET requests, POST requests in WordPress usually require the X-WP-Nonce header to pass the internal CSRF check if any cookies are present. For a purely unauthenticated attack, we should attempt the request without a nonce first. If it fails, we will extract the REST nonce.
- Identify Script Loading: The WPGuppy Lite plugin typically enqueues its assets on pages where the chat UI is present or via a general initialization.
- Create Trigger Page:
wp post create --post_type=page --post_status=publish --post_title="Chat Test" --post_content='[wpguppy_lite_chat]'(Shortcode inferred from common WPGuppy patterns). - Navigate and Extract:
Navigate to the newly created page and look for the localized JS object, usually namedguppylite_varsorwpguppy_lite_vars. - Browser Eval:
browser_eval("window.guppylite_vars?.nonce")orbrowser_eval("window.guppylite_vars?.rest_nonce").
5. Exploitation Strategy
Step 1: Discover Target User IDs
The attacker can identify target user IDs via the standard WordPress REST API:GET /wp-json/wp/v2/users
Step 2: Request Channel Authorization
Assuming we want to intercept messages between User 1 (Admin) and User 2 (Customer). The predictable channel name is private-guppy-chat-1-2.
HTTP Request:
POST /wp-json/guppylite/v2/channel-authorize HTTP/1.1
Host: target.local
Content-Type: application/x-www-form-urlencoded
X-WP-Nonce: [OPTIONAL_NONCE]
channel_name=private-guppy-chat-1-2&socket_id=12345.67890
Step 3: Analyze Response
A successful exploit will return a JSON object containing the authentication string required to join the private channel.
Expected Success Response:
{
"auth": "pusher_key:xxxxxxxxxxxxxxxxxxxx",
"channel_data": "..."
}
Note: If the plugin returns the auth string to an unauthenticated user for a channel they don't belong to, the vulnerability is confirmed.
6. Test Data Setup
- Users: Ensure at least two users exist.
- User 1:
wp user create victim1 victim1@example.com --role=administrator - User 2:
wp user create victim2 victim2@example.com --role=subscriber
- User 1:
- Plugin Config: Install and activate
wpguppy-liteversion 1.1.4. - Interaction: (Optional but helpful) Simulate a chat initialization between the two users so the channel is recognized by the system.
7. Expected Results
- The REST endpoint returns a
200 OKstatus code. - The response body contains an
authtoken or signature for a private channel. - The request succeeds even when no authentication cookies or headers are provided.
8. Verification Steps
- Check REST Response: Verify the JSON response contains the
authkey. - Verify Authentication Level: Confirm the request was made without a
Cookieheader orAuthorizationheader. - Manual Code Audit: Use WP-CLI to inspect the route registration:
wp eval "print_r( $GLOBALS['wp_rest_server']->get_routes()['guppylite/v2'] );"
Check if thechannel-authorizeentry has apermission_callbackand if that callback performs anis_user_logged_in()orcurrent_user_can()check.
9. Alternative Approaches
- Different Channel Patterns: If
private-guppy-chat-1-2fails, try variations likeguppy-chat-1-2,private-chat-1-2, or check the JS source on the frontend to find the exact string format used inwpguppy-lite.js. - GET vs POST: If
POSTis blocked, attempt aGETrequest with the same parameters. - Information Leakage in Params: Check if other endpoints in
guppylite/v2(likeget-messages) are also missing permission checks, which would allow direct history disclosure instead of just real-time interception.
Summary
The One to one user Chat by WPGuppy plugin for WordPress is vulnerable to unauthenticated information disclosure due to a missing permission check on its REST API authorization endpoint. Attackers can obtain valid authentication signatures for private chat channels, allowing them to intercept and monitor real-time private messages between any two users by guessing their user IDs.
Vulnerable Code
// From the REST route registration logic (likely in an initialization class) register_rest_route('guppylite/v2', '/channel-authorize', array( 'methods' => 'POST', 'callback' => array($this, 'channel_authorize'), 'permission_callback' => '__return_true', // Vulnerability: Allows unauthenticated access )); --- // The callback function typically generates an HMAC for Pusher/Websocket usage public function channel_authorize($request) { $channel_name = $request->get_param('channel_name'); $socket_id = $request->get_param('socket_id'); // Logic to generate Pusher auth signature without verifying if the // current user belongs to the requested channel_name $auth = $this->pusher->socket_auth($channel_name, $socket_id); return new WP_REST_Response($auth, 200); }
Security Fix
@@ -10,7 +10,13 @@ register_rest_route('guppylite/v2', '/channel-authorize', array( 'methods' => 'POST', 'callback' => array($this, 'channel_authorize'), - 'permission_callback' => '__return_true', + 'permission_callback' => function ($request) { + if (!is_user_logged_in()) { + return false; + } + // Additional logic should verify if the user is a participant + // in the requested channel_name + return true; + }, ));
Exploit Outline
1. Identify target user IDs by querying the public WordPress users endpoint: /wp-json/wp/v2/users. 2. Determine the target channel name convention, which typically follows a predictable pattern like 'private-guppy-chat-{user_id_1}-{user_id_2}'. 3. Send an unauthenticated POST request to /wp-json/guppylite/v2/channel-authorize containing the guessed channel_name and a dummy socket_id. 4. Capture the JSON response containing the 'auth' signature generated by the plugin's internal secret key. 5. Use the provided authentication signature and the third-party service (e.g., Pusher) to subscribe to the private chat channel. 6. Listen to the stream to intercept and view private messages sent between the two victim users in real-time.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.