Разобранный серверный блейд на чёрном коврике под лампой, зонд касается вскрытой платы с трассировкой Host-заголовка. Экран анализатора светится текстом CVE-2026-48710 · BADHOST рядом с одинокой кр...


Меняю заголовок на Host: target.internal/health?x= - тот же запрос, тот же HTTP-путь, но в ответе 200 OK с полным содержимым административной панели. Один символ / в значении Host обрушивает модель авторизации целиком. Это CVE-2026-48710 - Host Header Injection уязвимость в Starlette, ASGI-фреймворке под капотом FastAPI, которую CISA внесла в каталог активно эксплуатируемых уязвимостей (KEV) 2 сентября 2026 года с дедлайном устранения 16 сентября. Исследователи назвали её BadHost. И затрагивает она не только классические веб-приложения - под ударом инфраструктура AI-инференса на Python: vLLM, LiteLLM, MCP-серверы и сотни тысяч зависимых проектов.

Два пути в одном запросе: корень FastAPI уязвимости​

Starlette - легковесный ASGI-фреймворк, на котором построены FastAPI и десятки AI-инструментов. Внутри него при обработке HTTP-запроса живут два отдельных представления пути. И вот тут начинается самое интересное. Подробнее - в нашем подробном разборе безопасность llm приложений.

Сырой ASGI-путь - значение scope["path"], извлечённое напрямую из строки запроса (GET /admin HTTP/1.1). Роутер Starlette использует именно его для выбора обработчика эндпоинта.

Реконструированный URL - объект request.url, который Starlette собирает конкатенацией схемы (http/https), содержимого заголовка Host и пути. Разработчикам доступен как request.url.path.

В штатном режиме значения совпадают. Запрос GET /admin с Host: example.com даёт scope["path"] = /admin и request.url.path = /admin. Разработчики считают их взаимозаменяемыми - и пишут middleware авторизации через request.url.path, не подозревая о расхождении.

До версии 1.0.1 Starlette не валидировал содержимое заголовка Host перед подстановкой в URL. Символы /, ? и # - структурные разделители в URI (путь, query string, фрагмент). Попав в позицию hostname при конкатенации, они сдвигают границы между компонентами URL при повторном парсинге. По данным GitHub Security Advisory GHSA-86qp-5c8j-p5mr, это и есть корневая причина BadHost.

Конкретный механизм расхождения. Атакующий отправляет запрос:
Код:
GET /admin HTTP/1.1
Host: example.com/health?x=
Starlette конструирует URL: http://example.com/health?x=/admin. При парсинге этой строки request.url.path становится /health, а ?x=/admin уходит в query string. Роутер работает с scope["path"] = /admin и вызывает обработчик защищённого эндпоинта.

Middleware видит /health (публичный маршрут) - пропускает. Роутер выполняет /admin. Никакой криптографии, никакого подбора токенов - чистое расхождение интерпретаций того, какой ресурс защищается. Классический time-of-check / time-of-use внутри одного стека обработки.

CVSS v3.1: 6.5 MEDIUM, вектор AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N - эксплуатация по сети, не требует ни привилегий, ни действий пользователя. Классификация: CWE-444 (Inconsistent Interpretation of HTTP Requests) и CWE-1289 (Improper Validation of Unsafe Equivalence in Input). Оба CWE про одно: уязвимость не в отдельном компоненте, а на стыке их интерпретаций.

Практическая эксплуатация BadHost атаки​

📚 Часть контента скрыта. Этот материал доступен участникам сообщества с рангом One Level или выше
Получить доступ просто — достаточно зарегистрироваться и проявить активность на форуме

, /metrics. Проверяем: curl -v http://target:8000/health - ответ 200 OK.

Шаг 3. Инъекция в Host header. Подставляем публичный маршрут так, чтобы request.url.path стал равен ему:

curl --http1.1 -v -H "Host: target:8000/health?x=" http://target:8000/admin
Примечание: команда протестирована с curl 8.x по HTTP/1.1. Некоторые версии curl/libcurl могут валидировать или ре-эскейпить значение Host - при ложноотрицательном результате используйте raw-сокет или Burp Repeater.
Уязвимое приложение вернёт 200 OK с содержимым /admin. Демонстрация X41 D-Sec (исследователи, обнаружившие BadHost) воспроизводит именно этот паттерн - по данным CSO Online, «They sent the same request with one extra character in the Host header, and the page returned a 200 OK». Один символ - и 403 превращается в 200. Красота.

Шаг 4. Вариации payload. Кроме / работают ? и #, и разные URL-парсеры обрабатывают их по-разному:
  • Host: target:8000/docs? - маскировка под документацию FastAPI
  • Host: target:8000?/health# - сдвиг через query string
  • Host: target:8000#/public - сдвиг через фрагмент
В Burp Intruder ставим позицию после hostname в заголовке Host, загружаем payload-лист из комбинаций /health?, /docs?, /public?, ?/, #/. Grep по status code 200 выявляет успешные обходы. Для blind-сценариев (одинаковое тело ответа) сравниваем длину response body и наличие заголовков аутентификации (X-Auth-User, Set-Cookie) - их появление или исчезновение подтверждает расхождение путей.

SSRF через Host заголовок в AI-инфраструктуре​

Host Header Injection уязвимость в Starlette открывает вектор не только обхода авторизации. Если приложение использует request.url для формирования исходящих запросов (callback URL, webhook-нотификации, redirect после OAuth), атакующий контролирует целевой хост - прямой SSRF. По MITRE ATT&CK это Exploit Public-Facing Application (T1190, Initial Access).

Для AI-инфраструктуры сценарий особенно неприятен. LLM-gateway принимает запрос, извлекает request.url.scheme и request.url.netloc для проксирования к model-serving бэкенду. Подменённый Host перенаправляет запрос на внутренний сервис - metadata API облака (169.254.169.254) для получения IAM-креденшалов, Redis с кэшированными API-ключами или model endpoint без аутентификации.

Бизнес-логика атаки: злоумышленник получает доступ к дорогостоящим GPU-ресурсам инференса, крадёт API-ключи для перепродажи доступа к коммерческим моделям или - в случае MCP-серверов - вызывает произвольные инструменты от имени агента. Это покрывается OWASP A10:2021 (Server-Side Request Forgery) и A05:2021 (Security Misconfiguration).

Цепочка CVE-2026-48710 + CVE-2026-42271: от Starlette bypass до RCE​

CISA в записи KEV отмечает, что CVE-2026-48710 может быть объединена (chained) с CVE-2026-42271. На практике - эскалация от обхода авторизации до выполнения произвольного кода на хосте.

CVSS 8.7 HIGH (CVSS v4.0). Два эндпоинта для тестирования MCP-серверов (POST /mcp-rest/test/connection и POST /mcp-rest/test/tools/list) принимали конфигурацию с полями command, args и env для stdio-транспорта. При подключении LiteLLM выполнял указанную команду - CWE-77 и CWE-78 (Command Injection). CISA KEV: добавлена 8 июня 2026, дедлайн 22 июня 2026. Затронуты litellm:litellm и Red Hat OpenShift AI.

Цепочка атаки разворачивается в три шага:
  1. BadHost (CVE-2026-48710) обходит авторизацию на LiteLLM-прокси - запрос с подменённым Host попадает к административным эндпоинтам
  2. Атакующий вызывает POST /mcp-rest/test/connection с конфигурацией stdio-транспорта и произвольной командой
  3. LiteLLM порождает процесс - полноценный RCE на хосте шлюза
Формальный CVSS 6.5 для BadHost отражает изолированный сценарий (C:L/I:L/A:N - ограниченная утечка и модификация данных). В реальном деплое LiteLLM за одним Starlette-приложением - это полная компрометация AI-gateway с lateral movement во внутреннюю сеть. Один HTTP-запрос → обход auth → второй запрос → shell. Для воспроизведения CVE-2026-42271 доступен публичный PoC-репозиторий learner202649/CVE-2026-42271-PoC на GitHub.

CISA не добавляет уязвимости в KEV по формальному CVSS - добавление означает подтверждённую эксплуатацию в дикой природе. Если попало в KEV - кто-то уже зашёл.

Уязвимость vLLM и MCP-серверов: кто под ударом​

По данным X41 D-Sec (обнаруживших BadHost при аудите vLLM, организованном OSTIF и спонсированном Alpha-Omega Project), Starlette имеет более 400 000 зависимых проектов на GitHub. Не все уязвимы - но три группы с максимальным риском выделяются особо.

FastAPI/Starlette-приложения без обратного прокси. Dev-серверы, staging, внутренние API. Uvicorn с --host 0.0.0.0 без nginx - типичная конфигурация при разработке и у команд, которые про reverse proxy в продакшне «ну, потом прикрутим».

Model-serving и AI-gateway. vLLM, LiteLLM, OpenAI-compatible proxy, evaluation dashboards. По данным CSO Online, «Research, evaluation and development setups for AI software often do not [have a reverse proxy], and many run the application server facing the network directly». ML-инженеры деплоят быстро - безопасность AI-агентов при этом уходит на второй план.

MCP-серверы. Спецификация Model Context Protocol требует наличия неаутентифицированных OAuth discovery endpoints - это даёт атакующему гарантированную точку входа для эксплуатации BadHost без необходимости угадывать публичные маршруты. Подарок прямо из спеки.

Затронутые продукты по данным NVD: encode:starlette, Red Hat AI Inference Server, Red Hat Ansible Automation Platform, Red Hat Migration Toolkit for Applications.

Существенный нюанс: команда может не подозревать о наличии уязвимой Starlette. Зависимость приходит транзитивно через FastAPI. Проверка: pip show starlette | grep Version. В контейнерных деплоях: docker run --rm <image> pip show starlette. Это категория OWASP A06:2021 (Vulnerable and Outdated Components) - уязвимый компонент, о котором разработчик даже не знает.

Обход валидации хоста: детектирование и патч​

Индикаторы эксплуатации в логах​

Ключевой признак попытки эксплуатации - URI-значимые символы /, ?, #, @ в значении заголовка Host. В access-логах nginx или uvicorn:

grep -P 'Host:\s[I][^,\s][/I][/?#@][^,\s]*' /var/log/access.log

Для SIEM: правило по полю http.request.header.host с regex, ловящим символы, недопустимые в hostname по RFC 9112.

Второй индикатор - защищённые маршруты, возвращающие неожиданный 200 OK. Если /admin должен отвечать 401/403, а в логах появляется 200 - смотрите соответствующий запрос на предмет модифицированного Host.

ProjectDiscovery выпустили nuclei-шаблоны для обеих CVE: CVE-2026-48710.yaml и CVE-2026-42271.yaml. Массовая проверка инфраструктуры одной командой: nuclei -t http/cves/2026/CVE-2026-48710.yaml -l targets.txt. Исследователи X41 D-Sec также создали сервис badhost.org для проверки конкретных хостов.

Патч Starlette 1.0.1 и верификация​

Starlette 1.0.1 добавляет валидацию заголовка Host - значения с URI-значимыми символами отклоняются до реконструкции URL. Обновление: pip install "starlette>=1.0.1". Для FastAPI - убедитесь, что транзитивная зависимость действительно обновилась (проверить pip freeze | grep starlette и pinned-версии в requirements.txt / poetry.lock).

Верификация после патча: отправляем запрос с инъекцией и проверяем HTTP-код ответа:

curl -s -o /dev/null -w "%{http_code}" -H "Host: target/health?" http://target:8000/admin

На патченной версии ожидается 400 Bad Request (невалидный Host отклонён) или тот же 403/401, что и без инъекции. Ответ 200 - патч не применён.

Reverse proxy - defence-in-depth, не замена патча. Конфигурация nginx должна содержать явный default_server для отклонения запросов к неизвестным хостам:
NGINX:
server {
    listen 80 default_server;
    return 444;
}
Без этого блока запросы с произвольным Host могут пройти к бэкенду. По данным Hacker News-обсуждения, «the biggest risk is incorrect usage of the default_server directive» - большинство туториалов по nginx этому не учат. А зря.

Чек-лист для баг-баунти отчёта по инъекции HTTP заголовка​

Элемент отчётаСодержание
НазваниеHost Header Authentication Bypass via CVE-2026-48710 (BadHost)
SeverityMedium (6.5 base), повышается при chaining
PreconditionsStarlette < 1.0.1, нет compliant reverse proxy, auth через request.url.path
Steps to reproducecurl с модифицированным Host, скриншоты 403 → 200
ImpactОбход авторизации, доступ к защищённым эндпоинтам
ChainingCVE-2026-42271 (LiteLLM RCE), SSRF через контролируемый Host
RemediationStarlette >= 1.0.1 + reverse proxy + переход на scope["path"] в middleware
ReferencesGHSA-86qp-5c8j-p5mr, CISA KEV

При chaining с CVE-2026-42271 severity в отчёте обосновывается как Critical - демонстрация полной цепочки от одного HTTP-запроса до RCE существенно повышает bounty. В отчёте важно показать именно переход: один запрос → обход auth → второй запрос → выполнение команды. Программы вроде HackerOne любят такие цепочки - это не «теоретический Medium», а практический Critical с PoC.

Восемь лет эта уязвимость жила в Starlette. Не нашли SAST-сканеры, не нашёл fuzzing HTTP-парсеров, не поймал code review с помощью LLM - по отзывам исследователей, баг оказался «too subtle for current Anthropic models». Нашли руками X41 D-Sec при аудите vLLM - и обнаружили проблему не в vLLM, а тремя уровнями зависимостей глубже.

Каждый компонент в изоляции ведёт себя разумно: роутер берёт путь из scope, URL builder склеивает Host с path, middleware проверяет URL. Баг рождается на стыке интерпретаций. pip audit ловит known CVE в прямых зависимостях, но не отвечает на вопрос: «что мой middleware делает с заголовком, который уже прошёл через три слоя парсинга?».

Таких уязвимостей «на стыке» в AI-стеке будет больше - MCP-серверы, agent-фреймворки, model-gateway собираются из десятков библиотек, у каждой собственная модель валидации входных данных. Не автоматизированный аудит зависимостей решит эту проблему, а ручной разбор interaction boundaries между компонентами - как именно один слой передаёт данные другому и какие допущения при этом нарушаются. Хочется руки в крови - на HackerLab есть web-задачи с HTTP smuggling и обходом авторизации, где расхождение путей между слоями и есть ключ к флагу.
 
Мы в соцсетях:

Взломай свой первый сервер и прокачай скилл — Начни игру на HackerLab

🚀 Первый раз на Codeby?
Гайд для новичков: что делать в первые 15 минут, ключевые разделы, правила
Начать здесь →
🧭 Навигатор · ИБ 2026
Не знаешь, какой трек твой?
5 направлений ИБ, реальные зарплаты и точка входа для каждого — в одном треде.
JuniorSenior+
100K → 600K+ ₽ /мес
Открыть навигатор →
🔴 Свежие CVE, 0-day и инциденты
То, о чём ChatGPT ещё не знает — обсуждаем в реальном времени
Threat Intel →
💼 Вакансии и заказы в ИБ
Pentest, SOC, DevSecOps, bug bounty — работа и проекты от проверенных компаний
Карьера в ИБ →