First, what "serialization" even means
PHP has a built-in way to take a data structure — an array, an object, a set of plugin settings — and convert it into a plain text string so it can be stored somewhere, like a database field, and later converted back into usable data. That conversion is called serialization; converting the string back into a live PHP object or array is deserialization. WordPress itself leans on this constantly: plugin settings, widget configurations, and all sorts of structured options get stored in the wp_options table as serialized strings, then deserialized back into working data whenever WordPress loads them.
None of that is a problem by itself. It becomes one the moment a plugin deserializes a string that an attacker got to control.
What actually goes wrong
A PHP Object Injection vulnerability happens when a plugin takes untrusted input — something submitted through a form, a URL parameter, an uploaded file, anything a visitor can influence — and runs it through PHP's deserialization function without first confirming it only came from the plugin's own trusted, previously-serialized data. If an attacker can hand-craft their own serialized string and get the plugin to deserialize it, they can potentially create PHP objects the plugin never intended to create.
On its own, that's already unwanted. What makes it dangerous is what security researchers call a "POP chain" — short for property-oriented programming chain. If the codebase already sitting on the server (in WordPress core, an active plugin, or the theme) happens to contain classes with certain methods that run automatically when an object is created or destroyed, an attacker who can inject a carefully chosen combination of objects can chain those existing methods together into something the original developers never intended — deleting files, reading sensitive data, or in the worst case, executing arbitrary code.
The important nuance: the vulnerable plugin doesn't need to contain the dangerous code itself. It only needs to deserialize untrusted input. The actual "weapon" can live in a completely different, otherwise-unrelated plugin or in WordPress core — which is exactly why this bug class is so hard to reason about from the outside, and why its real-world severity depends heavily on what else happens to be installed on the same site.
Why this is a different kind of problem than the others in this series
Every other vulnerability class covered so far in this series involves the attacker introducing something new: injected script in cross-site scripting, an injected database command in SQL injection, an uploaded file that becomes a webshell in arbitrary file upload. PHP Object Injection is closer in spirit to CSRF, in the sense that it repurposes something that's already legitimately there — a logged-in session in CSRF's case, existing application code in this case — rather than smuggling in something new. That makes it genuinely harder to spot by just looking at a plugin in isolation, because the danger often only exists in combination with unrelated code elsewhere on the same site.
A real, verified example
In January 2024, researchers disclosed CVE-2023-6933, an unauthenticated PHP Object Injection vulnerability in Better Search Replace — a widely used WordPress migration plugin with over 1 million active installations, built by WP Engine — affecting all versions up to and including 1.4.4, with a CVSS score of 9 (critical), verified directly against Patchstack's vulnerability database.
The plugin deserialized untrusted input without properly validating it first, letting an unauthenticated attacker inject a PHP object. Per Patchstack's own writeup, the plugin itself contained no exploitable POP chain — but if a POP-chain-capable class existed anywhere else on the same site, in another plugin or theme, the impact could scale up to arbitrary file deletion, sensitive data retrieval, or code execution. That "it depends what else is installed" caveat is exactly the trait that makes this vulnerability class distinct from the others covered in this series. The issue was fixed in version 1.4.5.
Why non-experts genuinely can't verify this one themselves
With something like a weak password or an outdated plugin banner, a site owner can at least form a rough judgment by looking. PHP Object Injection isn't like that. Confirming whether a specific plugin's deserialization call is actually exploitable requires knowing whether a matching POP chain exists anywhere in the site's full set of active code — core, every plugin, and the theme — which is a code-auditing task, not something visible from the WordPress dashboard. This is one of the clearer cases where automated vulnerability scanning against known CVEs is doing something a manual review realistically can't: catching a bug whose actual danger depends on combinations of code a person would never think to cross-reference by hand.
What to actually do about it
- Keep every plugin and theme updated, not just the ones handling obviously sensitive data. A PHP Object Injection vulnerability in even an obscure or "utility" plugin can become dangerous purely because of what else is installed alongside it.
- Remove plugins and themes you're not actually using. Every additional piece of code on the server is a potential ingredient in a POP chain. Deactivated-but-still-installed code can still count, depending on hosting configuration — so remove what you don't need rather than just switching it off.
- Treat "PHP Object Injection" or "insecure deserialization" in a plugin's changelog as a priority update, even if the disclosure notes no known POP chain at the time — a chain elsewhere on your specific site is exactly the variable that can't be assessed from a changelog.
As with every vulnerability class in this series, the fix for a specific disclosed flaw is the update that closes it. These habits reduce your exposure while you're getting there — they don't replace patching.
Get plugin vulnerabilities explained the moment they're disclosed — not months later.
Install free →