Introduction
A PHP Object Injection flaw in the Admin Columns WordPress plugin lets any authenticated user with Contributor-level privileges achieve full remote code execution on the underlying web server. It scores a CVSS 8.8. Two things make this one stand out. The authentication barrier is low, and the plugin bundles a Laravel library containing a ready-made POP gadget chain that turns the deserialization weakness into immediate, practical code execution.
Admin Columns is a widely used WordPress plugin from CodePress. It extends the WordPress admin interface so administrators can customize, sort, and manage columns on post, page, user, and taxonomy list screens. It has over 100,000 active installations and a rating of 4.9 out of 5 stars from more than 1,650 reviews. A commercial Pro version adds integrations for WooCommerce, Advanced Custom Fields, and Yoast SEO, and the plugin is a common fixture on both small and enterprise WordPress deployments.
Technical Information
Root Cause: Unsafe Deserialization in IdsToCollection
The vulnerability is in the IdsToCollection::get_ids_from_string() function, at line 42 of classes/Formatter/IdsToCollection.php. This function calls PHP's native unserialize() on post meta values without setting the allowed_classes option. PHP 7.0 introduced the allowed_classes parameter precisely to prevent Object Injection, by controlling which classes can be instantiated during deserialization. Without it, a crafted serialized string can instantiate any PHP class available in the runtime.
The data flow begins in Meta.php (line 34 in classes/Formatter/), which reads custom field meta values from WordPress posts and passes them to IdsToCollection for processing. This is where attacker-controlled data enters the vulnerable deserialization path.
Attack Flow
Exploitation follows a clear sequence:
-
Initial Access: An attacker authenticates to the WordPress site with Contributor-level credentials. Contributor is one of the lowest default WordPress roles, commonly granted to guest authors or external content contributors.
-
Payload Injection: The attacker creates or edits a post and sets a custom meta field value to a crafted serialized PHP object string. WordPress allows Contributors to set custom meta fields on their own posts.
-
Trigger Deserialization: When the Admin Columns plugin renders the column view for the post (for example, when an administrator views the post list in the WordPress admin panel), the
Meta.phpformatter reads the custom field value and passes it toIdsToCollection::get_ids_from_string(). -
Object Instantiation: The
get_ids_from_string()function callsunserialize()on the attacker-controlled meta value. Because noallowed_classesrestriction is in place, PHP instantiates whatever class the serialized payload specifies. -
POP Chain Execution: The crafted payload uses classes from the Laravel
serializable-closurelibrary, bundled in the plugin'svendor/directory, to achieve arbitrary code execution.
The Bundled POP Gadget Chain
Whether Object Injection escalates to Remote Code Execution depends entirely on which exploitable classes are available in the application's scope. In many PHP Object Injection cases, attackers have to find suitable gadget chains in the application's own codebase or its dependencies. Here, the Admin Columns plugin ships with the Laravel serializable-closure library, which provides a complete, self-contained POP (Property-Oriented Programming) chain.
Two classes in this library are central to the chain:
Native.php (located at vendor/laravel/serializable-closure/src/Serializers/Native.php, line 148): This serializer class implements magic methods (__wakeup() or __destruct()) that PHP invokes automatically during deserialization. These methods process serialized closure data and give the gadget chain its entry point.
ClosureStream.php (located at vendor/laravel/serializable-closure/src/Support/ClosureStream.php, line 47): This class implements a PHP stream wrapper that can execute arbitrary PHP code when its stream methods are invoked. The class is annotated with #[AllowDynamicProperties], which enables dynamic property injection, and the POP chain depends on that capability.
An attacker builds a serialized payload that chains the Native serializer's magic methods through to ClosureStream's code execution capability, and runs arbitrary PHP code as the web server user. The attack needs no file upload, no other vulnerability, and no external dependency.
CVSS v3.1 Breakdown
The CVSS vector is CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H, which gives a score of 8.8 (High).
| Metric | Value | Implication |
|---|---|---|
| Attack Vector | Network | Exploitable remotely over the internet |
| Attack Complexity | Low | No special conditions or race conditions required |
| Privileges Required | Low | Contributor-level access is sufficient |
| User Interaction | None | No victim action needed; triggered when an admin views the post list |
| Scope | Unchanged | Impact confined to the vulnerable component |
| Confidentiality | High | Full system data accessible |
| Integrity | High | Full system modification possible |
| Availability | High | Full system disruption possible |
Low Attack Complexity combined with Low Privileges Required is what makes this vulnerability so accessible. The attacker does not need to be an Administrator. A Contributor account is enough, and on sites that allow user registration with Contributor roles, the barrier to exploitation is little more than creating an account.
CWE-502: Deserialization of Untrusted Data
This vulnerability is a textbook instance of CWE-502, which covers applications that deserialize untrusted data without sufficiently ensuring the resulting data will be valid. In PHP, this takes the form of PHP Object Injection: unserialize() processes attacker-controlled serialized strings and creates objects whose magic methods (__wakeup(), __destruct(), __toString(), etc.) execute operations the developer never intended. The OWASP PHP Object Injection documentation names the use of unserialize() on user-controlled input as the root-cause pattern.
Affected Systems and Versions
The vulnerability affects the Admin Columns plugin (slug: codepress-admin-columns) for WordPress:
- Affected versions: All versions up to and including 7.0.18
- Fixed version: 7.0.19 (released May 29, 2026)
- WordPress requirement: 5.9 or higher
- PHP requirement: 7.4 or higher
- Active installations: Over 100,000
Any authenticated user with Contributor-level access or above (Contributor, Author, Editor, or Administrator roles) can exploit the vulnerability. Sites that allow open user registration with Contributor roles face the highest risk.
Site owners should evaluate both the free version on the WordPress plugin directory and the commercial Admin Columns Pro version, because the vulnerable code path is in the core plugin's formatter classes and bundled vendor libraries.
Vendor Security History
The Wordfence Threat Intelligence database has a dedicated vulnerability listing page for the Admin Columns plugin, which indicates that CVE-2026-7654 is not the first security issue found in this software. WPScan also keeps a separate vulnerability listing for the plugin.
A comparable authenticated PHP Object Injection vulnerability was found in the "Filter Custom Fields & Taxonomies Light" plugin (versions up to 1.05). It shares the same underlying weakness: insecure deserialization of custom field meta values in a WordPress plugin. This suggests a recurring vulnerability class across the WordPress plugin ecosystem, particularly in plugins that process serialized post meta data.
CodePress's response to CVE-2026-7654 was timely. The patched version 7.0.19 was released on May 29, 2026, approximately one week before the CVE was formally published on the NVD on June 5, 2026. The timeline suggests that the discoverer, Osvaldo Noe Gonzalez Del Rio, coordinated responsible disclosure with the vendor, so the patch was available before public disclosure.
References
- NVD Entry for CVE-2026-7654
- Wordfence Threat Intelligence Advisory: Admin Columns <= 7.0.18 PHP Object Injection to RCE
- Wordfence: Admin Columns Vulnerability Listing
- Admin Columns on WordPress Plugin Directory
- Vulnerable Code: IdsToCollection.php (line 42)
- Vulnerable Code: Meta.php (line 34)
- POP Chain: Native.php Serializer (line 148)
- POP Chain: ClosureStream.php (line 47)
- Plugin Repository Changeset 3553297 (Fix)
- CWE-502: Deserialization of Untrusted Data
- OWASP: PHP Object Injection
- WPScan: Admin Columns Vulnerabilities
- Patchstack: PHP Object Injection in WordPress



