Следующая новость
Предыдущая новость

Системный изолятор. Изолируем процессы в Windows средствами Less Privileged App Container (LPAC)

22.03.2018 14:45
Системный изолятор. Изолируем процессы в Windows средствами Less Privileged App Container (LPAC)

Содержание статьи

  • Создаем Less Privileged App Container
  • Пишем LPAC loader для любого приложения
  • Проверка на изоляцию
  • Заключение

Начиная с восьмой версии в Windows появился встроенный механизм контейнеров, которые позволяют изолировать процессы путем значительного усечения их прав. Этому системному механизму дали название Less Privileged App Container (LPAC), он поддерживается некоторыми приложениями, например браузером Chrome. В этой статье я покажу, как использовать его в своих программах.

Сендбокс-изоляция часто применяется в защитных приложениях, а также на ее основе строятся компоненты антивирусов, называемые HIPS (Host-based Intrusion Prevention System), и отдельные приложения для изолированных сред типа Sandboxie. Такие защитные механизмы реализованы через драйвер-фильтр режима ядра. Он сложен в написании и тестировании, имеет громадное количество шаблонного кода и должен перехватывать значительное количество функций NTAPI, чтобы менять их параметры на лету, таким образом создавая песочницу внутри файловой системы.

Существует более простой способ устроить изоляцию произвольных приложений. Инженеры Microsoft уже позаботились об этом и интегрировали интересный механизм в ядро Windows. Его суть заключается в том, что система жестко ограничивает доступ к устройствам (таким как микрофон, камера, GPS или модуль 4G), файлам в системе (иногда — даже для чтения) и процессам (ограничиваются межпроцессные взаимодействия). Также ограничения накладываются на работу с сетью (например, на открытие портов или сокетов), обращения к сетевому реестру и оконному интерфейсу других приложений.

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

Создаем Less Privileged App Container

Прежде чем запускать приложение, нам нужно создать сам контейнер. В этом нам поможет функция WinAPI CreateAppContainerProfile. Вот ее прототип:

HRESULT WINAPI CreateAppContainerProfile(   _In_  PCWSTR              pszAppContainerName,   _In_  PCWSTR              pszDisplayName,   _In_  PCWSTR              pszDescription,   _In_  PSID_AND_ATTRIBUTES pCapabilities,   _In_  DWORD               dwCapabilityCount,   _Out_ PSID                *ppSidAppContainerSid ); 

И сам код создания контейнера:

WCHAR sandbox_name[] = L"SandboxLPAC"; WCHAR sandbox_desc[] = L"My SandboxLPAC";  PSID sid = NULL; HRESULT status;  result = CreateAppContainerProfile(sandbox_name, sandbox_name, sandbox_desc, NULL, 0, &sid); 

В случае ошибки неплохо было бы проверить, не создан ли наш контейнер ранее; если создан, то мы получим его SID. Вот прототип функции WinAPI, которая выясняет SID уже созданного контейнера:

HRESULT WINAPI DeriveAppContainerSidFromAppContainerName(   _In_  PCWSTR pszAppContainerName,   _Out_ PSID   *ppsidAppContainerSid ); 

Далее код реализации проверки. Как видишь, он очень прост.

if (HRESULT_CODE(status) == ERROR_ALREADY_EXISTS) status = DeriveAppContainerSidFromAppContainerName(sandbox_name, &sid); 

Так или иначе мы получаем SID контейнера.

INFO

Security Identifier (SID) — идентификатор безопасности, структура данных в Windows, которая может идентифицировать системные объекты, например элементы управления доступом (Access Control Entries, ACE), токены доступа (Access Token), дескрипторы безопасности (Security Descriptor). SID всегда начинается с буквы S, далее идут числа, которые обозначают номер редакции ОС, источники выдачи, удостоверяющие центры и другую информацию.

Насколько просто обойти изоляцию LPAC?

  • Теоретически можно, но готовых эксплоитов еще нет
  • Ее научились обходить, правда пока только в Windows 8
  • Ее научились обходить уже и в Windows 10

Системный изолятор. Изолируем процессы в Windows средствами Less Privileged App Container (LPAC) Загрузка ...

Пишем LPAC loader для любого приложения

Итак, контейнер LPAC создан, SID получен. Теперь наша задача — заставить Windows запустить произвольное приложение в этом контейнере. Но сначала нам необходимо разобрать процесс запуска приложений и понять, как можно задавать определенные атрибуты запуска и какие системные структуры отвечают за это.

Для запуска приложений в Windows используется функция WinAPI CreateProcess с массой параметров, и именно эта функция имеет ключевое значение в нашей задаче. Давай посмотрим на ее прототип и разберем основные параметры.

BOOL WINAPI CreateProcess(   _In_opt_    LPCTSTR               lpApplicationName,   _Inout_opt_ LPTSTR                lpCommandLine,   _In_opt_    LPSECURITY_ATTRIBUTES lpProcessAttributes,   _In_opt_    LPSECURITY_ATTRIBUTES lpThreadAttributes,   _In_        BOOL                  bInheritHandles,   _In_        DWORD                 dwCreationFlags,   _In_opt_    LPVOID                lpEnvironment,   _In_opt_    LPCTSTR               lpCurrentDirectory,   _In_        LPSTARTUPINFO         lpStartupInfo,   _Out_       LPPROCESS_INFORMATION lpProcessInformation ); 

Поле lpApplicationName — это путь к исполняемому файлу, который мы собираемся запускать. Далее идут поля lpCommandLine, lpProcessAttributes, lpThreadAttributes, bInheritHandles, которые сейчас не представляют для нас интереса. Можем им всем присвоить значение NULL (FALSE).

А вот на поле dwCreationFlags мы остановимся подробнее. Оно отвечает за флаги, которые устанавливают приоритет процесса и регламентируют его создание. Например, если в это поле передать значение CREATE_NO_WINDOW для консольного приложения, то оно запустится без создания консольного окна. А если передать значение CREATE_SUSPENDED, тогда процесс (основной поток) будет создан приостановленным (в состоянии ожидания пробуждения функцией ResumeThread). Нам же необходимо задать флаг EXTENDED_STARTUPINFO_PRESENT: он «разрешит» нам расширенные параметры запуска приложения.

Далее идет поле lpStartupInfo, которое имеет тип LPSTARTUPINFO. Это указатель на структуру STARTUPINFO, которая регламентирует параметры основного окна приложения или терминала, а также его дескриптор.

Важный момент. При передаче флага запуска EXTENDED_STARTUPINFO_PRESENT мы можем вместо стандартной структуры STARTUPINFO передать ее усовершенствованную версию — структуру STARTUPINFOEX. Она имеет вид:

typedef struct _STARTUPINFOEX {    STARTUPINFO                 StartupInfo;   PPROC_THREAD_ATTRIBUTE_LIST lpAttributeList;  } STARTUPINFOEX, *LPSTARTUPINFOEX; 

Можно считать, что это стандартная структура STARTUPINFO (поле StartupInfo), дополненная списком атрибутов запуска (поле lpAttributeList). Эти атрибуты можно проинициализировать функцией WinAPI InitializeProcThreadAttributeList:

BOOL WINAPI InitializeProcThreadAttributeList(    _Out_opt_  LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList,   _In_       DWORD                        dwAttributeCount,   _Reserved_ DWORD                        dwFlags,   _Inout_    PSIZE_T                      lpSize  ); 

А теперь добавляем их в список параметров функцией UpdateProcThreadAttribute:

BOOL WINAPI UpdateProcThreadAttribute(    _Inout_   LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList,   _In_      DWORD                        dwFlags,   _In_      DWORD_PTR                    Attribute,   _In_      PVOID                        lpValue,   _In_      SIZE_T                       cbSize,   _Out_opt_ PVOID                        lpPreviousValue,   _In_opt_  PSIZE_T                      lpReturnSize  ); 

Обрати внимание на поле PVOID lpValue, к нему мы еще вернемся. А теперь переходим к практике. Весь код манипуляций с атрибутами потоков выглядит таким образом:

SIZE_T size_of_attr = 0; STARTUPINFOEX ex_start_info = { 0 }; SECURITY_CAPABILITIES secap = { 0 };  InitializeProcThreadAttributeList(NULL, 1, NULL, &size_of_attr); ex_start_info.lpAttributeList = (LPPROC_THREAD_ATTRIBUTE_LIST)malloc(size_of_attr); InitializeProcThreadAttributeList(ex_start_info.lpAttributeList, 1, NULL, &size_of_attr);  UpdateProcThreadAttribute(ex_start_info.lpAttributeList,                  0,                  PROC_THREAD_ATTRIBUTE_SECURITY_CAPABILITIES,                 &secap,                  sizeof(secap),                  NULL,                  NULL); 

Первые три строки кода (если не считать объявление переменных) создают PROC_THREAD_ATTRIBUTE_LIST, то есть список параметров запуска. Вызов WinAPI UpdateProcThreadAttribute модифицирует его должным образом и создает нужную нам структуру STARTUPINFOEX ex_start_info, которую мы передадим функции CreateProcess. Четвертое поле этой функции, lpValue, получает на вход структуру secap, которая имеет тип SECURITY_CAPABILITIES и выглядит так:

typedef struct _SECURITY_CAPABILITIES {    SID                  AppContainerSid;   PSID_AND_ATTRIBUTES  Capabilities;   DWORD                CapabilityCount;   DWORD                Reserved;  } SECURITY_CAPABILITIES, *PSECURITY_CAPABILITIES; 

Чтобы «настроить» ее под свои нужды, заполняем поля перед вызовом UpdateProcThreadAttribute. Первое поле структуры — это AppContainerSid, в него мы передаем SID нашего контейнера LPAC. Второе поле — Capabilities, оно тоже является структурой:

typedef struct _SID_AND_ATTRIBUTES {    PSID  Sid;   DWORD Attributes;  } SID_AND_ATTRIBUTES, *PSID_AND_ATTRIBUTES; 

Третье поле (SECURITY_CAPABILITIES) называется CapabilityCount. Это счетчик параметров процесса, которыми мы его наделяем.

Давай посмотрим на код, который реализует все перечисленное выше и создает нам корректно заполненную структуру SECURITY_CAPABILITIES.

    SID_AND_ATTRIBUTES sid_attr;     DWORD sid_size = SECURITY_MAX_SID_SIZE;      sid_attr = (SID_AND_ATTRIBUTES *)malloc(sizeof(SID_AND_ATTRIBUTES));      ZeroMemory(secap, sizeof(SECURITY_CAPABILITIES));     ZeroMemory(sid_attr, sizeof(SID_AND_ATTRIBUTES));      sid_attr.Sid = malloc(SECURITY_MAX_SID_SIZE);      CreateWellKnownSid(capabili, NULL, sid_attr.Sid, &sid_size);      sid_attr.Attributes = SE_GROUP_ENABLED;      &secap->Capabilities = sid_attr;     &secap->AppContainerSid = sid; 

Единственная сложность, которую ты можешь встретить в этом коде, заключается в неизвестной еще функции CreateWellKnownSid. Она создает SID для значений, которые были предопределены заранее. Самое интересное для нас — это ее первое поле, в котором через переменную capabili передается перечисление типа WELL_KNOWN_SID_TYPE, где на момент написания статьи содержится 94 пункта. Они наделяют наш SID различными правами. Ознакомиться с полным списком можно в MSDN по ссылке. Для собственных экспериментов можно выбрать любой по вкусу. ????

Итак, все основные системные структуры созданы, сам контейнер LPAC создан, осталось только запустить блокнот в контейнере. Зададим нужные параметры вызова и наши заранее подготовленные структуры.

    PROCESS_INFORMATION pinfo = { 0 };      BOOL ok = CreateProcessA("c:\windows\notepad.exe",                      NULL,                      NULL,                      NULL,                      FALSE,                      EXTENDED_STARTUPINFO_PRESENT,                      NULL,                      NULL,                     (LPSTARTUPINFOA)&ex_start_info,                      &pinfo); 

Результат работы можно увидеть в программе ProcessExplorer: notepad.exe запустится внутри контейнера Less Privileged App Container.

Системный изолятор. Изолируем процессы в Windows средствами Less Privileged App Container (LPAC)
Блокнот внутри контейнера LPAC

Какое предустановленное приложение по умолчанию запускается в LPAC?

  • Internet Explorer, начиная с Windows 8.1
  • «Проводник» во всех версиях Windows 10
  • MS Edge, начиная с версии Windows 10 1703

Системный изолятор. Изолируем процессы в Windows средствами Less Privileged App Container (LPAC) Загрузка ...

Проверка на изоляцию

Как проверить, работает ли процесс в контейнере LPAC или нет, программным путем, без использования сторонних приложений? Достаточно получить хендл интересующего нас процесса. Если процесс сторонний, то нам поможет функция WinAPI OpenProcess, а для своего пригодится GetCurrentProcess. Далее открываем токен доступа процесса (Access token) и смотрим его TOKEN_INFORMATION_CLASS, который будет равен TokenIsAppContainer в том случае, если процесс работает внутри контейнера.

BOOL InLPAC(HANDLE h_proc) {     HANDLE proc_token;     DWORD len;     BOOL lpac = 0;      OpenProcessToken(h_proc, TOKEN_QUERY, &proc_token);      if (!GetTokenInformation(proc_token, TokenIsAppContainer, &lpac, sizeof(lpac), &return_len))         return false;      return lpac; } 

Системный изолятор. Изолируем процессы в Windows средствами Less Privileged App Container (LPAC)

INFO

Маркер доступа (Access token) — объект Windows, содержащий привилегии учетной записи пользователя, от которого был запущен процесс. Помимо этого, Access token содержит информацию об ограничениях доступа к потоку, здесь же перечислены SID и списки привилегий процесса. Посмотреть структуру маркера доступа можно, введя в WinDbg команду dt_TOKEN.

На вход этой функции необходимо передать хендл интересующего нас процесса, и она вернет TRUE, если процесс работает внутри Less Privileged App Container, и FALSE, если процесс выполняется вне его.

С какими новыми методами защиты совместно работает LPAC?

  • No Child Proc и Protected Processes
  • Win32k Syscall Filtering, SEHOP и ASLR
  • LPAC работает независимо от других компонентов

Системный изолятор. Изолируем процессы в Windows средствами Less Privileged App Container (LPAC) Загрузка ...

Заключение

Мы разобрали Less Privileged App Container, встроенную реализацию изолированной среды в Windows. Чтобы раскрыть тему, нам пришлось детально рассмотреть процесс запуска приложений в Windows средствами WinAPI CreateProcess, а также узнать о нескольких важных системных структурах, без которых ничего бы не получилось. Я надеюсь, что эта статья поможет тебе в исследовании системных механизмов Windows.

Источник

Последние новости