PHP Object Injection in WordPress Jumped 2.5x in August
By WP Vanguard Team
WP Vanguard's vulnerability database logged 17 PHP object injection advisories against WordPress plugins in July 2026. In August, that count reached 43, roughly 2.5 times higher in a single month. Nothing about how plugin authors call unserialize() changed between those two months. What most likely changed is where researchers were looking, and that gap between "the code got worse" and "the scrutiny got sharper" is worth taking seriously, because it changes what a site owner should actually do about it.
August 2026 was a heavy month across the board: 929 vulnerabilities disclosed, 75 of them critical, 313 high, 541 medium. Object injection is a small slice of that total, 43 out of 929, but it's the slice that grew the fastest relative to July, when the overall count was actually higher at 1,108 vulnerabilities with 60 critical. Fewer total bugs, and a disproportionate jump in one specific vulnerability class, is the pattern that points at research focus rather than a sudden drop in code quality.
This post covers three things the rest of this series doesn't: what PHP object injection actually is at the mechanism level, why WordPress's plugin-and-theme architecture makes it an unusually exposed target for this specific bug class, and what actually fixes it, as opposed to what merely reduces the odds.
How PHP object injection actually works
PHP's unserialize() function takes a specially formatted string and rebuilds it into a live PHP value, including full objects with their class identity intact. That's different from json_decode(), which only ever produces arrays, strings, numbers, and booleans. A serialized string can say "I am an instance of class Foo with these property values," and when unserialize() reads that string, it goes and instantiates class Foo if that class is already loaded in the current request.
That instantiation is where the danger lives. PHP classes can define magic methods that fire automatically at specific lifecycle moments, without anyone calling them directly. __wakeup() runs immediately after an object is unserialized. __destruct() runs when an object is garbage collected, typically at the end of the request. __toString() runs whenever the object gets used somewhere a string is expected. None of these require the attacker to call a function by name. They fire because the object exists and the runtime reaches that lifecycle point.
If an attacker can control the serialized string that reaches unserialize(), they control which class gets instantiated and what property values it starts with. If one of the classes already loaded in that request has a magic method that does something dangerous with its own properties (writes a file using a property as the path, calls a callback stored in a property, deletes something a property points to) the attacker just found a way to trigger that action without ever calling the function that normally guards it.
Security researchers call a working exploit chain built this way a POP chain, short for property-oriented programming. The name is a nod to return-oriented programming in binary exploitation: instead of injecting new code, the attacker reuses fragments of code that are already present, in this case class methods that already exist in the application. Building one is a research task. Finding the plugin that calls unserialize() on untrusted input is comparatively mechanical.
Who is affected
The flagship August case is CVE-2026-4703, an unauthenticated PHP object injection vulnerability in WS Form LITE, a drag-and-drop form builder plugin. It's rated CVSS 9.8, critical. Every version up to and including 1.10.80 is affected, and the fix landed in 1.10.82. Patchstack's advisory describes the flaw as deserialization of untrusted input from form submission meta values, reachable without authentication because form submission is, by design, something anonymous visitors do.
That's the shape most object injection bugs take. Something in the request, a form field, a cookie, a query parameter, a serialized option value, gets passed to unserialize() without the code checking where it came from. The bug isn't exotic. It's a single missed trust boundary around one function call.
Being affected doesn't require any unusual configuration on the site owner's part. If you run WS Form LITE at or below 1.10.80, you're running the vulnerable code path, whether or not your forms use every field type the plugin offers. Update-and-move-on is the correct response for this one specifically, and we cover what to check on the way there.
Why the shared gadget pool makes WordPress an unusual target
A POP chain needs a class with a dangerous magic method, and that class has to be loaded in the same request as the vulnerable unserialize() call. On most PHP applications, that's a real constraint. The codebase is one thing, written by one team, and the pool of loaded classes is whatever that one application defines.
WordPress doesn't work that way. A single front-end request loads WordPress core, plus every active plugin, plus the active theme, whether or not that request touches functionality from all of them. That's normal WordPress bootstrapping, not a misconfiguration. The practical effect is that the pool of classes available to a would-be POP chain, on a typical WordPress install, is the union of core plus every plugin and theme that happens to be active, not just the code of the plugin with the vulnerable unserialize() call.
That means the vulnerable call and the exploitable magic method don't have to live in the same plugin. A completely unrelated plugin, maybe a page builder, a caching tool, or an SEO helper, can define a class with a __destruct() or __wakeup() method that writes a file based on a property value. On its own, that class is harmless: nothing in that plugin ever lets an attacker control the property before the method runs. But if a different active plugin calls unserialize() on attacker-controlled input, the two combine. The vulnerable plugin supplies the injection point. The unrelated plugin supplies the gadget.
This is genuinely different from a SQL injection or an XSS bug, where the vulnerable code and the impact both live in the same request handler you're auditing. Object injection risk on a WordPress site is a property of the whole active plugin set, not of any single plugin in isolation, which is part of why it's hard to reason about from inside one plugin's codebase.
A research signal, not a coding regression
It's worth being precise about what the July-to-August jump does and doesn't tell us. The data is real: 17 advisories became 43. The interpretation, that this reflects where researchers focused their attention rather than a sudden decline in how plugin authors write deserialization code, is the most likely explanation given the pattern, not something we can prove from advisory counts alone.
A few things support that reading. Object injection bugs don't require new PHP language features or new plugin functionality to exist; they're a class of bug that's been possible in PHP for as long as unserialize() has accepted arbitrary strings. When a bug class that old sees a sudden spike in disclosures, and the total vulnerability count for the month actually went down, the more plausible story is that public writeups, conference talks, or a widely reused set of gadget classes made this bug class easier to spot across many plugins at once, not that dozens of plugin authors independently started writing worse code in the same four-week window.
We don't have visibility into which specific researchers or disclosure programs drove the August numbers, and we're not going to guess. What we can say with confidence is that the underlying mechanism, unserialize() on untrusted input plus a gadget class somewhere in the loaded code, hasn't changed at all. The bug has always been there in these plugins. August is when more of it got found and reported.
If you want the trend context for how this compares to other vulnerability classes tracked across the same period, our June 2026 vulnerability roundup covered object injection too, and the pattern of a niche bug class outpacing the month's overall total isn't unique to August.
What to do now
There's no safe configuration of unserialize() for untrusted input. Not a stricter regex on the input, not a length check, not a class whitelist that "should" be safe. The only durable fix is to stop using unserialize() for data that crosses a trust boundary and use json_decode() instead, because JSON has no concept of PHP class identity and can't instantiate anything.
// Vulnerable: rebuilds live PHP objects from request data
$data = unserialize($_POST['form_meta']);
// Fixed: JSON can only produce arrays, strings, numbers, booleans
$data = json_decode($_POST['form_meta'], true);
// Legacy mitigation only, when the format truly can't change:
$data = unserialize($raw, ['allowed_classes' => false]);
That last line is a mitigation, not a fix, and it's worth understanding why it works at all. Passing allowed_classes => false tells PHP to refuse to instantiate any class during unserialization. Any object marker in the string gets converted into a harmless __PHP_Incomplete_Class stand-in instead of a live object.
No live object means no magic method ever fires, which means the entire POP chain mechanism has nothing to hook into. It's a real reduction in risk, and the PHP manual itself recommends it as a fallback. But it still involves calling a function whose own documentation says untrusted input shouldn't reach it regardless of the options passed. Treat it as a stopgap for legacy code you can't rewrite this week, not as the destination.
If you run WS Form LITE, update to 1.10.82 or later immediately; that's a critical, unauthenticated bug with no workaround short of the patched version. On a site that ran a version at or below 1.10.80 before you patched, check for unexpected PHP files in wp-content/uploads/ with recent modification times, unfamiliar entries in the WordPress cron table (wp cron event list), and any admin user or option value you don't recognize.
A successful POP chain on this class of bug commonly ends in either a webshell drop or a privilege escalation. If you find anything, treat the install as compromised and follow a proper cleanup process rather than just deleting the suspicious file, since PHP backdoors in WordPress usually leave more than one point of persistence.
For the mechanics of how an object injection bug typically gets chained into full compromise once it's live on a site, see how WordPress sites get hacked. And if you're auditing your own plugin's codebase for this pattern rather than responding to one specific CVE, our general WordPress security checklist covers the baseline hardening steps that apply regardless of which vulnerability class hits next.
References
Related reading
Check Your WordPress Site Security
Free scan, no login required. Find vulnerabilities before attackers do.
Scan Your Site FreeGet weekly WordPress security tips
Vulnerability alerts, plugin updates, and security guides. No spam. Unsubscribe any time.