
PoC Python pour CVE-2026-97657 : forge des jetons de récupération AES-CTR via la malléabilité XOR pour contourner l'authentification et prendre le contrôle de comptes WordPress dans Abandoned Cart Pro ≤10.8.0.
PoC en Python 3 pour CVE-2026-97657 — Abandoned Cart Pro for WooCommerce (Tyche Softwares) ≤ 10.8.0. CNA : Wordfence.
| Produit | Abandoned Cart Pro for WooCommerce |
| Affecté | ≤ 10.8.0 (mettre à niveau vers une version corrigée supérieure à 10.8.0) |
| CWE | CWE-639 — contournement d'autorisation via une clé contrôlée par l'utilisateur |
| CVSS 3.1 | 9.8 Critique |
| Marqueur de vérification | POCBIT-97657-OK (vérification de session/admin) |
| PoCbit | https://pocbit.org/pocs/cve-2026-97657 |
Les liens de récupération utilisent des jetons AES-CTR sans MAC (disposition compatible avec Wcal_Aes_Ctr : nonce CTR de 8 octets + texte chiffré, en base64). Le plugin charge la encrypt_key propre à chaque e-mail depuis le paramètre de requête user_email contrôlé par l'attaquant. Dans wcap_email_track_links() (Pro ; analogue Lite wcal_email_track_links()), la vérification wcap_nonce / HMAC ne s'exécute que lorsque wcap_nonce est non vide, donc l'omettre contourne la vérification d'intégrité. Après le déchiffrement, l'abandoned_id (ID de la ligne de panier) est utilisé sans prouver qu'il appartient à cet e-mail, puis wp_set_auth_cookie() est exécuté lorsque la connexion automatique est activée (par défaut).
Un attaquant non authentifié disposant d'une URL de récupération légitime issue de son propre e-mail de rappel peut malformer par XOR le texte chiffré CTR pour remplacer l'ID de panier intégré par l'ID de panier abandonné d'une victime (de même longueur que l'ID source), ouvrir l'URL forgée sans wcap_nonce, et se connecter en tant que la victime (y compris les administrateurs ayant abandonné un panier).
Forme typique de l'URL (Pro) :
GET /?wacp_action=checkout_link&[email protected]&validate=<token>
(Des variantes wcap_action / wcal_action existent ; les URLs de track-links peuvent inclure des paramètres supplémentaires tels que c=.)
cd CVE-2026-97657
pip install -r requirements.txt
requests, urllib3| Mode | Objectif | Arguments requis |
|---|---|---|
check (par défaut) | Lire le readme.txt du plugin, signaler une version ≤ 10.8.0 | -t URL ou --list FILE |
forge | Construire localement une URL de récupération forgée (sans HTTP) | --recovery-link, --source-cart-id, --target-cart-id |
exploit | Forger le token + requête HTTP GET + vérifier la session WordPress | Identique à forge |
Flux de travail recommandé (laboratoire autorisé) :
check en masse ou unique — confirmer le slug du plugin + version ≤ 10.8.0.forge — valider le remplissage de l'ID de panier et la forme de l'URL avant d'envoyer du trafic.exploit — une requête GET vers l'URL de récupération forgée ; confirmer les cookies + /wp-admin/.Exploit/forge n'utilise pas -t / --list. L'hôte cible est extrait de --recovery-link (schéma + hôte).
| Exigence | Raison |
|---|---|
| Une URL de récupération légitime | Fournit un nonce CTR valide + le chiffré et (généralement) votre ligne user_email / encrypt_key dans ac_sent_history |
| Plugin ≤ 10.8.0 avec auto-login activé (par défaut) | Les versions corrigées ou l'auto-login désactivé bloquent wp_set_auth_cookie() |
| Utilisateur WordPress enregistré victime avec une ligne de panier abandonné | Le chemin de prise de contrôle connecte le user_id depuis ac_abandoned_cart_history ; les paniers purement invités utilisent une branche différente |
URL checkout_link fortement préférée | Le texte en clair est {abandoned_cart_id}&url=.... Les tokens track_links encodent email_sent_id, pas l'ID de panier — un mauvais type d'ID casse le forge |
| IDs de longueur égale | La malléabilité XOR du CTR ne permute les octets que 1:1 ; compléter avec des zéros (voir ci-dessous) |
--recovery-linkcheckout_link complet depuis l'e-mail (ou depuis les webhooks / l'export du plugin si activé).Exemples de formes Pro (d'après la documentation du fournisseur / les 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>
Collez l'URL exactement comme dans l'e-mail (y compris validate ; l'ordre des paramètres de requête n'a pas d'importance).
| Source | Champ |
|---|---|
| WooCommerce Abandoned Cart → Cart details (admin) | ID du panier abandonné (clé primaire de la base de données) |
Charge utile du Webhook (contexte wcap_cart, checkout_link) | "id": "383" sur la ressource panier |
| MySQL (lab) | wp_ac_abandoned_cart_history.id (le préfixe de table Pro peut différer) |
--source-cart-id doit être l'ID à l'intérieur du texte en clair de votre jeton (votre ligne de panier abandonné). --target-cart-id est la ligne de la victime dans la même table.
Le PoC rejette les longueurs différentes. Complétez les deux ID à la même largeur :
| Source (le vôtre) | Cible (victime) | OK ? |
|---|---|---|
383 | 001 | Oui (3 caractères) |
0383 | 0001 | Oui (4 caractères) |
383 | 1 | Non — exécutez forge avec 0383 / 0001 à la place |
| Option | Modes | Signification |
|---|---|---|
-t, --target | check | Racine du site WordPress, par ex. https://shop.example.com |
--list FILE | check | Une URL de site par ligne ; les commentaires # sont ignorés |
--mode | all | check (par défaut), forge, exploit |
--recovery-link URL | forge, exploit | URL de récupération complète issue de l'e-mail de rappel |
--source-cart-id | forge, exploit | L'ID de votre ligne de panier abandonné (dans le texte en clair du jeton) |
--target-cart-id | forge, exploit | L'ID de la ligne de panier abandonné de la victime (même longueur de chaîne) |
-j N | check (list) | Workers parallèles (par défaut 8) |
--timeout SEC | check, exploit | Délai d'expiration HTTP (par défaut 15) |
--insecure | check, exploit | Désactiver la vérification du certificat TLS |
--jsonl FILE | check, exploit | Ajouter un objet JSON par exécution |
--vuln-list FILE | check | Écrire les URL dont la version ≤ 10.8.0 |
-q, --quiet | all | Moins de sortie console |
--no-color | all | Terminal en clair |
Codes de sortie
| Code | Quand |
|---|---|
| 0 | Exploit : success: true ; forge : toujours ; check : toujours (-t unique) |
| 1 | Exploit : échec de la vérification de connexion/admin |
| 2 | Exécution de la liste check sans sémantique -t (informatif) |
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
Sondes :