Skip to content
KitploitKITPLOIT
FerramentasBlog
Log in
Enviar
FerramentasBlog
Enviar

Ferramentas de Hacking, PenTest e Cibersegurança para o seu Arsenal de Segurança!

Kitploit é um diretório de ferramentas de hacking, cibersegurança e pentesting. Descubra as últimas atualizações de projetos para encontrar vulnerabilidades, analisar sistemas, automatizar testes e fortalecer sua segurança.

··Feeds·Contato·Privacidade·© 2026 Kitploit

Diretório de Ferramentas

Categorias

Ver todas as categorias
Loading categories
CVE-2025-67923 — JetEngine <= 3.7.7 — Cross-Site Scripting Armazenado Não Autenticado via CCT REST API | Kitploit
Ferramentas/GitHubGitHub/randomrobbiebf/cve-2025-67923
Análise de VulnerabilidadesExploraçãoExploração de Aplicações WebSegurança WebSegurança de API
GitHubrandomrobbiebf/cve-2025-67923

CVE-2025-67923

JetEngine <= 3.7.7 — Cross-Site Scripting Armazenado Não Autenticado via CCT REST API

Ver Repositório
19há 6 mesesAinda não revisado

Mais Populares

Ver todos →

Descubra as ferramentas mais usadas pela nossa comunidade.

Explore todas as ferramentas

Navegue pela nossa coleção de ferramentas

Ver todas as ferramentas →
Compartilhar

Relatório de Exploração CVE-2025-67923

JetEngine <= 3.7.7 — Cross-Site Scripting Armazenado Não Autenticado via API REST CCT

Data: 11 de março de 2026 Pontuação CVSS: 7.1 (Alta) Vetor CVSS: CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:L Plug-in Afetado: JetEngine <= 3.7.7 Slug do Plug-in: jet-engine Corrigido em: 3.7.8 CVE: CVE-2025-67923 CWE: CWE-79 Pesquisador: Bonds (via Patchstack) Relatado: 19 de outubro de 2025 Divulgado: janeiro de 2026 Versão do WordPress Testada: Mais recente


Resumo Executivo

O CVE-2025-67923 é uma vulnerabilidade de Cross-Site Scripting Armazenado Não Autenticado no plug-in WordPress JetEngine, afetando todas as versões até 3.7.7 inclusive. Um atacante não autenticado pode gravar HTML/JavaScript arbitrário em um campo de texto de um Tipo de Conteúdo Personalizado (CCT) por meio da API REST pública, que é então injetado sem sanitização no DOM por meio de um sumidouro innerHTML do JavaScript no widget Maps Listing, quando uma vítima visita qualquer página que contenha esse widget.

O ataque não exige autenticação nem privilégios. Ele exige apenas que:

  1. Exista um CCT do JetEngine com acesso de gravação pela API REST definido como public.
  2. Um widget Maps Listing em qualquer página de front-end esteja configurado para exibir o campo de texto desse CCT como o rótulo do marcador do mapa.

✅ VULNERABILIDADE CONFIRMADA — Payload de XSS Armazenado Gravado e Executado

Impacto confirmado:

  • HTML/JS arbitrário não autenticado armazenado no banco de dados do CCT sem sanitização
  • O XSS é disparado para todo visitante que carregar qualquer página com o widget Maps Listing
  • Roubo de cookies, sequestro de sessão, coleta de credenciais armazenadas, comprometimento total do administrador

Detalhes da Vulnerabilidade

Resumo Técnico

A vulnerabilidade é uma combinação de duas fraquezas independentes:

  1. Ausência de sanitização de entrada (armazenamento): O método sanitize_field_value() do manipulador de itens do CCT não possui um caminho de sanitização para campos do tipo text. O caso default: apenas converte timestamps — o HTML bruto passa e é persistido no banco de dados.

  2. Sumidouro de XSS baseado em DOM (renderização): O widget Maps Listing lê o valor do campo armazenado via get_marker_label(), envolve-o em htmlspecialchars(json_encode(...)) para o atributo HTML data-markers, e o JavaScript do front-end lê o atributo com getAttribute() (que decodifica entidades HTML) e, em seguida, insere markerData.label diretamente via innerHTML — executando qualquer HTML/JavaScript incorporado.

Componentes Afetados

ArquivoProblema
includes/modules/custom-content-types/inc/rest-api/public-controller.php:424create_item_permissions_check retorna true quando CCT rest_put_access = 'public' — nenhuma autenticação necessária
includes/modules/custom-content-types/inc/item-handler.php:489ramo default: de sanitize_field_value() — nenhuma sanitização de HTML para campos do tipo text
includes/modules/maps-listings/inc/render.php:233get_marker_label() retorna o valor meta bruto sem esc_html()
includes/modules/maps-listings/inc/render.php:247htmlspecialchars(json_encode($result)) — apenas protege o limite do atributo, não a injeção via innerHTML
includes/modules/maps-listings/assets/js/frontend-maps.js:112pinData.content = general.marker.html.replace('_marker_label_', markerData.label) — rótulo bruto inserido na string HTML
includes/modules/maps-listings/assets/js/public/mapbox-maps.js:175el.innerHTML = data.content — sumidouro de execução de XSS
includes/modules/maps-listings/assets/js/public/leaflet-maps.js:34contentHtml.innerHTML = content — sumidouro de execução de XSS

Cadeia de Exploração

Etapa 1 — Gravação REST Não Autenticada

O controlador REST público do CCT verifica permissões 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' );
}

Quando um CCT é configurado com rest_put_access = 'public' (uma configuração suportada e documentada para formulários públicos), o endpoint é totalmente não autenticado. Qualquer cliente HTTP pode enviar um POST para /wp-json/jet-cct/{slug}.

Etapa 2 — Armazenamento sem Sanitização

O manipulador da REST chama $handler->update_item($params), que chega a 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;
}

Um campo do tipo text cai no ramo default:. maybe_to_timestamp() retorna o valor inalterado para strings que não são datas. O payload de XSS `` é armazenado literalmente.

Etapa 3 — Rótulo Recuperado sem Escape

Quando o widget Maps Listing é renderizado, get_marker_label() lê o 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 );
                // No esc_html() here — raw value returned
            }
            break;
    }

    return $result;  // Returns ""
}

O valor retornado é colocado no array de dados do marcador:

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

Etapa 4 — A Codificação do Atributo Não Impede XSS via innerHTML

O array de marcadores é codificado para um atributo HTML:

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

json_encode serializa `` como a string "". htmlspecialchars então codifica em HTML o JSON completo, produzindo:

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

Isso é gravado no atributo HTML data-markers. A codificação apenas protege o limite do atributo. Quando o JavaScript lê o atributo, o navegador decodifica as entidades HTML, restaurando os caracteres originais:

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

Etapa 5 — Injeção via innerHTML (o XSS é Disparado)

Em frontend-maps.js, a string bruta do rótulo é inserida diretamente em uma string de conteúdo HTML:

// frontend-maps.js:112
pinData.content = general.marker.html.replace( '_marker_label_', markerData.label );
// pinData.content = '<div class="jet-map-marker-wrap">
//   
// </div>'
Baixar ferramenta