Skip to content
KitploitKITPLOIT
ИнструментыЭксплойтыБлог
Log in
Отправить
ИнструментыЭксплойтыБлог
Отправить

Инструменты для хакинга, пентеста и кибербезопасности — ваш арсенал защиты!

Kitploit — это каталог инструментов для хакинга, кибербезопасности и пентестинга. Находите последние обновления проектов для поиска уязвимостей, анализа систем, автоматизации тестирования и усиления вашей безопасности.

··Ленты·Контакты·Конфиденциальность·© 2026 Kitploit

Каталог инструментов

Категории

Все категории
Loading categories
CVE-2025-67923 — JetEngine <= 3.7.7 — Неаутентифицированный сохранённый межсайтовый скриптинг (XSS) через CCT REST API | Kitploit
Инструменты/GitHubGitHub/randomrobbiebf/cve-2025-67923
Анализ уязвимостейЭксплуатацияЭксплуатация веб-приложенийВеб-безопасностьБезопасность API
GitHubrandomrobbiebf/cve-2025-67923

CVE-2025-67923

JetEngine <= 3.7.7 — Неаутентифицированный сохранённый межсайтовый скриптинг (XSS) через CCT REST API

Репозиторий
196 месяцев назадЕщё не проверено

Популярное

Смотреть все →

Откройте для себя самые используемые инструменты нашего сообщества.

Изучить все инструменты

Просмотрите нашу коллекцию инструментов

Смотреть все инструменты →
Поделиться

Отчёт об эксплуатации CVE-2025-67923

JetEngine <= 3.7.7 — Неаутентифицированная хранимая межсайтовая скриптинг-уязвимость (XSS) через CCT REST API

Дата: 11 марта 2026 г. Оценка CVSS: 7.1 (Высокая) Вектор CVSS: CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:L Затронутый плагин: JetEngine <= 3.7.7 Slug плагина: jet-engine Исправлено в: 3.7.8 CVE: CVE-2025-67923 CWE: CWE-79 Исследователь: Bonds (через Patchstack) Сообщено: 19 октября 2025 г. Раскрыто: январь 2026 г. Протестированная версия WordPress: последняя


Краткое резюме

CVE-2025-67923 — это неаутентифицированная хранимая межсайтовая скриптинг-уязвимость (XSS) в плагине JetEngine для WordPress, затрагивающая все версии вплоть до 3.7.7 включительно. Неаутентифицированный злоумышленник может записать произвольный HTML/JavaScript в текстовое поле Custom Content Type (CCT) через публичный REST API, после чего эти данные без санитизации попадают в DOM через JavaScript-приёмник innerHTML в виджете Maps Listing, когда жертва посещает любую страницу, содержащую этот виджет.

Атака не требует аутентификации и каких-либо привилегий. Требуется только, чтобы:

  1. Существовал CCT JetEngine с доступом на запись через REST API, установленным в значение public.
  2. Виджет Maps Listing на любой странице фронтенда был настроен на отображение текстового поля этого CCT в качестве подписи маркера карты.

✅ УЯЗВИМОСТЬ ПОДТВЕРЖДЕНА — нагрузка хранимого XSS записана и выполнена

Подтверждённое воздействие:

  • Неаутентифицированный произвольный HTML/JS сохраняется в базе данных CCT без санитизации
  • XSS срабатывает для каждого посетителя, загружающего любую страницу с виджетом Maps Listing
  • Кража cookie, перехват сессии, сбор сохранённых учётных данных, захват учётной записи администратора

Детали уязвимости

Техническое резюме

Уязвимость представляет собой комбинацию двух независимых слабостей:

  1. Отсутствие санитизации ввода (хранение): Метод sanitize_field_value() обработчика элементов CCT не имеет пути санитизации для полей типа text. Ветка default: лишь преобразует временные метки — необработанный HTML проходит насквозь и сохраняется в базе данных.

  2. DOM-приёмник XSS (рендеринг): Виджет Maps Listing считывает сохранённое значение поля через get_marker_label(), оборачивает его в htmlspecialchars(json_encode(...)) для HTML-атрибута data-markers, а JavaScript на фронтенде считывает атрибут через getAttribute() (который декодирует HTML-сущности), после чего вставляет markerData.label напрямую через innerHTML — выполняя любой встроенный HTML/JavaScript.

Затронутые компоненты

ФайлПроблема
includes/modules/custom-content-types/inc/rest-api/public-controller.php:424create_item_permissions_check возвращает true, когда rest_put_access = 'public' — аутентификация не требуется
includes/modules/custom-content-types/inc/item-handler.php:489ветка default: в sanitize_field_value() — отсутствие HTML-санитизации для полей типа text
includes/modules/maps-listings/inc/render.php:233get_marker_label() возвращает необработанное значение мета-поля без esc_html()
includes/modules/maps-listings/inc/render.php:247htmlspecialchars(json_encode($result)) — защищает только границу атрибута, но не инъекцию через innerHTML
includes/modules/maps-listings/assets/js/frontend-maps.js:112pinData.content = general.marker.html.replace('_marker_label_', markerData.label) — необработанная подпись вставляется в HTML-строку
includes/modules/maps-listings/assets/js/public/mapbox-maps.js:175el.innerHTML = data.content — приёмник выполнения XSS
includes/modules/maps-listings/assets/js/public/leaflet-maps.js:34contentHtml.innerHTML = content — приёмник выполнения XSS

Цепочка эксплуатации

Шаг 1 — Неаутентифицированная запись через REST

Публичный REST-контроллер CCT проверяет права через 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' );
}

Когда CCT настроен с rest_put_access = 'public' (поддерживаемая и документированная конфигурация для публичных форм), конечная точка полностью неаутентифицирована. Любой HTTP-клиент может отправить POST-запрос на /wp-json/jet-cct/{slug}.

Шаг 2 — Хранение без санитизации

REST-обработчик вызывает $handler->update_item($params), который в итоге доходит до 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;
}

Поле типа text попадает в ветку default:. maybe_to_timestamp() возвращает значение без изменений для строк, не являющихся датами. XSS-нагрузка `` сохраняется дословно.

Шаг 3 — Извлечение подписи без экранирования

При рендеринге виджета Maps Listing get_marker_label() считывает поле:

// 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 ""
}

Возвращаемое значение помещается в массив данных маркера:

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

Шаг 4 — Кодирование атрибута не предотвращает XSS через innerHTML

Массив маркеров кодируется для HTML-атрибута:

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

json_encode сериализует `` в строку "". Затем htmlspecialchars HTML-кодирует весь JSON, в результате чего получается:

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

Это записывается в HTML-атрибут data-markers. Кодирование защищает только границу атрибута. Когда JavaScript считывает атрибут, браузер декодирует HTML-сущности, восстанавливая исходные символы:

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

Шаг 5 — Инъекция через innerHTML (срабатывание XSS)

В frontend-maps.js необработанная строка подписи вставляется непосредственно в строку HTML-содержимого:

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

Затем эта строка содержимого устанавливается как innerHTML элемента маркера:

Скачать инструмент