Авторская лекция · РБПО · Процесс №18

Проверка безопасности приложения

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

№18
процесс в ГОСТ Р 56939
11
классов типовых уязвимостей
38
вопросов чек-листа эксперта
6
кейсов из реальной практики
Преподаватель Пиков Виталий Александрович
Раздел курса Безопасная разработка ПО (РБПО)
Нормативная база ГОСТ Р 56939-2024, процесс №18
01 · Контекст

Процесс №18 в жизненном цикле РБПО

В ГОСТ Р 56939-2024 разработка безопасного ПО разбита на 25 взаимосвязанных процессов. Большинство из них работают на этапах проектирования, кодирования и тестирования — там же, где работают SAST, DAST, фаззинг и код-ревью. Процесс №18 — «Проверка безопасности приложения» — стоит особняком: это последний рубеж, где живой эксперт ищет то, что пропустили автоматические инструменты.

Где этот процесс в жизненном цикле

Проверка безопасности приложения относится к нефункциональному тестированию: она проверяет не «работает ли ПО так, как задумано», а «нельзя ли использовать ПО против его пользователей». Запускается после функционального тестирования, обычно — на стадии готового кандидата в релиз. Таблица ниже — упрощённая модель водопадного жизненного цикла; в современном DevSecOps процессы перекрываются: SAST идёт на каждый коммит, фаззинг параллельно с экспертной проверкой, а сама проверка безопасности может запускаться итеративно.

Когда запускается Что проверяет Кто выполняет
На этапе кодированияСвойства кода (SAST, ревью)Разработчик / SAST-инструмент
На этапе тестированияПоведение работающего ПО (DAST, фаззинг)Тестировщик / DAST-инструмент
Перед выпуском (процесс №18)Стойкость к атакам в модели нарушителяЭксперт по безопасности
В эксплуатацииРеакция на инциденты, мониторингSOC, AppSec-инженер

Зачем нужен ручной анализ, если есть автоматика

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

Главное отличие: SAST/DAST ловят то, что уже занесено в каталог их правил. Эксперт ловит то, что требует понимания смысла — нарушение бизнес-логики (можно перевести деньги без проверки баланса в одном API, тогда как соседний API проверяет), сломанный инвариант (двухфакторка обходится повторным открытием сессии), несоответствие декларируемой и фактической модели угроз. В каталоге CWE для этого зарезервирована категория CWE-840 Business Logic Errors — её сканер «открыть» может, а проверить — нет.

02 · Задача

Представить себя нарушителем

Главная задача эксперта — представить себя нарушителем и попытаться нарушить безопасность исследуемой системы в рамках разумной модели нарушителя.

Под «разумной моделью нарушителя» обычно понимается:

  • непривилегированный локальный пользователь — имеет доступ к рабочему столу, может запускать любые свои программы;
  • удалённый неаутентифицированный пользователь — может посылать сетевые запросы по любым открытым портам;
  • удалённый аутентифицированный пользователь — имеет валидную учётную запись с минимальными правами;
  • инсайдер с доступом к коду — может изучить исходники, бинарники, конфигурации, файлы данных.

Эксперт не пытается «сломать всё подряд». Он принимает модель угроз, договорённую с заказчиком, и систематически проверяет, может ли нарушитель указанного уровня обойти декларируемые механизмы защиты.

Терминологическая оговорка. Перечень выше — учебная классификация по точке входа нарушителя. Не путать с уровнями возможностей нарушителя по Методическому документу ФСТЭК «Методика оценки угроз безопасности информации» от 05.02.2021 (базовый / базовый повышенный / средний / высокий) — там классификация по другому критерию.

Важное замечание докладчика. Все приведённые далее примеры — из реальных случаев исследования ПО. К какому продукту и какому разработчику относится каждый пример, не упоминается; участки скриншотов, раскрывающие эту информацию, замазаны.

03 · Метод

Систематический перебор классов уязвимостей

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

Из чего состоит метод

Знание класса

Эксперт держит в голове 11 типовых классов: от обхода аутентификации до некорректной ASLR. Это не словарь сигнатур, а «карта типичных мест».

Знание признака

Для каждого класса — набор быстрых индикаторов: подозрительно короткий хеш, конкатенация в SQL, незакрытое отладочное API, лишнее имя в выводе dbus-send.

Поверхностный осмотр

На каждое подозрительное место — 1–2 минуты. Файлы конфигурации, листинги директорий, вывод ps, netstat, strings, поиск по бинарникам.

Целевой эксперимент

Если поверхностный осмотр зацепил — эксперт пишет короткий PoC: подсовывает кавычку, генерирует «недопустимый» запрос, перехватывает трафик, пробует dbus-send от имени непривилегированного пользователя.

Почему это работает. Любая уязвимость — это нарушение какого-то предположения, сделанного автором кода. Предположений конечное число: «клиент валидирует данные», «никто не читает приватный ключ», «никто не знает протокол сервиса». Эксперт идёт по списку этих предположений и проверяет каждое.

04 · Уязвимости

Аутентификация

Самая массовая категория. Условно делится на четыре подкласса: возможен обход; применяется не та криптография; криптография применяется, но неправильно; криптография не применяется там, где нужна.

4.1 Возможности обхода

Самая частая ошибка — забыть проверить, прошла ли аутентификация. Бывает, что аутентификация реализуется только в клиентской программе, а сервер полагает: «если запрос пришёл — значит, всё в порядке». Бывает, что в протокол добавляют много повышающих безопасность дополнительных параметров, которые не проверяются или проверяются неправильно.

Часто обходы заметны при визуальном просмотре исходного текста. Если ПО небольшое — удобнее всего сгенерировать каждый поддерживаемый запрос сторонней программой и посмотреть, что будет. Если ПО большое, а протокол не очень сложный — пишется простой сканер:

for (перебор по параметрам запросов)
  { Send (текущий вариант);
    Error = Receive ();
    if (Error != NOT_IMPLEMENTED && Error != ACCESS_DENIED)
      cout << параметры запроса;
  }

Любой ответ, отличный от «не реализовано» или «доступ запрещён», — повод копнуть глубже. Псевдокод применим к протоколам с явной семантикой кодов ответа (HTTP, gRPC, SOAP). Для бинарных протоколов признак «эта команда что-то сделала» — таймаут или disconnect vs. структурированный ответ; критерий определяется по конкретному протоколу.

4.2 Применяется не та криптография

По документам для хеширования паролей применяется только ГОСТ Р 34.11-2012 (хеш-функция «Стрибог»), а реально — как повезёт. Или отечественная криптография вообще отсутствует, хотя должна присутствовать.

Реальный кейс: в одной отечественной сборке Linux отечественная криптография отсутствовала из-за ошибки при сборке — прилинковали не ту библиотеку.

Что делает эксперт: ищет по исходникам и бинарникам строки наподобие gost, crypt; смотрит экспортируемые символы через nm или strings; проверяет, какие библиотеки реально подгружены — ldd для ELF, dumpbin /dependents для PE; проверяет OID-ы алгоритмов в выпускаемых сертификатах. Версии заимствованных библиотек могут быть устаревшие, с неустранёнными уязвимостями.

4.3 Криптография применяется неправильно

Часто для хеширования паролей применяют слабые хеш-функции (например, MD5) без марканта/соли. В таких случаях сразу обращает на себя внимание необычно короткий хеш. Для проверки, используется ли соль, достаточно создать две учётные записи с одинаковыми паролями и сравнить хеши. Алгоритм шифрования/подписи в большинстве случаев проще всего установить с помощью одного из многочисленных онлайн-калькуляторов.

Попадались случаи, когда секретный ключ хранился в общедоступном файле (см. кейс №1 ниже).

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

4.4 Криптография не применяется когда надо

Базовые ошибки, которые ловятся одним tcpdump или Wireshark:

  • пароль передаётся по сети в открытом виде;
  • по сети передают не пароль, а незашифрованный хеш — это позволяет перехватить его и передать повторно (pass-the-hash);
  • для идентификации сетевого соединения используется легкоугадываемое значение, например порядковый номер.

Эксперт обязательно перехватывает трафик пары «клиент — сервер» во всех штатных сценариях: вход, выход, смена пароля, восстановление сессии. Странности видны сразу.

05 · Практика

Найди уязвимость: шесть кейсов из реальной практики

Шесть фрагментов из реальных проверок: листинг файлов, хеш пароля, сетевой пакет, JWT-токен, окно файлового менеджера, вывод D-Bus. В каждом — характерный признак уязвимости, который опытный эксперт видит за секунды. Прочитайте, попробуйте найти проблему сами, потом раскройте подсказку.

1 Листинг каталога с сертификатами
file system · защищённый документооборот
Отечественная система защищённого документооборота. JWT-токены в трафике. Дамп каталога /.../archive/install/unix/Release/bin/cert/:
-rw-r--r-- 1 radmin radmin 1266 авг 31 10:21 10.168.250.201.crt
-rw-r--r-- 1 radmin radmin 1018 авг 31 10:21 10.168.250.201.key
-rw-r--r-- 1 radmin radmin 1702 авг 31 10:21 10.168.250.67.crt
-rw-r--r-- 1 radmin radmin 1241 авг 31 10:21 10.168.250.67.csr
-rw-r--r-- 1 radmin radmin  989 авг 31 10:21 10.168.250.67.key
-rw-r--r-- 1 radmin radmin 1675 авг 31 10:21 10.168.253.21.crt
-rw-r--r-- 1 radmin radmin 1316 авг 31 10:21 10.168.253.21.csr
-rw-r--r-- 1 radmin radmin 1058 авг 31 10:21 10.168.253.21.key
-rw-r--r-- 1 radmin radmin 1706 авг 31 10:21 privatekey.txt
-rw-r--r-- 1 radmin radmin   32 авг 31 10:21 rootCA.crt
-rw-r--r-- 1 radmin radmin 1456 авг 31 10:21 rootCA.key
-rw-r--r-- 1 radmin radmin 1702 авг 31 10:21 rootCA.srl
-rw-r--r-- 1 radmin radmin   18 авг 31 10:21 server.crt
-rw-r--r-- 1 radmin radmin 1332 авг 31 10:21 server.csr
-rw-r--r-- 1 radmin radmin 1142 авг 31 10:21 server.key
CWE-312 Хранение чувствительной информации в открытом виде. Файл privatekey.txt с правами -rw-r--r-- доступен на чтение всем пользователям системы. Для секретного ключа JWT это эквивалентно компрометации механизма подписи: любой локальный пользователь может выпустить произвольные токены. Полнота воздействия зависит от того, что ещё валидирует сервер (kid, iss, aud, привязка токена к session_id в БД и т. п.). Кейс №4 показывает, что в этой системе сервер не валидирует ничего — поэтому утечка ключа здесь действительно даёт полный контроль над выпуском токенов.
2 MD5-хеш пароля в БД
database · отечественная СЗИ
Дамп таблицы пользователей. В одной из колонок видно:
Your String  →  Qwerty-123
MD5 Hash     →  f7f12464d28bec02951b713dd016cb58

current request user: Администратор
                pwd: f7f12464d28bec02951b713dd016cb58
CWE-759CWE-328 Хеширование пароля без соли и слабым алгоритмом. Подозрительно короткий хеш (32 hex-символа) — почти наверняка MD5. Прогон через любой онлайн-калькулятор подтверждает: md5("Qwerty-123") = f7f12464d28bec02951b713dd016cb58. Соль не используется (две учётные записи с одинаковыми паролями имеют одинаковый хеш) — это CWE-759 (Use of a One-Way Hash without a Salt); сама MD5 как алгоритм для хранения паролей — CWE-328 (Use of Weak Hash). Радужные таблицы для MD5 без соли — массовый ширпотреб; пароль восстанавливается за секунды.
3 Перехваченный HTTP-запрос
network · Wireshark · корпоративный портал
Трафик между клиентом и сервером, поток TCP в Wireshark:
POST /spring-security-jdbc-example/login.html HTTP/1.1
Host: java-srv:8080
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:84.0) Gecko/20100101 Firefox/84.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9
Accept-Language: ru-RU,ru;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 75
Origin: http://java-srv:8080
Connection: keep-alive
Upgrade-Insecure-Requests: 1

_csrf=fbd7a3d8-d139-4764-8e23-f12fee9f95e2&username=admin&password=password
CWE-319 Передача чувствительных данных в открытом виде. Логин и пароль передаются обычным HTTP без TLS — в рамках корпоративной сети их видит любой, кто имеет доступ к снифферу или к промежуточному маршрутизатору. CSRF-токен в форме адресует другую модель угроз — защиту от межсайтовой подделки запросов, а не от MITM в открытом канале; смешивать их нельзя, но визуальное присутствие токена создаёт впечатление «безопасность есть». Параллельно — слабый пароль password: либо тестовая учётка не была удалена в проде, либо политика паролей отсутствует.
4 Самописный JWT-токен
JWT · payload · подписанная нагрузка
Декодированный payload JWT-токена, выпущенного отечественной системой защиты:
{
  "alg": "HS512",
  "typ": "JWT"
}
.
{
  "AccessLevel":        "0",
  "FileCommandId":      "1",
  "FileId":             "346",
  "FileSourceVersionId": "0",
  "FileVersionId":      "1",
  "IpAddress":          "Слава России!",
  "UserGuid":           "' or 1=1; drop table",
  "iat":               "меня заставляют отправлять сетевые пакеты",
  "sub":               "УПЯЧКА!!!111 ЖЕПЬ ЕБРИЛО! ХЫВТОНЕ ПОЛЯЧСЫ!!!!"
}
CWE-20 + CWE-89 Двойная ошибка: JWT-парсер не валидирует структуру, SQL-слой не использует параметризацию. Эксперт подменил содержимое полей IpAddress, UserGuid, iat, sub на произвольный текст и SQL-инъекцию — токен принят сервером, инъекция дошла до БД. Это два независимых дефекта в разных слоях: (1) самописный JWT-парсер не проверяет даже типы полей — стандартное iat должно быть Unix-таймстампом, а здесь строка (CWE-20); (2) значения полей попадают в SQL-запросы напрямую, без параметризации (CWE-89). JWT-парсер и не должен ловить SQL-метасимволы — это задача SQL-слоя через PreparedStatement; но и пропускать структурно некорректный токен он не должен. Фоном работает кейс №1: секретный ключ подписи лежит в privatekey.txt с правами 644, поэтому подменить токен любому пользователю системы тривиально.
5 Файловый менеджер мессенджера
file system · мессенджер · path traversal
В корпоративном мессенджере есть передача файлов. Получатель видит окно «Files Received», нажимает «Show in Folder» — открывается стандартный проводник Windows. Эксперт отправил файл с именем:
\..\..\..\..\Resources\Translation.xaml

# А затем посмотрел, куда он попал:
C:\tmp2\Resources\
  Translation(1).xaml   14.04.2020
  Translation(2).xaml   14.04.2020
  Translation.xaml      18.08.2013
CWE-22 Path traversal при сохранении вложений. Имя файла принимается от другого пользователя «как есть» и склеивается с базовым путём — обратные слеши и .. не санитизируются. Файл с переданным именем «выпрыгнул» из своего каталога и перезаписал ресурс приложения Translation.xaml от 18.08.2013. Аналогичным образом можно перезаписать конфиги, исполняемые файлы, файлы автозагрузки. То же самое демонстрируется фейковым именем \..\..\..\..\..\..\..\..\..\..\etc\shadow в Linux-варианте. Простая «проверка строки на наличие ..» здесь не помогает — обходится через ....\\, %5c..%5c, NTFS short names. Канонический способ защиты в Windows — PathCanonicalize/GetFullPathName с проверкой, что результат начинается с базового каталога; в .NET — Path.GetFullPath и сравнение префиксов.
6 D-Bus интроспекция стороннего сервиса
D-Bus · непривилегированный пользователь
Эксперт от имени обычного пользователя выполняет два запроса к шине:
$ dbus-send --print-reply --dest=org.freedesktop.DBus \
            /org/freedesktop/DBus \
            org.freedesktop.DBus.ListActivatableNames

   ... ru.hornshooves.app  ...

$ dbus-send --print-reply --dest=ru.hornshooves.app / \
            org.freedesktop.DBus.Introspectable.Introspect

<node>
  <interface name="ru.hornshooves.app">
    <method name="Hello">
      <arg direction="out" type="s"/>
    </method>
    <method name="GetSomeName">
      <arg direction="in"  type="s"/>
      <arg direction="in"  type="u"/>
      <arg direction="out" type="s"/>
    </method>
  </interface>
</node>

$ dbus-send --print-reply --dest=ru.hornshooves.app / \
            ru.hornshooves.app.GetSomeName string:"aaa" uint32:666
CWE-862 Отсутствие авторизации на D-Bus-интерфейсе. В кейсе показана сессионная шина пользователя (dbus-send без флага --system): на ней авторизации почти нет по дизайну, поэтому любой процесс этого пользователя может вызвать GetSomeName с произвольными параметрами. Это типичная картина для open-source библиотек, чьи D-Bus-интерфейсы попадают в продукт «по умолчанию». Если бы метод регистрировался на системной шине (--system) — потребовалась бы polkit-политика и конфиг в /etc/dbus-1/system.d/; их отсутствие там было бы более грубой ошибкой. Реальный пример того же класса: в Astra Linux 1.6 D-Bus давал доступ к меню «Пуск» версии 1.4 — не уязвимость, но иллюстрация случайно вставленного НДВ «в хорошем смысле».

Что объединяет все шесть кейсов. Ни один из них не обнаруживается обычным SAST-сканером: листинг каталога, хеш в дампе БД, перехваченный трафик, payload JWT, окно мессенджера, вывод dbus-send — всё это контекст, а не код. Зато для эксперта, прошедшего через сотни проверок, каждый из этих шести случаев — характерный признак, который сразу запускает «копать сюда».

06 · Уязвимости

SQL-инъекции

Хороший обзор: Википедия — Внедрение SQL-кода. Тема старая, но встречается до сих пор почти в каждой проверке.

Простой эмпирический тест

Проще всего выявить, введя в форму ввода кавычку, апостроф и т. п. Появление синтаксической ошибки сигнализирует о SQL-инъекции:

Ошибка! При проверке пользователя обнаружена ошибка: [DBERRCODE(42601)]

ОШИБКА: ошибка синтаксиса (примерное положение: «А»)
СТРОКА 2: state = 'A'

Параллельно потенциальные SQL-инъекции часто видны визуально в коде. Признак — ручная конкатенация строк запроса:

// Так делать нельзя — конкатенация даёт SQL-инъекцию:
String sql = "SELECT * FROM Users WHERE UserName = '"
              + userName + "'";
var command = new SqlCommand(sql, connection);

// Так — корректно, через параметризацию:
public boolean login(String username, String password) {
    PreparedStatement stmt = connection.prepareStatement(
        "SELECT * FROM users WHERE username = ? AND password = ?");
    stmt.setString(1, username);
    stmt.setString(2, password);
    ResultSet rs = stmt.executeQuery();
    ...
}

При построении SQL-запроса должны применяться специальные средства, реализующие все необходимые проверки. Эти средства не должны быть самописными.

Оговорка к примеру. Сравнение пароля прямым SQL-запросом (как в коде выше) — анти-паттерн даже с параметризацией: пароль должен сравниваться через bcrypt/Argon2 в коде приложения, а в БД лежать солёный хеш. Пример иллюстрирует только защиту от инъекции, не корректную работу с паролями (см. раздел 4.3).

07 · Уязвимости

XSS

Хороший обзор: OWASP Community — XSS. В общем случае ищутся с большим трудом, но иногда их наличие очевидно.

Реальный пример из проверки — параметр back_url на странице авторизации:

// Безобидный вариант:
http://127.0.0.1/login.html?back_url=http://127.0.0.1/

// А что, если подставить javascript:?
http://127.0.0.1/login.html?back_url=javascript:%0Aalert(document.cookie)

В реальной системе ввод второго URL приводил к выполнению JavaScript в контексте сайта — alert с содержимым cookie. Дальше это превращается в кражу сессии любого пользователя, перешедшего по подделанной ссылке.

Универсальный индикатор. Ошибка при вводе в форму кавычки или апострофа может сигнализировать не только об SQL-инъекции, но и о XSS — на сервере что-то «не съело» специальный символ и вернуло его в ответ. Это сигнал копать.

08 · Уязвимости

Path traversal

В любом месте, где пользователю предлагается указать имя файла, эксперт пробует ввести имя с путём — и смотрит, что получится.

Что подставлять

  • ../, ./ — переход на уровень выше;
  • \ в начале строки (актуально для Windows);
  • /etc/shadow, C:\Windows\System32\config\SAM — абсолютные пути к чувствительным файлам;
  • странные кодировки Unicode для обхода фильтров (%c0%ae%c0%ae, %252e%252e);
  • длинные пути с превышением MAX_PATH;
  • NTFS-потоки данных вида file.txt:hidden.

Реальный кейс — окно поиска файлов в защищённой системе. Введя \..\..\..\..\..\..\..\..\..\..\etc\shadow в поле поиска, эксперт получил окно, предлагающее «скачать /etc/shadow» (см. также кейс №5 выше — то же самое в формате имени присоединяемого файла).

Если делается санитизация — стоит проверить, можно ли её обойти необычными кодировками или какими-то другими ухищрениями. Стандартный тест: подставить %2e%2e%2f, ..%2f, %c0%ae%c0%ae%c0%af и сравнить, обрабатывает ли их сервер так же, как чистые ../.

09 · Уязвимости

Самописные драйверы и модули ядра

Любой самописный драйвер — гарантированный источник проблем. Три типичных подкласса.

9.1 Неисчерпаемый источник сбоев

Включаем максимальное количество функций драйвера/модуля и запускаем SyzKaller, либо пишем простейший «фаззер»:

for (;;)
  for (i = 0; i < MAX_SYSCALL; i++)
    { if (NonFuzzable (i))
        continue;
      r1 = rand (); r2 = rand ();
      r3 = rand (); r4 = rand ();
      r5 = rand (); r6 = rand ();
      syscall (i, r1, r2, r3, r4, r5, r6);
    }

Результат предсказуем: BSOD на Windows, kernel oops на Linux, паника ядра — за минуты. Драйвер, который не выживает после такой проверки, не должен попадать в продукт.

9.2 Утечки данных (Heartbleed-паттерн)

Корректный код IOCTL-обработчика выставляет размер выходного буфера после валидации параметров каждой команды:

switch (code)
  { case CODE1:
      if (!CheckParameters1 ()) return ошибка;
      SetOutBufferSize (как хочет клиент);
      return DoRequest1 ();
    case CODE2:
      if (!CheckParameters2 ()) return ошибка;
      SetOutBufferSize (как хочет клиент);
      return DoRequest2 ();
    default:
      return ошибка;
  }

На каком-то этапе автор делает «безобидный» рефакторинг — выносит общий вызов SetOutBufferSize наружу. А спустя пару релизов добавляет команду без параметров:

SetOutBufferSize (как хочет клиент);
switch (code)
  { case CODE1: if (!CheckParameters1()) return ошибка; return DoRequest1 ();
    case CODE2: if (!CheckParameters2 ()) return ошибка; return DoRequest2 ();
    case CODE3_NO_PARAMETERS:
      return DoRequest3 ();   // нет CheckParameters — буфер уже выставлен!
    default:  return ошибка;
  }

В результате при обработке запроса с кодом CODE3 клиент получает до OutputBufferLength байт неинициализированной части системного буфера — фактически утечка содержимого соседних аллокаций ядра. Реальный кейс — драйвер промышленного ПО под Windows через METHOD_NEITHER (прямой пользовательский указатель, без ограничения page-aligned аллокацией) отдавал до мегабайта памяти ядра одной командой.

9.3 Недостаточно ограниченные права

Часто забывают ограничить права непривилегированных пользователей к самописным устройствам. Эксперт смотрит листинг /dev/ или \\.\, ищет устройства с правами 666 / FILE_ANY_ACCESS и пробует обращаться к ним от имени обычного пользователя.

10 · Уязвимости

D-Bus и COM/DCOM

Многие библиотеки open source предоставляют доступ к своим функциям через D-Bus. Доступ пользователей к этим функциям, как правило, предоставляется без разграничения, на основе случайной политики безопасности, самопроизвольно возникшей при заимствовании сторонних библиотек.

Опасные сервисы по умолчанию

Практически во всех Linux-системах с systemd и графической средой присутствуют сервисы, через которые непривилегированное приложение может вмешиваться в графическую сессию:

  • org.freedesktop.login1 — интерфейс systemd-logind; метод LockSession() блокирует собственную сессию пользователя без авторизации, LockSessions() для всех сессий обычно закрыт polkit-правилом, но в неаккуратной конфигурации может оказаться открытым;
  • org.freedesktop.ScreenSaver — стандартный интерфейс DE (GNOME/KDE/Xfce); вызовы Lock, Inhibit, UnInhibit доступны любому приложению пользователя.

Пример «случайного НДВ». В Astra Linux 1.6 D-Bus давал доступ к меню «Пуск» версии 1.4 — не уязвимость, просто пример случайно вставленного НДВ «в хорошем смысле»: библиотека графического стека экспортировала больше методов, чем ожидал интегратор.

Стандартный осмотр шины

Получить список сервисов:

$ dbus-send [--system] --print-reply --dest=org.freedesktop.DBus \
            /org/freedesktop/DBus \
            org.freedesktop.DBus.List[Activatable]Names

Получить список узлов и интерфейсов конкретного сервиса:

$ dbus-send [--system] --print-reply --dest=сервис /узел \
            org.freedesktop.DBus.Introspectable.Introspect

Дальше — пробовать каждый интерфейс от имени непривилегированного нарушителя (см. кейс №6 выше). Можно обнаружить много чёрных ходов, позволяющих делать странные вещи.

D-Bus можно прослушивать, если это специально не запрещено. Потенциально по D-Bus могут передаваться пароли и другая чувствительная информация. В Windows ближайшим аналогом D-Bus является COM/DCOM — те же вопросы задаются и о нём.

11 · Уязвимости

Графические интерфейсы

Графический стек — отдельный мир уязвимостей. Сценарии «приложение видит чужие окна», «приложение посылает чужому окну сообщение», «приложение перехватывает клавиатуру» — на разных платформах решаются по-разному.

X Window System

Linux desktop · традиционный

На рабочем столе все приложения видят все окна. Любое приложение может отправить любое сообщение любому окну. Можно накрыть чужое окно своим и перехватывать клавиатурный ввод.

Готовые «фокусы» для проверки:

XSendEvent xkey xwatchwin XQueryTree

Wayland · Android · Win Vista+

Современные стеки

На уровне дизайна стека защита от классических X-атак есть. В Windows с Vista — UIPI и Session 0 Isolation. В Wayland — изоляция клиентов на уровне протокола. В Android — изоляция UI через permissions в манифесте. Но это не значит, что класс атак исчез — он сместился.

В Wayland проблемы переехали в брокеры и мосты: xdg-desktop-portal для ScreenCast/RemoteDesktop, мост Xwayland (фактически восстанавливает X-модель угроз для совместимости), вопрос nested security context. В Android типовые атаки требуют либо особых разрешений (Accessibility Service, Overlay), либо абьюза доверенных компонентов: tapjacking, toast overlay (Cloak & Dagger без permissions), TapTrap (USENIX Security 2025), банковские трояны через Accessibility (Anubis, ToxicPanda).

Эксперт проверяет: какой стек используется по умолчанию. Astra Linux SE и RedOS поставляются с Xorg как основным графическим стеком. Также — не открыт ли «X-сервер для совместимости» (Xwayland, XRDP, X11 forwarding) в рабочих сценариях.

Wayland UIPI UAC xdg-portal a11y-abuse

Окна привилегированных приложений на общем рабочем столе

Сумел запустить из привилегированного приложения файловый менеджер или консоль — захватил контроль над ОС. Актуально в любом графическом интерфейсе, не только в X Window System. Самый известный пример прошлого — Shatter attack (Chris Paget, август 2002); в 2003–2007 годах было известно много уязвимых для shatter-атак продуктов — антивирусов и персональных файрволов того периода (исторически — Agnitum Outpost, Symantec Antivirus и др.). Современный аналог в актуальных продуктах — уязвимости через Window messages в win32k (например, CVE-2020-1054).

Что проверяет эксперт. Открывает «Системный монитор» / диспетчер задач, ищет процессы привилегированного ПО, у которых есть видимые окна на общем рабочем столе. Затем смотрит, к какому пользователю эти окна принадлежат, и пробует послать им сообщения от имени непривилегированного пользователя.

12 · Уязвимости

Мандатное управление доступом

Мандатное управление доступом (МУД) — отдельный класс защитных механизмов. У него два характерных вида проблем: программа просто не работает в МУД-окружении, либо работает, но позволяет утечки.

12.1. «Чума секретности» — программа не работает

Как правило, программы, изначально не предназначавшиеся для работы в условиях мандатного управления доступом, в них не работают. Обычно достаточно построить нетривиальную политику работы с секретами, поработать несколько дней — и всё рушится само собой. Нормальных реализаций МУД, пригодных для повседневного использования, видимо, нет.

Простейший пример «чумы секретности». В орфографический словарь попало слово, помеченное как секретное. Засекретился весь словарь. Засекретились все документы, где проверялась орфография. Засекретился шаблон нового документа по умолчанию. Засекретилась вся система документооборота. Откатить — невозможно.

12.2. Утечки помеченной информации

Как правило, информация, помеченная как секретная, всё равно может утекать в несекретные области ОС. Типичные пути:

Поисковые индексы
Indexer индексирует содержимое файла, а индекс хранит как обычный файл. Поиск секретного слова возвращает положительный результат и в несекретной сессии.
Журнальные файлы
Системные журналы пишутся обычно с правами root и без учёта мандатных меток. Имена файлов, ID процессов, фрагменты команд — всё это попадает в journalctl в открытом виде.
Графические интерфейсы
Заголовок окна с именем секретного файла, превью в Alt-Tab, история «недавних документов» — типичные каналы утечки на уровне DE.
Привилегированные системные процессы
Классический сценарий: пользователь с низким мандатным уровнем заставляет привилегированный сервис (демон, планировщик задач, индексатор поиска, антивирус, сервис обновлений) прочитать высокоуровневый файл и оставить артефакт в низкоуровневой области. Каналы — временные файлы, кэш, логи, дампы при падении, очереди печати. На уровне прикладной программы выглядит «легитимно», а декларируемая защита МУД обходится через посредника, который сам не знает про метки.
Внешние накопители информации
USB-флешки, сетевые диски, облачное хранилище — если МУД не покрывает их монтирование, всё содержимое выходит за периметр без меток.
13 · Уязвимости

Замкнутая программная среда

Замкнутая (изолированная) программная среда — режим, в котором можно запускать только разрешённые исполняемые файлы. На бумаге — неприступно. На практике — неисчерпаемый источник обходов.

13.1. Типичные пути обхода

  • Файлы-исключения, доступные для записи непривилегированным пользователям. Очень характерно для «защищённых» надстроек над Windows.
  • Интерпретаторы из whitelist (LOLBin / GTFOBin). Если в перечне разрешённых исполняемых оказался интерпретатор (bash, perl, python, powershell, mshta, wscript, cscript, regsvr32) — через него запускается произвольный код:
    $ bash script.txt
    > for /f "delims=;" %i in (file.txt) do %i  // в .bat — %%i; cmd должен быть в whitelist
    Тот же класс — awk, find -exec, vim -c, gdb -ex, в Windows — certutil, installutil, msbuild. Полные каталоги: LOLBAS и GTFOBins.
  • Эксплойт 38473 из ExploitDB — обход политики noexec через memfd_create() + fexecve(): исполняемый файл запускается из анонимной памяти, минуя точку монтирования с noexec. Применим как путь обхода ЗПС в тех ОС, где замкнутая программная среда опирается именно на noexec точек монтирования (характерно для ряда отечественных дистрибутивов).
  • Ярлыки на рабочем столе, если запрещено запускать файловый менеджер: ярлык можно создать вручную или подменить чужой.
  • Файлы конфигурации, например ~/.bashrc: попадают в нагрузку оболочки и могут содержать произвольные команды.
  • Перенаправление ввода-вывода, если запрещён доступ к консольным устройствам.
  • Журнальные файлы: вывод программы, не получившей доступа к консоли, может оказываться в доступном на чтение журнальном файле.

13.2. Обход через фронтенд

Если фронтенд приложения реализован веб-подобными технологиями, файлы HTML, JS и т. п., как правило, не рассматриваются замкнутой программной средой как исполняемые и могут невозбранно модифицироваться.

Запреты на обращения к опасным интерфейсам могут обходиться динамической генерацией интерпретируемого программного кода: даже если запрещён прямой eval, можно собрать строку из частей и передать в безобидно выглядящий обработчик событий.

Кроме того, замкнутая программная среда — неисчерпаемый источник сбоев в основной работе пользователя: периодически блокируются нужные операции, тормозят легитимные приложения, падают сторонние компоненты. Эксперт смотрит не только на пути обхода, но и на удобство — слишком жёсткая ЗПС всё равно будет отключена администраторами «потому что мешает».

14 · Защитные механизмы

Средства отладки и ASLR

Двa механизма из «инфраструктуры безопасности» ОС, которые эксперт всегда проверяет: блокировка отладочных интерфейсов и качество рандомизации адресных пространств.

14.1. Средства отладки

Отладочные интерфейсы могут использоваться как непосредственно для НСД, так и для преодоления отдельных элементов защиты. Что должно быть:

В Linux-системах

обязательный минимум
  • Yama LSM включён (в современных ядрах это уже так по умолчанию: CONFIG_SECURITY_YAMA=y).
  • Значение kernel.yama.ptrace_scope — не менее 1 (по умолчанию в Ubuntu/Debian/Astra), для защищённых сегментов — 2 или 3.
  • Интерфейс ptrace, файлы maps/mem в /proc/<pid>/ недоступны непривилегированным приложениям.

В Windows-системах

разумный компромисс

В идеале нельзя делать OpenProcess и OpenThread на компоненты защищённого ПО. В реальности полностью добиться этого, скорее всего, не получится — должен быть какой-то разумный компромисс. В любом случае, стороннее непривилегированное приложение не должно иметь возможности применять средства отладки к защищённому ПО.

Реальный кейс. Однажды в эксплуатируемом промышленном ПО нашёлся неудалённый стенд отладки веб-интерфейса — позволял отлаживать SQL-инъекции прямо через GET-параметры. Стенд оставался доступен по сети без какой-либо аутентификации.

14.2. ASLR — рандомизация адресных пространств

Основное средство защиты от эксплуатации переполнений буферов и некоторых других видов уязвимостей. Что проверяется:

  • Достаточная энтропия: известны случаи успешного подбора распределения памяти при количестве вариантов до примерно 100 000.
  • Различие между процессами: в одновременно выполняющихся процессах распределение должно отличаться. Сходное или полностью одинаковое распределение — типичный недостаток.
  • Отсутствие злоупотребления отключением: средства отключения ASLR для отдельных процессов не должны применяться без необходимости.
  • Защита от обхода через обработку исключений: злоупотребление средствами обработки исключений позволяет нарушителю угадывать варианты неограниченно долго невзирая на ASLR.
15 · Инструмент

Чек-лист эксперта: 38 контрольных вопросов

Практический опросник для проведения процесса №18. Ниже — все 38 вопросов, сгруппированные по тематическим разделам. Каждый вопрос — это и тест для разработчика, и пункт плана для эксперта по безопасности.

Опросник заимствован из материалов В. Г. Проскурина (ИСП РАН), редакция 2017–2020 гг. Несколько пунктов могут выглядеть «двумерно» относительно реалий 2025 года: к устаревшей криптографии добавились SHA-1, TLS 1.0/1.1, RSA с e=3 без OAEP, ECDSA с предсказуемыми nonce; к D-Bus и COM/DCOM добавились gRPC внутри продукта, REST-внутри-продукта, Android Binder, Unix-сокеты с собственным протоколом. Структура опросника от этого не теряет ценности.

№ 1–4 Аутентификация и протоколы
  1. Какие запросы к серверу доступны без аутентификации? Достаточно ли ограничен перечень таких запросов? Как это проверялось — визуальным просмотром кода, вводом тестовых запросов вручную, созданием автоматического сканера на основе системы тестов?
  2. Допустимо ли, если сервер получает пакеты не от штатной клиентской программы, а от сторонней программы? Если нет — каким образом реализуется запрет подключения таких клиентов? Если да — точно ли это не приводит к нарушениям безопасности (например, если функция безопасности реализована не на сервере, а только в клиентской программе)?
  3. Какой протокол аутентификации применяется? Если самописный — кто его разрабатывал и тестировал? Если стандартный — точно ли он подходит к данной конкретной ситуации?
  4. Есть ли в протоколе дополнительные параметры, повышающие безопасность (ограничения по адресам подключения, времени подключения, сроку действия ключей и т. п.)? Они точно используются? Как это проверялось?
№ 5–15 Криптография и работа с ключами
  1. Как сервер устанавливает длины буферов с передаваемыми клиенту данными? Нет ли HeartBleed-подобных уязвимостей?
  2. Какие криптосистемы применяются? Точно ли реальность соответствует документации? Как это проверялось? Проверялись ли особые случаи (например, в Linux-совместимой ОС самый первый пользователь, создаваемый при установке)? Не применяются ли самописные криптоалгоритмы?
  3. Какой заимствованный код используется в проекте? Какие в нём известны уязвимости? Проводилась ли работа по анализу заимствованного кода как минимум на уровне мониторинга открытых источников? Проводится ли такая работа на регулярной основе? Какая реакция предусмотрена в случае обнаружения в заимствованной библиотеке критической уязвимости?
  4. Применяются ли в проекте устаревшие криптосистемы наподобие DES, MD5, RC4? Если да — безопасно ли это?
  5. При хешировании паролей используется ли маркант (соль, salt, seed)? Если нет — почему? Проверялось ли это прямым сравнением хешей одинаковых паролей у разных пользователей?
  6. Как вводятся ключи шифрования? Если они хранятся внутри системы — как реализовано это хранение? Кому могут быть доступны эти данные в каких ситуациях?
  7. Передаются ли пароли по сети в открытом виде? Проверялось ли это перехватом и анализом трафика?
  8. Передаются ли по сети хеши аутентификации в открытом виде? Проверялось ли это перехватом и анализом трафика?
  9. Если защита трафика полагается на полное его шифрование сторонним протоколом (например, SSL) — как обрабатывается ситуация, когда шифрование по каким-то причинам невозможно? Не пойдёт ли трафик в открытом виде?
  10. Как сервер идентифицирует сетевые соединения клиентов? Не используются ли легкоугадываемые идентификаторы (например, порядковые номера), передаваемые прямо в пакете с данными?
  11. Какие генераторы случайных чисел применяются в проекте? Не применяются ли для криптографических задач линейные рекурренты или другие неподходящие алгоритмы?
№ 16–18 Инъекции и валидация ввода
  1. Проводились ли проверки кода на наличие SQL-инъекций? Какие средства формирования SQL-запросов применяются в проекте? Не формируются ли SQL-запросы вручную (sprintf и т. п.)? Если подавать на вход непарные кавычки, апострофы и т. п. — не появляются ли в ответах сервера ошибки SQL?
  2. Проводились ли проверки кода на предмет наличия XSS-уязвимостей? Как сервер реагирует на вручную сформированные пакеты, похожие на штатные, но с необычными заполнениями полей? Как сервер реагирует на команды JavaScript, размещаемые внутри пакета? Может ли нарушитель добиться их некритичного размещения внутри веб-системы? Если имитировать ввод в формы непарных кавычек, апострофов и т. п. — не появляются ли в ответах ошибки веб-интерфейса? Насколько корректно сервер принимает пакеты с текстом в необычных кодировках?
  3. Как обрабатываются ситуации, когда клиент указывает имя файла вместе с путём? Не пытается ли сервер применить этот путь к своей файловой системе? Как при этом обрабатываются подстроки .., строки, начинающиеся с \ в Windows и т. п.? Если делается санитизация — можно ли её обойти необычными кодировками текста или какими-то другими ухищрениями?
№ 19–25 Драйверы, права доступа, IPC и интерфейсы ОС
  1. Драйверы и модули ядра, содержащиеся в проекте, проверялись ли SyzKaller-подобными фаззерами?
  2. Как ограничиваются права доступа к файлам приложения? Что может сделать с файлами приложения непривилегированный пользователь, используя предоставленные права? Если приложение работает в Windows — те же вопросы задаются в отношении ключей реестра и объектов корневой файловой системы (\??\* и т. п.).
  3. Как ограничиваются права доступа к устройствам, обслуживаемым исследуемым драйвером? Драйвер полностью полагается на права доступа, заданные в ОС, или дополнительно реализует собственные проверки? Что может сделать с драйвером непривилегированный пользователь?
  4. Как ограничивается доступ к реализованным в проекте сервисам D-Bus или интерфейсам COM/DCOM? Что может делать непривилегированный пользователь, используя предоставленные права?
  5. Принимаются ли в проекте специальные меры, блокирующие доступ сторонних приложений через отладочные интерфейсы операционной системы? Не стоит ли их принять?
  6. Принимаются ли в проекте специальные меры, блокирующие доступ сторонних приложений через графический интерфейс операционной системы? Не стоит ли их принять?
  7. Не создаёт ли анализируемое ПО на общем рабочем столе окна, обслуживаемые высокопривилегированным процессом? Могут ли такие окна создаваться непредумышленно — например, при запуске справки или после аварийного завершения Windows Explorer? Что может сделать непривилегированный пользователь, направляя в эти окна специально подобранные комбинации сообщений? Не стоит ли избавиться от таких окон?
№ 26–38 Защитные механизмы, анализ кода, чувствительные данные
  1. Поддерживает ли анализируемое ПО мандатное управление доступом? Если да — все ли функции работают в секретных сессиях? Как это проверялось? Возможны ли утечки информации, помеченной как секретная, через поисковые индексы, журнальные файлы, графические интерфейсы, привилегированные системные процессы, внешние накопители информации, другими путями?
  2. Поддерживает ли анализируемое ПО изолированную (замкнутую) программную среду? Если да — блокированы ли типичные пути обхода через файлы-исключения, скрипты, эксплойт 38473 из ExploitDB, ярлыки на рабочем столе, файлы конфигурации, перенаправление ввода-вывода, журнальные файлы и т. д.? Распространяются ли правила ЗПС на интерпретируемый и динамически генерируемый программный код, COM-подобные интерфейсы и т. д.? Не вызывают ли правила ЗПС сбоев при доступе к принтерам, внешним накопителям, сети и т. д.?
  3. Скомпилирован ли проект с поддержкой рандомизации распределения адресных пространств (ASLR)? Насколько качественна эта рандомизация — устранены ли типичные недостатки? Может ли нарушитель игнорировать ASLR, используя злоупотребление средствами обработки исключений?
  4. Проводился ли статический анализ исходных текстов проекта? Проводится ли он на регулярной основе? Как организовано реагирование на предупреждения статического анализатора?
  5. Какие принимаются меры для защиты от переполнений буфера? Используются ли в коде проекта небезопасные функции обработки строк? Присутствуют ли в программе переполнения буферов?
  6. Какие принимаются меры для защиты от обращений к освобождённой памяти? Присутствуют ли в программе ошибки обращения к освобождённой памяти?
  7. Какие принимаются меры для защиты от ошибок двойного освобождения памяти? Присутствуют ли в программе ошибки двойного освобождения памяти?
  8. Используются ли в проекте форматные строки (printf и т. п.)? Какие принимаются меры для защиты от ошибок в работе с форматными строками? Присутствуют ли в программе ошибки работы с форматными строками?
  9. Какие принимаются меры для защиты от утечек памяти? Присутствуют ли в программе утечки памяти?
  10. Какие принимаются меры для обеспечения корректной многопоточности (недопущение race conditions и deadlocks)? Присутствуют ли в программе ошибки обеспечения корректной многопоточности?
  11. Построена ли для приложения поверхность атаки? Предпринимались ли попытки её построить? Полностью ли она покрыта вышеперечисленными вопросами?
  12. Проводилось ли фаззинг-тестирование приложения? Планируется ли его проводить? Учитывалась ли перспектива проведения фаззинг-тестирования при проектировании архитектуры и в ходе разработки приложения?
  13. Работает ли приложение с чувствительными данными (пароли, ключи шифрования, идентификаторы банковских счетов и т. п.)? Какими? Проводилось ли для приложения автоматизированное выявление утечек чувствительных данных? Были ли выявлены утечки и какие?

Как пользоваться чек-листом. Опросник работает в двух режимах. Разработчику — как контрольный самотест перед сдачей продукта. Эксперту — как план интервью с командой разработки и план собственных проверок. Каждый «нет» в ответах — повод для отдельного исследования.