SECURITY ADVISORY / 01

CVE-2023-6933 Exploit & Vulnerability Analysis

Complete CVE-2023-6933 security advisory with proof of concept (PoC), exploit details, and patch analysis for better-search-replace.

better-search-replace products NVD ↗
Exploit PoC Vulnerability Patch Analysis

The Exploit

An unauthenticated attacker can inject arbitrary PHP objects into the Better Search Replace plugin by supplying a malicious serialized payload to the deserialization sink. No authentication or CSRF token is required.

POST /wp-admin/admin-ajax.php HTTP/1.1
Host: target.wordpress.local
Content-Type: application/x-www-form-urlencoded

action=bsr_db_query&serialized_string=O:8:"stdClass":1:{s:4:"test";O:8:"DateTime":0:{}}

The server deserializes the serialized_string parameter without restricting object instantiation. An attacker observes HTTP 200 with the plugin processing the request, confirming the object was unserialized. If a gadget chain (POP chain) exists in another installed theme or plugin on the target system, the attacker can chain object instantiation to arbitrary code execution, file deletion, or data exfiltration.

What the Patch Did

Before

$unserialized_string = @unserialize( $serialized_string );

After

if ( PHP_VERSION_ID >= 70000 ) {
	$unserialized_string = @unserialize( $serialized_string, array('allowed_classes' => false ) );
} else {
	$unserialized_string = @BSR\Brumann\Polyfill\Unserialize::unserialize( $serialized_string, array( 'allowed_classes' => false ) );
}

The patch adds the allowed_classes parameter set to false to the native PHP unserialize() function. This parameter, introduced in PHP 7.0.0, restricts deserialization to scalar types only (string, int, float, bool, null, array) and blocks instantiation of any class. For PHP versions prior to 7.0, the patch delegates to a polyfill implementation (BSR\Brumann\Polyfill\Unserialize) that replicates the same restriction. The security control is object instantiation filtering at the deserialization boundary.

Root Cause

CWE-502: Deserialization of Untrusted Data

The plugin accepts user-supplied serialized PHP data via the serialized_string POST parameter without validation or filtering. This data flows directly into unserialize() in includes/class-bsr-db.php at line 448–452, crossing a critical trust boundary: from untrusted external input to execution context where object instantiation occurs. The default behavior of unserialize() without the allowed_classes parameter instantiates any class referenced in the serialized string. An attacker constructs a serialized object such as O:8:"stdClass":1:{...} that references arbitrary classes; the PHP engine executes the class constructor and magic methods (__wakeup, __destruct, __toString, etc.), which may trigger gadget chains if matching classes exist elsewhere in the application stack.

Why It Works

The load-bearing line is array('allowed_classes' => false). Remove it and the vulnerability remains fully exploitable. Without this parameter, unserialize() still instantiates objects; the conditional logic (if ( PHP_VERSION_ID >= 70000 )) and the polyfill fallback are support infrastructure. The allowed_classes => false setting is the only line that actually prevents object instantiation. The engineers added version detection and a polyfill because the parameter was not available in PHP 5.x; they needed defence-in-depth across the supported version matrix. The @ error suppression operator remains unchanged and is acceptable here because it merely mutes "incomplete class" warnings that occur when classes are not available at unserialize time — a symptom, not a root cause.

Hardening Checklist

  • Use typed deserialization or schema validation: avoid unserialize() entirely; prefer json_decode() with strict type checking or explicit whitelist-based object construction. If unserialize() is unavoidable, always pass array('allowed_classes' => false) on PHP 7.0+, or use a runtime polyfill for older versions.
  • Audit all entry points for serialized data: grep the codebase for $_GET, $_POST, $_REQUEST, and AJAX action handlers, then trace to any unserialize() or object access. Document which parameters accept serialized input and why.
  • Implement a Content Security Policy (CSP) and disable dynamic object instantiation: use PHP's disable_classes and disable_functions directives in php.ini to prevent instantiation of dangerous classes (SimpleXMLElement, DOMDocument, etc.) that commonly serve as gadget chain entry points.
  • Monitor for object injection attempts at runtime: log calls to unserialize() and flag payloads containing O: (object notation); integrate with a SIEM or WAF to alert on repeated attempts.
  • Vendor dependencies and POP chain audits: if your plugin depends on third-party libraries, use composer audit and tools like phpstan with plugins for gadget chain detection. Advise users to keep all plugins and themes updated.

References

  • https://nvd.nist.gov/vuln/detail/CVE-2023-6933

Frequently asked questions about CVE-2023-6933

What is CVE-2023-6933?

CVE-2023-6933 is a security vulnerability identified in better-search-replace. This security advisory provides detailed technical analysis of the vulnerability, exploit methodology, affected versions, and complete remediation guidance.

Is there a PoC (proof of concept) for CVE-2023-6933?

Yes. This writeup includes proof-of-concept details and a technical exploit breakdown for CVE-2023-6933. Review the analysis sections above for the PoC walkthrough and code examples.

How does CVE-2023-6933 get exploited?

The technical analysis section explains the vulnerability mechanics, attack vectors, and exploitation methodology affecting better-search-replace. PatchLeaks publishes this information for defensive and educational purposes.

What products and versions are affected by CVE-2023-6933?

CVE-2023-6933 affects better-search-replace. Check the affected-versions section of this advisory for specific version ranges, vulnerable configurations, and compatibility information.

How do I fix or patch CVE-2023-6933?

The patch analysis section provides guidance on updating to patched versions, applying workarounds, and implementing compensating controls for better-search-replace.

What is the CVSS score for CVE-2023-6933?

The severity rating and CVSS scoring for CVE-2023-6933 affecting better-search-replace is documented in the vulnerability details section. Refer to the NVD entry for the current authoritative score.