
Prueba de concepto en Python para CVE-2026-97657: falsifica tokens de recuperación AES-CTR mediante maleabilidad XOR para eludir la autenticación y tomar el control de cuentas de WordPress en Abandoned Cart Pro ≤10.8.0.
PoC en Python 3 para CVE-2026-97657 — Abandoned Cart Pro for WooCommerce (Tyche Softwares) ≤ 10.8.0. CNA: Wordfence.
| Producto | Abandoned Cart Pro for WooCommerce |
| Afectado | ≤ 10.8.0 (actualizar a una versión corregida superior a 10.8.0) |
| CWE | CWE-639 — omisión de autorización mediante clave controlada por el usuario |
| CVSS 3.1 | 9.8 Crítica |
| Marcador de verificación | POCBIT-97657-OK (comprobación de sesión/administrador) |
| PoCbit | https://pocbit.org/pocs/cve-2026-97657 |
Los enlaces de recuperación utilizan tokens AES-CTR sin MAC (diseño compatible con Wcal_Aes_Ctr: nonce CTR de 8 bytes + texto cifrado, en base64). El plugin carga la encrypt_key por correo electrónico desde el parámetro de consulta user_email controlado por el atacante. En wcap_email_track_links() (Pro; análogo en Lite wcal_email_track_links()), la comprobación de wcap_nonce / HMAC se ejecuta solo cuando wcap_nonce no está vacío, por lo que omitirlo evita la verificación de integridad. Tras el descifrado, el abandoned_id (ID de la fila del carrito) se utiliza sin comprobar que pertenezca a ese correo electrónico, y luego se ejecuta wp_set_auth_cookie() cuando el inicio de sesión automático está habilitado (por defecto).
Un atacante no autenticado con una URL de recuperación legítima de su propio correo de recordatorio puede manipular por XOR el texto cifrado CTR para cambiar el ID de carrito incrustado por el ID de carrito abandonado de una víctima (de la misma longitud que el ID de origen), abrir la URL falsificada sin wcap_nonce e iniciar sesión como la víctima (incluidos los administradores que abandonaron un carrito).
Forma típica de la URL (Pro):
GET /?wacp_action=checkout_link&[email protected]&validate=<token>
(existen variantes wcap_action / wcal_action; las URL de enlaces de seguimiento pueden incluir parámetros adicionales como c=.)
cd CVE-2026-97657
pip install -r requirements.txt
requests, urllib3| Modo | Propósito | Argumentos requeridos |
|---|---|---|
check (predeterminado) | Leer el readme.txt del plugin, marcar la versión ≤ 10.8.0 | -t URL o --list FILE |
forge | Construir localmente una URL de recuperación falsificada (sin HTTP) | --recovery-link, --source-cart-id, --target-cart-id |
exploit | Falsificar token + HTTP GET + verificar sesión de WordPress | Igual que forge |
Flujo de trabajo recomendado (laboratorio autorizado):
check masivo o individual — confirmar el slug del plugin + versión ≤ 10.8.0.forge — validar el relleno del ID de carrito y la forma de la URL antes de enviar tráfico.exploit — un GET a la URL de recuperación falsificada; confirmar cookies + /wp-admin/.Exploit/forge no usa -t / --list. El host objetivo se toma de --recovery-link (esquema + host).
| Requisito | Motivo |
|---|---|
| Una URL de recuperación legítima | Proporciona un nonce CTR válido + texto cifrado y (normalmente) tu fila de user_email / encrypt_key en ac_sent_history |
| Plugin ≤ 10.8.0 con auto-login habilitado (predeterminado) | Las compilaciones parcheadas o el auto-login deshabilitado bloquean wp_set_auth_cookie() |
| Usuario registrado de WordPress víctima con una fila de carrito abandonado | La ruta de toma de control inicia sesión con user_id de ac_abandoned_cart_history; los carritos de invitados puros usan una rama diferente |
URL de checkout_link muy preferida | El texto plano es {abandoned_cart_id}&url=.... Los tokens de track_links codifican email_sent_id, no el ID de carrito — un tipo de ID incorrecto rompe el forge |
| IDs de igual longitud | La maleabilidad XOR de CTR solo invierte bytes 1:1; rellenar con ceros (ver abajo) |
--recovery-linkcheckout_link completo del correo (o de los webhooks / exportación del plugin si está habilitada).Ejemplos de formas Pro (de la documentación del proveedor / webhooks):
https://shop.example/?wacp_action=checkout_link&validate=<base64-token>
https://shop.example/?wacp_action=checkout_link&[email protected]&validate=<token>
https://shop.example/?wacp_action=track_links&[email protected]&validate=<token>&c=<coupon-token>
Pega la URL exactamente como aparece en el correo electrónico (incluyendo validate; el orden de los parámetros de consulta no importa).
| Origen | Campo |
|---|---|
| WooCommerce Abandoned Cart → Cart details (admin) | ID del carrito abandonado (clave primaria de la BD) |
Carga útil del Webhook (contexto wcap_cart, checkout_link) | "id": "383" en el recurso del carrito |
| MySQL (laboratorio) | wp_ac_abandoned_cart_history.id (el prefijo de tabla de Pro puede variar) |
--source-cart-id debe ser el ID dentro del texto plano de tu token (tu fila de carrito abandonado). --target-cart-id es la fila de la víctima en la misma tabla.
El PoC rechaza longitudes que no coinciden. Rellena ambos IDs con la misma anchura:
| Origen (tuyo) | Destino (víctima) | ¿OK? |
|---|---|---|
383 | 001 | Sí (3 caracteres) |
0383 | 0001 | Sí (4 caracteres) |
383 | 1 | No — ejecuta forge con 0383 / 0001 en su lugar |
| Opción | Modos | Significado |
|---|---|---|
-t, --target | check | Raíz del sitio WordPress, p. ej. https://shop.example.com |
--list FILE | check | Una URL de sitio por línea; los comentarios con # se ignoran |
--mode | all | check (predeterminado), forge, exploit |
--recovery-link URL | forge, exploit | URL de recuperación completa del correo de recordatorio |
--source-cart-id | forge, exploit | Tu ID de fila de carrito abandonado (en el texto plano del token) |
--target-cart-id | forge, exploit | ID de fila de carrito abandonado de la víctima (misma longitud de cadena) |
-j N | check (list) | Trabajadores en paralelo (predeterminado 8) |
--timeout SEC | check, exploit | Tiempo de espera HTTP (predeterminado 15) |
--insecure | check, exploit | Deshabilitar la verificación del certificado TLS |
--jsonl FILE | check, exploit | Añadir un objeto JSON por ejecución |
--vuln-list FILE | check | Escribir las URL con versión ≤ 10.8.0 |
-q, --quiet | all | Menos salida en consola |
--no-color | all | Terminal sin color |
Códigos de salida
| Código | Cuándo |
|---|---|
| 0 | Exploit: success: true; forge: siempre; check: siempre (-t único) |
| 1 | Exploit: falló la verificación de inicio de sesión/admin |
| 2 | Ejecución de lista de check sin semántica de -t (informativo) |
python poc.py -t https://shop.example.com --mode check
python poc.py --list targets.example.txt --mode check -j 16 --vuln-list hits.txt --jsonl scan.jsonl
Sondas: