PHP vulnerabilities

PHP deserialization and object injection explained

Illustration: PHP deserialization and object injection

Insecure deserialization ranks among the most dangerous flaws in the PHP ecosystem: it regularly leads to remote code execution, often without authentication. This article explains what PHP object injection is, why a simple text string can take over a site, and how developers and site administrators alike protect themselves, with real WordPress examples.

Serializing and deserializing: what are we talking about

Serialization turns a PHP variable (array, object) into a text string that can be stored or transmitted. The unserialize() function does the reverse: it rebuilds the original variable. The problem is that rebuilding an object also means instantiating its class and triggering some of its methods. The PHP documentation is explicit: never pass untrusted user input to unserialize(), because rebuilding the data can load and execute code.

An attacker who controls the serialized string can therefore request the creation of objects of any class present in the site's code, with whatever properties they choose. That is the definition of PHP object injection, classified as CWE-502 (deserialization of untrusted data).

What does a serialized string look like? Readable text describing the type and the content. For example, O:4:"User":1:{s:4:"name";s:3:"Bob";} represents an object of class User with a name property set to Bob. All it takes is swapping the class name and properties to target a class that is useful for the attack. That is why a naive filter that merely checks whether the data looks serialized provides no security at all.

Magic methods and the gadget chain

On its own, a forged object does nothing. What makes the attack possible are the magic methods that PHP calls automatically at certain moments:

  • __wakeup(): called right after the object is rebuilt
  • __destruct(): called when the object is destroyed (at the end of the script, for example)
  • __toString(): called when the object is used as a string

If one of these methods, in a class on the site, performs a sensitive operation (writing a file, calling a function whose name is stored in a property, and so on), the attacker can reach it. By chaining several classes whose methods call one another, they build a gadget chain, or POP chain (Property Oriented Programming), that ends in a dangerous action. No code is injected directly: the attacker only hijacks code that is already there.

Diagram: attacker-controlled data, unserialize, magic methods and a POP chain leading to a dangerous action
The principle: forged data wakes up methods that chain together until the final action.

A WordPress example: GiveWP (CVE-2024-5932)

In August 2024, Wordfence disclosed a critical flaw in the GiveWP donation plugin, installed on more than 100,000 sites. An unauthenticated attacker could inject a PHP object through the give_title parameter of the donation form. Because a gadget chain existed in the plugin's own code, the injection led to remote code execution and file deletion.

ItemValue
IdentifierCVE-2024-5932
TypePHP object injection (CWE-502), unauthenticated
Affected versionsUp to and including 3.14.1
Patched version3.14.2 (August 7, 2024)
CVSS score10.0 (Wordfence), 9.8 (NVD)
ImpactCode execution, file deletion
CVE-2024-5932: an object injection given the maximum score by Wordfence.

The patch did not close the matter: in September 2024, CVE-2024-8353 showed that the initial check could be bypassed because of stripslashes_deep processing applied to the data. Additional hardening was added in version 3.16.2. This is typical of these flaws: fixing the obvious entry point is not always enough, and a second security review is often needed before the issue can be considered closed.

Phar, maybe_unserialize and WordPress core

Two cases shed light on the role of the environment. First, the phar:// wrapper. Before PHP 8.0, merely handling a Phar file (via file_exists, getimagesize, and so on) automatically deserialized its metadata. That is the vector BuddyForms exposed (CVE-2023-26326, CVSS 9.8): an attacker uploaded a disguised Phar file, then had it called through phar://. PHP 8.0 removed this automatic deserialization of Phar metadata, which greatly reduces this risk on modern hosting.

Next, WordPress core itself. In December 2023, version 6.4.2 neutralized a POP chain introduced in the WP_HTML_Token class: its destructor called a function whose name came from a property. WordPress added a __wakeup() method that throws an exception as soon as anyone tries to deserialize this object. The team noted that the flaw was not directly exploitable on its own, but could become serious when combined with certain plugins, especially on multisite. It is a good illustration of a POP chain: core supplied the gadget, and a vulnerable plugin would have supplied the entry point.

Timeline of PHP object injection cases in the WordPress ecosystem from 2023 to 2025
Deserialization remains a recurring source of critical flaws, in core and plugins alike.

Protecting yourself: developers and administrators

For a plugin or theme developer, the rules are clear:

  • Never pass user input to unserialize(); prefer json_decode() and json_encode() for exchanging data.
  • If deserialization cannot be avoided, use the allowed_classes option (set to false to accept no objects at all), available since PHP 7.0 and strictly typed since PHP 7.1.
  • Validate and sign stored data (for example with hash_hmac()) to make sure it has not been tampered with.
  • Be wary of input that reaches WordPress's maybe_unserialize(), which calls unserialize() as soon as the data looks like serialized content.

For a site administrator who does not touch the code, the defense is more conventional but decisive:

  • Update core, plugins and themes without delay; these flaws are almost always fixed in a new release.
  • Use a supported PHP version (8.1 or later), which notably rules out automatic deserialization of Phar metadata.
  • Reduce the number of installed plugins: less code means fewer potential gadget chains.
  • Monitor file integrity and keep tested backups, so you can detect and repair a compromise.

Key takeaways

  • PHP object injection abuses unserialize() on attacker-controlled data: it rebuilds objects whose magic methods run on their own.
  • A gadget (POP) chain links classes that are already present to reach a dangerous action, without injecting any code.
  • Real cases (GiveWP CVE-2024-5932, CVSS 10.0; BuddyForms CVE-2023-26326) have hit widely installed WordPress plugins.
  • In code: never pass user input to unserialize(); on the site: updates, supported PHP, fewer plugins, integrity monitoring.

Where WP Masquerade fits in

WP Masquerade does not fix a plugin flaw, nor does it filter a deserialization payload: it is not a web application firewall, and its guide says so clearly. Its contribution is complementary. Hiding public signatures and hardening make the profiling that precedes the targeting of a specific plugin harder. Above all, after a successful attack, the core integrity check (Pro) flags modified files, and email alerts warn you quickly. An upcoming Pro feature adds alerts on vulnerable plugins, with details of the flaw, so you can update as early as possible. The fundamental defense against object injection remains prompt updates and code that never deserializes user input.

Videos to go further

A visual explanation of how insecure deserialization works.
A hands-on introduction to PHP object injection and gadget chains.

Sources

Other sources consulted: further CVE records, official documentation and Wordfence database.

WordPress security, without the noise

One e-mail when something matters: new attacks seen in the wild, plugin updates and practical guides. No spam, unsubscribe in one click.

Start free, go Pro when you’re ready

The free version already hides WordPress and moves the login page. Pro adds aliases, two-factor authentication and monitoring.