Skip to content
KitploitKITPLOIT
ToolsBlog
Submit
ToolsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

··Feeds·Contact·Privacy·© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
Tools/GitHubGitHub/dinosn/givewp-cve-2026-82222-rce-lab
Vulnerability AnalysisExploitationWeb Application ExploitationLearning & EducationLabs & Practice
GitHubdinosn/givewp-cve-2026-82222-rce-lab

givewp-cve-2026-82222-rce-lab

Authorized Docker lab and clean PoC for validating CVE-2026-82222 RCE in GiveWP 4.16.5.1 and the 4.16.7.2 fix.

View Repository
19h 4m agoNot yet reviewed

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share

CVE-2026-82222 — GiveWP Marker-Only RCE Validation Lab

Security-research material for reproducing and validating CVE-2026-82222 in an isolated Docker lab.

For Direct RCE Poc and url scan use CVE-2026-8222-RCE.py

Verdict

Status: proven in the supplied lab

GiveWP 4.16.5.1 permits an initially unauthenticated attacker to persist a PHP object graph, revive it through GiveWP session handling, and execute a fixed marker command as the WordPress web-server user.

Tested positive result:

root@kitploit:~
GiveWP:    4.16.5.1
WordPress: 6.6.2
PHP:       8.1.30
Result:    /tmp/CVE-2026-82222-RCE-GETBAG created by www-data

The end-to-end result was also reproduced against pristine, non-instrumented GiveWP 4.16.5.1 source. GiveWP 4.16.7.2 blocked the HTTP carrier in the tested configuration and independently blocked the terminal gadget during a direct control.

This demonstrates command execution inside the WordPress container. It does not demonstrate root access, container escape, lateral movement, or host compromise.

Safety boundary

Use this repository only on systems you own or are explicitly authorized to test.

The supplied PoC is intentionally constrained:

  • It executes only touch /tmp/CVE-2026-82222-RCE-GETBAG.
  • It offers no arbitrary-command option.
  • It creates no shell, callback, persistence, or privilege escalation.
  • It refuses non-loopback targets unless the operator supplies the explicit --allow-authorized-non-loopback flag.
  • Docker publishes WordPress only on 127.0.0.1.
  • The Compose project name is derived from the checkout path so one clone cannot tear down another clone's containers or volumes.
  • The lab uses pristine official plugin source; it does not instrument or patch the vulnerable target.
  • The HTTP test creates a disposable donor user, metadata, and GiveWP session rows. Use the included reset command after testing.

    Quick start

    Requirements:

    • Docker with Compose v2
    • Python 3.10 or later
    • curl
    • unzip
    • sha256sum or shasum
    • Network access to downloads.wordpress.org for official plugin archives

    Run the complete vulnerable/patched matrix:

    root@kitploit:~
    ./lab verify
    

    The command:

    1. Downloads GiveWP 4.16.5.1 and 4.16.7.2 from WordPress.org.
    2. Verifies both SHA-256 hashes.
    3. Builds a fresh loopback-only WordPress 6.6.2/PHP 8.1 lab with normal WordPress registration explicitly disabled.
    4. Tests pristine 4.16.5.1 and requires marker creation by the web user.
    5. Builds a second fresh lab with pristine 4.16.7.2.
    6. Requires the HTTP marker to remain absent.
    7. Bypasses ingress in a direct control and confirms the patched ProviderForwarder terminal also rejects the string callable.

    The patched lab remains running at the end. Remove it with:

    root@kitploit:~
    ./lab reset
    

    To use another loopback port:

    root@kitploit:~
    LAB_PORT=8099 ./lab verify
    

    Expected evidence

    The vulnerable control must end with concrete terminal evidence:

    root@kitploit:~
    [PASS] E1: unauthenticated registration issued auth cookie
    [PASS] E3: serialized graph persisted in own last_name
    [PASS] E4: donation-form nonce obtained
    [PASS] E5: session write reached expected post-sink HTTP status=500
    [PASS] E6: session read/destruction trigger completed
    marker present and owned by the WordPress web user
    RESULT: VULNERABLE CONTROL CONFIRMED
    

    The patched control must show:

    root@kitploit:~
    [PASS] P1: patched registration gate blocked auth cookie
    DIRECT_MARKER=absent
    HTTP marker absent and direct terminal gadget blocked
    RESULT: PATCHED CONTROL CONFIRMED
    

    An HTTP 500, stored payload, exception, or detector hit without the marker is not accepted as RCE proof.

    Manual lab lifecycle

    Start and test the vulnerable release:

    root@kitploit:~
    ./lab start vulnerable
    ./lab test
    

    Start and test the patched release:

    root@kitploit:~
    ./lab start patched
    ./lab test
    

    Inspect current state:

    root@kitploit:~
    ./lab status
    

    Remove containers, volumes, test users, sessions, and marker state:

    root@kitploit:~
    ./lab reset
    

    Cached plugin ZIPs and extracted assets are preserved for faster reruns. Remove those exact generated assets as well with:

    root@kitploit:~
    ./lab reset --purge-assets
    

    Affected and tested versions

    VersionAssessment
    GiveWP 4.16.5.1RCE reproduced end to end
    GiveWP 4.16.6–4.16.7.1Reported affected; not individually reproduced here
    GiveWP 4.16.7.2Patched negative controls reproduced
    Later releasesNot individually tested; update to the latest supported release

    The public advisory identifies releases through 4.16.7.1 as affected. This repository directly proves only the two versions in its positive/negative test matrix.

    References:

    • Patchstack advisory
    • CVE-2026-82222
    • GiveWP hardening commit
    • Official GiveWP plugin page

    Root cause

    The exploit combines several behaviors:

    1. GiveWP 4.16.5.1 exposes a registration action that creates and authenticates a low-privilege donor even when normal WordPress registration is disabled.
    2. That user can persist serialized data in their own name metadata.
    3. Give\Helpers\Utils::maybeSafeUnserialize() uses allowed_classes => false, producing __PHP_Incomplete_Class, but a later serialization preserves the original class names and properties.
    4. The graph is stored in a GiveWP purchase session.
    5. A later unrestricted maybe_unserialize() revives the shipped classes.
    6. Automatic destruction enters a complete POP chain to system().

    Four literal namespace backslashes are required in the HTTP carrier. The two effective stripping passes reduce them 4 -> 2 -> 1. The payload is NUL-free, and PHP 8.1.30 was verified to hydrate the plain serialized name for the private Session::$attributeName property.

    Complete POP chain

    root@kitploit:~
    TCPDF::__destruct()
      -> TCPDF::_destroy(true)
      -> foreach ($this->imagekeys as $file)
      -> Symfony Session::getIterator()
      -> Session::getAttributeBag()
      -> Session::getBag($this->attributeName)
      -> $this->storage->getBag($attributeName)
      -> DonationFactory->__call('getBag', [$attributeName])
      -> call_user_func_array('system', [$attributeName])
      -> system('touch /tmp/CVE-2026-82222-RCE-GETBAG')
    

    Attacker-controlled graph:

    root@kitploit:~
    TCPDF
    ├── file_id = unique request identifier
    └── imagekeys = Give\Vendors\Symfony\Component\HttpFoundation\Session\Session
        ├── attributeName = fixed marker command
        └── storage = Give\TestData\Factories\DonationFactory
            └── loadedProviders["getBag"] = "system"
    

    The critical hidden transition is PHP's implicit IteratorAggregate dispatch. TCPDF::$imagekeys is untyped, so assigning a Symfony Session causes foreach to invoke Session::getIterator().

    The marker command executes before Symfony enforces the getAttributeBag(): AttributeBagInterface return type. The resulting TypeError and HTTP 500 are post-sink effects.

    What earlier attempts missed

    The rejected candidate graph used:

    root@kitploit:~
    TCPDF::$objcopy
      -> DonationFactory::$loadedProviders['__destruct'] = 'system'
    

    That graph cannot work. TCPDF::_destroy() only unsets objcopy; PHP does not route automatic destruction through __call('__destruct', ...).

    The missing operation was:

    root@kitploit:~
    foreach ($this->imagekeys as $file) {
    

    The earlier review followed destructors and explicit method calls but did not recursively inspect the implicit object protocol triggered by foreach. Symfony Session supplies the missing bridge:

    • foreach invokes getIterator().
    • getIterator() reaches getBag($attributeName).
    • Attacker-controlled storage is a DonationFactory.
    • The undefined getBag invokes ProviderForwarder::__call().
    • loadedProviders['getBag'] = 'system' selects the callable.
    • attributeName supplies the command argument.

    Source evidence

    Relevant GiveWP 4.16.5.1 locations:

    ComponentLocation
    Guarded initial unserializesrc/Helpers/Utils.php:203-217,237-241
    Donation carrierincludes/process-donation.php:157
    GiveWP session persistenceincludes/class-give-session.php:364-368,489-508
    Unrestricted session revivalincludes/class-give-session.php:347-350
    TCPDF destructorvendor/tecnickcom/tcpdf/tcpdf.php:2050-2052
    Attacker-controlled iterationvendor/tecnickcom/tcpdf/tcpdf.php:7885-7907
    Symfony iterator bridgevendor/vendor-prefixed/symfony/http-foundation/Session/Session.php:134-136
    Symfony getBag bridgevendor/vendor-prefixed/symfony/http-foundation/Session/Session.php:259-283
    Terminal callablesrc/TestData/Framework/ProviderForwarder.php:19-23
    GiveWP Composer autoloadersgive.php:624-625

    Official release hashes:

    root@kitploit:~
    give.4.16.5.1.zip
    95fc6709b6ed284074bf09be096e28d6299fd4a103848c2b1c3bf8a5772cf98c
    
    give.4.16.7.2.zip
    c5bc98da2abb748c64d31a43679ff5f223092dfff6d214132a55dcbc948e91ae
    

    The lab re-extracts each plugin from its verified ZIP before every fresh start, copies it into WordPress without modification, and byte-compares the deployed complete plugin file tree with the verified source. The three official Docker base images are also pinned to their multi-architecture manifest digests.

    Why 4.16.7.2 blocks the chain

    GiveWP 4.16.7.2 adds independent defenses across registration, serialized input handling, session revival, data migration, and gadget hardening.

    The terminal hardening requires the resolved object to implement the expected provider contract:

    root@kitploit:~
    if ( ! $provider instanceof Contract\Provider ) {
        return null;
    }
    

    The attacker-controlled string system is therefore rejected. The direct control in this repository bypasses the HTTP carrier and verifies that this terminal defense alone leaves its marker absent.

    Checking a real deployment

    A negative PoC result by itself does not prove a site is patched. A WAF, different routing, disabled registration, missing legacy form, session configuration, or disabled PHP command functions can all prevent the marker on a still-vulnerable codebase.

    Preferred validation procedure:

    1. Record the deployed GiveWP version and source revision.
    2. Back up or snapshot the WordPress site and database.
    3. Restore that snapshot into an isolated staging environment.
    4. Disable unnecessary outbound network access.
    5. Run this marker-only verifier against the clone.
    6. Update GiveWP to the latest supported release, with 4.16.7.2 as the minimum release containing the tested fixes.
    7. Repeat the identical test and require marker absence.
    8. Independently verify the installed plugin version and patched source.

    Version check:

    root@kitploit:~
    wp plugin get give --fields=name,status,version
    

    Remediation and incident review

    Update GiveWP to the latest supported version. After updating:

    • Clear PHP opcode caches where applicable.
    • Confirm no older GiveWP copy remains active or web-accessible.
    • Review unexpected WordPress or donor accounts.
    • Search user metadata and GiveWP sessions for serialized TCPDF, Symfony Session, or loadedProviders graphs.
    • Correlate registration, profile changes, donation requests, and subsequent HTTP 500 responses from the same session.
    • Investigate unexpected web-server child processes or filesystem changes.

    If an internet-exposed vulnerable installation contains object-injection artifacts, treat it as a potential compromise rather than only updating the plugin.

    Repository layout

    root@kitploit:~
    .
    ├── .github/workflows/validate.yml
    ├── .gitignore
    ├── README.md
    ├── SECURITY.md
    ├── docker-compose.yml
    ├── lab
    ├── poc
    │   ├── direct-pop-control.php
    │   └── poc.py
    └── scripts
        └── fetch-assets.sh
    

    Not committed:

    root@kitploit:~
    official plugin ZIPs
    extracted GiveWP source
    instrumented target source
    cookies or session data
    runtime evidence/logs
    internal target addresses or reusable credentials
    obsolete objcopy payloads
    historical validation output
    

    Validation boundaries

    ClaimStatus
    Persistent PHP object-injection carrierProven
    Revival of shipped classesProven
    Complete stock POP chainProven
    Fixed marker executed as the web userProven
    Reproduction against pristine 4.16.5.1Proven
    Negative controls against pristine 4.16.7.2Proven
    Every intermediate affected release testedNot tested
    Root privilegeNot claimed
    Container escapeNot tested
    Host compromiseNot tested

    The proof standard is deliberately strict: only an observable command marker through the unmodified stock chain is labeled RCE. HTTP acceptance, serialization, exceptions, and detector hits are intermediate evidence.

    Download Tool