
# Autorisierte Docker-Lab-Umgebung und sauberer PoC zur Validierung von CVE-2026-82222 RCE in GiveWP 4.16.5.1 sowie des Fixes in 4.16.7.2.
Sicherheitsforschungsmaterial zur Reproduktion und Validierung von CVE-2026-82222 in einem isolierten Docker-Labor.
Für direkten RCE-PoC und URL-Scan verwenden Sie CVE-2026-8222-RCE.py
Status: im bereitgestellten Labor nachgewiesen
GiveWP 4.16.5.1 erlaubt es einem zunächst nicht authentifizierten Angreifer, einen PHP-Objektgraphen zu persistieren, ihn über die GiveWP-Sitzungsverwaltung wiederzubeleben und einen festen Marker-Befehl als WordPress-Webserver-Benutzer auszuführen.
Getestetes positives Ergebnis:
GiveWP: 4.16.5.1
WordPress: 6.6.2
PHP: 8.1.30
Result: /tmp/CVE-2026-82222-RCE-GETBAG created by www-data
Das End-to-End-Ergebnis wurde auch gegen unveränderte, nicht instrumentierte GiveWP-4.16.5.1-Quellen reproduziert. GiveWP 4.16.7.2 blockierte den HTTP-Träger in der getesteten Konfiguration und blockierte unabhängig davon das Terminal-Gadget während einer direkten Kontrolle.
Dies demonstriert die Befehlsausführung innerhalb des WordPress-Containers. Es demonstriert keinen Root-Zugriff, keinen Container-Ausbruch, keine laterale Bewegung und keine Host-Kompromittierung.
Verwenden Sie dieses Repository nur auf Systemen, die Ihnen gehören oder für die Sie ausdrücklich autorisiert sind, sie zu testen.
Der bereitgestellte PoC ist bewusst eingeschränkt:
touch /tmp/CVE-2026-82222-RCE-GETBAG aus.--allow-authorized-non-loopback-Flag.127.0.0.1.Der HTTP-Test erstellt einen Wegwerf-Spenderbenutzer, Metadaten und GiveWP-Sitzungszeilen. Verwenden Sie nach dem Test den enthaltenen Reset-Befehl.
Voraussetzungen:
curlunzipsha256sum oder shasumdownloads.wordpress.org für offizielle Plugin-ArchiveFühren Sie die vollständige verwundbare/gepatchte Matrix aus:
./lab verify
Der Befehl:
ProviderForwarder-Terminal den String-Callable ebenfalls ablehnt.Das gepatchte Labor läuft am Ende weiter. Entfernen Sie es mit:
./lab reset
Um einen anderen Loopback-Port zu verwenden:
LAB_PORT=8099 ./lab verify
Die verwundbare Kontrolle muss mit konkreten Terminal-Beweisen enden:
[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
Die gepatchte Kontrolle muss Folgendes zeigen:
[PASS] P1: patched registration gate blocked auth cookie
DIRECT_MARKER=absent
HTTP marker absent and direct terminal gadget blocked
RESULT: PATCHED CONTROL CONFIRMED
Ein HTTP-500, gespeicherte Nutzlast, eine Ausnahme oder ein Detektor-Treffer ohne den Marker wird nicht als RCE-Beweis akzeptiert.
Starten und testen Sie die verwundbare Version:
./lab start vulnerable
./lab test
Starten und testen Sie die gepatchte Version:
./lab start patched
./lab test
Aktuellen Zustand prüfen:
./lab status
Container, Volumes, Testbenutzer, Sitzungen und Marker-Zustand entfernen:
./lab reset
Zwischengespeicherte Plugin-ZIPs und extrahierte Assets werden für schnellere Wiederholungsläufe aufbewahrt. Entfernen Sie auch diese exakt generierten Assets mit:
./lab reset --purge-assets
| Version | Bewertung |
|---|---|
| GiveWP 4.16.5.1 | RCE end-to-end reproduziert |
| GiveWP 4.16.6–4.16.7.1 | Als betroffen gemeldet; hier nicht einzeln reproduziert |
| GiveWP 4.16.7.2 | Gepatchte Negativkontrollen reproduziert |
| Spätere Versionen | Nicht einzeln getestet; auf die neueste unterstützte Version aktualisieren |
Das öffentliche Advisory identifiziert Versionen bis einschließlich 4.16.7.1 als betroffen. Dieses Repository beweist direkt nur die beiden Versionen in seiner Positiv-/Negativ-Testmatrix.
Referenzen:
Der Exploit kombiniert mehrere Verhaltensweisen:
Give\Helpers\Utils::maybeSafeUnserialize() verwendet allowed_classes => false, was __PHP_Incomplete_Class erzeugt, aber eine spätere Serialisierung bewahrt die ursprünglichen Klassennamen und Eigenschaften.maybe_unserialize() belebt die mitgelieferten Klassen wieder.system() ein.Im HTTP-Träger sind vier wörtliche Namespace-Backslashes erforderlich. Die beiden effektiven Stripping-Durchläufe reduzieren sie 4 -> 2 -> 1. Die Nutzlast ist NUL-frei, und PHP 8.1.30 wurde verifiziert, um den einfachen serialisierten Namen für die private Session::$attributeName-Eigenschaft zu hydratisieren.
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')
Angreifer-kontrollierter Graph:
TCPDF
├── file_id = eindeutige Anfragekennung
└── imagekeys = Give\Vendors\Symfony\Component\HttpFoundation\Session\Session
├── attributeName = fester Marker-Befehl
└── storage = Give\TestData\Factories\DonationFactory
└── loadedProviders["getBag"] = "system"
Der kritische versteckte Übergang ist PHPs impliziter IteratorAggregate-Dispatch. TCPDF::$imagekeys ist untypisiert, sodass die Zuweisung einer Symfony-Session dazu führt, dass foreach Session::getIterator() aufruft.
Der Marker-Befehl wird ausgeführt, bevor Symfony den Rückgabetyp getAttributeBag(): AttributeBagInterface durchsetzt. Der resultierende TypeError und HTTP-500 sind Post-Sink-Effekte.
Der abgelehnte Kandidatengraph verwendete:
TCPDF::$objcopy
-> DonationFactory::$loadedProviders['__destruct'] = 'system'
Dieser Graph kann nicht funktionieren. TCPDF::_destroy() setzt nur objcopy zurück; PHP leitet die automatische Zerstörung nicht durch __call('__destruct', ...).
Die fehlende Operation war:
foreach ($this->imagekeys as $file) {
Die frühere Überprüfung folgte Destruktoren und expliziten Methodenaufrufen, untersuchte aber nicht rekursiv das implizite Objektprotokoll, das durch foreach ausgelöst wird. Die Symfony-Session liefert die fehlende Brücke:
foreach ruft getIterator() auf.getIterator() erreicht getBag($attributeName).storage ist eine DonationFactory.getBag ruft ProviderForwarder::__call() auf.loadedProviders['getBag'] = 'system' wählt den Callable aus.attributeName liefert das Befehlsargument.Relevante GiveWP-4.16.5.1-Stellen:
| Komponente | Ort |
|---|---|
| Geschütztes initiales Unserialize | src/Helpers/Utils.php:203-217,237-241 |
| Spenden-Träger | includes/process-donation.php:157 |
| GiveWP-Sitzungspersistenz | includes/class-give-session.php:364-368,489-508 |
| Uneingeschränkte Sitzungswiederbelebung | includes/class-give-session.php:347-350 |
| TCPDF-Destruktor | vendor/tecnickcom/tcpdf/tcpdf.php:2050-2052 |
| Angreifer-kontrollierte Iteration | vendor/tecnickcom/tcpdf/tcpdf.php:7885-7907 |
| Symfony-Iterator-Brücke | vendor/vendor-prefixed/symfony/http-foundation/Session/Session.php:134-136 |
Symfony-getBag-Brücke | vendor/vendor-prefixed/symfony/http-foundation/Session/Session.php:259-283 |
| Terminal-Callable | src/TestData/Framework/ProviderForwarder.php:19-23 |
| GiveWP-Composer-Autoloader | give.php:624-625 |
Offizielle Release-Hashes:
give.4.16.5.1.zip
95fc6709b6ed284074bf09be096e28d6299fd4a103848c2b1c3bf8a5772cf98c
give.4.16.7.2.zip
c5bc98da2abb748c64d31a43679ff5f223092dfff6d214132a55dcbc948e91ae
Das Labor extrahiert jedes Plugin vor jedem frischen Start aus seinem verifizierten ZIP neu, kopiert es unverändert in WordPress und vergleicht den bereitgestellten vollständigen Plugin-Dateibaum byteweise mit der verifizierten Quelle. Die drei offiziellen Docker-Basisimages sind ebenfalls auf ihre Multi-Architektur-Manifest-Digests festgepinnt.
GiveWP 4.16.7.2 fügt unabhängige Verteidigungen über Registrierung, Behandlung serialisierter Eingaben, Sitzungswiederbelebung, Datenmigration und Gadget-Härtung hinzu.
Die Terminal-Härtung erfordert, dass das aufgelöste Objekt den erwarteten Provider-Vertrag implementiert:
if ( ! $provider instanceof Contract\Provider ) {
return null;
}
Der angreifer-kontrollierte String system wird daher abgelehnt. Die direkte Kontrolle in diesem Repository umgeht den HTTP-Träger und verifiziert, dass diese Terminal-Verteidigung allein ihren Marker abwesend lässt.
Ein negatives PoC-Ergebnis allein beweist nicht, dass eine Website gepatcht ist. Eine WAF, anderes Routing, deaktivierte Registrierung, fehlendes Legacy-Formular, Sitzungskonfiguration oder deaktivierte PHP-Befehlsfunktionen können den Marker auf einer weiterhin verwundbaren Codebasis verhindern.
Bevorzugtes Validierungsverfahren:
Versionsprüfung:
wp plugin get give --fields=name,status,version
Aktualisieren Sie GiveWP auf die neueste unterstützte Version. Nach der Aktualisierung:
TCPDF-, Symfony-Session- oder loadedProviders-Graphen.Wenn eine internetexponierte verwundbare Installation Objektinjektions-Artefakte enthält, behandeln Sie sie als potenzielle Kompromittierung und nicht nur als Plugin-Aktualisierung.
.
├── .github/workflows/validate.yml
├── .gitignore
├── README.md
├── SECURITY.md
├── docker-compose.yml
├── lab
├── poc
│ ├── direct-pop-control.php
│ └── poc.py
└── scripts
└── fetch-assets.sh
Nicht eingecheckt:
offizielle Plugin-ZIPs
extrahierte GiveWP-Quellen
instrumentierte Zielquellen
Cookies oder Sitzungsdaten
Laufzeitbeweise/Protokolle
interne Zieladressen oder wiederverwendbare Anmeldedaten
veraltete objcopy-Nutzlasten
historische Validierungsausgaben
| Behauptung | Status |
|---|---|
| Persistenter PHP-Objektinjektions-Träger | Nachgewiesen |
| Wiederbelebung der mitgelieferten Klassen | Nachgewiesen |
| Vollständige Stock-POP-Kette | Nachgewiesen |
| Fester Marker als Web-Benutzer ausgeführt | Nachgewiesen |
| Reproduktion gegen unverändertes 4.16.5.1 | Nachgewiesen |
| Negativkontrollen gegen unverändertes 4.16.7.2 | Nachgewiesen |
| Jede Zwischenversion als betroffen getestet | Nicht getestet |
| Root-Privileg | Nicht beansprucht |
| Container-Ausbruch | Nicht getestet |
| Host-Kompromittierung | Nicht getestet |
Der Beweisstandard ist bewusst streng: Nur ein beobachtbarer Befehlsmarker durch die unveränderte Stock-Kette wird als RCE bezeichnet. HTTP-Akzeptanz, Serialisierung, Ausnahmen und Detektor-Treffer sind Zwischenbeweise.