Бесконечные атаки и всевозможные взломы вынуждают Microsoft вводить всё более жёсткие квоты на использование системных ресурсов. Начиная с NT-4, каждое ядро несёт в себе что-то новое, а выделяется на этом фоне Vista NT-6. Не смотря на то, что век её был коротким, она принесла с собой много революционных идей, особенно в плане безопасности. Если на XP NT-5 мы могли с лёгкостью внедряться в системные процессы и переполнять их буферы в целях захвата власти, то на Win-7 сделать это уже не так просто. Одним из препятствий на пути становится "контроль целостности" защищённых объектов, о котором и пойдёт речь в данной части статьи.
Оглавление:
1. Введение
Человек любопытен по своей природе. Чем больше Microsoft прячет от нас детали реализации своих механизмов, тем больший интерес они возбуждают. В случае с подсистемой безопасности получаем вообще лавинообразный эффект, т.к. именно в ней зарыты все рычаги несанкционированного доступа к объектам Win.
Проблема в том, что инженеры майков и рады-бы полностью запереть ядро на большой замок, но вот несколько его "воинов" остались в тылу врага на пользовательской территории, а значит ядру нужно как-то поддерживать с ними связь. В качестве примера можно привести такие системные файлы как: csrss.exe (подсистема клиент-сервера), winlogon, и explorer.exe. Большая ставка делалась на мобилизацию всей системной кухни из пользовательского пространства в специально отведённый для этих целей изолированный сеанс(0) – теперь все системные файлы и службы (кроме перечисленных выше) никак не пересекаются с юзерскими. Нужно сказать, что это было лучшим из возможных решений.
После того-как неугомонные хакеры нашли дыры в нулевом сеансе и опять повеяло сквозняком, инженерам пришлось искать новые меры безопасности и ограничивать доступ к объектам системы дополнительными средствами. Одним из таких средств и стал "Integrity Control", что на исконно русском звучит как контроль целостности. Если рассматривать грубо, то на данный момент дела в подсистеме безопасности обстоят так..
2. Токен пользователя, и дескриптор безопасности объекта
1. При входе в систему мы сразу упираемся в таможенную зону Winlogon, где входящие в состав Lsass.exe процедуры вручают нам мандат пребывания на территории, в виде структуры "TOKEN". Этот токен нужно воспринимать как цифровой паспорт пользователя, внутри которого (помимо прочего) хранятся:
Подсистема сервера проверки подлинности Lsass.exe (Local Security Authority SubSystem) назначает каждому пользователю не один, а макс.16 различных идентификаторов SID, каждый из которых несёт в себе определённую инфу. Чтобы ознакомиться с основными свойствами вашего токена и списком его SID, можно в ком.строке ввести команду:
C этого момента, при доступе к защищённым объектам системы, мы должны будем предъявлять данный токен системным сторожам, на основании чего они решат, дать нам запрашиваемый доступ, или нет. Проверки осуществляются только сопоставлением SID.
2. За некоторым исключением, почти все объекты Win являются защищаемыми. К ним относятся: файл\каталог, процесс\поток, порт\устройство, расшаренные принтеры, именованные каналы Pipe, всевозможная мелочь типа семафоры, эвенты, мутанты, таймеры и прочее. С полным списком можно ознакомиться в программе WinObj из пакета
Чтобы защитить объект, система привязывает к нему дескриптор безопасности SD. У каждого объекта свой дескриптор, а прочитать и модифицировать его можно функциями Get\SetSecurityInfo(), предварительно открыв через CreateFile(). На физ.уровне SD представляет собой одноимённую структуру в памяти SECURITY_DESCRIPTOR, о которой прекрасно осведомлён ядерный отладчик WinDbg. Указатель на дескриптор безопасности прописан в структуре заголовка объекта OBJECT_HEADER:
Значит структура начинается с информации о владельце объекта – это поля Owner, и Group. Далее идут два указателя на списки ACL (Access Control List, список контроля доступа), которые бывают двух видов – DACL (Discretionary, дискреционный, избирательный), и SACL (системный). Непосредственно атрибуты защиты объекта хранятся в его списке DACL, а второй SACL был введён лишь для аудита, т.е. для отслеживания несанкционированных обращений к объекту, с последующей записью их в лог. Два этих списка являются родственными душами, поэтому структуры у них одинаковы:
Структуры ACL – это лишь заголовки списков DACL и SACL, а сами-же списки организованы в виде записей ACE (Access Control Entry). Кол-во таких записей в каждом из списков указывается в поле "AceCount" его заголовка. В записях ACE хранится информация о доверенных лицах в системе, которым разрешён/запрещён доступ к данному объекту. Для DACL часто используются всего два типа ACE:
ACCESS_ALLOWED_ACE через дворд "Mask" предписывает, что именно разрешено делать обладателю данного SID при доступе к объекту. Соответственно если тип указывается как DENIED_ACE, то запись характеризует запрещения доступа. Ниже представлена битовая карта поля "Mask":
Всё это мутно для непросвещённых, что характерно для подсистемы безопасности в целом. Видимо инженеры руководствовались здесь тем, чтобы усложнить пользователю разбор деталей реализации. Однако если поймать связь компонентов, картина проясняется.
Для визуального просмотра подноготной имеет смысл воспользоваться отладчиком – мы можем запросить дескриптор безопасности SD как своего\любого процесса, так и отдельно взятого системного объекта. Если планируем освятить объект, сначала нужно получить к нему доступ через расширение
Так мы получили свойства дескриптора безопасности своего процесса где видно, что его владельцем "Owner" является администратор, а в списке доступа DACL имеется всего три записи АСЕ. Первая с маской "Полный доступ"
В штатной библиотеке Win Advapi32.dll имеется огромное кол-во функций, с помощью которых можно легко модифицировать записи АСЕ. Множество примеров их использования приводится в книге
В качестве разминки посмотрим на ConvertSecurityDescriptorToStringSecurityDescriptor(), которая извлекает маску защиты объекта из списка DACL. Эта функция возвращает маску в виде текстовой строки на языке SDDL (Security Descriptor Definition Language). Полученную строку можно заточить под свои нужны, перевести опять в бин чз ConvertStringSecurityDescriptorToSecurityDescriptor(), и запихать её обратно в дескриптор при помощи, например, SetSecurityInfo(). Вот пример извлечения строки DACL:
В скрепке лежит инклуд Advapi32.inc, где можно найти полное описание литеров строк SDDL.
Каждый АСЕ заключается в скобки, а элементы записи в ней отделены точкой с запятой. Таким образом, мы видим здесь строки трёх записей АСЕ, где представлены их тип (А, Allowed), дворд с НЕХ-маской, а так-же SID пользователей, которым принадлежит запись:
3. Устройство механизма "Mandatory Integrity Control"
Но такую картину мы могли наблюдать только на системах ХР с ядром NT-5.
Чтобы покончить с этим беспределом, начиная с Висты продуманы Microsoft ввели доп.уровень проверок доступа под названием MIC, или "Обязательный контроль целостности", что на англ.манер звучит как "Mandatory Integrity Control". Теперь доступ к объектам проверяется не только посредством записей АСЕ в списках DACL, но и записью типа SYSTEM_MANDATORY_LABEL в списке SACL. Как видим, запись данного типа присутствует в дескрипторе безопасности выше, и имеет следующий вид:
Здесь имеется маска(3), которая принадлежит пользователю с SID =
На данный момент, все современные системы поддерживают 7 уровней целостности объектов, константы которых увеличиваются с шагом 0x1000. Для обозначения уровней "Integrity Level", система выделяет SID с начальным значением
Как мы уже знаем, у любых объектов имеется владелец Owner, а в свою очередь владелец имеет свой токен и SID. Процессы могут получать доступ на запись только к тем объектам, уровень IL которых меньше или равно. Вообще-то словосочетание "уровень целостности" не совсем подходит для данного механизма – просто воспринимайте его как "уровень доверия" объекта, к вызывающему субъекту. То-есть объекты не доверяют процессам, если их уровень ниже его\собственного. С введением этого механизма, огромная армия малвари отправилась на свалку истории, освободив нишу для новых идей.
В системе имеется монитор ссылок SRM "Security Reference Monitor". Когда процесс ссылается на объект, именно SRM решает, следует-ли предоставить данному процессу права доступа к объекту. Это делается путём сравнения прикреплённого к процессу токена, со-списком управления доступом к объекту DACL. Монитор сравнивает идентификаторы безопасности SID в записи DACL, с SID в токене пользователя, чтобы определить, какой уровень доступа должен быть предоставлен процессу. Если какой-либо из SID в DACL отказывает в доступе, запрос процесса отвергается с сообщением об ошибке ACCESS_DENIED.
На рисунке ниже представлена обобщённая схема механизма MIC.
Зелёные блоки здесь обозначают токены пользователей, а синие – порождаемые ими процессы. Поскольку объекты типа Process наследуют права своих родителей, их дескрипторы безопасности SD получают уровни целостности владельцев. Когда левый процесс пытается получить доступ к правому\системному процессу, монитор ссылок берёт уровень IL из токена, и сверяет его с уровнем в дескрипторе безопасности объекта. В данном случае IL в токене вызывающего субъекта меньше, поэтому доступ не предоставляется! Однако если системный процесс запросит запись в адресное пространство пользовательского процесса, монитор SRM не станет возражать, т.к. судя по уровню "IntegrityLevel" он является доверенным:
Найти значения уровней целостности "IntegrityLevel" в токене не так просто, как может показаться на первый взгляд. Они зарыты глубоко, хотя ссылки на них лежат на поверхности. Выше уже упоминалось, что подсистема безопасности Lsass.exe назначает каждому пользователя не один, а сразу несколько идентификаторов SID (см.whoami в консоли), среди которых имеется и SID уровня целостности. Дело в том, что в токене лежит лишь указатель на массив идентификаторов, а индекс SID-IL в этом массиве представлен полем "IntegrityLevelIndex". Проведём такой эксперимент в отладчике WinDbg..
На массив идентификаторов SID указывает здесь поле "UserAndGroups" со-значением
Ещё одним немаловажным моментом является введение обязательных политик доступа "Mandatory Policy", которые тесно связаны с уровнями целостности объектов. Если посмотреть на содержимое токена выше, по смещению
4. Практика – список активных процессов, и уровни их целостности
В практической части попробуем запросить уровни целостности всех активных процессов. Значит сначала создаём цикл при помощи функций CreateToolhelp32Snapshot() + Process32Next(), которые перечислят все процессы как диспетчер задач Win. Теперь внутри цикла открываем найденный процесс через OpenProcess(), и запрашиваем его токен OpenProcessToken(). Получив дескриптор токена, вызываем функцию GetTokenInformation(), передав ей параметр "TokenIntegrityLevel". Так мы получим SID уроня целостности процесса, и остаётся обозначить его строкой через LookupAccountSid(). Здесь нужно отметить, что для доступа к системным процессам, необходимо обзавестись привилегией отладки SE_DEBUG.
Функция GetTokenInformation() способна вытащить из токена огромное кол-во информации, в зависимости от одного из 48 передаваемых ей параметров. Список этих параметров спрятан в спойлере ниже, а нужный нам числи в розысках под номером 25:
Рассмотрим бегло этот скрин..
Обратите внимание на дескрипторы Handle процессов – их значение увеличивается на 8-байт. Это потому, что при запуске процесса на исполнение, система создаёт в его окружении "таблицу дескрипторов", куда помещает все открытые данным процессом хэндлы. На самом деле эти значения являются просто индексами в таблице, где лежат указатели на непосредственно дескрипторы. Когда я делал этот скрин, то не закрывал открытые процессы через CloseHandle(). Другой пример лежит в скрепке, где дескрипторы прихлопываются, и соответственно индекс не увеличивается, а просто перезаписывается в одном поле таблицы, с одинаковым значением.
Далее видим идентификаторы SID уровней целостности "IntegrityLevel", с привязанными к ним пояснительными строками. У всех системных процессов он равен
Браузер Хром для каждой вкладки создаёт новый процесс с ненадёжным уровнем(0), в результате чего скрипты и прочее содержимое интернет-страниц не представляют опасности для системы. Процессам Хрома на запись доступна только его собственная папка на диске, куда он кэширует данные посещённых страниц.
5. Заключение
Эксперименты в отладчике и ручной поиск всевозможных полей в системных структурах, открывает двери в шелл-кодинг. Ведь хорошо известно, что в этой области программирования мы лишены возможности использовать функции Win32-API, и всё приходится делать ручками и головой. Именно поэтому я уделил большую часть топика логам дебагера. Как показали опыты, системы Виста+ это далеко не дырявая ХР, и инженеры майков потрудились на славу, чтобы оградить нас от нечисти. Они предложили нам вполне комфортную подушку безопасности, которую мы всё-таки попытаемся "проткнуть шилом" в следующей части статьи.
Уже по привычке, в скрепку кладу два представленных здесь исполняемых файла для тестов,
а так-же инклуд Advapi32.inc для тех, кто захочет сам собрать код из исходников. Всем удачи и пока!
Оглавление:
1. Введение
2. Токен и дескриптор безопасности
3. Устройство механизма "Mandatory Integrity Control"
4. Практика - список процессов и их уровня IL
5. Заключение
1. Введение
Человек любопытен по своей природе. Чем больше Microsoft прячет от нас детали реализации своих механизмов, тем больший интерес они возбуждают. В случае с подсистемой безопасности получаем вообще лавинообразный эффект, т.к. именно в ней зарыты все рычаги несанкционированного доступа к объектам Win.
Проблема в том, что инженеры майков и рады-бы полностью запереть ядро на большой замок, но вот несколько его "воинов" остались в тылу врага на пользовательской территории, а значит ядру нужно как-то поддерживать с ними связь. В качестве примера можно привести такие системные файлы как: csrss.exe (подсистема клиент-сервера), winlogon, и explorer.exe. Большая ставка делалась на мобилизацию всей системной кухни из пользовательского пространства в специально отведённый для этих целей изолированный сеанс(0) – теперь все системные файлы и службы (кроме перечисленных выше) никак не пересекаются с юзерскими. Нужно сказать, что это было лучшим из возможных решений.
После того-как неугомонные хакеры нашли дыры в нулевом сеансе и опять повеяло сквозняком, инженерам пришлось искать новые меры безопасности и ограничивать доступ к объектам системы дополнительными средствами. Одним из таких средств и стал "Integrity Control", что на исконно русском звучит как контроль целостности. Если рассматривать грубо, то на данный момент дела в подсистеме безопасности обстоят так..
2. Токен пользователя, и дескриптор безопасности объекта
1. При входе в систему мы сразу упираемся в таможенную зону Winlogon, где входящие в состав Lsass.exe процедуры вручают нам мандат пребывания на территории, в виде структуры "TOKEN". Этот токен нужно воспринимать как цифровой паспорт пользователя, внутри которого (помимо прочего) хранятся:
• наши привилегии в системе,
• отметки о раcсовой принадлежности к какой-либо группе,
• массив идентификаторов SID "Security Identifier".
Подсистема сервера проверки подлинности Lsass.exe (Local Security Authority SubSystem) назначает каждому пользователю не один, а макс.16 различных идентификаторов SID, каждый из которых несёт в себе определённую инфу. Чтобы ознакомиться с основными свойствами вашего токена и списком его SID, можно в ком.строке ввести команду:
whoami /all /fo list
. Лично у себя я насчитал 13 идентификаторов, с полным их описанием.C этого момента, при доступе к защищённым объектам системы, мы должны будем предъявлять данный токен системным сторожам, на основании чего они решат, дать нам запрашиваемый доступ, или нет. Проверки осуществляются только сопоставлением SID.
2. За некоторым исключением, почти все объекты Win являются защищаемыми. К ним относятся: файл\каталог, процесс\поток, порт\устройство, расшаренные принтеры, именованные каналы Pipe, всевозможная мелочь типа семафоры, эвенты, мутанты, таймеры и прочее. С полным списком можно ознакомиться в программе WinObj из пакета
Ссылка скрыта от гостей
. Отметим, что если жёсткий диск отформатирован в FAT-32, то все файлы (включая системные из папки Windows) теряют защитную оболочку, и доступ к ним сможет получить любой из пользователей. Чтобы защитить объект, система привязывает к нему дескриптор безопасности SD. У каждого объекта свой дескриптор, а прочитать и модифицировать его можно функциями Get\SetSecurityInfo(), предварительно открыв через CreateFile(). На физ.уровне SD представляет собой одноимённую структуру в памяти SECURITY_DESCRIPTOR, о которой прекрасно осведомлён ядерный отладчик WinDbg. Указатель на дескриптор безопасности прописан в структуре заголовка объекта OBJECT_HEADER:
C-подобный:
lkd> dt _security_descriptor
;//--------------------------------
nt!_SECURITY_DESCRIPTOR
+0x000 Revision : UChar ;// ревизия структуры (всегда =1)
+0x001 Sbz1 : UChar ;// резерв (байт выравнивания на 16-бит)
+0x002 Control : Uint2B ;// свойства дескриптора SD
+0x004 Owner : Ptr32 Void ;// линк на SID владельца объекта
+0x008 Group : Ptr32 Void ;// линк на SID группы, в которую входит владелец
+0x00c Sacl : Ptr32 _ACL ;// линк на список SACL
+0x010 Dacl : Ptr32 _ACL ;// линк на список DACL
Значит структура начинается с информации о владельце объекта – это поля Owner, и Group. Далее идут два указателя на списки ACL (Access Control List, список контроля доступа), которые бывают двух видов – DACL (Discretionary, дискреционный, избирательный), и SACL (системный). Непосредственно атрибуты защиты объекта хранятся в его списке DACL, а второй SACL был введён лишь для аудита, т.е. для отслеживания несанкционированных обращений к объекту, с последующей записью их в лог. Два этих списка являются родственными душами, поэтому структуры у них одинаковы:
C-подобный:
lkd> dt _acl
;//--------------------------------
nt!_ACL
+0x000 AclRevision : UChar
+0x001 Sbz1 : UChar
+0x002 AclSize : Uint2B ;// общий размер списка ACL
+0x004 AceCount : Uint2B ;// кол-во записей ACЕ в нём
+0x006 Sbz2 : Uint2B
Структуры ACL – это лишь заголовки списков DACL и SACL, а сами-же списки организованы в виде записей ACE (Access Control Entry). Кол-во таких записей в каждом из списков указывается в поле "AceCount" его заголовка. В записях ACE хранится информация о доверенных лицах в системе, которым разрешён/запрещён доступ к данному объекту. Для DACL часто используются всего два типа ACE:
• ACCESS_ALLOWED_ACE – запись описывает разрешения доступа,
• ACCESS_DENIED_ACE – запись запрещений доступа.
C-подобный:
lkd> dt _access_allowed_ace –b
;//--------------------------------
uxtheme!_ACCESS_ALLOWED_ACE
+0x000 Header : _ACE_HEADER
+0x000 AceType : UChar
+0x001 AceFlags : UChar
+0x002 AceSize : Uint2B
+0x004 Mask : Uint4B
+0x008 SidStart : Uint4B
ACCESS_ALLOWED_ACE через дворд "Mask" предписывает, что именно разрешено делать обладателю данного SID при доступе к объекту. Соответственно если тип указывается как DENIED_ACE, то запись характеризует запрещения доступа. Ниже представлена битовая карта поля "Mask":
Всё это мутно для непросвещённых, что характерно для подсистемы безопасности в целом. Видимо инженеры руководствовались здесь тем, чтобы усложнить пользователю разбор деталей реализации. Однако если поймать связь компонентов, картина проясняется.
Для визуального просмотра подноготной имеет смысл воспользоваться отладчиком – мы можем запросить дескриптор безопасности SD как своего\любого процесса, так и отдельно взятого системного объекта. Если планируем освятить объект, сначала нужно получить к нему доступ через расширение
!handle
, которое возвращает хэндлы всех открытых на данный момент объектов. Но я хочу изъять детали дескриптора своего процесса SecurDesc.exe, для чего запустив его на исполнение буду преследовать следующий алго:
C-подобный:
;//--- Запрашиваю адрес своего процесса --------------------
lkd> !process 0 0 SecurDesc.exe
PROCESS 89b8d248 SessionId: 1 Cid: 0b48 Peb: 7ffdf000 ParentCid: 0748
DirBase: 5fe8d980 ObjectTable: a5b47e50 HandleCount: 134.
Image : SecurDesc.EXE
;//--- Получаю указатель на заголовок его объекта ----------
lkd> !object 89b8d248
Object: 89b8d248 Type: (847cfe38) Process
ObjectHeader: 89b8d230 (new version)
HandleCount: 5 PointerCount: 24
;//--- Линк на "SecurityDescriptor" лежит в заголовке +14h ------
lkd> dt _object_header 89b8d230
nt!_OBJECT_HEADER
+0x000 PointerCount : 0n24
+0x004 HandleCount : 0n5
+0x004 NextToFree : 0x00000005 Void
+0x008 Lock : _EX_PUSH_LOCK
+0x00c TypeIndex : 0x7 ''
+0x00d TraceFlags : 0 ''
+0x00e InfoMask : 0x8 ''
+0x00f Flags : 0 ''
+0x010 ObjectCreateInfo : 0x864dd940 _OBJECT_CREATE_INFORMATION
+0x010 QuotaBlockCharged : 0x864dd940 Void
+0x014 SecurityDescriptor : 0x9a9576f7 Void ;//<--- Дескриптор безопастности!
+0x018 Body : _QUAD
;//--- Рсширение !sd выводит детали дескриптора ------------
;//--- Внимание! Нужно сбросить 3-мл.бита адреса -----------
lkd> !sd 0x9a9576f0 1
->Revision: 0x1
->Sbz1 : 0x0
->Control : 0x8814
SE_DACL_PRESENT
SE_SACL_PRESENT
SE_SACL_AUTO_INHERITED
SE_SELF_RELATIVE
->Owner : S-1-5-32-544 (Alias: BUILTIN\Администраторы)
->Group : S-1-5-21-1365493694-xx-xx-513 (Group: G31TM-P21\None)
->Dacl :
->Dacl : ->AclRevision: 0x2
->Dacl : ->Sbz1 : 0x0
->Dacl : ->AclSize : 0x50
->Dacl : ->AceCount : 0x3
->Dacl : ->Sbz2 : 0x0
->Dacl : ->Ace[0]: ->AceType : ACCESS_ALLOWED_ACE_TYPE
->Dacl : ->Ace[0]: ->AceFlags: 0x0
->Dacl : ->Ace[0]: ->AceSize : 0x18
->Dacl : ->Ace[0]: ->Mask : 0x001fffff
->Dacl : ->Ace[0]: ->SID : S-1-5-32-544 (Alias: BUILTIN\Администраторы)
->Dacl : ->Ace[1]: ->AceType : ACCESS_ALLOWED_ACE_TYPE
->Dacl : ->Ace[1]: ->AceFlags: 0x0
->Dacl : ->Ace[1]: ->AceSize : 0x14
->Dacl : ->Ace[1]: ->Mask : 0x001fffff
->Dacl : ->Ace[1]: ->SID : S-1-5-18 (Well Known Group: NT AUTHORITY\система)
->Dacl : ->Ace[2]: ->AceType : ACCESS_ALLOWED_ACE_TYPE
->Dacl : ->Ace[2]: ->AceFlags: 0x0
->Dacl : ->Ace[2]: ->AceSize : 0x1c
->Dacl : ->Ace[2]: ->Mask : 0x00121411
->Dacl : ->Ace[2]: ->SID : S-1-5-5-0-180542 (no name mapped)
->Sacl :
->Sacl : ->AclRevision: 0x2
->Sacl : ->Sbz1 : 0x0
->Sacl : ->AclSize : 0x1c
->Sacl : ->AceCount : 0x1
->Sacl : ->Sbz2 : 0x0
->Sacl : ->Ace[0]: ->AceType : SYSTEM_MANDATORY_LABEL_ACE_TYPE
->Sacl : ->Ace[0]: ->AceFlags: 0x0
->Sacl : ->Ace[0]: ->AceSize : 0x14
->Sacl : ->Ace[0]: ->Mask : 0x00000003
->Sacl : ->Ace[0]: ->SID : S-1-16-12288 (Label: Mandatory Label\High Mandatory Level)
Так мы получили свойства дескриптора безопасности своего процесса где видно, что его владельцем "Owner" является администратор, а в списке доступа DACL имеется всего три записи АСЕ. Первая с маской "Полный доступ"
= 0x001fffff
описывает разрешения "Allowed" для пользователей из группы админы, вторая принадлежит юзеру SYSTEM, а третья (с маской немного ограниченных прав 0x00121411) отведена для пользователя текущей сессии, о чём свидетельствует её сессионный SID = S-1-5-5-0-xxx
.В штатной библиотеке Win Advapi32.dll имеется огромное кол-во функций, с помощью которых можно легко модифицировать записи АСЕ. Множество примеров их использования приводится в книге
Ссылка скрыта от гостей
. То-есть по неизвестной причине Microsoft предоставила полный карт-бланш (неограниченные полномочия) любому пользователю, включая обычных юзверей – достаточно запросить привилегию SE_SECURITY_NAME, после чего использовать функции из класса SetSecurityDescriptorXX().В качестве разминки посмотрим на ConvertSecurityDescriptorToStringSecurityDescriptor(), которая извлекает маску защиты объекта из списка DACL. Эта функция возвращает маску в виде текстовой строки на языке SDDL (Security Descriptor Definition Language). Полученную строку можно заточить под свои нужны, перевести опять в бин чз ConvertStringSecurityDescriptorToSecurityDescriptor(), и запихать её обратно в дескриптор при помощи, например, SetSecurityInfo(). Вот пример извлечения строки DACL:
C-подобный:
format pe console
include 'win32ax.inc'
entry start
;//----------
pDacl dd 0
pDesc dd 0
align 16
buff db 0
;//----------
.code
start: invoke SetConsoleTitle,<'*** Security Desc Info ***',0>
;//----- Запрашиваем дескриптор SD своего процесса --------------
invoke GetCurrentProcess
invoke GetSecurityInfo,eax,\
SE_KERNEL_OBJECT,\
DACL_SECURITY_INFO,\
0,0,pDacl,0,pDesc
;//----- Переводим его свойства в строку SDDL -------------------
invoke ConvertSecurityDescriptorToStringSecurityDescriptor,\
[pDesc],1,\
DACL_SECURITY_INFO,\
buff,0
cinvoke printf,<10,' Current process DACL string:',10,10,' %s',0>,dword[buff]
@exit: cinvoke _getch ;// клавиша
cinvoke exit,0 ;// выход!
;//----------
section '.idata' import data readable
library msvcrt,'msvcrt.dll',kernel32,'kernel32.dll',advapi32,'advapi32.dll'
include 'api\msvcrt.inc'
include 'api\kernel32.inc'
include 'api\advapi32.inc'
include 'equates\advapi32.inc'
В скрепке лежит инклуд Advapi32.inc, где можно найти полное описание литеров строк SDDL.
Каждый АСЕ заключается в скобки, а элементы записи в ней отделены точкой с запятой. Таким образом, мы видим здесь строки трёх записей АСЕ, где представлены их тип (А, Allowed), дворд с НЕХ-маской, а так-же SID пользователей, которым принадлежит запись:
BA\SY\S-1-5-5-0-xx
. Чтобы получить доступ к данному объекту, достаточно изменить SID любой из записей например на "AU"
(Authenticated Users, все зареганные), или "BU"
(BuiltIn Users, юзеры). Можно пойти по обратному пути, и аналогичным образом наоборот поднять статус своего процесса до админа "ВА"
.3. Устройство механизма "Mandatory Integrity Control"
Но такую картину мы могли наблюдать только на системах ХР с ядром NT-5.
Чтобы покончить с этим беспределом, начиная с Висты продуманы Microsoft ввели доп.уровень проверок доступа под названием MIC, или "Обязательный контроль целостности", что на англ.манер звучит как "Mandatory Integrity Control". Теперь доступ к объектам проверяется не только посредством записей АСЕ в списках DACL, но и записью типа SYSTEM_MANDATORY_LABEL в списке SACL. Как видим, запись данного типа присутствует в дескрипторе безопасности выше, и имеет следующий вид:
C-подобный:
->Sacl :
->Sacl : ->AclRevision: 0x2
->Sacl : ->Sbz1 : 0x0
->Sacl : ->AclSize : 0x1c
->Sacl : ->AceCount : 0x1
->Sacl : ->Sbz2 : 0x0
->Sacl : ->Ace[0]: ->AceType : SYSTEM_MANDATORY_LABEL_ACE_TYPE
->Sacl : ->Ace[0]: ->AceFlags: 0x0
->Sacl : ->Ace[0]: ->AceSize : 0x14
->Sacl : ->Ace[0]: ->Mask : 0x00000003
->Sacl : ->Ace[0]: ->SID : S-1-16-12288 (Label: Mandatory Label\High Mandatory Level)
Здесь имеется маска(3), которая принадлежит пользователю с SID =
S-1-16-12288
. Идентификатор с данным значением и представляет собой уровень целостности данного объекта "Integrity Level". Он не оставляет пространства для разговора со-своей тушкой всем пользователям, уровень которых ниже этого значения. В отличии от DACL, стойкие ноты записей АСЕ в системных списках SACL подменить довольно сложно, хотя и тут имеется своя брешь. Давайте разберём устройство этого механизма, чтобы определить, в какую сторону вообще нужно копать при попытках доступа к защищённым объектам Win7+.На данный момент, все современные системы поддерживают 7 уровней целостности объектов, константы которых увеличиваются с шагом 0x1000. Для обозначения уровней "Integrity Level", система выделяет SID с начальным значением
S-1-16-xx
, где последнее число(хх) представляется константой уровня в 10-тичном виде. Например 0х1000 равно S-1-16-4096
, 0х2000 = 8192, и т.д. В табличке ниже перечислены все возможные уровни IL с кратким описанием их владельцев:Как мы уже знаем, у любых объектов имеется владелец Owner, а в свою очередь владелец имеет свой токен и SID. Процессы могут получать доступ на запись только к тем объектам, уровень IL которых меньше или равно. Вообще-то словосочетание "уровень целостности" не совсем подходит для данного механизма – просто воспринимайте его как "уровень доверия" объекта, к вызывающему субъекту. То-есть объекты не доверяют процессам, если их уровень ниже его\собственного. С введением этого механизма, огромная армия малвари отправилась на свалку истории, освободив нишу для новых идей.
В системе имеется монитор ссылок SRM "Security Reference Monitor". Когда процесс ссылается на объект, именно SRM решает, следует-ли предоставить данному процессу права доступа к объекту. Это делается путём сравнения прикреплённого к процессу токена, со-списком управления доступом к объекту DACL. Монитор сравнивает идентификаторы безопасности SID в записи DACL, с SID в токене пользователя, чтобы определить, какой уровень доступа должен быть предоставлен процессу. Если какой-либо из SID в DACL отказывает в доступе, запрос процесса отвергается с сообщением об ошибке ACCESS_DENIED.
На рисунке ниже представлена обобщённая схема механизма MIC.
Зелёные блоки здесь обозначают токены пользователей, а синие – порождаемые ими процессы. Поскольку объекты типа Process наследуют права своих родителей, их дескрипторы безопасности SD получают уровни целостности владельцев. Когда левый процесс пытается получить доступ к правому\системному процессу, монитор ссылок берёт уровень IL из токена, и сверяет его с уровнем в дескрипторе безопасности объекта. В данном случае IL в токене вызывающего субъекта меньше, поэтому доступ не предоставляется! Однако если системный процесс запросит запись в адресное пространство пользовательского процесса, монитор SRM не станет возражать, т.к. судя по уровню "IntegrityLevel" он является доверенным:
Найти значения уровней целостности "IntegrityLevel" в токене не так просто, как может показаться на первый взгляд. Они зарыты глубоко, хотя ссылки на них лежат на поверхности. Выше уже упоминалось, что подсистема безопасности Lsass.exe назначает каждому пользователя не один, а сразу несколько идентификаторов SID (см.whoami в консоли), среди которых имеется и SID уровня целостности. Дело в том, что в токене лежит лишь указатель на массив идентификаторов, а индекс SID-IL в этом массиве представлен полем "IntegrityLevelIndex". Проведём такой эксперимент в отладчике WinDbg..
C-подобный:
;//---->> Запустим свой процесс, и запросим карту всех\активных процессов
lkd> !process 0 0
**** NT ACTIVE PROCESS DUMP ****
PROCESS 8a000030 SessionId: 1 Cid: 0e90 Peb: 7ffd3000 ParentCid: 0ed0
DirBase: 5fea7cc0 ObjectTable: 9bda1878 HandleCount: 41.
Image : SecurInfo.EXE
;//---->> Находим среди них свой, и запрашиваем его детали
lkd> !process 8a000030 1
PROCESS 8a000030 SessionId: 1 Cid: 0e90 Peb: 7ffd3000 ParentCid: 0ed0
DirBase : 5fea7cc0 ObjectTable: 9bda1878 HandleCount: 40.
Image : SecurInfo.EXE
VadRoot : 8acd94d0 Vads 25 Clone 0 Private 66. Modified 0. Locked 0.
DeviceMap: 86aa5140
Token : a6eca0f0
;//---->> Имеем адрес своего токена – посмотрим на его структуру
lkd> dt _token a6eca0f0
nt!_TOKEN
+0x000 TokenSource : _TOKEN_SOURCE
+0x040 Privileges : _SEP_TOKEN_PRIVILEGES
+0x074 SessionId : 1
+0x078 UserAndGroupCount : 0xf
+0x08c DefaultOwnerIndex : 4
+0x090 UserAndGroups : 0xa6eca2d0 _SID_AND_ATTRIBUTES
+0x094 RestrictedSids : (null)
+0x0a4 TokenType : 1 ( TokenPrimary )
+0x0b4 IntegrityLevelIndex : 0xe
+0x0b8 MandatoryPolicy : 3
На массив идентификаторов SID указывает здесь поле "UserAndGroups" со-значением
0xA6ECA2D0
, каждая запись в котором представляется структурой "SID_AND_ATTRIBUTES". Общее кол-во записей в массиве хранится в поле "UserAndGroupCount" и в данном случае это 0x0F=16
структур в массиве. В свою очередь, индекс SID уровня целостности нашего процесса в этом массиве, лежит в поле "IntegrityLevelIndex", и равен 0x0E=14
. Теперь можно запросить массив идентификаторов, и найти в нём SID-IL:Ещё одним немаловажным моментом является введение обязательных политик доступа "Mandatory Policy", которые тесно связаны с уровнями целостности объектов. Если посмотреть на содержимое токена выше, по смещению
0xb8
можно обнаружить в нём одноимённое поле со-значением(3). В сишном хидере Winnt.h имеются константы с описанием данных политик где видно, что для доступа к защищённым объектам нам явным образом мешает политика(1) "NO_WRITE_UP" – проблема решается сбросом этого значения в нуль:
C-подобный:
TOKEN_MANDATORY_POLICY_OFF = 0x0 ;// Руль! Для токена не применяется обязательная политика целостности.
TOKEN_MANDATORY_POLICY_NO_WRITE_UP = 0x1 ;// Процесс не может выполнять запись в объекты с более высоким уровнем.
TOKEN_MANDATORY_POLICY_NEW_PROCESS_MIN = 0x2 ;// Уровень, который является меньшим из уровня родителя и уровня исполняемого файла.
TOKEN_MANDATORY_POLICY_VALID_MASK = 0x3 ;// POLICY_NO_WRITE_UP + POLICY_NEW_PROCESS_MIN
4. Практика – список активных процессов, и уровни их целостности
В практической части попробуем запросить уровни целостности всех активных процессов. Значит сначала создаём цикл при помощи функций CreateToolhelp32Snapshot() + Process32Next(), которые перечислят все процессы как диспетчер задач Win. Теперь внутри цикла открываем найденный процесс через OpenProcess(), и запрашиваем его токен OpenProcessToken(). Получив дескриптор токена, вызываем функцию GetTokenInformation(), передав ей параметр "TokenIntegrityLevel". Так мы получим SID уроня целостности процесса, и остаётся обозначить его строкой через LookupAccountSid(). Здесь нужно отметить, что для доступа к системным процессам, необходимо обзавестись привилегией отладки SE_DEBUG.
Функция GetTokenInformation() способна вытащить из токена огромное кол-во информации, в зависимости от одного из 48 передаваемых ей параметров. Список этих параметров спрятан в спойлере ниже, а нужный нам числи в розысках под номером 25:
C-подобный:
;//--- Класс запросов для Set/Get TokenInformation() -----
;// https://docs.microsoft.com/en-us/windows/win32/api/winnt/ne-winnt-token_information_class
TokenUser = 1 ;// SID пользователя (атрибуты не определены)
TokenGroups = 2 ;// массив SID групп (и их атрибуты)
TokenPrivileges = 3 ;// массив LUID привилегий (и их атрибуты)
TokenOwner = 4 ;// владелец
TokenPrimaryGroup = 5 ;// группа пользователя токена
TokenDefaultDacl = 6 ;// DACL, который будет назначен всем объектам, использующим этот токен
TokenSource = 7 ;// текстовый (и числовой) ID создателя токена
TokenType = 8 ;// тип
TokenImpersonationLevel = 9 ;// уровень олицетворения
TokenStatistics = 10 ;// общая информация о текене
TokenRestrictedSids = 11 ;// ограниченный SID
TokenSessionId = 12 ;// SID сессии Logon
TokenGroupsAndPrivileges = 13
TokenSessionReference = 14
TokenSandBoxInert = 15
TokenAuditPolicy = 16
TokenOrigin = 17
TokenElevationType = 18
TokenLinkedToken = 19
TokenElevation = 20
TokenHasRestrictions = 21
TokenAccessInformation = 22
TokenVirtualizationAllowed = 23
TokenVirtualizationEnabled = 24
TokenIntegrityLevel = 25 ;// SID уровня целостности
TokenUIAccess = 26 ;// Уровень целостности: S-1-16-8208
TokenMandatoryPolicy = 27
TokenLogonSid = 28
TokenIsAppContainer = 29
TokenCapabilities = 30
TokenAppContainerSid = 31
TokenAppContainerNumber = 32
TokenUserClaimAttributes = 33
TokenDeviceClaimAttributes = 34
TokenRestrictedUserClaimAttributes = 35
TokenRestrictedDeviceClaimAttributes = 36
TokenDeviceGroups = 37
TokenRestrictedDeviceGroups = 38
TokenSecurityAttributes = 39
TokenIsRestricted = 40
TokenProcessTrustLevel = 41
TokenPrivateNameSpace = 42
TokenSingletonAttributes = 43
TokenBnoIsolation = 44
TokenChildProcessFlags = 45
TokenIsLessPrivilegedAppContainer = 46
TokenIsSandboxed = 47
MaxTokenInfoClass = 48
C-подобный:
format pe console
include 'win32ax.inc'
entry start
;//----------
.data
pe32 PROCESSENTRY32
;//---- Структура "TOKEN_PRIVILEGES"
align 16
newState:
PrivilegeCount dd 1
Luid1 dq 0
Attribute1 dd SE_PRIVILEGE_ENABLED
sidStr rb 128
Name rb 128
DomainName rb 128
cbName dd 0
cbDomainName dd 0
peUse dd 0
hToken dd 0
hSnap dd 0
pcbBytesNeeded dd 0
align 16
buff db 0
;//----------
.code
proc GetIntegrityLevel ;//<--- Процедура запроса уровня целостности
mov edi,Name ;// Очистить буфер для строки
mov ecx,128/4
xor eax,eax
rep stosd
invoke GetTokenInformation,[hToken],\
TokenIntegrityLevel,\
buff,32,pcbBytesNeeded
mov esi,dword[buff]
push esi
invoke ConvertSidToStringSid,esi,sidStr
pop esi
invoke LookupAccountSid,0,esi,\
0,cbName,\
0,cbDomainName,peUse
invoke LookupAccountSid,0,esi,\
Name,cbName,\
DomainName,cbDomainName,peUse
invoke CharToOem,Name,Name
cinvoke printf,<' %-13s %s',10,0>, dword[sidStr],Name
@@: ret
endp
;//*********************************************************
start: invoke SetConsoleTitle,<'*** Process Integrity Level ***',0>
;//----- Выставляем привилегию "SeDebug" в своём токене!
invoke GetCurrentProcess
invoke OpenProcessToken,eax,\
TOKEN_QUERY + TOKEN_ADJUST_PRIVILEGES,hToken
invoke LookupPrivilegeValue,0,<'SeDebugPrivilege',0>,Luid1
invoke AdjustTokenPrivileges,[hToken],0,newState,0,0,0
or eax,eax
jnz @f
cinvoke printf,<10,' SeDebugPrivilege error!',0>
jmp @exit
@@: invoke CloseHandle,[hToken]
;//***********************************************
cinvoke printf,<10,' Process PID Hndl Integrity Level',\
10,' -------------------------- ---- ---- -----------------------------',10,0>
;//----- Обход всех активных процессов ------------------------
invoke CreateToolhelp32Snapshot,TH32CS_SNAPPROCESS,0
mov [hSnap],eax
invoke Process32First,[hSnap],pe32
@next: invoke Process32Next,[hSnap],pe32 ;// EAX=0: ошибка
or eax,eax
je @stop
mov eax,[pe32.th32ProcessID]
invoke OpenProcess, PROCESS_QUERY_LIMITED_INFORMATION, TRUE, eax
push eax eax
mov esi,pe32
add esi,PROCESSENTRY32.szExeFile
cinvoke printf,<' %-27s %4d %04x',0>,\
esi,[pe32.th32ProcessID],eax
pop eax
invoke OpenProcessToken,eax,TOKEN_QUERY,hToken
call GetIntegrityLevel
pop eax
invoke CloseHandle,eax
jmp @next
@stop: invoke CloseHandle,[hSnap]
@exit: cinvoke _getch ;// клавиша
cinvoke exit,0 ;// выход!
;//----------
section '.idata' import data readable
library msvcrt,'msvcrt.dll',kernel32,'kernel32.dll',\
user32,'user32.dll',advapi32,'advapi32.dll'
include 'api\msvcrt.inc'
include 'api\kernel32.inc'
include 'api\user32.inc'
include 'api\advapi32.inc'
include 'equates\advapi32.inc'
Рассмотрим бегло этот скрин..
Обратите внимание на дескрипторы Handle процессов – их значение увеличивается на 8-байт. Это потому, что при запуске процесса на исполнение, система создаёт в его окружении "таблицу дескрипторов", куда помещает все открытые данным процессом хэндлы. На самом деле эти значения являются просто индексами в таблице, где лежат указатели на непосредственно дескрипторы. Когда я делал этот скрин, то не закрывал открытые процессы через CloseHandle(). Другой пример лежит в скрепке, где дескрипторы прихлопываются, и соответственно индекс не увеличивается, а просто перезаписывается в одном поле таблицы, с одинаковым значением.
Далее видим идентификаторы SID уровней целостности "IntegrityLevel", с привязанными к ним пояснительными строками. У всех системных процессов он равен
0х4000=16384
, а у тех, что крышует админ 0х3000=12288
High. По этой причине, мои процессы не смогут получить доступ на запись к системным процессам. Браузер Хром для каждой вкладки создаёт новый процесс с ненадёжным уровнем(0), в результате чего скрипты и прочее содержимое интернет-страниц не представляют опасности для системы. Процессам Хрома на запись доступна только его собственная папка на диске, куда он кэширует данные посещённых страниц.
5. Заключение
Эксперименты в отладчике и ручной поиск всевозможных полей в системных структурах, открывает двери в шелл-кодинг. Ведь хорошо известно, что в этой области программирования мы лишены возможности использовать функции Win32-API, и всё приходится делать ручками и головой. Именно поэтому я уделил большую часть топика логам дебагера. Как показали опыты, системы Виста+ это далеко не дырявая ХР, и инженеры майков потрудились на славу, чтобы оградить нас от нечисти. Они предложили нам вполне комфортную подушку безопасности, которую мы всё-таки попытаемся "проткнуть шилом" в следующей части статьи.
Уже по привычке, в скрепку кладу два представленных здесь исполняемых файла для тестов,
а так-же инклуд Advapi32.inc для тех, кто захочет сам собрать код из исходников. Всем удачи и пока!