Kea Control Agent доступен по сети: проверяем возможность загрузки hooks с правами root
Сканер обнаружил Kea Control Agent и написал про выполнение кода. Перезагружать DHCP, закрывать порт или готовиться к восстановлению сервера? Я, Семёнов Евгений Сергеевич, директор ООО «АйТи-Фреш», начинаю с проверки конкретной цепочки: кто достучится до API, какие команды ему разрешены, откуда загрузится библиотека и чьи права она получит. Покажу порядок диагностики и результаты лабораторной проверки одного пакета в разных режимах.
Открытый API ещё не доказывает удалённый захват сервера
Для выполнения собственного кода через hooks атакующему нужны сразу несколько условий. Он должен добраться до административного API, получить возможность передать подходящую конфигурацию и указать доступную процессу библиотеку под своим контролем. Дальше загрузку должны разрешить ограничения Kea и операционной системы. Если загружающий процесс имеет UID 0, код библиотеки получает его привилегии. Именно последнее звено превращает проблему DHCP в потенциальную компрометацию сервера.
ISC описывает этот механизм в CVE-2025-32801 как локальное повышение привилегий: непривилегированный пользователь размещает файл и обращается к недостаточно защищённому API. Затронуты upstream-версии 2.4.0–2.4.1, 2.6.0–2.6.2 и 2.7.0–2.7.8; первые исправления вышли в 2.4.2, 2.6.3 и 2.7.9. Версии до 2.4 ISC не оценивала. Сетевой доступ расширяет круг потенциальных атакующих, но сам по себе не объясняет, как их библиотека попадёт на сервер. [Уведомление ISC](https://kb.isc.org/docs/cve-2025-32801).
На 5 сентября 2026 года актуальны стабильная Kea 3.2.0 и LTS 3.0.4. Ветка 2.6 завершила поддержку у ISC; сопровождение дистрибутивом проверяется отдельно. Для существующего внедрения с Control Agent я рассматриваю поддерживаемую LTS и план миграции: в 3.0 агент объявлен устаревшим, а в 3.2 удалён. Прямой HTTP(S) API демонов сохраняет административные возможности, поэтому обновление архитектуры не заменяет ограничение доступа. [Версии ISC](https://www.isc.org/kea/), [миграция с Control Agent](https://kb.isc.org/docs/kea-control-agent-migration).
Сначала проверяю bind address и реальную аутентификацию
Начните с фактического слушающего сокета: `sudo ss -lntp`. Порт 8000 — стандартный, но искать нужно все слушатели нужного процесса. `127.0.0.1` ограничивает прямые подключения локальным сетевым пространством; `0.0.0.0` означает все IPv4-интерфейсы. Между слушателем и атакующим остаются маршрутизация и firewall. Отдельно проверьте IPv6, reverse proxy и опубликованные контейнерные порты. Надпись «внутренний адрес» меня не успокаивает, пока API доступен из пользовательского VLAN.
В Control Agent адрес задаёт `http-host`, порт — `http-port`; без этих параметров используется `127.0.0.1:8000`. Для Basic важен непустой список `authentication.clients`: одного блока с `type: basic` недостаточно. Я проверяю три обращения: без учётных данных, с неправильным паролем и с рабочим. Пароль передаю только через защищённый транспорт; Basic поверх обычного HTTP не обеспечивает его конфиденциальность. [Конфигурация Control Agent](https://kea.readthedocs.io/en/kea-3.0.4/arm/agent.html).
Для первичной проверки подходят запросы ниже. Адрес документационный — замените его своим. Выполните их с административного узла и из сегмента, которому управление запрещено. Смотрите одновременно HTTP-код и JSON: HTTP 200 ещё не означает успешное выполнение команды. `list-commands` показывает поддерживаемые команды, но наличие `config-set` в списке не доказывает разрешение записи при RBAC или фильтрации посредником. Пароль также не создаёт автоматически роль «только мониторинг». [Справочник API](https://kea.readthedocs.io/en/kea-3.0.4/api.html).
- Проверка самого CA: `curl --noproxy '*' -sS -i --connect-timeout 2 --max-time 5 -H 'Content-Type: application/json' --data '{"command":"list-commands"}' http://192.0.2.10:8000/`
- Проверка маршрута к DHCPv4: `curl --noproxy '*' -sS -i --connect-timeout 2 --max-time 5 -H 'Content-Type: application/json' --data '{"command":"list-commands","service":["dhcp4"]}' http://192.0.2.10:8000/`
- Отказ reverse proxy проверяйте вместе с недоступностью прямого backend-порта: иначе защиту посредника можно обойти.
Выясняю, какой процесс загрузит библиотеку
Здесь легко проверить не того пользователя. Запрос без `service`, либо с пустым списком, обрабатывает сам Control Agent. Библиотеку из `Control-agent.hooks-libraries` загружает `kea-ctrl-agent`. При `"service":["dhcp4"]` агент пересылает команду через Unix-сокет, а библиотеку из `Dhcp4.hooks-libraries` загружает уже `kea-dhcp4`. Поэтому непривилегированный CA перед DHCP-сервером, работающим от root, не исключает root-impact. [Алгоритм обработки команд CA](https://kea.readthedocs.io/en/kea-2.6.4/arm/agent.html).
Я сопоставляю PID, эффективный UID, командную строку и unit каждого адресата. Строка `User=kea` в найденном файле ещё не доказывает, что именно так запущен работающий процесс: встречаются drop-in, собственные unit и ручные старты. Для Linux смотрю также capabilities и ограничения службы. `NoNewPrivileges=yes` ограничивает получение новых привилегий через запуск программ, но не отбирает уже имеющиеся права у кода, загруженного внутрь процесса. [Семантика systemd](https://github.com/systemd/systemd/blob/main/man/systemd.exec.xml).
Следующая точка — Unix-сокет и весь путь к нему. Проверяю владельца, группы, ACL и права родительского каталога через `namei -l` и `getfacl`. Если посторонний пользователь может обращаться прямо к DHCP-сокету, пароль HTTP-агента его не остановит. Добавление учётной записи мониторинга в группу Kea тоже требует осознанного решения: доступ к сокету предоставляет административный API. [Файлы и права Kea](https://kb.isc.org/docs/kea-files).
- Процессы: `ps -C kea-ctrl-agent,kea-dhcp4,kea-dhcp6 -o pid,euid,egid,args`.
- Имена служб: `systemctl list-unit-files '*kea*'`. Затем для фактического unit проверьте `systemctl cat` и свойства `User`, `Group`, `ExecStart`, `AmbientCapabilities`, `CapabilityBoundingSet`.
- Для выбранного PID сопоставьте поля `Uid`, `Gid`, `CapEff` и `NoNewPrivs` в `/proc/PID/status`; отдельно проверьте действующий профиль AppArmor или SELinux.
Проверяю путь hooks и не использую config-test как песочницу
`library` обозначает локальную библиотеку, а не URL для скачивания. Файл должен существовать в файловой системе, которую видит адресат команды. Для рассматриваемой цепочки нужно установить, кто способен создать или подменить этот файл. Проверяйте доступные на запись каталоги, общие тома и пути развёртывания. Возможность изменять JSON не равна возможности передать ELF-бинарник: штатный `config-set` сам по себе не является загрузчиком файлов. Механизм эксплуатации исследован командой SUSE. [Разбор первооткрывателей](https://security.opensuse.org/2025/05/28/kea-dhcp-security-issues.html).
Начиная с 2.6.3 в соответствующей стабильной ветке, hooks разрешено загружать из каталога, заданного при сборке. Его показывает `kea-ctrl-agent -W` или `kea-dhcp4 -W` как Hooks directory. При запуске каталог можно переопределить переменной `KEA_HOOKS_PATH`; это не поле REST-запроса. Проверяйте фактическое окружение процесса, владельцев библиотек и каталогов. В Kea 3.0 появился также флаг `-X`, отключающий ограничения путей и прав. Обновлённый бинарник с таким флагом требует отдельной оценки. [Ограничение каталога](https://kea.readthedocs.io/en/kea-2.6.3/arm/hooks.html#configuring-hook-libraries), [флаг запуска CA](https://kea.readthedocs.io/en/kea-3.0.4/arm/agent.html).
И неприятная деталь: проверка совместимости библиотеки тоже может исполнять её код. В исходниках Kea 3.0.4 проверка hooks включает открытие через `dlopen` и вызов `version()`; для CA она выполняется и при `config-test`. Это вывод из кода, а не предположение по названию команды. Ошибка ABI или совместимости с многопоточностью не доказывает, что исполнения ещё не было. [Парсер CA](https://github.com/isc-projects/kea/blob/Kea-3.0.4/src/bin/agent/simple_parser.cc#L176), [загрузчик библиотек](https://github.com/isc-projects/kea/blob/Kea-3.0.4/src/lib/hooks/library_manager.cc#L378).
Лабораторная проверка: один пакет, три режима
Для статьи я проверил именно лабораторный запуск, без привязки к клиентскому проекту. Среда — Ubuntu 24.04, архитектура amd64, пакет `kea-ctrl-agent` версии `2.4.1-3ubuntu0.2`; сам бинарник сообщает `2.4.1`. Пакет и зависимости распакованы отдельно, системная служба не устанавливалась. Это существенно: ручной старт не воспроизводит штатную защиту Ubuntu через service user, AppArmor и подготовленную конфигурацию. Canonical прямо учитывает эти меры при оценке CVE-2025-32801. [Позиция Ubuntu](https://ubuntu.com/security/CVE-2025-32801).
Во всех запусках использовался адрес `127.0.0.1:18083`, без публикации наружу. Сначала агент работал с эффективным UID 0, без аутентификации. Существенная часть конфигурации выглядела так: `{"Control-agent":{"http-host":"127.0.0.1","http-port":18083}}`; дополнительно логирование направлялось в stdout. POST с телом `{"command":"list-commands"}` вернул HTTP 200 и `result: 0`. В списке присутствовали `config-set`, `config-test` и `config-write`.
Затем я добавил внутрь `Control-agent` блок `authentication` с `type: basic`, `realm: lab` и одним клиентом с одноразовым лабораторным паролем. Без credentials и с неправильным паролем тот же запрос получил HTTP 401 и `Unauthorized`; с правильными — HTTP 200 и `result: 0`. В третьем режиме убрал Basic и запустил тот же бинарник через `setpriv --reuid=65534 --regid=65534 --clear-groups`. Эффективный UID 65534 подтверждён через `/proc`; ответ снова оказался успешным.
Чем закончилась проверка: root и непривилегированный процесс вернули совершенно одинаковый список из 11 команд. По такому ответу сканер не определит права процесса. Аутентификация при этом действительно изменила возможность обращения к API. Все тестовые процессы после проверки остановлены. Загрузка собственной библиотеки, удалённая достижимость извне и получение root этим опытом не проверялись — приписывать их результатам было бы технической ошибкой.
- UID 0, без Basic: HTTP 200, result 0.
- UID 0, Basic включён: без пароля и с неверным паролем — HTTP 401; с правильным — HTTP 200.
- UID 65534, без Basic: HTTP 200, result 0, тот же список команд.
- Эквивалент проверочного запроса: `curl --noproxy '*' -i -H 'Content-Type: application/json' --data '{"command":"list-commands"}' http://127.0.0.1:18083/`.
Что я исправляю первым
Если административный API открыт пользовательским сегментам, сначала ограничиваю сетевой доступ, сохраняя необходимые связи управления и HA. Для локального клиента выбираю loopback и аутентификацию. Для удалённого — отдельный управленческий маршрут, firewall и HTTPS с проверкой сертификатов; где инфраструктура позволяет, добавляю клиентские сертификаты. Перенос на другой номер порта можно отложить: он не заменяет эти меры. [Рекомендации ISC по защите API](https://kb.isc.org/docs/kea-security).
Для существующего CA на 3.0.4 ниже приведён фрагмент настроек доступа: его нужно объединить с рабочей конфигурацией, сохранив сокеты, hooks и логирование. Файл пароля заранее создаётся с уникальным секретом и доступом только доверенным администраторам и service user. Если CA стоит за reverse proxy, защищаю и прямой backend-путь. Иначе получается типичная ошибка: красивый вход с паролем снаружи и свободный административный порт за ним.
Далее перевожу демоны на отдельные непривилегированные учётные записи, используя поддерживаемые настройки пакета. DHCPv4 может требовать capabilities для привилегированного порта и raw sockets, поэтому слепая замена `User=root` способна остановить выдачу адресов. Библиотеки и скрипты оставляю недоступными сервису на запись; каталог данных отделяю от каталога кода. После изменений проверяю получение и продление аренды, мониторинг, DDNS и HA, если они используются. [Запуск Kea без root](https://kea.readthedocs.io/en/kea-3.0.4/arm/install.html#running-kea-from-a-non-root-account-on-linux).
- Фрагмент для объединения с объектом Control-agent: ```json { "http-host": "127.0.0.1", "http-port": 8000, "authentication": { "type": "basic", "realm": "kea-control-agent", "directory": "/etc/kea", "clients": [ { "user": "kea-api", "password-file": "kea-api-password" } ] } } ```
- Перед обновлением сохраните конфигурацию, данные аренды и план отката. Сверьте происхождение пакета и исправления поставщика; одного вывода `-v` недостаточно.
Как сформулировать вывод, который поможет принять решение
В отчёте я разделяю факты и условия. «Из пользовательского VLAN без пароля выполнен list-commands» — проверяемый факт. «Доступна замена конфигурации» требует подтверждения правил авторизации. «Возможна загрузка контролируемой библиотеки от root» требует проверки файла, разрешённого пути, адресата команды и его полномочий. Если доставка библиотеки не установлена, так и пишу. Это не повод закрывать находку: доступ к управлению DHCP уже требует исправления.
Даже без собственного машинного кода административный API способен причинить существенный ущерб: изменить параметры выдачи адресов или отключить DHCP. Отдельная проблема — чтение конфигурации: ответ `config-get` может содержать пароли баз данных. Поэтому я не прикладываю его целиком в общий тикет. Для расследования сохраняю необходимые артефакты с ограниченным доступом, отмечаю время проверок и сравниваю активную конфигурацию с утверждённой. [Поведение команд управления](https://kea.readthedocs.io/en/kea-3.0.4/arm/ctrl-channel.html).
При признаках чужих библиотек или несанкционированной переконфигурации простого закрытия порта недостаточно: нужны сохранение доказательств и расследование возможной компрометации. А для обычной находки сканера я добиваюсь короткого, воспроизводимого заключения по пунктам ниже. Непроверенное условие остаётся неопределённостью, а проверенная граница доступа — конкретным результатом. Тогда администратор понимает, что исправлять сегодня и какую часть риска ещё предстоит подтвердить.
- Доступ: адрес, порт, исходный сегмент, прямой путь и посредники.
- Управление: аутентификация, разрешённые команды, маршрутизация к DHCP-демонам.
- Код: источник .so, разрешённый каталог, права записи, параметры запуска и ограничения ОС.
- Привилегии: фактический UID загружающего процесса, capabilities, контейнерная изоляция.
- Статус: подтверждённые условия, непроверенные звенья, исправления и результаты повторной проверки.
Частые вопросы
Можно ли передать ссылку на .so в hooks-libraries?
Поле library обозначает локальный файл, а не команду скачивания. Для рассматриваемой цепочки библиотека должна находиться в файловой системе адресата и проходить ограничения доступа и пути.
Если Control Agent работает без root, сервер защищён?
Нужно проверить адресата команды. Непривилегированный CA может пересылать команды DHCP-демону с UID 0 через доступный ему Unix-сокет.
Можно ли закрыть находку после получения HTTP 401?
Только в части проверенного входа без credentials. Дополнительно проверяются прямые порты, Unix-сокеты, права авторизованных клиентов и другие сетевые сегменты.
Я помогу проверить доступ к API, права демонов и реальные условия загрузки hooks на вашем сервере. Оставьте заявку в rf-buh — согласуем аудит и план исправлений.
Бесплатная консультация →

