Skip to content
KitploitKITPLOIT
OutilsExploitsBlog
Log in
Soumettre
OutilsExploitsBlog
Soumettre

Outils de Hacking, PenTest et Cybersécurité pour votre Arsenal de Sécurité !

Kitploit est un répertoire d'outils de hacking, de cybersécurité et de pentesting. Découvrez les dernières mises à jour des projets pour trouver des vulnérabilités, analyser des systèmes, automatiser les tests et renforcer votre sécurité.

··Flux·Contact·Confidentialité·© 2026 Kitploit

Répertoire d'outils

Catégories

Voir toutes les catégories
Loading categories
CVE-2025-67923 — JetEngine <= 3.7.7 — Cross-Site Scripting stocké non authentifié via CCT REST API | Kitploit
Outils/GitHubGitHub/randomrobbiebf/cve-2025-67923
Analyse des VulnérabilitésExploitationExploitation d'Applications WebSécurité WebSécurité des API
GitHubrandomrobbiebf/cve-2025-67923

CVE-2025-67923

JetEngine <= 3.7.7 — Cross-Site Scripting stocké non authentifié via CCT REST API

Voir le dépôt
19il y a 6 moisPas encore vérifié

Populaires

Voir tout →

Découvrez les outils les plus utilisés par notre communauté.

Explorer tous les outils

Parcourez notre collection d'outils

Voir tous les outils →
Partager

Rapport d'exploitation de CVE-2025-67923

JetEngine <= 3.7.7 — Cross-Site Scripting stocké non authentifié via l'API REST CCT

Date: 11 mars 2026 Score CVSS: 7.1 (Élevé) Vecteur CVSS: CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:L Plugin affecté: JetEngine <= 3.7.7 Slug du plugin: jet-engine Corrigé dans: 3.7.8 CVE: CVE-2025-67923 CWE: CWE-79 Chercheur: Bonds (via Patchstack) Signalé: 19 octobre 2025 Divulgué: janvier 2026 Version de WordPress testée: Dernière


Résumé exécutif

CVE-2025-67923 est une vulnérabilité de Cross-Site Scripting stocké non authentifié dans le plugin WordPress JetEngine affectant toutes les versions jusqu'à 3.7.7 incluse. Un attaquant non authentifié peut écrire du HTML/JavaScript arbitraire dans un champ texte d'un type de contenu personnalisé (CCT) via l'API REST publique, qui est ensuite injecté non désinfecté dans le DOM via un sink JavaScript innerHTML dans le widget Maps Listing lorsqu'une victime visite une page contenant ce widget.

L'attaque ne nécessite ni authentification ni privilèges. Elle exige uniquement que :

  1. Un CCT JetEngine existe avec l'accès en écriture de l'API REST défini sur public.
  2. Un widget Maps Listing sur une page frontale soit configuré pour afficher le champ texte de ce CCT comme étiquette du marqueur de carte.

✅ VULNÉRABILITÉ CONFIRMÉE — Charge utile XSS stockée écrite et exécutée

Impact confirmé :

  • HTML/JS arbitraire non authentifié stocké dans la base de données CCT sans désinfection
  • XSS déclenchée pour chaque visiteur chargeant une page contenant le widget Maps Listing
  • Vol de cookies, détournement de session, collecte d'identifiants stockés, prise de contrôle de l'administrateur

Détails de la vulnérabilité

Résumé technique

La vulnérabilité est une combinaison de deux faiblesses indépendantes :

  1. Absence de désinfection des entrées (stockage) : La méthode sanitize_field_value() du gestionnaire d'éléments CCT n'a aucun chemin de désinfection pour les champs de type text. Le cas default: ne fait que convertir les horodatages — le HTML brut passe et est persisté en base de données.

  2. Sink XSS basé sur le DOM (rendu) : Le widget Maps Listing lit la valeur de champ stockée via get_marker_label(), l'enveloppe dans htmlspecialchars(json_encode(...)) pour l'attribut HTML data-markers, et le JavaScript frontal lit l'attribut avec getAttribute() (qui décode les entités HTML), puis insère markerData.label directement via innerHTML — exécutant tout HTML/JavaScript intégré.

Composants affectés

FichierProblème
includes/modules/custom-content-types/inc/rest-api/public-controller.php:424create_item_permissions_check renvoie true lorsque le CCT a rest_put_access = 'public' — aucune authentification requise
includes/modules/custom-content-types/inc/item-handler.php:489Branche default: de sanitize_field_value() — aucune désinfection HTML pour les champs de type text
includes/modules/maps-listings/inc/render.php:233get_marker_label() renvoie la valeur meta brute sans esc_html()
includes/modules/maps-listings/inc/render.php:247htmlspecialchars(json_encode($result)) — protège uniquement la limite de l'attribut, pas l'injection innerHTML
includes/modules/maps-listings/assets/js/frontend-maps.js:112pinData.content = general.marker.html.replace('_marker_label_', markerData.label) — étiquette brute insérée dans la chaîne HTML
includes/modules/maps-listings/assets/js/public/mapbox-maps.js:175el.innerHTML = data.content — sink d'exécution XSS
includes/modules/maps-listings/assets/js/public/leaflet-maps.js:34contentHtml.innerHTML = content — sink d'exécution XSS

Chaîne d'exploitation

Étape 1 — Écriture REST non authentifiée

Le contrôleur REST public des CCT vérifie les permissions via check_user_permissions() :

// public-controller.php:370-376
public function check_user_permissions( $request, $context ) {
    $content_type = $this->get_content_type_from_request( $request );
    $cap = $content_type->get_arg( $context );

    if ( ! $cap || 'public' === $cap ) {
        return true;  // No authentication required
    } else {
        return current_user_can( $cap );
    }
}

public function create_item_permissions_check( $request ) {
    return $this->check_user_permissions( $request, 'rest_put_access' );
}

Lorsqu'un CCT est configuré avec rest_put_access = 'public' (une configuration prise en charge et documentée pour les formulaires publics), le point de terminaison est entièrement non authentifié. N'importe quel client HTTP peut envoyer une requête POST à /wp-json/jet-cct/{slug}.

Étape 2 — Stockage non désinfecté

Le gestionnaire REST appelle $handler->update_item($params), qui atteint sanitize_field_value() :

// item-handler.php:489-562
public function sanitize_field_value( $value, $field ) {
    $type = isset( $field['type'] ) ? $field['type'] : false;

    switch ( $type ) {
        case 'repeater':    // sanitizes sub-fields
            // ...
        case 'checkbox':    // handles boolean arrays
        case 'checkbox-raw':
            // ...
        case 'media':
        case 'gallery':     // sanitizes media JSON
            // ...
        case 'wysiwyg':
            $value = jet_engine_sanitize_wysiwyg( $value );  // sanitized
            break;

        default:
            // TEXT TYPE FALLS HERE — only timestamp conversion, NO HTML sanitization
            $value = $this->factory->maybe_to_timestamp( $value, $field );
    }

    return $value;
}

Un champ de type text atteint la branche default:. maybe_to_timestamp() renvoie la valeur inchangée pour les chaînes non datées. La charge utile XSS `` est stockée telle quelle.

Étape 3 — Étiquette récupérée sans échappement

Lorsque le widget Maps Listing effectue le rendu, get_marker_label() lit le champ :

// render.php:479-535
public function get_marker_label( $post = null, $settings = array() ) {

    // ...
    switch ( $label_type ) {
        case 'meta_field':
            $field = $settings['marker_label_field'];
            if ( $field ) {
                $result = jet_engine()->listings->data->get_meta( $field, $post );
                // No esc_html() here — raw value returned
            }
            break;
    }

    return $result;  // Returns ""
}

La valeur renvoyée est placée dans le tableau de données des marqueurs :

// render.php:231-237
$result[] = array(
    'id'        => $post_id,
    'latLang'   => $latlang,
    'label'     => $this->get_marker_label( $post, $settings ),  // raw XSS payload
    // ...
);

Étape 4 — L'encodage d'attribut n'empêche pas l'XSS via innerHTML

Le tableau des marqueurs est encodé pour un attribut HTML :

// render.php:247
return htmlspecialchars( json_encode( $result ) );

json_encode sérialise `` comme la chaîne "". htmlspecialchars encode ensuite en HTML l'intégralité du JSON, produisant :

[{...,&quot;label&quot;:&quot;&lt;img src=x onerror=alert(1)&gt;&quot;,...}]

Ceci est écrit dans l'attribut HTML data-markers. L'encodage ne protège que la limite de l'attribut. Lorsque JavaScript lit l'attribut, le navigateur décode les entités HTML, restaurant les caractères d'origine :

// Browser automatically decodes entities when reading via dataset / getAttribute
const markers = JSON.parse(el.dataset.markers);
// markers[0].label === ''

Étape 5 — Injection innerHTML (déclenchement de l'XSS)

Dans frontend-maps.js, la chaîne d'étiquette brute est insérée directement dans une chaîne de contenu HTML :

// frontend-maps.js:112
pinData.content = general.marker.html.replace( '_marker_label_', markerData.label );
// pinData.content = '<div class="jet-map-marker-wrap">
//   
// </div>'
Télécharger l’outil