Skip to content
KitploitKITPLOIT
StrumentiBlog
Log in
Invia
StrumentiBlog
Invia

Strumenti di Hacking, PenTest e Cybersecurity per il tuo Arsenale di Sicurezza!

Kitploit è una directory di strumenti di hacking, cybersecurity e pentesting. Scopri gli ultimi aggiornamenti dei progetti per trovare vulnerabilità, analizzare sistemi, automatizzare i test e rafforzare la tua sicurezza.

··Feed·Contatto·Privacy·© 2026 Kitploit

Directory degli strumenti

Categorie

Vedi tutte le categorie
Loading categories
CVE-2025-67923 — JetEngine <= 3.7.7 — Cross-Site Scripting persistente non autenticato tramite CCT REST API | Kitploit
Strumenti/GitHubGitHub/randomrobbiebf/cve-2025-67923
Analisi delle VulnerabilitàExploitSfruttamento di Applicazioni WebSicurezza WebSicurezza delle API
GitHubrandomrobbiebf/cve-2025-67923

CVE-2025-67923

JetEngine <= 3.7.7 — Cross-Site Scripting persistente non autenticato tramite CCT REST API

Vedi Repository
196 mesi faNon ancora revisionato

Più Popolari

Vedi tutti →

Scopri gli strumenti più utilizzati dalla nostra community.

Esplora tutti gli strumenti

Sfoglia la nostra collezione di strumenti

Vedi tutti gli strumenti →
Condividi

Rapporto di sfruttamento CVE-2025-67923

JetEngine <= 3.7.7 — Cross-Site Scripting memorizzato non autenticato tramite API REST CCT

Data: 11 marzo 2026 Punteggio CVSS: 7,1 (Alto) Vettore CVSS: CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:L Plugin interessato: JetEngine <= 3.7.7 Slug del plugin: jet-engine Corretto nella versione: 3.7.8 CVE: CVE-2025-67923 CWE: CWE-79 Ricercatore: Bonds (via Patchstack) Segnalato: 19 ottobre 2025 Divulgato: gennaio 2026 Versione di WordPress testata: Ultima


Sommario esecutivo

CVE-2025-67923 è una vulnerabilità di Cross-Site Scripting memorizzato non autenticato nel plugin WordPress JetEngine che interessa tutte le versioni fino alla 3.7.7 inclusa. Un attaccante non autenticato può scrivere HTML/JavaScript arbitrario in un campo di testo di un Custom Content Type (CCT) tramite l'API REST pubblica; tale contenuto viene poi iniettato non sanificato nel DOM tramite un sink JavaScript innerHTML nel widget Maps Listing quando una vittima visita qualsiasi pagina contenente quel widget.

L'attacco non richiede autenticazione né privilegi. Richiede solo che:

  1. Esista un CCT JetEngine con accesso di scrittura API REST impostato su public.
  2. Un widget Maps Listing su qualsiasi pagina front-end sia configurato per mostrare il campo di testo di quel CCT come etichetta del marcatore della mappa.

✅ VULNERABILITÀ CONFERMATA — Payload XSS memorizzato scritto ed eseguito

Impatto confermato:

  • HTML/JS arbitrario non autenticato memorizzato nel database CCT senza sanificazione
  • L'XSS scatta per ogni visitatore che carica qualsiasi pagina con il widget Maps Listing
  • Furto di cookie, dirottamento della sessione, raccolta di credenziali memorizzate, compromissione dell'admin

Dettagli della vulnerabilità

Riepilogo tecnico

La vulnerabilità è una combinazione di due debolezze indipendenti:

  1. Mancata sanificazione dell'input (archiviazione): il metodo sanitize_field_value() del gestore degli elementi CCT non ha alcun percorso di sanificazione per i campi di tipo text. Il caso default: converte solo i timestamp — l'HTML grezzo passa e viene persistito nel database.

  2. Sink XSS basato su DOM (renderizzazione): il widget Maps Listing legge il valore del campo memorizzato tramite get_marker_label(), lo avvolge in htmlspecialchars(json_encode(...)) per l'attributo HTML data-markers, e il frontend JavaScript legge l'attributo con getAttribute() (che decodifica le entità HTML), quindi inserisce markerData.label direttamente tramite innerHTML — eseguendo qualsiasi HTML/JavaScript incorporato.

Componenti interessati

FileProblema
includes/modules/custom-content-types/inc/rest-api/public-controller.php:424create_item_permissions_check restituisce true quando CCT rest_put_access = 'public' — nessuna autenticazione richiesta
includes/modules/custom-content-types/inc/item-handler.php:489ramo default: di sanitize_field_value() — nessuna sanificazione HTML per i campi di tipo text
includes/modules/maps-listings/inc/render.php:233get_marker_label() restituisce il valore meta grezzo senza esc_html()
includes/modules/maps-listings/inc/render.php:247htmlspecialchars(json_encode($result)) — protegge solo il confine dell'attributo, non l'iniezione innerHTML
includes/modules/maps-listings/assets/js/frontend-maps.js:112pinData.content = general.marker.html.replace('_marker_label_', markerData.label) — etichetta grezza inserita nella stringa HTML
includes/modules/maps-listings/assets/js/public/mapbox-maps.js:175el.innerHTML = data.content — sink di esecuzione XSS
includes/modules/maps-listings/assets/js/public/leaflet-maps.js:34contentHtml.innerHTML = content — sink di esecuzione XSS

Catena di sfruttamento

Passo 1 — Scrittura REST non autenticata

Il controller REST pubblico CCT controlla i permessi tramite 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;  // Nessuna autenticazione richiesta
    } else {
        return current_user_can( $cap );
    }
}

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

Quando un CCT è configurato con rest_put_access = 'public' (una configurazione supportata e documentata per moduli pubblici), l'endpoint è completamente non autenticato. Qualsiasi client HTTP può inviare una POST a /wp-json/jet-cct/{slug}.

Passo 2 — Archiviazione non sanificata

Il gestore REST chiama $handler->update_item($params) che raggiunge 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':    // sanifica i sotto-campi
            // ...
        case 'checkbox':    // gestisce array booleani
        case 'checkbox-raw':
            // ...
        case 'media':
        case 'gallery':     // sanifica JSON multimediale
            // ...
        case 'wysiwyg':
            $value = jet_engine_sanitize_wysiwyg( $value );  // sanificato
            break;

        default:
            // I CAMPI DI TIPO TEXT FINISCONO QUI — solo conversione timestamp, NESSUNA sanificazione HTML
            $value = $this->factory->maybe_to_timestamp( $value, $field );
    }

    return $value;
}

Un campo di tipo text raggiunge il ramo default:. maybe_to_timestamp() restituisce il valore invariato per stringhe non data. Il payload XSS `` viene memorizzato verbatim.

Passo 3 — Etichetta recuperata senza escaping

Quando il widget Maps Listing viene renderizzato, get_marker_label() legge il campo:

// 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 );
                // Nessun esc_html() qui — valore grezzo restituito
            }
            break;
    }

    return $result;  // Restituisce ""
}

Il valore restituito viene inserito nell'array dei dati del marcatore:

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

Passo 4 — La codifica dell'attributo non previene l'XSS innerHTML

L'array dei marcatori viene codificato per un attributo HTML:

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

json_encode serializza `` come la stringa "". htmlspecialchars codifica quindi in HTML l'intero JSON, producendo:

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

Questo viene scritto nell'attributo HTML data-markers. La codifica protegge solo il confine dell'attributo. Quando JavaScript legge l'attributo, il browser decodifica le entità HTML, ripristinando i caratteri originali:

// Il browser decodifica automaticamente le entità quando legge tramite dataset / getAttribute
const markers = JSON.parse(el.dataset.markers);
// markers[0].label === ''

Passo 5 — Iniezione innerHTML (l'XSS scatta)

In frontend-maps.js, la stringa dell'etichetta grezza viene inserita direttamente in una stringa di contenuto HTML:

Scarica lo strumento