CVE-2026-57387

picu – Online Photo Proofing Gallery <= 3.5.1 - Unauthenticated Stored Cross-Site Scripting

highImproper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
7.2
CVSS Score
7.2
CVSS Score
high
Severity
3.6.1
Patched in
7d
Time to patch

Description

The picu – Online Photo Proofing Gallery plugin for WordPress is vulnerable to Stored Cross-Site Scripting in versions up to, and including, 3.5.1 due to insufficient input sanitization and output escaping. This makes it possible for unauthenticated attackers to inject arbitrary web scripts in pages that will execute whenever a user accesses an injected page.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=3.5.1
PublishedJuly 8, 2026
Last updatedJuly 14, 2026
Affected pluginpicu

What Changed in the Fix

Changes introduced in v3.6.1

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan: CVE-2026-57387 (picu – Online Photo Proofing Gallery) ## 1. Vulnerability Summary The **picu – Online Photo Proofing Gallery** plugin (<= 3.5.1) is vulnerable to **Unauthenticated Stored Cross-Site Scripting (XSS)**. The vulnerability exists because the plugin fails to…

Show full research plan

Exploitation Research Plan: CVE-2026-57387 (picu – Online Photo Proofing Gallery)

1. Vulnerability Summary

The picu – Online Photo Proofing Gallery plugin (<= 3.5.1) is vulnerable to Unauthenticated Stored Cross-Site Scripting (XSS). The vulnerability exists because the plugin fails to sanitize or escape user-provided data (specifically the client's name or feedback) collected during the unauthenticated photo approval process. This data is stored in the database and subsequently rendered in the WordPress administrative dashboard (e.g., in the "All Collections" list table or "Collection History" metabox) without proper output escaping.

2. Attack Vector Analysis

  • Entry Point: The unauthenticated frontend "Collection" page (Proofing Gallery).
  • Vulnerable Action: wp_ajax_nopriv_picu_approve_collection (AJAX handler for gallery approval).
  • Payload Parameter: client_name (or potentially client_email / client_comment depending on the exact version implementation).
  • Authentication: None (Unauthenticated).
  • Preconditions:
    1. A picu_collection post must exist.
    2. The collection must be in a state accessible to the public (Status: sent).

3. Code Flow

  1. Source: An unauthenticated user visits a collection URL (e.g., ?picu_collection=[hash]) and submits the "Approve Collection" form.
  2. Transmission: The frontend JavaScript sends a POST request to wp-admin/admin-ajax.php with the action picu_approve_collection.
  3. Processing: The AJAX handler (likely in frontend/includes/picu-frontend-actions.php) retrieves $_POST['client_name'].
  4. Storage: The plugin calls update_post_meta( $post_id, '_picu_approved_by', $client_name ) or stores it in the _picu_collection_history meta (seen in backend/includes/picu-helper.php).
  5. Sink: An administrator navigates to the "All Collections" list (wp-admin/edit.php?post_type=picu_collection).
  6. Rendering: The plugin's custom column handler for picu_clients (referenced by CSS class .column-picu_clients in backend/css/picu-admin.css) retrieves the stored name and echoes it directly into the HTML table without using esc_html().

4. Nonce Acquisition Strategy

The unauthenticated approval action requires a frontend nonce to pass check_ajax_referer.

  1. Shortcode Identification: The plugin uses the [picu_collection] shortcode to render the gallery.
  2. Page Creation: Use WP-CLI to create a public page containing a collection.
  3. Navigation: Use the browser_navigate tool to visit the collection's public URL.
  4. Nonce Extraction: Use browser_eval to extract the nonce from the localized JavaScript object.
    • Variable Name: picu_data (standard for this plugin's frontend).
    • Key: nonce.
    • Script: window.picu_data?.nonce

5. Exploitation Strategy

Step 1: Discover Collection

Locate an existing collection. If none exists, create one as described in "Test Data Setup".

Step 2: Extract Nonce and ID

Navigate to the collection URL and extract the required parameters:

  • post_id: The ID of the collection post.
  • nonce: The AJAX nonce from window.picu_data.nonce.

Step 3: Inject Payload

Submit the approval request with the XSS payload.

  • Tool: http_request
  • Method: POST
  • URL: http://localhost:8080/wp-admin/admin-ajax.php
  • Headers: Content-Type: application/x-www-form-urlencoded
  • Body Parameters:
    • action: picu_approve_collection
    • nonce: [EXTRACTED_NONCE]
    • post_id: [POST_ID]
    • client_name: <img src=x onerror="alert('XSS_SUCCESS')">
    • client_email: victim@example.com
    • selection: ["0"] (A JSON array representing selected image IDs)

Step 4: Trigger Execution

Navigate to the Admin Dashboard as an administrator to trigger the stored script.

  • Target URL: http://localhost:8080/wp-admin/edit.php?post_type=picu_collection

6. Test Data Setup

  1. Create Collection:
    # Create the collection
    ID=$(wp post create --post_type=picu_collection --post_status=sent --post_title="Proofing Gallery" --porcelain)
    # Assign a hash/slug if not generated automatically
    wp post term add $ID picu_collection_status sent
    
  2. Find URL: Retrieve the permalink to use for nonce extraction.
    wp post get $ID --field=guid
    

7. Expected Results

  • The http_request should return a successful response (e.g., {"success":true}).
  • When the Admin Dashboard is viewed, the browser should execute the alert('XSS_SUCCESS') payload.
  • Inspecting the HTML of the "Collections" list will show the <img> tag rendered unescaped within the <td> belonging to the picu_clients column.

8. Verification Steps

  1. Check Meta Storage:
    wp post meta get [POST_ID] _picu_approved_by
    # Result should contain the raw <img ...> payload
    
  2. Check History Storage:
    wp post meta get [POST_ID] _picu_collection_history
    # Result will be a serialized array; search for the payload string
    

9. Alternative Approaches

If the client_name in the list table is sanitized, attempt injection via:

  • client_comment: Some versions allow feedback comments stored in _picu_collection_history.
  • Admin Notice Trigger: If the approval triggers an admin notice (via picu_add_notification in picu-admin-notices.php), the payload may execute on any admin page because picu_display_admin_notices echos $notification['message'] raw.
  • Payload Variant: Use <script src="..."></script> to attempt more complex actions like admin user creation.
Research Findings
Static analysis — not yet PoC-verified

Summary

The picu – Online Photo Proofing Gallery plugin is vulnerable to unauthenticated stored XSS because it fails to sanitize and escape client-provided metadata, such as names or comments, submitted during the gallery approval process. This allows attackers to inject malicious scripts that execute in the context of an administrator's browser when they view the collection in the WordPress backend.

Vulnerable Code

// backend/includes/picu-admin-notices.php line 185
foreach( $notifications as $notification ) {
    echo '<div class="' . $notification['type'] . '"><p>' . $notification['message'] . '</p></div>';
}

---

// backend/includes/picu-helper.php line 269
// Storing history without sanitizing the 'data' or 'meta' arrays which can contain client_name
update_post_meta( $post_id, '_picu_collection_history', $history );

Security Fix

--- a/backend/includes/picu-admin-notices.php
+++ b/backend/includes/picu-admin-notices.php
@@ -182,7 +182,7 @@
 		// If there are notifications, display them
 		if ( isset( $notifications ) AND is_array( $notifications ) ) {
 			foreach( $notifications as $notification ) {
-				echo '<div class="' . $notification['type'] . '"><p>' . $notification['message'] . '</p></div>';
+				echo '<div class="' . esc_attr( $notification['type'] ) . '"><p>' . wp_kses_post( $notification['message'] ) . '</p></div>';
 			}
 
 			// Delete notification option

Exploit Outline

1. Identify a public picu collection URL (e.g., /?picu_collection=[hash]). 2. Visit the page and extract the unauthenticated AJAX nonce from the localized JavaScript object 'window.picu_data.nonce'. 3. Send a POST request to the 'wp-admin/admin-ajax.php' endpoint with the action 'picu_approve_collection'. 4. Include an XSS payload (e.g., <img src=x onerror=alert(1)>) in the 'client_name' or 'client_comment' parameter. 5. Wait for an administrator to view the 'All Collections' list table or the specific 'Collection History' metabox in the WordPress dashboard to trigger the stored payload.

Check if your site is affected.

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