
GiveWP Plugin Flaw Allows Unauthenticated Remote Code Execution
A severe GiveWP flaw let attackers execute commands on WordPress donation sites with no account. Fixed in 4.16.7.2 as CVE-2026-82222.
A vulnerability in the GiveWP WordPress donation plugin allowed an attacker with no account to run arbitrary commands on the underlying web server, according to a security advisory from Patchstack. GiveWP is a popular plugin that provides donation forms, payment gateways, donor management and reporting for nonprofits and other fundraising organizations. The flaw, tracked as CVE-2026-82222, received a CVSS severity score of 10.0, the highest possible rating. It affected GiveWP versions 4.16.7.1 and below, and was fixed in version 4.16.7.2, released on August 27, 2026. Patchstack, a WordPress security company, said its users were protected by mitigation rules and that the issue was discovered and reported by researcher Udin Chan. The advisory lists the plugin as having around 100,000 installations, which makes the update urgent for many site owners.
The technical problem is known as PHP object injection. Websites built on WordPress and PHP often store complex data as a text string, a process called serialization, and later turn that string back into a live program object through a function called unserialize. If an attacker can control the serialized text, the PHP application can be tricked into creating a dangerous object that runs attacker-chosen code. GiveWP tried to make this safe with a helper function called safeUnserialize, which used the PHP option allowed_classes set to false. However, that option does not delete the attacker's object. Instead, it converts the object into a placeholder called __PHP_Incomplete_Class that keeps the original class name and all its properties. When that placeholder is later serialized again, PHP writes the original dangerous bytes back out unchanged. As the Patchstack advisory explains, the helper hid the payload for one read and then handed the untouched serialized object back to storage, so the attack simply happened at the next unguarded read.
The first step in the attack requires a logged-in donor. That sounds like a barrier, but researchers described a way to get an account for free. GiveWP exposes an unauthenticated registration action called give_action=user_register, which never checks the WordPress setting that controls whether visitors can register. Even on a site with registration disabled, an attacker could send a crafted request to create an account and receive an authentication cookie. Before version 4.16.6 there was no nonce requirement on this handler. A nonce is a temporary token meant to prove a request came from an allowed page. Version 4.16.6 added a nonce requirement, but the nonce is only emitted by a page that uses the [give_register] shortcode, and WordPress nonces for logged-out visitors are identical for every anonymous request to the same site. That means an attacker can harvest the nonce once and reuse it on any site that renders that shortcode publicly. In short, the account barrier was not a real barrier.
Once inside, the attacker plants a serialized gadget chain in their own account's last name field through the profile page. The term gadget chain refers to a sequence of methods in already-loaded code that, when an object is destroyed or serialized, ultimately calls a dangerous function. GiveWP ships with two libraries that together form a complete chain: the TCPDF library and the Give\TestData classes. When the injected object is destroyed, it enters TCPDF::__destruct, which calls _destroy, and that reaches a magic method in the ProviderForwarder trait. That method uses call_user_func_array with an attacker-controlled callable and argument. Because the attacker controls an array property called loadedProviders inside the injected object, they can set it to the PHP system function. The result is execution of an arbitrary operating system command as the web server user.
The donation flow connects these pieces. When the logged-in attacker submits a donation, the plugin builds a donor information record. It takes the last_name value from the database, where the serialized gadget has been stored, and runs it through the "safe" unserialize helper. Because the data comes from the database rather than directly from the request, ordinary input validation never sees it. The placeholder object is then serialized on its way into the wp_give_sessions table, and the original dangerous bytes are stored intact. The next time the site reads that session, it unserializes the bytes without the allowed_classes guard, which brings the real gadget to life. The full chain described by Patchstack has four steps: register an account, plant the gadget in the profile, poison the session by submitting a donation without a last_name value, and then request any front-end page with the same cookie to trigger the command.
The advisory also explains a reachability change between versions. On GiveWP 4.16.5.1 and below, a default installation was enough: the plugin shipped with an active manual (Test Donation) gateway and an active offline gateway, and only needed one published donation form. No Test Mode, open registration, debug mode, or administrator action was required. Versions 4.16.6 through 4.16.7.1 made the legacy donation processor bail out when the submitted form is a Visual Form Builder (v3) form, so a fresh default install was no longer exploitable through that path. However, a single give_forms post lacking formBuilderSettings re-arms the chain, in any post status including draft and trashed. That covers sites upgraded from older versions, any form import or restore, and any site where an administrator has enabled the Option-Based Form Editor.
The patch includes an important lesson about deserialization flaws. Before the final fix, GiveWP released version 4.16.6, which attempted to detect the __PHP_Incomplete_Class placeholder and, when found, return the raw serialized string. That returned the original payload bytes verbatim, leaving the attack just as possible later. Version 4.16.7.2 changed this to return false instead, which stops the payload. The developers then closed the chain at five independent points. The write path in process-donation.php now rejects a donation outright if any name field holds serialized data, and the user meta fallback runs through give_clean, which returns an empty string for serialized input. Three read sinks now pass allowed_classes set to false: the session getter, the session table read, and the donor wall. The donor wall matters because it was reachable by an anonymous visitor through the public [give_donor_wall] shortcode with no session cookie at all. The gadget itself was fixed: ProviderForwarder::__call now verifies that the resolved provider implements the expected contract before calling it. Donor and billing name meta are passed through sanitize_text_field when payments are saved. Finally, a migration called SanitizeSerializedObjectPayloads walks usermeta, give_donormeta, give_donationmeta and give_sessions and replaces any nested object with an empty string. That last step
How to Protect Yourself
- Open your WordPress admin dashboard, go to Plugins, and update GiveWP to version 4.16.7.2 or newer right away.
- If you run a donation site, look in your user list for new accounts you did not create and remove any you do not recognize.
- Ask your hosting provider whether they apply firewall rules for known GiveWP attacks, and enable a WordPress security plugin that updates its blocklist automatically if not.
- Keep regular backups of your website files and database, which is the stored information behind your site, so you can restore quickly if something looks wrong.
- Check that your WordPress registration option is turned off unless you genuinely need public sign-ups, even though this flaw bypassed it before the fix.
Vulnerabilities & Fixes
- CVE-2026-82222 Unauthenticated PHP object injection leading to remote code execution in GiveWP; fixed in version 4.16.7.2. View the fix & details →
Terms Explained
- PHP object injection An attack where an attacker supplies crafted text that a website turns into a program object, allowing unwanted actions.
- Remote code execution The ability for an attacker to run commands on a web server as if they were using it directly.
- Serialized data A text format used to store complex website information so it can be saved and restored later.
- Gadget chain A sequence of built-in code parts that an attacker connects to turn object injection into a dangerous action.
- Nonce A temporary token a website uses to verify that a request comes from an allowed page, though it can sometimes be reused by anonymous visitors.
- CVSS score A number from 0 to 10 used to rate how severe a security issue is, with 10 being the most critical.