CVE-2026-82222 es una inyección de objetos PHP crítica y sin autenticación en GiveWP, un plugin de donaciones para WordPress con más de 100.000 instalaciones. Todas las versiones hasta 4.16.7.1 están afectadas. La vulnerabilidad tiene puntuación CVSS 10.0 y conduce a ejecución remota de código. Udin Chan la reportó a través de Patchstack el 28/07/2026. La versión 4.16.7.2 la corrige.

src/Helpers/Utils.php
if ( self::containsPhpIncompleteClass( $unserializedData ) ) {
    return false; // 4.16.6 returned $data here, re-arming the payload
}

El exploit encadena tres fallos. Primero, el helper safeUnserialize() en src/Helpers/Utils.php llama a unserialize() con allowed_classes en false. Esa opción no elimina los objetos. Los convierte en placeholders __PHP_Incomplete_Class que conservan el nombre de clase original y todas las propiedades. Cuando ese placeholder se vuelve a serializar, PHP reemite los bytes originales. La carga útil sobrevive intacta.

Segundo, el flujo de donaciones en includes/process-donation.php procesa los datos de la cuenta con ese helper y los guarda en la tabla wp_give_sessions. El atacante coloca un gadget serializado en su propio campo last_name mediante profile.php. Al enviar la donación, el valor pasa por safeUnserialize() y se escribe en la tabla de sesiones. La siguiente lectura llama a unserialize() sin protección, y el objeto gadget vuelve a la vida.

Tercero, la gadget chain combina la biblioteca TCPDF incluida con las clases Give\TestData. El atacante controla la propiedad array loadedProviders del objeto deserializado. Al destruirse el objeto, TCPDF::__destruct() invoca _destroy(), que llama a un método indefinido y cae en el método mágico __call(). Este pasa el valor directamente a call_user_func_array() sin validación. Si loadedProviders vale system, se ejecutan comandos arbitrarios del sistema operativo con los permisos del usuario del servidor web.

La cadena necesita una cuenta, y GiveWP la regala. El manejador give_action=user_register ignora la opción users_can_register de WordPress. Cualquiera puede crear una cuenta y recibir una cookie de autenticación aunque el registro esté desactivado. La versión 4.16.6 añadió un nonce, pero los nonces de visitantes sin sesión son idénticos en todo el sitio, y el shortcode [give_register] emite uno en cualquier página pública. Se recolecta una vez y se reutiliza.

La secuencia completa: registrarse con give_action=user_register, plantar el gadget en last_name, obtener un nonce de donación con action=give_donation_form_nonce y enviar una donación con action=give_process_donation omitiendo give_last. El servidor escribe el gadget en wp_give_sessions antes de devolver HTTP 500. Cualquier petición posterior al frontend con la misma cookie dispara deserialización, destrucción y system(). La salida se refleja en la respuesta HTTP. Hasta 4.16.5.1 basta una instalación por defecto. Las versiones 4.16.6 a 4.16.7.1 reducen la superficie, pero siguen siendo explotables en condiciones habituales como formularios legacy o el Option-Based Form Editor.

El parche 4.16.7.2 rompe la cadena en cinco puntos. safeUnserialize() ahora devuelve false al detectar una __PHP_Incomplete_Class; el intento anterior de 4.16.6 devolvía la cadena cruda, lo que rearmaba la carga útil. process-donation.php rechaza donaciones con datos serializados en los campos de nombre, y el fallback de usermeta pasa por give_clean(). Los tres sumideros de lectura class-give-session.php, class-give-db-sessions.php y class-give-donor-wall.php ahora pasan explícitamente allowed_classes en false; el donor wall era el más crítico porque el shortcode público [give_donor_wall] era accesible de forma anónima. ProviderForwarder::__call() ahora verifica el provider resuelto antes de llamarlo. Los metadatos de nombre de donor y billing pasan por sanitize_text_field(). Una migración llamada SanitizeSerializedObjectPayloads recorre usermeta, give_donormeta, give_donationmeta y give_sessions, sustituyendo los objetos anidados por cadenas vacías para limpiar cargas plantadas antes de la actualización.

El bypass de registro sigue sin resolverse en 4.16.7.2, pero sin la inyección de objetos ya no conduce a ejecución de código. Patchstack lo trata como un problema de control de acceso independiente. Los administradores deben actualizar de inmediato, verificar que la migración de saneamiento se ejecutó realmente, revisar las cuatro tablas en busca de cargas residuales y considerar bloquear give_action=user_register con una regla WAF si el registro no es necesario.