РАЗБОР На проверке

Определение сервера, приложения и фреймворка

Сергей Попов
Сергей Попов Red Team · 6,7 тыс. сообщений
Подписаться
325
[ обложка статьи ]
Режим чтения
Матовый чёрный стол с небольшим LCD-терминалом на антистатическом коврике: экран светится бирюзово-янтарным светом, показывая строки HTTP-заголовков Server и X-Powered-By. Рядом смотаный Ethern...


Заголовок Server: Website.com в HTTP-ответе - реальный пример из OWASP Web Security Testing Guide. За изменённым баннером скрывается конкретный веб-сервер, но порядок остальных полей в ответе совпадает с поведением Nginx, а не Apache. Один признак - гипотеза. Два - направление. Три из разных слоёв стека - основание для записи в отчёт. Здесь разберём процедуры определения технологического стека (fingerprinting): от веб-сервера до фреймворка и приложения по методологии WSTG-INFO-02, WSTG-INFO-08 и объединённой WSTG-INFO-09.

Паспорт наблюдения​

Прежде чем запускать инструменты, стоит определиться с форматом записи. Каждый обнаруженный признак фиксируется в виде паспорта наблюдения - структуры, которая связывает конкретный артефакт с архитектурным слоем и уровнем уверенности:
  • URL или хост - адрес, по которому получен ответ.
  • Слой архитектуры - к чему относится признак: CDN / балансировщик, reverse proxy, веб-сервер, application server, фреймворк, CMS.
  • Признак - конкретное наблюдение: значение заголовка, имя cookie, путь к файлу, HTML-комментарий.
  • Предполагаемый продукт - название ПО, на которое указывает признак.
  • Версия - точная версия, если она явно раскрыта; «не установлена», если признак указывает на продукт без номера версии.
  • Степень уверенности - высокая (несколько независимых признаков из разных слоёв), средняя (один-два признака без противоречий), низкая (единственный признак, который может быть подделан).
Зачем так формально? Без этой структуры легко спутать заголовок от CDN с версией исходного сервера приложения (origin server). Результат фингерпринтинга веб-сервера сам по себе не доказывает наличие уязвимости - это входные данные для следующих проверок, не готовый finding.

Определение веб-сервера (WSTG-INFO-02)​

Задача - установить тип и определение версии сервера, на котором работает целевое приложение. Серверы с устаревшим ПО без актуальных патчей безопасности могут быть подвержены эксплойтам, специфичным для конкретных версий.

Цель тестирования по WSTG: определить версию и тип работающего веб-сервера для дальнейшего поиска известных уязвимостей.

Методы: сбор серверных баннеров (banner grabbing), провоцирование ответов на некорректные запросы и применение автоматических средств. Общий принцип один - вызвать у сервера ответ, который можно сопоставить с базой известных ответов и поведений, и соотнести с известным типом сервера.

Сбор серверных баннеров​

Сбор серверных баннеров (banner grabbing) - отправка HTTP-запроса и анализ заголовков ответа. Для этого подходят curl -I (шлёт HEAD-запрос и показывает только заголовки), httpx для массовой проверки больших списков хостов или openssl s_client для запросов через TLS/SSL.
Код:
curl -I https://example.org
В ответе Apache заголовок Server явно раскрывает тип и версию - Server: Apache/2.4.43 (Unix). Порядок заголовков характерен: Date, Server, Last-Modified, ETag, Accept-Ranges, Content-Length, Connection, Content-Type.

Для Nginx порядок иной: Server, Date, Content-Type, далее Content-Length, Last-Modified.

Для lighttpd - ещё один вариант: Content-Type идёт первым, далее Accept-Ranges, ETag, Last-Modified, Content-Length, Connection, Date, а Server - последним.

Но приложения, заботящиеся о безопасности, могут модифицировать заголовок Server. WSTG приводит ответ с Server: Website.com. Тут тестировщик может предположить тип сервера по порядку оставшихся полей: если последовательность совпадает с Nginx (Server, Date, Content-Type), это косвенно указывает на Nginx. Метод не даёт однозначного результата - разные серверы могут использовать одинаковый порядок, а поля могут быть изменены или удалены.

Отправка некорректных запросов​

Веб-серверы можно идентифицировать по их ответам на ошибки - особенно если страницы ошибок не кастомизированы. Один из способов: отправка намеренно некорректного запроса с несуществующей версией HTTP-протокола.

WSTG демонстрирует запрос GET / SANTA CLAUS/1.1. Ответы принципиально различаются:
  • Apache возвращает HTTP/1.1 400 Bad Request с HTML в формате <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"> и текстом «Your browser sent a request that this server could not understand».
  • Nginx возвращает 404 Not Found (другой код ошибки на тот же запрос!) с характерным HTML - теги <center> и <hr>, версия в <center>nginx/1.17.3</center>.
  • lighttpd возвращает HTTP/1.0 400 Bad Request с XML-декларацией и XHTML Transitional DOCTYPE.
Один запрос - три принципиально разных ответа. Стандартные страницы ошибок содержат множество отличительных признаков: формат DOCTYPE, структура HTML, сам код ответа. Даже при скрытом заголовке Server эти различия позволяют определить тип сервера. Для идентификации по стандартным страницам, не раскрывающим тип напрямую, WSTG ссылается на каталог стандартных ошибок.

Автоматические средства сканирования веб-технологий​

Автоматические сканеры выполняют запросы, аналогичные описанным выше, плюс шлют серверо-специфичные пробы. Они сопоставляют ответы с базами известных реакций значительно быстрее ручного тестирования - потому чаще дают точный результат.

WSTG перечисляет инструменты с функцией фингерпринтинга:
  • Netcraft Site Report - онлайн-сервис, собирающий информацию о сайтах, включая данные о веб-сервере.
  • Nikto - open-source сканер командной строки.
  • Nmap - open-source инструмент командной строки с GUI Zenmap; nmap service detection определяет версии сервисов.
  • WhatWeb и httpx (ключ -tech-detect) - определяют ПО веб-сервера наряду с другими технологиями стека.

Учебный сценарий: reverse proxy и слои стека​

Контекст стенда. Локальный reverse proxy (Nginx) установлен перед приложением. Заголовок Server возвращает одно значение, X-Powered-By - другое.

Действия. Отправить обычный HEAD-запрос и зафиксировать заголовки. Изучить cookie в Set-Cookie. Запросить несуществующий путь и проанализировать страницу ошибки. Записать, какой слой даёт каждый признак.

Условный результат. Заголовок Server: nginx/1.24.0 - слой proxy (уверенность высокая: баннер и порядок заголовков совпадают). Заголовок X-Powered-By: PHP/8.2 - слой application server за proxy (уверенность средняя: заголовок может быть подменён). Cookie laravel_session - слой фреймворка (уверенность средняя: cookie может быть переименован). Вывод разделяет proxy и фреймворк; конкретная версия Laravel не установлена при отсутствии подтверждения.

Ложный маркер. Администратор заменил заголовок Server на произвольную строку через proxy_set_header. Поддельный Server-заголовок не меняет реально установленный компонент - если порядок остальных заголовков, страницы ошибок и реакция на некорректные запросы по-прежнему соответствуют Nginx, единичное изменение баннера не опровергает другие наблюдения.

Идентификация фреймворка веб-приложения (WSTG-INFO-08)​

Подавляющее большинство веб-приложений частично или полностью построены на известных фреймворках и CMS (WordPress, phpBB, MediaWiki и др.). Знание компонентов серьёзно помогает при тестировании и сокращает объём работы. Эти приложения обладают характерными HTML-заголовками, cookie и структурами каталогов. Большинство фреймворков оставляют несколько маркеров в типичных местах - именно так работают автоматические инструменты: ищут маркер в заранее определённом месте и сравнивают его с базой известных сигнатур (signature). Для повышения точности обычно используется несколько маркеров одновременно.

Цель тестирования по WSTG: определить компоненты, используемые веб-приложением.

При тестировании методом чёрного ящика (Black-Box Testing) WSTG выделяет шесть категорий мест для поиска: HTTP-заголовки, cookie, HTML-код, специфичные файлы и каталоги, расширения файлов, сообщения об ошибках.

Заголовки и cookie как признаки фреймворка​

HTTP-заголовки. Самый базовый способ - значение X-Powered-By в HTTP-ответе. Пример: X-Powered-By: Mono указывает на .NET-совместимую среду. Заголовок легко отключить или подменить - WSTG приводит значение X-Powered-By: Blood, sweat and tears (и правда - пот и слёзы, если кто-то на это купится).

Иногда заголовков несколько. Пример: X-Powered-By: PHP/5.4.16-1~dotdeb.1 сообщает версию PHP, а X-Generator: Swiftlet дополнительно указывает на конкретный фреймворк. При сборе информации о веб-приложении нужно проверять все HTTP-заголовки на подобные утечки - анализ HTTP-заголовков не ограничивается одним полем.

Cookies. Более надёжный способ - специфичные для фреймворка cookie. Laravel автоматически устанавливает laravel_session и XSRF-TOKEN. Имена cookie можно переименовать (для Laravel - через cookie в config/session.php), но такие изменения вносятся реже, чем скрытие X-Powered-By. Cookie - более надёжный признак для определения CMS сайта, чем заголовки, именно потому что их реже трогают.

HTML-код, маркеры, каталоги и расширения​

HTML-код. Техника основана на поиске паттернов в исходном коде страницы. Распространённые маркеры: HTML-комментарии, ссылки на CSS/JS-папки фреймворка, переменные скриптов. Angular добавляет атрибут ng-version к корневому элементу. Информация часто сидит в <head>, в <meta>-тегах или в конце страницы - но анализировать следует весь ответ. Иногда раскрытие оказывается в футере - «Proudly published with Ghost» указывает на платформу Ghost.

Общие маркеры (General Markers). В HTML можно искать паттерны: %framework_name%, powered by, built upon, running.

Специфичные маркеры (Specific Markers):

ФреймворкМаркер
Adobe ColdFusion<!-- START headerTags.cfm
Microsoft ASP.NET__VIEWSTATE
ZK<!-- ZK ...-->, скрипты в zkau/
Indexhibitndxz-studio в <link> / <a href>
Next.js<script id="[B]NEXT_DATA[/B]">, заголовок x-powered-by: Next.js
Nuxt<div id="[B]nuxt", window.[/B]NUXT__, пути /_nuxt/
htmxJS-объект htmx, скрипт htmx.org@...

Специфичные файлы и каталоги. Каждый компонент имеет характерную файловую структуру. Для её обнаружения применяется принудительный просмотр (dirbusting) - перебор известных путей с мониторингом HTTP-ответов. Для WordPress характерны /wp-includes/, /wp-admin/, /wp-content/. Ответы 403, 302 (редирект на wp-login.php) и 200 соответственно - надёжный индикатор. Файл readme.txt плагина по пути /wp-content/plugins/<plugin-name>/readme.txt раскрывает версию через поле Stable tag.

Совет из WSTG: перед dirbusting'ом проверить robots.txt - строка Disallow: /wp-admin/ прямо указывает на WordPress. Для словарей пригодны FuzzDB wordlists of predictable files/folders. Если компонент имеет открытый код, полезно развернуть его временную копию - посмотреть структуру и понять, какие файлы могут остаться на сервере.

Расширения файлов. URL-адреса могут содержать расширения: .php - PHP, .aspx - Microsoft ASP.NET, .jsp - Java Server Pages.

Сообщения об ошибках. Ошибки приложения раскрывают стек. Пример из WSTG: ошибка парсинга PHP в WordPress содержит путь wp-content и имя functions.php - указывает и на CMS, и на язык одновременно.

Таблицы распространённых идентификаторов​

Идентификация по cookie:

ФреймворкИмя cookie
WordPresswordpress_logged_in_[I], wp-settings-[/I]
Laravellaravel_session
Djangosessionid, csrftoken (по таблице WSTG-INFO-08)
Express.js (Node)connect.sid
ASP.NET Core.AspNetCore.Session, .AspNetCore.Antiforgery.*
phpBBphpbb3_*
DrupalSESS[I] / SSESS[/I] (имя - хеш)
CakePHPCAKEPHP (настраиваемо)
TYPO3fe_typo_user
DNN PlatformDotNetNukeAnonymous

Примечание: сигнатуры Django (sessionid, csrftoken) указаны в таблице Common Identifiers WSTG-INFO-08. Имена sessionid и csrftoken - задокументированные дефолтные значения параметров SESSION_COOKIE_NAME и CSRF_COOKIE_NAME в Django Settings Reference. Deployment Checklist описывает лишь флаги CSRF_COOKIE_SECURE и SESSION_COOKIE_SECURE, относящиеся к защите этих cookie по HTTPS, а не сами имена.

Идентификация по HTML-коду:

ПриложениеМаркер
WordPress<meta name="generator" content="WordPress X.X" />
phpBB<body id="phpbb"
MediaWiki<meta name="generator" content="MediaWiki X.X" />
Joomla<meta name="generator" content="Joomla! - Open Source Content Management" />
Drupal<meta name="Generator" content="Drupal X (https://drupal.org)" />
Ghost<meta name="generator" content="Ghost X.X" />
DotNetNukeDNN Platform - https://www.dnnsoftware.com
Angularng-version="X.X.X" на корневом элементе
Next.js<script id="[B]NEXT_DATA[/B]" type="application/json">

Автоматическое распознавание: WhatWeb и аналоги​

WhatWeb - один из наиболее известных open-source инструментов для определения технологий сайта. Входит в стандартную сборку Kali Linux, написан на Ruby. Текущая стабильная версия - 0.6.4, выпущенная 3 апреля 2026 года (по данным официального репозитория urbanadventurer/WhatWeb).

Механизм WhatWeb основан на сигнатурах - каждая проверка использует один или несколько способов сопоставления:
  • Текстовые строки (с учётом регистра)
  • Регулярные выражения
  • Запросы из Google Hack Database (ограниченный набор)
  • MD5-хеши
  • Распознавание URL-паттернов
  • Паттерны HTML-тегов
  • Пользовательский Ruby-код для пассивных и активных операций (пассивный и активный фингерпринтинг)
Почему WhatWeb даёт более точные результаты, чем ручная проверка отдельных заголовков? Потому что инструмент комбинирует независимые признаки из разных слоёв. Поддерживаются ключи --plugins для выбора плагинов, --aggression для уровня интенсивности и различные форматы логирования.

WSTG в разделе Tools к проверке INFO-08 указывает на существование множества специализированных утилит помимо универсальных, включая инструменты, ориентированные на конкретные фреймворки. Раздел References и Whitepapers ссылается на работы Saumil Shah «An Introduction to HTTP fingerprinting» и Anant Shrivastava «Web Application Finger Printing».

Учебный сценарий: статические пути и HTML-маркер​

Контекст стенда. Учебное приложение развёрнуто на разрешённом стенде. В HTML-коде главной страницы присутствует тег <meta name="generator">, в структуре каталогов - характерные статические пути.

Действия. Проверить HTML-код на наличие маркера в <meta name="generator"> - допустим, обнаружен паттерн, совпадающий с шаблоном конкретной CMS. Через dirbusting проверить наличие специфичных каталогов этой CMS. Сопоставить две сигнатуры. Если стенд разрешает - обратиться к документированному version endpoint'у (например, readme.html или аналог).

Условный результат. Продукт подтверждён двумя независимыми признаками: HTML-маркер (слой - HTML-код, уверенность средняя) и структура каталогов (слой - файловая система, уверенность средняя). Вместе они дают высокую уверенность в идентификации продукта. Версия указана в отчёте только при достаточном свидетельстве - например, если version endpoint явно раскрывает номер.

Ложный маркер. На стенде размещён favicon.ico, скопированный с другого продукта. Скопированный favicon не доказывает использование этого продукта - для определения CMS требуется сопоставление нескольких независимых признаков. Вывод по одному файлу без подтверждения другими наблюдениями имеет низкую степень уверенности.

Статус WSTG-INFO-09: определение приложения​

Проверка WSTG-INFO-09 «Fingerprint Web Application» в текущей версии WSTG объединена с WSTG-INFO-08 «Fingerprint Web Application Framework». На официальной странице (WSTG-INFO-09) содержится только уведомление: «This content has been merged into: Fingerprint Web Application Framework».

Определение конкретного приложения (WordPress, phpBB, MediaWiki) рассматривается как часть общей процедуры идентификации фреймворка из WSTG-INFO-08. Отдельная методология для INFO-09 не существует в текущем снапшоте - все описанные техники (заголовки, cookie, HTML-маркеры, каталоги, расширения, ошибки) покрывают задачу определения и фреймворка, и приложения одновременно.

От отпечатка к следующей гипотезе​

Собранные отпечатки - отправная точка, не конечный результат. Типичная последовательность:
  1. Проверить инвентарь. Сопоставить определённые компоненты с известным ПО организации (если инвентарь доступен). Это позволяет отличить ожидаемый стек от неавторизованных или забытых компонентов.
  2. Сопоставить версию с бюллетенями. Если версия определена - проверить наличие известных уязвимостей в бюллетенях вендора.
  3. Учитывать backport-патчи. Тут кроется ловушка, на которой горят многие. Как отмечает Red Hat в документации по бэкпортированию, номер версии пакета сам по себе не говорит, уязвим ли он. Вендор может держать старую upstream-версию (например, PHP 5.3), но с применёнными патчами безопасности. Red Hat приводит пример: исправление CVE-2014-3670 для PHP 5.3 в RHEL 6 было выполнено бэкпортом без обновления до upstream PHP 5.4. Инструменты, ориентирующиеся только на номер версии, дают ложные срабатывания.
  4. Выбрать релевантный тест. На основании определённого стека выбрать дальнейшие проверки, специфичные для конкретной CMS, фреймворка или версии сервера.
Типичные ошибки:
  • Принимать ответ CDN за версию backend. Заголовок Server от CDN не отражает реально установленный исходный сервер приложения (origin server). Различать слои архитектуры - обязательное условие корректного вывода.
  • Сообщать о CVE по непроверенной версии или одному маркеру. Определение версии по единственному признаку - гипотеза, не факт. Заявлять о подверженности конкретной CVE допустимо при достаточном свидетельстве версии и проверке наличия backport-патчей.

Исправления и рекомендации​

Для веб-сервера (по WSTG-INFO-02). WSTG рекомендует: скрытие информации в заголовках (например, модуль mod_headers для Apache); использование защищённого reverse proxy как дополнительного слоя; поддержание актуальности ПО и установка патчей. Раскрытие информации о сервере само по себе не уязвимость - это информация, помогающая атакующему эксплуатировать другие уязвимости.

Для фреймворка и приложения (по WSTG-INFO-08). Меры вроде изменения имён cookie, скрытия каталогов через URL rewriting, удаления заголовков - всё это, по формулировке WSTG, сводится к «security through obscurity». Такие меры замедляют самых начинающих атакующих, а время лучше потратить на поддержание решений в актуальном состоянии.

На практике:
  • Обновлять подтверждённые компоненты и сверять патчи - с учётом различия upstream-версии и backport-патчей.
  • Убирать ненужные диагностические баннеры и заголовки (X-Powered-By, X-Generator, развёрнутые сообщения об ошибках).
  • Настраивать единые безопасные страницы ошибок, не раскрывающие путь к файлам или версию ПО.
  • Не рассчитывать на то, что скрытие баннера само по себе защитит от чего-либо.
Основа: OWASP Web Security Testing Guide, OWASP Foundation и авторы:
Дополнительные источники: WhatWeb README (версия 0.6.4); Red Hat - Backporting Security Fixes; Django Deployment Checklist.

Русский перевод и редакционная актуализация - Codeby. Изменения относительно оригинала: объединение проверок INFO-02, INFO-08 и INFO-09 в единую статью; добавлены учебные сценарии с ложными маркерами и паспортом наблюдения; добавлен раздел о backport-патчах и типичных ошибках интерпретации версий; структура реорганизована по слоям стека; уточнён статус cookie-сигнатур Django. Лицензия перевода и адаптации: CC BY-SA 4.0.



Большинство отчётов, которые всплывают на профильных площадках, содержат один и тот же паттерн: «обнаружен Apache 2.4.49 - значит CVE-2021-41773 (активно эксплуатируется, включена в CISA KEV; использовалась в ransomware-кампаниях) - значит Critical». Фингерпринт подаётся как доказанная эксплуатация. Баннер показал версию - уязвим. Один cookie - фреймворк подтверждён. Заголовок от CDN - это backend.

В реальности номер версии в баннере не учитывает backport-патчи вендора. Один cookie может быть переименован или скопирован. Заголовок Server от reverse proxy или CDN не имеет отношения к тому, что работает за ними. Определение технологического стека - не этап эксплуатации, а этап сбора гипотез, каждая из которых требует подтверждения независимыми признаками из разных слоёв. Два наблюдения - направление. Три из разных слоёв - основание для записи с указанием степени уверенности.

Те, кто продолжает ставить Critical на основании одного баннера без проверки backport-патчей, обесценивают отчёт целиком. Это не вопрос инструментов - WhatWeb или Nmap одинаково честно покажут то, что видят. Вопрос в том, хватит ли дисциплины не записывать гипотезу как факт.

Вся серия WSTG · Предыдущая статья
Полезно

Комментарии

0