CVE-2026-3017

Smart Post Show – Post Grid, Post Carousel & Slider, and List Category Posts <= 3.0.12 - Authenticated (Administrator+) PHP Object Injection

highDeserialization of Untrusted Data
7.2
CVSS Score
7.2
CVSS Score
high
Severity
3.0.13
Patched in
1d
Time to patch

Description

The Smart Post Show – Post Grid, Post Carousel & Slider, and List Category Posts plugin for WordPress is vulnerable to PHP Object Injection in all versions up to, and including, 3.0.12 via deserialization of untrusted input in the import_shortcodes() function. This makes it possible for authenticated attackers, with Administrator-level access and above, to inject a PHP Object. No known POP chain is present in the vulnerable software, which means this vulnerability has no impact unless another plugin or theme containing a POP chain is installed on the site. If a POP chain is present via an additional plugin or theme installed on the target system, it may allow the attacker to perform actions like delete arbitrary files, retrieve sensitive data, or execute code depending on the POP chain present.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=3.0.12
PublishedApril 13, 2026
Last updatedApril 14, 2026
Affected pluginpost-carousel

What Changed in the Fix

Changes introduced in v3.0.13

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan: CVE-2026-3017 (Smart Post Show) ## 1. Vulnerability Summary The **Smart Post Show** plugin (versions <= 3.0.12) is vulnerable to **PHP Object Injection** via the `import_shortcodes()` function. The vulnerability exists because the plugin accepts a user-provided JSON st…

Show full research plan

Exploitation Research Plan: CVE-2026-3017 (Smart Post Show)

1. Vulnerability Summary

The Smart Post Show plugin (versions <= 3.0.12) is vulnerable to PHP Object Injection via the import_shortcodes() function. The vulnerability exists because the plugin accepts a user-provided JSON string containing shortcode metadata and passes it through maybe_unserialize() without sufficient validation. Although the input is passed through sanitize_text_field(), this function does not prevent the injection of serialized PHP objects. Successful exploitation requires Administrator-level privileges and the presence of a usable POP chain in another plugin or theme.

2. Attack Vector Analysis

  • Endpoint: wp-admin/admin-ajax.php
  • Action: sp_pc_import_shortcodes (Inferred from function name and plugin prefix sp_pc_)
  • HTTP Parameter: shortcode (JSON-encoded string)
  • Nonce: spf_options_nonce
  • Authentication: Required (Administrator+). The capability check is manage_options (filterable via sp_post_carousel_import_export_user_capability).
  • Preconditions: Must be logged in as an administrator to obtain the nonce and satisfy the capability check.

3. Code Flow

  1. Entry Point: The AJAX action triggers Smart_Post_Show_Import_Export::import_shortcodes().
  2. Nonce Check: wp_verify_nonce( $_POST['nonce'], 'spf_options_nonce' ) verifies the request integrity.
  3. Capability Check: current_user_can( 'manage_options' ) ensures the user is an admin.
  4. Input Decoding:
    • $_POST['shortcode'] is captured and json_decode()'d.
    • map_deep() applies sanitize_text_field() to the values. sanitize_text_field removes HTML tags and null bytes but allows characters like :, {, }, and " used in serialized objects.
  5. Sink Call: import_shortcodes() calls $this->import($shortcodes).
  6. Vulnerable Sink: Inside import(), the code iterates over $shortcode['meta'].
    • includes/class-smart-post-show-import-export.php:
      $meta_value = maybe_unserialize( str_replace( '{#ID#}', $new_shortcode_id, $value ) );
      update_post_meta( $new_shortcode_id, $meta_key, $meta_value );
      
    • The maybe_unserialize() function executes on the string $value (after a simple string replacement), leading to PHP Object Injection.

4. Nonce Acquisition Strategy

The nonce spf_options_nonce is required. It is typically localized for the plugin's administration scripts.

  1. Identify Page: The "Import/Export" functionality is likely located under the "Smart Post Show" menu in the WordPress dashboard.
  2. Navigate: Use browser_navigate to go to wp-admin/edit.php?post_type=sp_post_carousel&page=sp_pc_settings (or the specific Import/Export sub-page).
  3. Extract Nonce: The nonce is likely part of a global configuration object.
    • Target Variable: window.spf_vars?.nonce or similar.
    • Alternative: Search the HTML source for spf_options_nonce.
    • Command: browser_eval("document.querySelector('#spf_options_nonce')?.value || spf_vars?.nonce")

5. Exploitation Strategy

Payload Construction

The payload must be a JSON string that mimics the plugin's export format.

{
  "shortcode": [
    {
      "title": "Exploit Post",
      "meta": {
        "exploit_meta_key": "O:8:\"stdClass\":0:{}"
      }
    }
  ]
}

HTTP Request (via http_request)

  • URL: http://localhost:8080/wp-admin/admin-ajax.php
  • Method: POST
  • Headers: Content-Type: application/x-www-form-urlencoded
  • Body:
    action=sp_pc_import_shortcodes&nonce=[NONCE]&shortcode={"shortcode":[{"title":"Exploit","meta":{"injection":"O:8:\"stdClass\":0:{}"}}]}
    

6. Test Data Setup

  1. Admin User: Ensure an admin user exists and is logged in (session cookies needed).
  2. Plugin Activation: Ensure the "Smart Post Show" plugin is active.
  3. Shortcode Page: Not strictly required for the exploit itself (since it's an admin-ajax action), but accessing the admin settings page is necessary to harvest the nonce.

7. Expected Results

  • Response: The plugin should return a JSON success message (e.g., {"success":true,...}).
  • Effect: A new post of type sp_post_carousel will be created. The update_post_meta call will attempt to store an unserialized stdClass object (or the target POP chain object) in the database for that post.

8. Verification Steps

  1. Check Post Creation: Use WP-CLI to see if the "Exploit" post was created.
    • wp post list --post_type=sp_post_carousel
  2. Check Meta Data: Inspect the meta value to see if it was stored as an object or a string.
    • wp post meta list [NEW_POST_ID]
  3. Check Logs: If a POP chain was used (e.g., Logger class), check for the side effect (file creation, log entry, etc.).

9. Alternative Approaches

  • Export Hook: If sp_pc_import_shortcodes is incorrect, check the admin page source for the id="spf-form" or similar and look for the hidden input action field.
  • Double JSON Encoding: The source code suggests a second json_decode if the first result is still a string. If the primary payload fails, try double-encoding the shortcode value.
  • Nested Objects: If stdClass is filtered, try a built-in WordPress class like WP_Block_Type_Registry if a chain is found.
Research Findings
Static analysis — not yet PoC-verified

Summary

The Smart Post Show plugin for WordPress is vulnerable to PHP Object Injection in versions up to 3.0.12 via the import_shortcodes() function. The vulnerability occurs because the plugin uses maybe_unserialize() on user-controlled meta data during the shortcode import process without sufficient validation, allowing administrative users to inject arbitrary PHP objects.

Vulnerable Code

// includes/class-smart-post-show-import-export.php:147
				if ( isset( $shortcode['meta'] ) && is_array( $shortcode['meta'] ) ) {
					foreach ( $shortcode['meta'] as $key => $value ) {
						// meta key.
						$meta_key = sanitize_key( $key );
						// meta value.
						$meta_value = maybe_unserialize( str_replace( '{#ID#}', $new_shortcode_id, $value ) );

						// update meta.
						update_post_meta( $new_shortcode_id, $meta_key, $meta_value );
					}
				}

---

// includes/class-smart-post-show-import-export.php:194
		$data = isset( $_POST['shortcode'] ) ? sanitize_text_field( wp_unslash( $_POST['shortcode'] ) ) : '';
		if ( ! $data ) {
			wp_send_json_error( array( 'message' => esc_html__( 'Nothing to import.', 'post-carousel' ) ), 400 );
		}

		// Decode JSON with error checking.
		$decoded_data = json_decode( $data, true );

// ... (truncated)

		$shortcodes = map_deep(
			$decoded_data['shortcode'],
			function ( $value ) {
				return is_string( $value ) ? sanitize_text_field( $value ) : $value;
			}
		);

		$status = $this->import( $shortcodes );

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/post-carousel/3.0.12/includes/class-smart-post-show-import-export.php /home/deploy/wp-safety.org/data/plugin-versions/post-carousel/3.0.13/includes/class-smart-post-show-import-export.php
--- /home/deploy/wp-safety.org/data/plugin-versions/post-carousel/3.0.12/includes/class-smart-post-show-import-export.php	2025-12-29 09:48:30.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/post-carousel/3.0.13/includes/class-smart-post-show-import-export.php	2026-03-25 09:39:38.000000000 +0000
@@ -147,10 +147,36 @@
 					foreach ( $shortcode['meta'] as $key => $value ) {
 						// meta key.
 						$meta_key = sanitize_key( $key );
-						// meta value.
-						$meta_value = maybe_unserialize( str_replace( '{#ID#}', $new_shortcode_id, $value ) );
 
-						// update meta.
+						// Raw meta value with placeholder replaced.
+						$meta_value_raw = str_replace( '{#ID#}', $new_shortcode_id, $value );
+
+						if ( is_string( $meta_value_raw ) && is_serialized( $meta_value_raw ) ) {
+
+							// @ suppresses warnings for malformed serialized data while import. Note: already sanitize the object each data, so this is just an extra precaution.
+							// WordPress built-in function maybe_unserialize() does not block objects in serialized data.
+							// For security, we use PHP's native unserialize() with 'allowed_classes' => false to stop creating objects.
+							// to prevent PHP Object Injection while still converting serialized arrays, booleans, and strings.
+							$meta_value = @unserialize( // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_unserialize .
+								$meta_value_raw,
+								array(
+									'allowed_classes' => false, // Disallow all classes to prevent object instantiation.
+								)
+							);
+
+							// Fallback for blocked objects or invalid serialization.
+							if ( false === $meta_value && 'b:0;' !== $meta_value_raw ) {
+								$meta_value = $meta_value_raw;
+							}
+						} else {
+							$meta_value = $meta_value_raw;
+						}
+
+						// Ensure no object is ever stored in DB.
+						if ( is_object( $meta_value ) ) {
+							$meta_value = $meta_value_raw;
+						}
+
 						update_post_meta( $new_shortcode_id, $meta_key, $meta_value );
 					}
 				}

Exploit Outline

To exploit this vulnerability, an authenticated administrator must first obtain a valid security nonce (spf_options_nonce) from the plugin's administration page. The attacker then sends a POST request to wp-admin/admin-ajax.php with the action parameter set to 'sp_pc_import_shortcodes'. The payload is delivered via the 'shortcode' parameter as a JSON-encoded string. This JSON must include a 'shortcode' array containing a 'meta' object. Inside this meta object, the attacker provides a serialized PHP object as a value. Although the plugin applies sanitize_text_field() to the values, this function does not remove characters required for PHP serialization. When the plugin processes the import, it calls maybe_unserialize() on the malicious meta value, triggering the instantiation of the injected object. If a suitable POP chain exists in the WordPress environment (through other plugins or themes), it can be leveraged for further impact such as remote code execution.

Check if your site is affected.

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