CVE-2026-109618 и CVE-2026-109619: проверяем обновление PHP публичным PoC

В предыдущих статьях мы обновили PHP 8.2 на CentOS 7 и собрали бэкпорт для PHP 7.4. Следующий вопрос вполне практический: как проверить, что исправление действительно работает?
Для этого есть публичный php2shell-poc от Qi Deng. Берём его за основу, читаем код и сравниваем поведение старой и обновлённой сборок. По дороге выясняется, что сообщениям самого PoC нельзя доверять без проверки: обычный HTTP-заголовок способен превратиться в сообщение о падении сервера.
Дополнительно разбираем материал eness о временной защите и бэкпорты Anton-Barinov. Проверяем не только обещания README, но и файлы конкретных ревизий.
Ниже — инструкция и разбор исходников.
Что считаем подтверждением исправления
Нужно проверить три вещи:
- Установлены пакеты с нужными исправлениями.
- Веб-запросы обслуживают процессы, загрузившие обновлённый код.
- В одинаковых условиях воспроизводимый дефект старой сборки перестал воспроизводиться после обновления.
Последний пункт относится к проверенному сценарию. Он не превращает CentOS 7 в поддерживаемую ОС и не доказывает отсутствие других уязвимостей в PHP, Битриксе или расширениях.
У двух CVE разные механизмы:
| Уязвимость | Что происходит |
|---|---|
| CVE-2026-109618 | Нехватка памяти прерывает операцию, оставляя некорректное состояние запроса. Дальнейшая очистка может обратиться к этому состоянию. |
| CVE-2026-109619 | Состояние предыдущего запроса переживает освобождение его памяти. Следующий запрос в том же воркере обращается к уже недействительным данным. |
Поэтому один OOM-тест не покрывает автоматически все варианты второй уязвимости. Отключение session.upload_progress тоже закрывает только часть путей.
В официальном сообщении PHP исправленными названы версии 8.2.35, 8.3.36, 8.4.27 и 8.5.12. Для PHP 7.4 предоставлен минимальный бэкпорт. В нашей старой ветке строка версии осталась 7.4.33, поэтому проверяем полный выпуск RPM и происхождение патча.
Что лежит в php2shell-poc
Разбираем ревизию 6d2168710509a2647545ba78d68afb1e3f745038 от 21 октября 2026 года. Это существенно: репозиторий меняется, и в нём уже появился пятый сценарий, которого не было в первой версии README.
| Сценарий | Файл | Условия и ограничения |
|---|---|---|
| A: данные сессии и LFI | verify_session_injection.py |
Для цепочки с выполнением кода требуется уязвимость включения локального файла в приложении |
| B: десериализация и use-after-free | verify_uaf_asan.php |
Локальная проверка; диагностический отчёт требует PHP, собранного с AddressSanitizer |
| C: разные форматы сессий | verify_handler_mismatch.py |
Нужны каталоги с разными обработчиками сериализации и общим хранилищем сессий |
| D: OOM в Apache/mod_php | verify_oom_trigger.py |
Отправляет multipart-запрос и оценивает HTTP-ответ |
| E: состояние между запросами FPM | verify_fpm_stale_state.py |
Требует подготовленной сессии, специального обработчика и попадания запросов в один воркер |
Для серверов из предыдущих статей в первую очередь интересен сценарий D: там nginx перед Apache/mod_php. Сценарий E нужен при проверке FPM. Остальные помогают исследовать отдельные условия, но не образуют универсальный набор тестов, после которого можно написать «обе CVE закрыты».
Сначала читаем, как PoC выносит вердикт
В OOM-скрипте нет проверки выполнения команды на сервере и анализа повреждения памяти. Он отправляет запрос и интерпретирует ответ.
HTTP 500 трактуется как вероятный OOM. Отсутствие ответа — как вероятное падение. Но ни то ни другое само по себе не доказывает конкретную CVE: причиной могут быть ошибка приложения, тайм-аут или штатное завершение воркера.
Есть и более наглядная проблема. После проверки статуса скрипт ищет слово connection во всём ответе и при совпадении сообщает об оборванном соединении. Обычный заголовок Connection: close способен вызвать это сообщение даже при HTTP 200.
Поэтому вывод этого PoC — повод изучить сервер, а не готовый результат аудита. Обычное завершение OOM-скрипта с кодом 0 также не означает, что сервер защищён.
В FPM-скрипте похожая ситуация: HTTP 502 считается свидетельством use-after-free. Для такого вывода нужна трассировка причины падения. Кроме того, скрипт ищет текст ошибки памяти в HTTP-ответе. При отключённом выводе ошибок он может не увидеть OOM, который записан в журнал.
У FPM-проверки код завершения 0 используется при объявлении уязвимости, а 1 — при невоспроизведении. Подключать её к автоматизации как обычную проверку «0 — всё хорошо» нельзя.
Почему не запускаем лабораторию одной командой из README
В текущих файлах лаборатории есть несколько несоответствий:
docker-compose.ymlиспользует изменяемые тегиphp:8.2-fpmиphp:8.2-apache. После обновления образов они уже могут содержать исправление. Для контрольного уязвимого окружения этого недостаточно.- Файлы
php.ini,nginx.confи каталогwebroot, на которые ссылается compose, не сохранены в этой ревизии. Их создаётsetup.sh. - Но
setup.shтакже перезаписывает сам compose-файл вариантом без Apache и сразу запускает контейнеры. - В исходном compose опубликован порт FPM 9000, порты не ограничены loopback, а FPM запускается с
privileged: trueи отключённым профилем seccomp.
Здесь достаточно причин сначала подготовить лабораторию, а уже потом запускать запросы. Для наших сборок используем изолированные копии окружения из предыдущих статей. Если берём лабораторию автора, отдельно согласовываем конфиги, фиксируем версии или digest образов, привязываем HTTP-порты к 127.0.0.1 и убираем ненужную публикацию FPM и повышенные привилегии.
Проверка стандартного Docker-образа PHP полезна для изучения PoC. Результат для наших RPM на CentOS нужно получить отдельно.
Готовим старую и новую сборки
Для каждой ветки нужны два состояния с одинаковыми настройками:
| Ветка | До исправления | После исправления |
|---|---|---|
| PHP 8.2 | Сборка до обновления | Наши RPM с PHP 8.2.35 |
| PHP 7.4 | Сборка до бэкпорта | Наши RPM 7.4.33-31.el7.local |
Сохраняем ОС, SAPI, расширения, конфиги PHP и Apache, настройки nginx и путь запроса. Меняем только проверяемые пакеты. Удобно использовать снимки одной тестовой машины.
PoC намеренно создаёт условия для нехватки памяти, поэтому испытание проводим на закрытом стенде. Рабочая база и исходящие интеграции Битрикса ему не нужны.
Сначала воспроизводим исследуемый дефект на старой сборке. Если этого не произошло, отрицательный результат новой сборки мало что добавляет. Возможно, запрос не дошёл до нужного обработчика или отсутствует необходимое условие.
Обычный OOM не считаем положительным воспроизведением уязвимости. Чтобы подтвердить ошибочное обращение к памяти, могут потребоваться стек падения и отдельная сборка с AddressSanitizer. Её результаты записываем отдельно от испытания наших RPM.
Проверяем пакеты и работающий PHP
На CentOS пакеты из основного PHP SRPM можно вывести так:
rpm -qa --qf '%{NAME} %{VERSION}-%{RELEASE} %{SOURCERPM}\n' |
awk '$3 ~ /^php-[0-9]/' |
sort
Список показывает, что установлено на диске. Затем проверяем версию через HTTP в нужном виртуальном хосте: php -v относится к CLI, а сайт может обслуживаться другим PHP.
На закрытой копии временно размещаем диагностический файл:
<?php
header('Content-Type: application/json; charset=utf-8');
header('Cache-Control: no-store');
$settings = [
'memory_limit',
'post_max_size',
'upload_max_filesize',
'session.upload_progress.enabled',
'session.upload_progress.name',
'session.serialize_handler',
'max_multipart_body_parts',
];
$result = [
'version' => PHP_VERSION,
'sapi' => PHP_SAPI,
'pid' => getmypid(),
];
foreach ($settings as $setting) {
$result[$setting] = ini_get($setting);
}
echo json_encode($result, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
Сохраняем ответ вместе с адресом backend и именем виртуального хоста. После проверки файл убираем.
Один ответ характеризует один запрос. Если работают несколько серверов, пулов или экземпляров Apache, проверяем каждый. Сопоставляем загруженные бинарные файлы и библиотеки с установленными пакетами.
После обновления нужен успешный полный рестарт всех соответствующих служб. Проверка только основного httpd оставляет открытым вопрос о httpd-scale и отдельном php-fpm. Отсутствие пометки (deleted) у отображённых библиотек само по себе тоже не подтверждает, что загружена нужная сборка.
Запускаем OOM-проверку
Получаем зафиксированную версию PoC на машине для испытаний:
POC_REV=6d2168710509a2647545ba78d68afb1e3f745038
git clone https://github.com/ikow/php2shell-poc.git
cd php2shell-poc
git checkout --detach "$POC_REV"
git rev-parse HEAD
В примере предполагаем, что подготовленная копия Apache доступна по HTTP на 127.0.0.1:18081, а в нужном виртуальном хосте есть /index.php. Сам PoC сервер не создаёт.
LAB_HOST=127.0.0.1
LAB_PORT=18081
LAB_PATH=/index.php
set -o pipefail
python3 poc/verify_oom_trigger.py \
--host "$LAB_HOST" \
--port "$LAB_PORT" \
--path "$LAB_PATH" \
--strategy headers 2>&1 | tee poc-headers.log
Скрипт использует обычный TCP-сокет без TLS. Порт 443 не превращает его запрос в HTTPS. Значение --host используется и для соединения, и в HTTP-заголовке Host: проверяем, что запрос попал именно в нужный виртуальный хост.
Кроме headers, в этой ревизии есть варианты nested и no_length. Это разные способы формирования запроса, а не три независимых подтверждения безопасности. Сначала разбираем один воспроизводимый случай и сохраняем его параметры.
Перед запросом отмечаем время. После него собираем журналы Apache, PHP, nginx и сообщения ядра. Если стенд работает в Docker, дополнительно сохраняем состояние контейнеров и их логи.
Нужно установить, какой процесс обработал запрос, дошёл ли он до PHP, что произошло с памятью и почему завершился воркер. HTTP 502 от nginx может быть следствием сбоя backend, но не объясняет его причину.
Повторяем тот же запрос на обновлённой копии. Между сравнениями не меняем лимиты, расширения и настройки прокси. Разные стратегии начинаем с одинакового исходного состояния стенда, чтобы предыдущая попытка не влияла на следующую.
Что отдельно нужно для FPM
Для второй CVE важен переход состояния между запросами в одном воркере. Два запроса, ушедшие в разные процессы, этот переход не проверяют.
В комментариях к FPM-PoC перечислены лабораторные условия: включённый upload progress, небольшой лимит памяти, один FPM-воркер, заранее подготовленная большая сессия и обработчик session_start(). Однако setup.sh рассматриваемой ревизии не создаёт ни session_oom.php, ни требуемую большую сессию. Автоматически готового стенда для сценария E здесь нет.
После отдельной подготовки этих условий команда выглядит так:
FPM_LAB_URL=http://127.0.0.1:18080
python3 poc/verify_fpm_stale_state.py \
--target "$FPM_LAB_URL" \
--session-path /session_oom.php \
--upload-path /index.php
До этого запуск не имеет диагностической ценности. Если обработчика или сессии нет, получили отсутствие условий для теста, а не подтверждение исправления.
Даже при одном настроенном воркере проверяем PID: процесс мог завершиться и быть заменён между запросами. Ограничение пула одним процессом используем только для воспроизводимости на стенде.
По официальному описанию, завершение FPM-воркера при ошибке старта запроса блокирует конкретную последовательность, возможную в mod_php. Другие пути могут возникать после успешного старта, во время работы приложения. Поэтому результат OOM-теста Apache нельзя переносить на FPM.
Как трактовать результат
| Наблюдение | Допустимый вывод |
|---|---|
| nginx вернул 403 или 413 | Этот запрос остановлен на входе; обработка запроса PHP не проверена |
| Получен HTTP 200 | Запрос обслужен; отсутствие уязвимости не доказано |
| Получен HTTP 500, 502 или нет ответа | Требуется установить причину по журналам и состоянию процессов |
| Зафиксировано превышение лимита памяти | Достигнут лимит; повреждение памяти или RCE этим не подтверждены |
| Есть отчёт AddressSanitizer о use-after-free | Найден дефект памяти; его нужно сопоставить с конкретным кодом и CVE |
| На старой сборке дефект не воспроизводится | Проверка исправления пока неубедительна |
| Тот же дефект воспроизводится после обновления | Нужно разбирать полноту патча и его фактическое применение |
Отдельно проверяем вход через nginx и непосредственную обработку backend на закрытой копии. Так можно разделить эффект фильтрации и эффект патча. Защитные ограничения рабочего сервера ради эксперимента не отключаем.
Отсутствие ошибок в журнале — тоже не универсальный критерий. Официальное описание CVE-2026-109619 подчёркивает, что некоторые операции обращаются к состоянию до решения о выводе диагностики. Выключенный display_errors не устраняет такой путь.
Что ещё предлагает eness: PoC, временные меры и исходный патч
В репозитории eness собраны не только правила Cloudflare. Рассматриваем ревизию e8db5ee1252ea3d6d084e3e421c6eb7a380efc11: PoC с попыткой выполнения команды, автоматическая настройка PHP, патч счётчиков upload progress и примеры фильтрации для разных веб-серверов.
Описание причин уязвимостей в README сосредоточено на указателях в upload progress. Официальные GHSA описывают более широкий набор ошибок OOM и состояния между запросами. Поэтому меры для одного обработчика не считаем заменой полного исправления. Утверждение README об отсутствии официальных патчей для всех старых веток также требует уточнения: минимальные исходные бэкпорты для 7.4, 8.0 и 8.1 уже опубликованы PHP.
Почему второй PoC не становится универсальным сканером
Файл poc_109618.py использует профиль Ubuntu/PHP 8.5.4 и адреса функций. В README упоминается параметр --syms, но в аргументах этой ревизии есть --dtor и --call, а --syms отсутствует.
Автоматического определения нужного адреса в скрипте нет. Нельзя подставить адрес нашего CentOS-сервера и считать отсутствие выполнения команды доказательством исправления. Кроме того, сетевой код всегда оборачивает соединение в TLS: передача URL с http:// не переключает его на обычный HTTP.
Это отдельный инструмент исследования конкретной сборки. В основной процедуре выше используем более простой запрос ikow и разбираем его последствия на сервере.
Что делает quick-fix
quick-fix/run.sh ищет установки PHP и отключает session.upload_progress.enabled, включая переопределения в конфигурации. У него есть --check, --dry-run, --audit, --probe и восстановление резервных копий через --revert. Обычный запуск изменяет файлы и перезагружает службы.
Полезно различать три результата: настройка выключена, пакет содержит патч, работающий веб-процесс загрузил этот пакет. Скрипт не делает их равнозначными. В рассматриваемой версии список PATCHED_VERSIONS вообще пуст; часть классификации строится на упоминании CVE в changelog пакета. Такой отчёт нужно сверять с составом сборки и работающими процессами.
Режим --probe проверяет фильтрацию multipart-запросов. Он не доказывает отсутствие повреждения памяти. Отключение upload progress оставляет обычную загрузку файлов, но затрагивает использующий его серверный индикатор прогресса.
Что меняет патч счётчиков
session-upload-progress-counters.patch меняет два файла расширения session. Вместо сохранения указателей на элементы массива счётчики хранятся как zend_long, а значения массива обновляются через отдельную функцию.
Это конкретное изменение механизма upload progress. В этом файле нет исправлений обработчика OOM в Zend и общего сброса состояния запросов. Применить только его к старому PHP и объявить обе CVE закрытыми нельзя.
Cloudflare и фильтрация перед PHP
Cloudflare сообщила о включении управляемых правил против php2shell для бесплатных и платных тарифов. Для своего домена проверяем фактические настройки и события WAF, включая исключения. Сообщение провайдера не подтверждает конфигурацию конкретной зоны.
В примерах eness предложены два подхода: блокировать multipart вне разрешённых маршрутов загрузки либо искать поле upload progress.
Первый подход уменьшает число доступных обработчиков, но ломает обычные multipart-формы на заблокированных адресах. Исключённые маршруты по-прежнему передают запрос в PHP. Список путей из примера для WordPress нельзя считать готовой конфигурацией Битрикса.
У второго примера обнаружилась принципиальная проблема:
any(http.request.body.form.names[*] == "PHP_SESSION_UPLOAD_PROGRESS")
По документации Cloudflare поле http.request.body.form.names заполняется для application/x-www-form-urlencoded. Исследуемый запрос использует multipart/form-data. Поэтому такое выражение нельзя выдавать за проверенное правило для данного PoC.
В Enterprise есть доступ к http.request.body.raw, по которому можно искать маркер в доступной части тела запроса. Но это дополнительная сигнатура: она зависит от фактического имени session.upload_progress.name, может давать ложные совпадения и не покрывает OOM-сценарии без этого поля. При усечении тела для инспекции WAF видит только его часть, тогда как origin получает полный запрос.
Блокировать все запросы с cookie PHPSESSID не нужно: это затрагивает обычные сессии пользователей. Фильтрацию проверяем на тестовом запросе и на штатных формах, фиксируя в Security Events, какое именно правило сработало.
Ещё одна обязательная проверка — путь до origin. Если backend доступен напрямую в обход Cloudflare, правило CDN такой трафик не обработает. Это отдельная задача ограничения сетевого доступа, а не свойство PHP-патча.
Apache, FrankenPHP и Lighttpd
Остальные примеры eness решают похожие локальные задачи:
| Пример | Что делает | Что нужно проверить |
|---|---|---|
| Apache / ModSecurity | Блокирует параметр upload progress | Включён ли анализ тела, совпадает ли имя параметра, срабатывает ли правило на реальном multipart-запросе |
| Apache / mod_rewrite | Запрещает multipart для выбранной области | Не нужны ли там загрузки и multipart-формы; применяется ли этот конфиг |
| FrankenPHP | Выключает upload progress через Caddyfile | Эффективную настройку в работающем PHP |
| Lighttpd | Запрещает multipart на выбранном хосте или участке сайта | Область действия и работоспособность штатных форм |
Эти меры дополняют установку исправлений. Отсутствие запроса в PHP благодаря фильтру и исправленный обработчик PHP — два разных результата, которые полезно записывать отдельно.
Что дают minimal и enhanced-патчи для старых веток
Anton-Barinov/php2shell-patches собирает официальные минимальные бэкпорты и варианты с дополнительными изменениями сообщества. Здесь проверена ревизия 344f877a82b4cbde0fed55a1745eddda069664f1.
Для PHP 7.4 файл minimal соответствует подходу официального бэкпорта PHP. Он завершает процесс при опасном OOM, сбрасывает ряд глобальных состояний, ограничивает обработку multipart и принудительно отключает session upload progress. Обычный приём файлов при этом сохраняется.
Вариант enhanced пытается сохранить upload progress, заменяя его отключение изменениями счётчиков и дополнительной обработкой сессии. Это самостоятельное изменение сообщества, требующее проверки на конкретной ветке.
| Для PHP 7.4 | minimal | enhanced в проверенной ревизии |
|---|---|---|
| Обработка опасного OOM | Немедленное завершение процесса | Та же основа |
| Session upload progress | Принудительно отключён | Сохранён с изменёнными счётчиками |
| Обычные multipart-загрузки | Сохранены | Сохранены по заявлению автора и его smoke-тестам |
| Дополнительные изменения session | Минимальное отключение пути | Учёт use_only_cookies, фильтрация имени файла, сброс serializer |
| Проверка совместимости с нашим BitrixVM | Через нашу RPM-сборку | Требует отдельной сборки и испытаний |
Почему нужно читать файл для своей версии
Общий README перечисляет больше изменений, чем фактически содержит enhanced-патч для 7.4. Например, дополнительные ограничения десериализации при upload progress и проверка фактически прочитанного объёма против post_max_size присутствуют в файле для PHP 5.6, но отсутствуют в проверенном файле для PHP 7.4.
Есть и ограничение заявленной фильтрации имён: удаление нулевого байта, | и переводов строк не удаляет PHP-теги. По одному такому фильтру нельзя сделать вывод, что цепочка с включением файла сессии через LFI устранена. Саму LFI в приложении он не исправляет.
Автор также отдельно указывает, что enhanced не закрывает исследуемый сценарий var_destroy/десериализации из Chain B. Значит, формулировка «улучшенный универсальный патч закрывает всё» не следует даже из описания проекта.
Приведённые расширенные результаты тестов относятся прежде всего к PHP 5.6. Для PHP 7.4 описаны сборка и smoke-проверки на Ubuntu 22.04. Это полезные сведения автора, но они не заменяют проверку CentOS 7, нашего набора расширений и Битрикса.
Что это означает для нашей RPM-сборки
Нельзя сравнивать исправления только по числу строк одного patch-файла. В нашем SRPM 7.4.33-31.el7.local ограничение max_multipart_body_parts уже приходит через php-cve-2023-0662.patch: он объявлен как Patch203 и применяется в spec. Поэтому отсутствие повторного добавления этого лимита в отдельном патче 2026 года не означает, что лимит пропущен.
Проверяем весь набор патчей и результирующий исходный код. Enhanced-файл рассчитан на определённое исходное дерево: его нельзя автоматически наложить поверх уже пропатченного Remi без разбора пересечений.
Кроме того, сборочный скрипт Anton-Barinov собирает отдельный CLI/FPM в /opt. Он не собирает Apache-модуль через apxs и не заменяет наши RPM. Для BitrixVM с mod_php сохраняем пакетную схему сборки и повторяем проверки расширений, загрузок и сессий.
Если PHP-индикатор прогресса не нужен, оснований менять выбранный минимальный бэкпорт только из-за слова enhanced нет. Если он нужен, сохранение функции — причина провести отдельное сравнение вариантов, включая тесты восстановления состояния после ошибок.
Автоматическое hardening — тоже не готовая настройка BitrixVM
harden/apply-hardening.sh рассчитан на пути FPM вида /etc/php/*/fpm/pool.d/ и PHP панели Hestia. Apache/mod_php нашего CentOS этим обходом не покрывается.
В числе предлагаемых настроек есть memory_limit=-1. Она убирает управляемый лимитом PHP OOM, но снимает ограничение памяти запроса и не исключает ошибку системного выделения памяти. На сервере с другими службами это нельзя включать как безусловное улучшение. После установки патча не нужно отключать лимит памяти просто ради того, чтобы OOM-PoC перестал срабатывать.
Проверка исправления и поиск следов взлома
В материале eness есть ещё одна полезная мысль: после обновления стоит проверить, что происходило до него. Установка нового PHP не удаляет уже появившиеся посторонние файлы.
В журнале процессов ищем неожиданные дочерние shell-процессы и сетевые утилиты у httpd или php-fpm. Сопоставляем их с запросами, ошибками PHP и изменениями файлов в webroot и временных каталогах. Приложение может вызывать внешние программы штатно, поэтому одно имя процесса не является доказательством взлома.
--audit в quick-fix — эвристический поиск, а не подтверждение чистоты сервера. Отсутствие истории процессов или логирования тела запросов нельзя компенсировать поиском строки, которая никогда не записывалась. Если находятся убедительные признаки компрометации, сохраняем журналы и разбираем инцидент отдельно от проверки патча.
Что сохранить в отчёте
Для каждого проверенного окружения записываем:
- полный выпуск RPM и происхождение исправлений;
- SAPI, расширения и настройки, влияющие на запрос;
- ревизию PoC, команду и адрес обработчика;
- результаты старой и новой сборок;
- журналы, PID и трассировки;
- подтверждение, что работающие процессы загрузили обновлённый код.
Обоснованный вывод должен называть проверенный сценарий и его ограничения. Например: дефект воспроизводился на старой сборке, не воспроизвёлся после обновления в тех же условиях, все обслуживающие сайт процессы используют новые пакеты. Такой вывод можно делать только после получения соответствующих результатов.
Фраза «PoC не сработал, сервер полностью безопасен» этих сведений не содержит. Публичный PoC помогает проверить гипотезу об исправлении, но не заменяет проверку состава патча, работающих процессов и остальных путей эксплуатации.
Источники
- php2shell-poc: README и набор проверок.
- Исходный код OOM-проверки.
- Исходный код проверки FPM.
- PHP: сообщение об исправлениях и минимальных бэкпортах.
- CVE-2026-109618 — RCE via OOM corruption.
- eness: PoC, quick-fix, патч счётчиков и настройки веб-серверов.
- Anton-Barinov: minimal и enhanced-бэкпорты.
- Cloudflare: поля имён формы, сырое тело запроса и усечение тела для инспекции.