
Протоколи взаємодії між додатками, які працюють на одному комп’ютері Windows, часто використовують іменовані канали (named pipes). Вони швидкі, підтримуються операційною системою і добре підходять для обміну даними між службами Windows, настільними додатками, фоновими агентами та іншими компонентами.
Зазвичай, в такій архітектурі привілейована служба Windows виступає як сервер іменованого каналу, а користувацький додаток — як клієнт. Оскільки обидва процеси працюють на одному комп’ютері, розробники часто вважають таке з’єднання внутрішнім і довіреним. Проте, на практиці, канал може бути доступний з середовища, де працюють різноманітні, непов’язані процеси з різними користувачами, сесіями та контекстами безпеки.
Локальне не означає довірене
Іменовані канали часто сприймаються як приватний канал зв’язку між додатками на одному комп’ютері. Це припущення є небезпечним.
На робочій станції Windows можуть працювати процеси з різними правами: LocalSystem, адміністратори, звичайні користувачі, облікові записи служб, а також окремі інтерактивні чи віддалені сесії. Також можуть бути встановлені сторонні програми, скрипти, діагностичні інструменти або зловмисне програмне забезпечення, що працює під обліковим записом зі скомпрометованими правами.
Будь-який процес, який знає ім’я каналу та має достатні права доступу, може спробувати підключитися. Windows не знає, який саме виконуваний файл розробник мав на увазі для використання каналу.
Тому іменований канал слід розглядати як відкритий локальний інтерфейс. Перед обробкою запиту додаток повинен перевірити, хто підключився, які дії дозволені цій особі, і чи є надані дані безпечними.
Ідентичність, контроль доступу та межі повноважень
Ризик найбільший, коли привілейована служба Windows взаємодіє з менш привілейованим настільним додатком.
Служба, що працює як LocalSystem, може мати можливість змінювати захищені файли та ключі реєстру, запускати процеси, змінювати конфігурацію системи, отримувати доступ до даних інших користувачів або взаємодіяти з драйверами ядра. Коли ці операції надаються через іменований канал, канал стає API для привілейованих функцій.
Успішне підключення лише доводить, що клієнт мав дозвіл відкрити канал. Це не доводить, що:
- клієнт є очікуваним додатком;
- підключений користувач авторизований;
- запит операції дозволений;
- команда є безпечною.
Тому дозволи на використання каналу слід визначати явно та обмежувати найменшим відповідним набором ідентифікаторів. Широкі дозволи для “Everyone”, “Authenticated Users” або всіх інтерактивних користувачів можуть дозволити непов’язаним процесам доступ до каналу.
Автентифікація та авторизація також мають бути розділені. Користувач може мати дозвіл запитувати статус служби, але не зупиняти її, змінювати захищені налаштування, запускати процеси або отримувати доступ до довільних файлів. Чутливі команди слід авторизувати індивідуально.
Імітація (impersonation) може допомогти, виконуючи операції в контексті безпеки клієнта, але нею слід користуватися обережно. Сервер повинен перевіряти, чи вдалася імітація, обмежувати роботу, що виконується під час імітації, і завжди відновлювати свою початкову ідентичність.
Відбивайтеся від кібератак, керованих ШІ
Дізнайтеся, як надмірні дозволи можуть перетворити інструменти ШІ на серйозний ризик безпеки.
Дізнайтеся, як практична стратегія нульової довіри може допомогти стримати загрози, керовані ШІ, перш ніж вони поширяться.
Читайте блог
Недовірені сервери, команди та дані
Клієнт повинен перевіряти сервер так само, як сервер перевіряє клієнта.
Передбачуване ім’я каналу є лише ідентифікатором. Воно не є секретом і не доводить, який процес створив канал. Зловмисник може створити канал з очікуваним ім’ям до запуску легітимного сервера, змусивши клієнта підключитися до процесу, керованого зловмисником.
Опція “first-pipe-instance” може допомогти виявити, що ім’я вже зайнято, але вона не замінює належний контроль доступу або перевірку ідентичності сервера.
Повідомлення, отримані через канал, також слід розглядати як недовірений ввід. Навіть автентифікований клієнт може надсилати:
- пошкоджені або занадто великі пакети даних;
- недійсні шляхи до файлів чи реєстру;
- непідтримувані комбінації команд;
- пошкоджені серіалізовані об’єкти;
- значення, призначені для виклику умов помилки.
Привілейована служба, яка перетворює такий ввід безпосередньо у файлові, реєстрові, процесні операції або команди, може стати “заплутаним заступником”: зловмисник надає інструкцію, а служба надає привілеї.
Запити повинні використовувати суворе кадрування повідомлень, обмежені розміри, списки дозволених команд, валідацію схеми, нормалізацію шляхів, авторизацію для конкретних операцій та безпечну обробку помилок.
Доступність та віддалене розкриття
Безпека іменованих каналів не обмежується лише підвищенням привілеїв та несанкціонованими командами.
Зловмисний або несправний процес може багаторазово підключатися, тримати з’єднання відкритими, надсилати неповні повідомлення або надсилати запити, що споживають надмірні ресурси ЦП, пам’яті або ядра.
Сервер повинен використовувати обмеження на кількість з’єднань, тайм-аути, скасування, обмежені розміри повідомлень, контрольовану паралельність та обмеження частоти запитів, де це доцільно.
Також небезпечно припускати, що кожен іменований канал доступний лише з локального комп’ютера. Іменовані канали Windows можуть підтримувати віддалений доступ у деяких конфігураціях.
Канали, призначені виключно для локальної взаємодії процесів (IPC), повинні явно блокувати мережеві ідентичності, такі як NT AUTHORITYNETWORK, або використовувати механізм, що гарантує виключно локальне спілкування.
Правильна модель загроз є простою: кожне з’єднання іменованого каналу слід розглядати як потенційно вороже, доки не буде перевірено ідентичність клієнта або сервера, дозволи, запитувана операція та вміст повідомлення.
Коли іменований канал стає межею безпеки
Іменований канал стає межею безпеки, коли процеси на його двох кінцях працюють з різними привілеями або під різними рівнями довіри.
Поширеним прикладом є служба Windows, що працює як LocalSystem, і настільний додаток, що працює під обліковим записом звичайного користувача. Служба може мати можливість змінювати захищені файли та ключі реєстру, запускати процеси, змінювати загальносистемну конфігурацію, отримувати доступ до даних інших користувачів або взаємодіяти з драйвером ядра. Настільний додаток зазвичай не може виконувати такі операції безпосередньо.
Коли служба приймає команди через іменований канал, канал стає інтерфейсом до цих привілейованих можливостей. Будь-яка слабкість у дозволах каналу, перевірках ідентичності, валідації команд або логіці авторизації може дозволити недовіреному локальному процесу зловживати привілеями служби.
Успішне підключення не доводить, що клієнт є очікуваним додатком. Воно лише доводить, що процес, який підключався, мав достатні дозволи для відкриття каналу. Інший процес, що працює під тим самим обліковим записом користувача, може мати абсолютно такі ж права. Тому сервер повинен перевіряти безпекову ідентичність, що стоїть за з’єднанням, а не покладатися на ім’я процесу, шлях до виконуваного файлу або секретність імені каналу.
Сервер також повинен окремо авторизувати кожну операцію. Клієнт, якому дозволено запитувати статус служби, не повинен автоматично отримувати дозвіл зупиняти службу, змінювати захищену конфігурацію, запускати процес або запитувати доступ до довільного файлу.
Це розходження особливо важливе, коли сервер обробляє шляхи, аргументи командного рядка, розташування реєстру, імена виконуваних файлів або серіалізовані команди, керовані клієнтом. Без суворої валідації служба може стати “заплутаним заступником”: клієнт обирає дію, але привілейована служба її виконує.
Наприклад, здавалося б, нешкідливий запит типу:
Читати файл: C:ProgramDataProductstatus.json
може стати небезпечним, якщо клієнт зможе замінити шлях на:
читати файл: C:WindowsSystem32configSAM
Та сама проблема стосується запитів на запуск процесів, видалення файлів, оновлення значень реєстру, встановлення компонентів або зв’язку з драйвером. Служба повинна не просто перевіряти, чи команда синтаксично правильна. Вона повинна перевірити, чи дозволено підключеній ідентичності виконувати цю конкретну операцію над цим конкретним ресурсом.
Таким чином, безпечний сервер іменованих каналів повинен застосовувати кілька перевірок перед виконанням привілейованого запиту:
- перевірити ідентичність клієнта Windows, що підключився;
- обмежити доступ через явний дескриптор безпеки каналу;
- авторизувати кожну команду окремо;
- валідувати всі шляхи, аргументи, ідентифікатори та розміри пакетів даних;
- відхиляти непідтримувані або неоднозначні операції;
- уникати надання загальнодоступних привілейованих функцій.
Останній пункт є критичним. Команда на кшталт “записати це значення в будь-який ключ реєстру” створює набагато більшу поверхню атаки, ніж вузько визначена команда “оновіть цей конкретний параметр програми”. Чим загальнішим стає протокол каналу, тим більше він нагадує привілейований локальний API — і тим ретельніше його слід захищати.
Правильний принцип проектування простий: сервер каналу ніколи не повинен виконувати операцію лише тому, що її запросив підключений клієнт. Він повинен виконувати операцію лише після підтвердження, хто її запитував, чи авторизована ця ідентичність, і чи залишається запит у вузько визначених межах безпеки.
Контроль доступу та авторизація клієнта
Сервер іменованого каналу повинен вирішувати, хто може підключитися, перш ніж почне обробляти повідомлення. Це починається з явного дескриптора безпеки, який надає доступ лише до необхідних ідентифікаторів Windows, таких як певний SID користувача, обліковий запис служби, група адміністраторів або сесія входу.
DACL каналу контролює доступ до обох кінців іменованого каналу. Коли клієнт намагається підключитися, Windows порівнює токен доступу клієнта та запитувані права з цим DACL. Покладання на стандартний дескриптор є ризикованим, оскільки його дозволи можуть бути ширшими, ніж потрібно додатку.
Доступ до каналу не автоматично авторизує кожну доступну команду. Клієнту може бути дозволено отримувати інформацію про стан, але заборонено змінювати конфігурацію, запускати процеси або отримувати доступ до захищених файлів. Тому авторизацію слід проводити для кожної чутливої операції, а не лише один раз при встановленні з’єднання.
Для локальної взаємодії між додатками, додатки також можуть перевіряти процес, пов’язаний з протилежним кінцем каналу:
- сервер може викликати GetNamedPipeClientProcessId;
- клієнт може викликати GetNamedPipeServerProcessId.
Ці API Windows повертають ідентифікатор процесу, пов’язаний з підключеним клієнтом або сервером. Їх слід викликати лише після встановлення з’єднання каналу.
Наступний допоміжний код C# отримує PID піра за допомогою нативних API Windows:
[DllImport(“kernel32.dll”, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetNamedPipeClientProcessId(SafePipeHandle pipe, out uint clientProcessId);
[DllImport(“kernel32.dll”, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetNamedPipeServerProcessId(SafePipeHandle pipe, out uint serverProcessId);
Ми також можемо викликати іншу функцію з kernel32.dll, QueryFullProcessImageName, щоб отримати шлях до виконуваного файлу з дескриптора процесу, відкритого з PROCESS_QUERY_INFORMATION або PROCESS_QUERY_LIMITED_INFORMATION. Повернений шлях потім можна порівняти з очікуваним розташуванням виконуваного файлу як додатковий крок перевірки.
З боку сервера перевірка повинна відбуватися відразу після прийняття з’єднання та перед читанням або виконанням команд:
Очікуваний виконуваний файл повинен знаходитися в каталозі, який звичайні користувачі не можуть змінити. В іншому випадку зловмисник може замінити файл, зберігаючи очікуваний шлях.
Для сильнішої перевірки додаток може додатково перевірити цифрову підпис Authenticode виконуваного файлу або порівняти його з затвердженим криптографічним хешем. Windows надає WinVerifyTrust для перевірки підписаних виконуваних файлів.
Однак, перевірка PID та шляху до виконуваного файлу повинна залишатися другорядним контролем, а не основним механізмом авторизації. Дослідження безпеки продемонстрували способи підробки PID, що повідомляється для клієнта іменованого каналу, і способи передачі підключеного дескриптора каналу іншому процесу. Повернений PID може ідентифікувати процес, який відкрив з’єднання, але не доводить, який процес зараз надсилає кожне повідомлення.
Тому безпечна реалізація повинна поєднувати кілька контролів:
- явний та обмежувальний DACL каналу;
- перевірка ідентичності або SID клієнта Windows;
- авторизація кожної привілейованої команди;
- сувора перевірка вмісту повідомлень;
- опціональна перевірка PID, шляху до виконуваного файлу, цифрового підпису або хешу як захист у глибині.
З’єднання слід відхиляти, якщо перевірка ідентичності не вдалася або не може бути завершена. Привілейована служба ніколи не повинна повертатися до прийняття запиту лише тому, що саме з’єднання каналу було успішним.
Імітація та привілейовані операції
Сервер іменованого каналу часто працює з більшими привілеями, ніж підключений до нього клієнт. Наприклад, служба Windows може працювати як LocalSystem, тоді як клієнтський додаток працює під обліковим записом звичайного користувача. Якщо служба виконує кожну запитувану операцію під власною ідентичністю, клієнт може опосередковано отримати доступ до файлів, ключів реєстру, процесів та системних ресурсів, до яких він не міг отримати доступ безпосередньо.
Імітація іменованого каналу дозволяє серверу тимчасово виконувати код під контекстом безпеки підключеного клієнта. Windows потім оцінює доступ до ресурсів, використовуючи токен клієнта, а не токен облікового запису служби.
У .NET, NamedPipeServerStream.RunAsClient() надає контрольований спосіб імітації підключеного клієнта:
server.WaitForConnection();
server.RunAsClient(() =>
{
string path = @”C:ProgramDataMyApplicationsettings.json”;
// Доступ перевіряється за допомогою ідентичності підключеного клієнта.
string content = File.ReadAllText(path);
ProcessClientData(content);
});
Цей підхід корисний, коли клієнт повинен мати можливість виконати операцію лише за умови, що його власний обліковий запис Windows вже має дозвіл. Наприклад, імітація може використовуватися при читанні файлу, що належить користувачеві, доступу до ключа реєстру, специфічного для користувача, або перевірці, чи має клієнт доступ до захищеного ресурсу.
Однак, імітація не є заміною авторизації. Сервер все одно повинен перевірити, чи дозволено клієнту запитувати операцію. Імітація лише змінює контекст безпеки, під яким Windows виконує перевірки доступу; вона не визначає, чи є команда доцільною.
Привілейована служба також повинна уникати непотрібних перемикань між ідентичністю клієнта та ідентичністю служби. Розглянемо запит, який просить службу прочитати файл, а потім встановити його вміст як конфігурацію.
Файл може бути прочитаний під час імітації клієнта, але встановлення може відбутися пізніше під LocalSystem. У цьому випадку клієнт все ще може впливати на привілейовану операцію, навіть якщо частина запиту була оброблена під імітацією.
Безпечніший дизайн — розділити операцію на чітко визначені етапи:
- Автентифікувати та авторизувати клієнта.
- Валідувати всі шляхи, аргументи та дані, керовані клієнтом.
- Імітувати лише для операцій, які повинні використовувати привілеї клієнта.
- Повернутися до ідентичності служби перед виконанням вузько визначених привілейованих завдань.
- Повторно валідувати будь-які дані, що переходять з імітованого етапу до привілейованого.
Обсяг імітації повинен бути якомога меншим. Тривала робота, зворотні виклики, асинхронні операції та непов’язана логіка служби не повинні виконуватися під ідентичністю клієнта.
При використанні нативних API Windows застосовується той самий шаблон:
[DllImport(“advapi32.dll”, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool ImpersonateNamedPipeClient(SafePipeHandle pipe);
[DllImport(“advapi32.dll”, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool RevertToSelf();
Сервер повинен перевірити, чи ImpersonateNamedPipeClient() вдалася, і завжди викликати RevertToSelf() у блоці finally:
if (!ImpersonateNamedPipeClient(server.SafePipeHandle))
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
try
{
// Виконується під контекстом безпеки підключеного клієнта.
PerformClientScopedOperation();
}
finally
{
if (!RevertToSelf())
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
}
Обробка помилок є критичною. Якщо імітація не вдається, а служба продовжує обробку, операція може виконатися під початковою привілейованою ідентичністю служби. Тому невдала спроба імітації повинна призвести до відхилення запиту, а не до мовчазного повернення до облікового запису служби.
Той самий принцип стосується і після імітації. Додаток повинен надійно відновити свою початкову ідентичність перед обробкою іншого клієнта або виконанням непов’язаної роботи. В іншому випадку пізніші операції можуть випадково виконатися під контекстом попереднього клієнта.
Привілейовані команди каналу також повинні бути вузькими та специфічними. Команда типу:
Записати будь-яке значення в будь-який ключ реєстру
створює набагато ширшу поверхню атаки, ніж:
Оновити затверджений параметр політики програми
Служба не повинна надавати загальний доступ до файлів, модифікацію реєстру, створення процесів або виконання команд лише тому, що вона може виконувати ці операції. Кожна привілейована команда повинна точно визначати, до яких ресурсів можна отримати доступ, які значення приймаються, і хто з клієнтів може її викликати.
Імітація є найефективнішою, коли використовується як один рівень у ширшій архітектурі безпеки. Сервер все ще повинен застосовувати обмежувальні дозволи каналу, перевіряти підключеного клієнта, авторизувати кожну команду, валідувати кожен запит і зберігати привілейовані операції вузько визначеними.
Ставтеся до повідомлень каналу як до недовіреного вводу
Перевірка процесу, підключеного до іменованого каналу, не робить його повідомлення безпечними. Легітимний додаток може бути скомпрометований, містити вразливість або передавати керовані користувачем дані до каналу. Зловмисний процес також може отримати або успадкувати дійсний дескриптор каналу.
З цієї причини кожне повідомлення, отримане через іменований канал, слід розглядати як недовірений ввід. Сервер повинен перевіряти як структуру повідомлення, так і запитувану ним операцію перед виконанням будь-якої привілейованої дії.
Небезпечна реалізація може десеріалізувати запит і виконати його безпосередньо:
PipeRequest request = Deserialize(data);
File.WriteAllText(request.Path, request.Content);
Навіть коли request має очікувану структуру, значення, такі як Path і Content, залишаються керованими клієнтом. Таким чином, привілейовану службу можна змусити перезаписати файли поза каталогом програми, змінити захищену конфігурацію або спожити надмірний дисковий простір.
Безпечніший підхід — надавати вузько визначені команди та перевіряти кожне поле:
private static void ProcessRequest(PipeRequest request)
{
if (request == null) throw new InvalidDataException(“The request is missing.”);
switch (request.Command)
{
case PipeCommand.UpdateConfiguration:
ValidateConfiguration(request.Configuration);
UpdateApprovedConfiguration(request.Configuration);
break;
case PipeCommand.GetStatus:
ReturnApplicationStatus();
break;
default:
throw new InvalidDataException(“Unsupported command.”);
}
}
Протокол повинен уникати загальних операцій, таких як:
WriteFile(path, content)
StartProcess(path, arguments)
SetRegistryValue(key, name, value)
ExecuteCommand(command)
Ці команди дозволяють клієнту вибирати як привілейовану операцію, так і її ціль. Перевагу слід надавати специфічним для програми запитам, дозволена поведінка яких контролюється сервером:
UpdateApplicationConfiguration(configuration)
RequestApplicationRepair()
InstallApprovedUpdate(updateId)
GetServiceStatus()
Валідація структури та розміру повідомлення
З’єднання іменованого каналу — це потік байтів, якщо додаток свідомо не використовує режим передачі повідомлень. Один виклик Read не гарантує повернення повного повідомлення програми, і сервер не повинен припускати, що межі читання відповідають межам запиту.
Протокол повинен визначати явне кадрування повідомлень, наприклад, заголовок фіксованого розміру, за яким йде пакет даних з попередньо вказаною довжиною:
[Version]
[Command]
[Payload Length]
Заявлена довжина повинна бути перевірена перед виділенням пам’яті або читанням пакету даних:
private const int MaxMessageSize = 1024
• 1024;
private static async Task<byte[]> ReadPayloadAsync(Stream pipe, int payloadLength, CancellationToken cancellationToken)
{
if (payloadLength MaxMessageSize) throw new InvalidDataException(“Invalid payload length.”);
byte[] payload = new byte
;
int offset = 0;
while (offset < payload.Length)
{
int read = await pipe.ReadAsync(payload, offset, payload.Length – offset, cancellationToken);
if (read == 0) throw new EndOfStreamException(“The pipe was closed before the message was complete.”);
offset += read;
}
return payload;
}
Без максимального розміру зловмисник може заявити дуже великий пакет даних і змусити службу виділити надмірну пам’ять. Додаток також повинен обмежувати розміри колекцій, довжину рядків, глибину вкладеності та кількість об’єктів, прийнятих десеріалізатором.
Валідація значень, а не лише типів
Успішна десеріалізація доводить лише те, що пакет даних міг бути перетворений на очікуваний тип об’єкта. Це не доводить, що значення є прийнятними.
Наприклад, шлях до файлу повинен бути нормалізований і перевірений на відповідність дозволеному каталогу:
private static string ValidatePath(string suppliedPath, string allowedDirectory)
{
string fullPath = Path.GetFullPath(suppliedPath);
string fullDirectory = Path.GetFullPath(allowedDirectory)
.TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar;
if (!fullPath.StartsWith(fullDirectory, StringComparison.OrdinalIgnoreCase))
{
throw new UnauthorizedAccessException(“The requested path is outside the allowed directory.”);
}
return fullPath;
}
Той самий принцип застосовується до шляхів реєстру, аргументів процесів, URL-адрес, ідентифікаторів, пакетів оновлень та значень конфігурації. Сервер повинен перевіряти кожне значення за списком дозволених або вузько визначеним діапазоном, а не намагатися блокувати відомі небезпечні значення.
Перевірка шляхів також вимагає обережності навколо символічних посилань, з’єднань, точок повторного аналізу та перегонів “час перевірки/час використання”. Для чутливих операцій з файлами перевірки лише рядкового шляху може бути недостатньо.
Безпечне відхилення недійсних запитів
Пошкоджені або неавторизовані повідомлення слід відхиляти без продовження часткової обробки. Сервер повинен уникати повернення клієнту стек-трасування, внутрішніх шляхів, маркерів безпеки або детальної інформації про винятки.
Помилки, що надсилаються через канал, повинні використовувати невеликий, контрольований набір кодів відповіді:
public enum PipeResult
{
Success,
InvalidRequest,
Unauthorized,
UnsupportedCommand,
InternalError
}
Детальна діагностична інформація може бути записана до захищених журналів служби, тоді як клієнт отримує лише інформацію, необхідну для обробки збою.
Кожен запит повинен проходити через передбачувану послідовність:
- Прочитати обмежене повідомлення.
- Валідувати версію протоколу та структуру повідомлення.
- Автентифікувати та авторизувати підключеного клієнта.
- Валідувати всі значення, керовані клієнтом.
- Виконати лише вузько визначену операцію.
- Повернути контрольовану відповідь.
Іменований канал — це лише механізм транспортування. Він не робить дані надійними, не гарантує правильне кадрування повідомлень і не запобігає надсиланню зловмисних запитів підключеним процесом. Отримуючий додаток залишається відповідальним за забезпечення виконання протоколу та захист кожної операції, що надається через нього.
Ризики відмови в обслуговуванні та віддаленого доступу
Кінцева точка іменованого каналу може бути захищена від неавторизованих команд, але все одно залишатися вразливою до атак відмови в обслуговуванні. Зловмиснику не завжди потрібен дозвіл на виконання привілейованої операції; запобігання спілкуванню легітимних додатків зі службою може бути достатнім для порушення роботи продукту.
Зловмисний або несправний процес може багаторазово підключатися до каналу, займати всі доступні екземпляри, тримати з’єднання відкритими, не надсилаючи повні повідомлення, або постійно перепідключатися після відключення. Як тільки всі екземпляри сервера будуть зайняті, легітимні клієнти не зможуть встановити з’єднання.
Той самий ризик існує після прийняття з’єднання. Клієнт може надсилати дані надзвичайно повільно, оголошувати пакет даних надвеликого розміру, зупинятися на півдорозі повідомлення або засипати сервер валідними, але дорогими запитами. Без обмежень, така поведінка може вичерпати потоки, завдання, пам’ять, час ЦП, дескриптори та внутрішні черги запитів.
Буфери іменованих каналів також споживають непагінований пул ядра. Тому кількість екземплярів каналів та обсяг буферизованих даних обмежені ресурсами системи. Створення необмеженої кількості екземплярів або вибір надмірно великих буферів може сприяти вичерпанню ресурсів.
Захисний сервер повинен встановлювати чіткі обмеження для:
- одночасних з’єднань та екземплярів каналів;
- розмірів повідомлень та полів;
- часу, відведеного на встановлення та завершення запиту;
- очікуючих запитів на клієнта;
- паралельних дорогих операцій;
- частоти запитів;
- ємності внутрішніх черг.
Блокуючі операції повинні підтримувати скасування і не повинні нескінченно чекати, доки клієнт надішле більше даних. Коли клієнт перевищує часові, розмірні обмеження або обмеження кількості запитів, сервер повинен розірвати це з’єднання та швидко звільнити його ресурси.
Обмеження слід застосовувати до початку дорогої роботи. Наприклад, сервер повинен відхилити надмірний оголошений розмір пакету даних перед виділенням відповідного буфера. Аналогічно, авторизація та базова перевірка запитів повинні відбуватися до доступу до диска, створення процесів, криптографічної роботи, запитів до бази даних або зв’язку з драйвером ядра.
Додаток також повинен уникати створення одного необмеженого робочого потоку для кожного з’єднання. Модель обмеженої паралельності запобігає вичерпанню пулу потоків процесу великою кількістю підключених клієнтів або створенню неконтрольованого беклогу. Обмеження частоти можуть застосовуватися на з’єднання, процес, ідентифікатор користувача або сесію входу, залежно від архітектури додатка.
Однак, управління доступністю не повинно покладатися лише на PID клієнта. Процес може багаторазово перезапускатися, використовувати кілька процесів або встановлювати з’єднання під тим самим обліковим записом користувача. Необхідно враховувати кілька сигналів разом, і сервер повинен зберігати глобальне обмеження, навіть коли присутні індивідуальні для клієнта.
Ще один часто упускається з уваги ризик — віддалена доступність. Іменовані канали Windows не обов’язково обмежуються комунікацією в межах локального комп’ютера. Вони також можуть підтримувати комунікацію між комп’ютерами через мережу, і Microsoft стверджує, що іменовані канали можуть бути віддалено доступними, коли запущена служба Windows Server.
Це означає, що використання локального імені каналу саме по собі не гарантує локальну комунікацію. Канал, призначений для комунікації між локальною службою та локальним настільним додатком, повинен явно забезпечувати цю вимогу.
Нативні сервери каналів можуть вказувати PIPE_REJECT_REMOTE_CLIENTS, що змушує Windows автоматично відхиляти віддалені з’єднання. Без цієї опції віддалені клієнти можуть бути прийняті та оцінені за дескриптором безпеки каналу.
Список контролю доступу каналу також може заборонити доступ до ідентичності NT AUTHORITYNETWORK. Там, де доступ повинен бути обмежений однією інтерактивною сесією, сервер може надати доступ до відповідного SID входу, а не до широких груп, спільних для локальних та віддалених користувачів.
Ці захисти слід поєднувати, а не розглядати як альтернативи:
- відхиляти віддалених клієнтів під час створення каналу, якщо API це підтримує;
- забороняти мережеві ідентичності в дескрипторі безпеки каналу;
- надавати доступ лише до необхідних користувачів або сесій входу;
- перевіряти ідентичність підключеного процесу;
- застосовувати обмеження на з’єднання, тайм-аути, розміри та паралельність.
Захист від відмови в обслуговуванні та обмеження віддаленого доступу є частиною моделі безпеки каналу. Сервер іменованого каналу не є безпечним лише тому, що відхиляються неавторизовані команди. Він також повинен залишатися доступним для легітимних клієнтів і контролювати, чи дозволено з’єднанням надходити з-за меж локального комп’ютера.
Проектування безпечної архітектури іменованих каналів
Безпечний дизайн іменованих каналів повинен мінімізувати як кількість відкритих операцій, так і обсяг привілейованого коду, який безпосередньо обробляє керовані клієнтом дані. Канал повинен діяти як вузька межа комунікації, а не як універсальний інтерфейс до операційної системи.
Практична архітектура розділяє обробку з’єднань, валідацію, авторизацію та привілейоване виконання:

Клієнт ніколи не повинен спілкуватися безпосередньо з універсальними привілейованими функціями. Натомість він повинен надсилати вузько визначений запит до шлюзу каналу. Шлюз валідує формат повідомлення та передає лише структурований запит до рівня авторизації. Привілейована робота починається лише після успішного завершення всіх перевірок безпеки.
Зберігайте протокол каналу вузьким
Протокол каналу повинен надавати бізнес-операції, а не примітиви операційної системи.
Наприклад, додатку може бути легітимно необхідно запитувати оновлення політики, встановлювати затверджене оновлення, отримувати статус служби або оновлювати конкретне значення конфігурації. Йому зазвичай не потрібні необмежені команди для запису довільних файлів, зміни довільних ключів реєстру, запуску довільних виконуваних файлів або виконання інструкцій командного рядка.
Вузькі операції роблять авторизацію та валідацію практичними. Сервер знає, до яких ресурсів кожна команда може отримати доступ, які поля очікуються, і які ідентифікатори клієнтів можуть її викликати.
Хороший протокол повинен включати:
- явну версію протоколу;
- фіксований набір типів запитів;
- унікальні ідентифікатори запитів;
- обмежені розміри пакетів даних;
- передбачувані формати відповідей та помилок;
- чіткі правила для непідтримуваних або пошкоджених повідомлень.
Сервер повинен відхиляти невідомі версії, команди, поля та стани, а не намагатися інтерпретувати їх м’яко.
Відокремлюйте доступ до з’єднання від дозволу на команду
Дозвіл на підключення до каналу не повинен означати дозвіл на використання кожної функції, що надається через нього.
Дескриптор безпеки каналу повинен обмежувати, які ідентифікатори Windows можуть встановлювати з’єднання. Після з’єднання сервер повинен ідентифікувати клієнта та авторизувати кожну команду незалежно.
Це дозволяє підтримувати різні рівні довіри через ту саму службу. Наприклад, звичайним користувачам може бути дозволено запитувати статус, тоді як лише адміністраторам або довіреному процесу керування може бути дозволено змінювати захищені налаштування.
Для особливо чутливих операцій може бути краще використовувати окремі іменовані канали:
Product.Status
Product.UserActions
Product.Admin
Product.Internal
Кожен канал може мати свої правила контролю доступу, обмеження повідомлень та набір підтримуваних команд. Це зазвичай безпечніше, ніж розміщувати кожну операцію за одним великим протоколом і покладатися виключно на внутрішні перевірки команд.
Однак, створення додаткових каналів автоматично не покращує безпеку. Кожна нова кінцева точка збільшує поверхню атаки і повинна бути захищена незалежно. Канали слід розділяти лише тоді, коли вони представляють справді різні межі довіри.
Використовуйте багаторівневу перевірку ідентичності
Жодна окрема перевірка ідентичності не повинна вважатися остаточною.
Архітектура може поєднувати:
- обмежувальний DACL каналу;
- SID підключеного користувача;
- сесію входу клієнта;
- PID піра;
- шлях до виконуваного файлу;
- цифровий підпис виконуваного файлу;
- приклад запиту-відповіді на рівні додатку;
- авторизацію для конкретної операції.
Перевірки PID та шляху до виконуваного файлу можуть допомогти виявити неочікувані додатки, але вони повинні залишатися елементами захисту в глибині. Процеси можуть змінюватися, дескриптори можуть успадковуватися або передаватися, а довірений процес може бути скомпрометований.
Найсильніші рішення повинні базуватися на ідентичностях безпеки Windows та вузько визначених дозволах, а не лише на видимому імені виконуваного файлу.
Ізолюйте привілейоване виконання
Компонент, відповідальний за читання повідомлень каналу, повинен виконувати якомога менше привілейованої роботи.
Обробка з’єднань, десеріалізація, кадрування та базова валідація піддаються впливу керованого зловмисником вводу. Зберігання цієї логіки окремо від привілейованих операцій зменшує вплив вразливості парсера або протоколу.
Рівень привілейованих операцій повинен отримувати лише валідовані, строго типізовані інструкції. Він не повинен отримувати сирі буфери повідомлень, довільні шляхи, командні рядки або серіалізовані об’єкти безпосередньо від клієнта.
Для високочутливих додатків дизайн може зайти далі, розділивши шлюз каналу та робочий процес з привілеями на різні процеси. Шлюз може працювати зі зниженими привілеями, валідувати вхідні запити та передавати лише затверджені операції меншому привілейованому компоненту через другий обмежений канал.
Цей додатковий процесний бар’єр збільшує складність, але може значно зменшити обсяг коду, що стикається з атаками, який працює як LocalSystem або інший потужний обліковий запис.
Контролюйте життєвий цикл кожного з’єднання
Кожне прийняте з’єднання повинно мати чіткий та обмежений життєвий цикл:
- Прийняти з’єднання.
- Ідентифікувати та валідувати піра.
- Застосувати обмеження на рівні з’єднання.
- Прочитати обмежений запит.
- Авторизувати та валідувати запитувану операцію.
- Виконати затверджену дію.
- Повернути контрольовану відповідь.
- Відключитися або чекати наступного обмеженого запиту.
Сервер не повинен дозволяти неавторизованим клієнтам тримати з’єднання необмежено довго. Тайм-аути бездіяльності, дедлайни запитів, обмеження з’єднань, скасування та обмежені черги повинні бути частиною архітектури з самого початку.
Тривалі операції не повинні без потреби блокувати читач каналу. Служба може прийняти запит, призначити ідентифікатор операції та дозволити клієнту запитувати прогрес через окремий запит статусу. Це запобігає монополізації ресурсів сервера одним з’єднанням.
Зробіть сервер авторитетним
Клієнт повинен запитувати результат, а сервер — визначати, як цей результат буде досягнутий.
Наприклад, клієнт може запросити встановлення затвердженого оновлення за ідентифікатором. Сервер повинен визначити розташування пакету, перевірити його підпис, визначити команду встановлення та забезпечити дотримання дозволеного призначення. Клієнт не повинен надавати шлях до виконуваного файлу, URL-адресу завантаження, аргументи командного рядка та цільовий каталог.
Це дозволяє зберігати рішення, що впливають на безпеку, всередині довіреного компонента та зменшує кількість керованих клієнтом значень, що перетинають межу привілеїв.
Сервер також повинен уникати довіри до рішень безпеки, раніше прийнятих клієнтом. Заяви на кшталт “користувач є адміністратором”, “цей файл підписаний” або “цей шлях є безпечним” повинні бути незалежно перевірені сервером.
Аудит безпеково-значущої діяльності
Безпечна архітектура повинна записувати достатньо інформації для розслідування підозрілої поведінки без розкриття конфіденційних даних.
Корисні події аудиту включають:
- відхилені з’єднання;
- невдалі перевірки ідентичності;
- неавторизовані команди;
- пошкоджені або надвеликі повідомлення;
- повторювані тайм-аути;
- несподівані ідентифікатори процесів;
- привілейовані операції та їх результати;
- аномальна частота з’єднань або запитів.
Журнали повинні ідентифікувати користувача Windows, сесію, PID піра, тип команди та результат, де це доцільно. Сирі секрети, токени автентифікації та повні конфіденційні пакети даних не повинні записуватися до журналів.
Повторювані збої можуть вказувати на атаку, але вони також можуть виявити дефектну версію клієнта або проблему розгортання. Тому дані аудиту повинні підтримувати як розслідування безпеки, так і усунення операційних несправностей.
Рекомендована архітектура
Для більшості привілейованих сценаріїв служб Windows, захищений дизайн складається з:
- локального іменованого каналу з явним дескриптором безпеки;
- окремих кінцевих точок для суттєво різних рівнів довіри;
- перевірки як ідентичності Windows, так і піра процесу;
- версіонованого, обмеженого за довжиною, специфічного для додатка протоколу;
- авторизації для кожної команди;
- суворої перевірки кожного керованого клієнтом значення;
- коротких та ретельно контрольованих областей імітації;
- невеликого рівня привілейованого виконання;
- обмежених з’єднань, черг та часу виконання;
- безпеково-орієнтованого журналу аудиту.
Центральний принцип полягає в тому, що іменований канал повинен надавати найменший можливий інтерфейс між рівнями довіри. Безпечна архітектура не намагається зробити довільні привілейовані операції безпечними. Вона уникає надання довільних привілейованих операцій взагалі.
Практичний контрольний список безпеки іменованих каналів
Перед тим, як надавати функціонал додатку через іменований канал, переконайтеся, що дизайн охоплює кожну з наступних областей:
- Визначте межу довіри. Розглядайте канал як відкритий локальний інтерфейс, особливо коли одна сторона працює з підвищеними привілеями.
- Явно обмежте доступ до каналу. Використовуйте вузький дескриптор безпеки замість того, щоб покладатися на стандартні дозволи або широкі групи, такі як Everyone.
- Відхиляйте віддалених клієнтів. Налаштуйте канал для локальної комунікації та забороняйте мережеві ідентичності, коли віддалений доступ не потрібен.
- Перевіряйте обидві кінцеві точки. Перевіряйте підключену ідентичність Windows та, де це доцільно, підтверджуйте PID піра, шлях до виконуваного файлу та цифрові підписи.
- Не довіряйте імені каналу. Передбачуване ім’я ідентифікує кінцеву точку, але не автентифікує процес, який його створив.
- Авторизуйте кожну команду. Дозвіл на підключення не повинен надавати дозвіл на всі операції, що надаються сервером.
- Зберігайте протокол вузьким. Надавайте специфічні для додатку дії, а не довільні можливості для роботи з файлами, реєстром, процесами або виконання команд.
- Ставтеся до всіх повідомлень як до недовірених. Валідуйте кадрування, версію протоколу, тип команди, розмір пакету даних, значення полів, шляхи та кількість об’єктів.
- Застосовуйте обмеження рано. Відхиляйте недійсні розміри та непідтримувані запити перед виділенням пам’яті або початком дорогої роботи.
- Використовуйте імітацію обережно. Імітуйте лише тоді, коли операція повинна використовувати дозволи клієнта, зберігайте обмежену область, і виходьте у закритий стан, якщо імітація не вдалася.
- Ізолюйте привілейоване виконання. Розділіть парсинг та валідацію від коду, який виконує привілейовані операції.
- Контролюйте використання ресурсів. Обмежуйте одночасні з’єднання, очікуючі запити, час бездіяльності, час виконання, глибину черги та частоту запитів.
- Повертайте контрольовані помилки. Уникайте розкриття стек-трасувань, внутрішніх шляхів, токенів або інших конфіденційних деталей реалізації.
- Аудит безпеково-значущих подій. Записуйте відхилені з’єднання, невдалі перевірки ідентичності, пошкоджені запити, неавторизовані команди та привілейовані операції.
- Припиняйте роботу при помилці. Якщо перевірка ідентичності, авторизація, валідація або імітація не можуть бути надійно завершені, відхиляйте запит.
Безпечна реалізація іменованих каналів не повинна залежати від одного захисту. Найсильніший дизайн поєднує обмежувальний контроль доступу, перевірку кінцевих точок, авторизацію на рівні операцій, сувору валідацію вводу та вузько визначені привілейовані функції.
Біографія автора:
Фарід Мустафаєв — розробник програмного забезпечення в ThreatLocker, який спеціалізується на розробці служб Microsoft Windows та кібербезпеці. Маючи понад 15 років досвіду в галузі, він має глибокі знання в .NET технологіях, включаючи ASP.NET WebAPI, Windows Services, Windows Forms, WPF, RESTful API та низькорівневі інструменти Windows. Він керував розробкою та зміцненням служб Windows, призначених для захисту систем від зловмисного програмного забезпечення та програм-вимагачів, включаючи роботу з інтеграціями на рівні ядра та кастомізованими покращеннями драйверів.
Раніше Мустафаєв працював технічним керівником, керуючи рішеннями архітектури, наставляючи розробників та створюючи масштабовані, підтримувані системи. Його досвід також включає архітектури на основі мікросервісів та хмарно-орієнтовані рішення на AWS, з акцентом на доступність, продуктивність та безпеку в розподілених середовищах.
Спонсоровано та написано ThreatLocker.
Джерело новини: www.bleepingcomputer.com
