Всем привет!
На повестке дня – подсистема безопасности Win. Рассмотрим такие понятия как: системные объекты, права пользователей и их привилегии, токены и дескрипторы безопасности, списки контроля доступа DACL/SACL и записи в них ACE, проведём экскурсию в процесс Lsass.exe, познакомимся с диспетчером объектов, системным монитором SRM и узнаем, каким образом утилите "runas.exe" удаётся запускать процессы с админскими правами. Одним словом попытаемся бросить камень в стеклянную форточку Win и посмотрим, что из этого выйдет.
Содержание:
1. Знакомство с объектами
2. Дескриптор безопасности объекта
4. Под занавес..
-------------------------------------------------------
1. Знакомство с объектами
Известно, что Win построена на объектной модели. Это значит, что если в операционных системах класса Linux всё является файлом, то в глазах большого Билли мир существует в виде объектов. К числу объектов можно отнести всё, с чем нам приходится иметь дело в процессе программирования – это армия всевозможных физ.устройств Device, драйверы и контролёры, процессы и потоки, файлы и папки, таймеры, семафоры, мьютексы, рабочий стол и многое другое. В программе М.Руссиновича "Object Explorer", на своей семёрке я насчитал 42 именованных типов объектов, и это только доступные нам как смертным. Помимо них, в нёдрах оси имеются ещё и безымянные ресурсы, так-что здесь есть где развернуться.
Чтобы хоть немного навести порядок в этой кухне, все объекты система сортирует по типам – так в литературе появился термин "Объект типа". На программном уровне, каждый объект описывается своей структурой "OBJECT_HEADER", за которой тут-же располагается непосредственно и сам объект. В заголовке Header, тушке объекта присвоено имя "Body", и это самое последнее поле структуры (см.рис.ниже). При таких раскладах ясно, что системе требуется некий руководящий орган, который управлял-бы всем этим сообществом. Протекторат возложил эту задачу на плечи диспетчера объектов – службе уровня ядра.
На рисунке ниже скрин программы, куда я вставил прототип структуры Header из ядерного отладчика WinDbg. Если выбрать в левом окне просмотрщика раздел "ObjectTypes", можно ознакомиться с полным списком именованных объектов ядра (правый клик запрашивает свойства):
Каждый из объектов имеет свой заголовок Header, его структура одинакова для всех. В данном случае, в заголовке видим число указателей на объект(18), счётчик открытых дескрипторов(4), состояние, тип объекта(7), маску, флаги, информацию о создании, и по смещению 14h – наиболее интересное для нас поле "SecurityDescriptor" SD, что в привычном нам понимании означает дескриптор безопасности объекта. Если мыслить глобально, то именно на этом крохотном дескрипторе держится вся подсистема контроля доступа к объектам Win, в чём мы убедимся позже.
Отметим, что дескриптор SD могут иметь только разделяемые именованные объекты системы, поскольку безымянные как-правило тусуются в самом ядре и по определению не нуждаются в защите. В эту-же корзину отправляется и понятие "разделяемый" – если объект используется кем-то одним, то защищаться ему не от кого. Таким образом, объекты бывают защищаемые (с активным дескриптором безопасности) и незащищаемые, когда дескриптор присутствует, но забит нулями.
2. Дескриптор безопасности объекта
Дескриптор SD содержит информацию о владельце объекта в виде его SID (Security Identifier), а так-же включает в себя следующие два списка контроля-доступа, которые назвали Access-Contol List [ACL]:
В свою очередь, в списке DACL имеются несколько записей Access-Control Entry [ACE], по одной на каждую группу пользователей в системе. Например если зарегистрированных юзеров с одинаковыми правами двое, они собираются в свою группу и для этой группы в списке DACL будет выделена одна запись АСЕ. Вторая запись может хранить параметры доступа к объектам учёткой System, а третью – диспетчер объектов может пожертвовать группе (или пользователю) с правами админа. Нужно понимать, что записи АСЕ в списках DACL являются последними элементами в цепочке контроля доступа к объектам, и вся вселенная вращается вокруг именно АСЕ.
Когда наш процесс пытается получить доступ к защищённому ресурсу, диспетчер объектов либо предоставляет, либо запрещает доступ. К примеру, это может быть попытка открыть системный файл функцией CreateFile(), с запросом на запись или удаление. Если у файлового объекта нет списка управления доступом DACL (такие объекты называют ещё Null-Dacl), диспетчер не задумываясь предоставит нам доступ. В противном случае, служебные процедуры диспетчера начнут поиск принадлежащей нам записи ACE в списке DACL объекта, с последующей проверкой наших прав. Как показала практика, такая схема работает отменно и никогда не даёт сбоев, если только не выдать себя за другое лицо.
2.1. Структура дескриптора SD
На физическом уровне, в заголовке OBJECT_HEADER объекта прописан лишь указатель на дескриптор безопасности SD, а сам дескриптор может находиться где угодно в пространстве ядра. Он оформлен в виде одноимённой структуры с таким прототипом:
Здесь, ревизия интересна только самой системе. Следующий байт так-же не несёт полезной нагрузки и вставлен для выравнивания структуры в памяти, на границу слова Word. А вот дальше уже интересней.. В поле "Control" лежит 16-битный набор флагов, которые определяют, действительный или нет в объекте список DACL. Если объект защищённый, то в поле Control обязательно должен быть установлен бит
В следующих полях "Owner/Group" дескриптора, прописаны линки на идентификатор SID владельца, и группы в которую он входит. Ну и в хвосте свои позиции заняли два указателя на столь важные для нас (и в первую очередь для самого объекта) списки DACL и SACL соответственно. Рассмотрим их подробней..
2.2. Списки DACL и записи в них АСЕ
Список контроля-доступа к объекту DACL можно воспринимать как таблицу с произвольным кол-вом строк, и 32 столбцами "AccessMask" (dword). В каждой из строк таблицы имеется запись АСЕ, которая закреплена за конкретной группой пользователей в системе. В столбцах-же, хранятся уже непосредственно права на доступ к объекту в виде логической единицы да/нет. Всю таблицу в лице списка DACL описывает структура DACL_HEADER, а строки с записями в ней – структура ACE_HEADER.
Здесь видно, что первые два поля опять отводятся под версию и байт выравнивания, а следом идёт размер всей таблицы DACL, и общее число записей в ней. Обратите внимание на слово-выравнивания структуры в памяти в последнем поле "Sbz2". Дело в том, что если не подправить эту структуру на границу параграфа (16,32,48,64 и т.д. байт), то следующая за ней структура окажется по невыровненным адресам в памяти, и для её чтения процессору придётся делать два обращения к ОЗУ. То-есть в данном случае структура заботится не о себе, а о своём гипотетическом (со)брате, который может тесно примкнуть к ней в пространстве виртуальной памяти.
Ну вот мы и добрались до иглы Кащея – это непосредственно запись "Access-Control Entry" ограничевающая доступ к объекту. Кол-во таких записей хранится в поле "AceCount" заголовка DACL, а структура ниже описывает только одну из них. Здесь имеем тип записи, какие-то левые флаги (всегда сброшены в нуль), размер данной записи, и принципиально важные для нас: 32-битная маска с атрибутами доступа + идентификатор пользователя SID, которому принадлежит данная запись. Забегая вперёд авансом скажу, что SID в записях АСЕ представляет собой уязвимость, поскольку достаточно подменить его на админский (или System), чтобы получить полный доступ к защищаемому объекту:
Что такое SID и от куда он берётся мы рассмотрим позже, а пока обратим внимание на "AccessMask".
Известно, что объектов в системе вагон и целая тележка, и каждый со-своим нравом. Они могут иметь специфичные права, под которые отводятся младшие 16-бит маски [15:0]. Следующий байт маски в диапазоне [23:16] занят под стандартные права доступа к объекту, где бит "Write_DAC" разрешает модификацию списка DACL. Обычно такими полномочиями обладает учётная запись Admin или System. Если взведён красный бит(S), перед нами запись АСЕ, а самая старшая тетрада выделена под универсальные права, которые мы пытаемся получить при вызовах функций типа CreateFile(), CreateProcess() и других, которые имеют параметр с атрибутами доступа в своём прототипе.
Если запустить программу "Object Explorer" (в том числе обычный проводник Win) и в свойствах какого-либо объекта перейти на вкладку "Безопасность", можно будет обнаружить список всех пользователей и назначенные им права на доступ к выбранному объекту – это окно называют ещё графическим редактором ACL. В штатной поставке Win идёт и консольная утилита для этих целей под названием "icacls.exe":
2.3. Эксперимент в отладчике WinDbg
Теперь посмотрим, как выглядят списки DACL и записи АСЕ в реальном приложении.
В самом простом варианте, можно запустить отладчик WinDbg в режиме ядра Kernel, после чего дать команду
Так мы получили адрес окружения нашего процесса в ядерной памяти
Уже лучше, и в поле по смещению
Разберём на атомы последнюю запись Аce[2] списка DACL..
Значит она стандартного типа "ACCESS_ALLOWED_ACE_TYPE" размером 0x1C=28 байт, имеет маску доступа к объекту
3. Формат идентификатора SID
Идентификатор безопасности Security-Identifier (SID) – двоичная структура, которая однозначно идентифицирует учётную запись пользователя, группы, службы, домена или компьютера. SID выдаётся подсистемой Lsass (Local Security Authority Sub-System) в момент установки операционной системы, а так-же при регистрации новых пользователей. Системе не удобно оперировать привычными нам именами в строковом виде, поэтому она идентифицирует пользователя при помощи SID. Соответственно в проверке доступа к защищённым объектам и списках DACL участвуют так-же только SID'ы, что продемонстрировано в эксперименте с отладчиком выше.
Структура SID хорошо документирована и состоит из нескольких частей, которые указаны на следующем рисунке. Чтобы ознакомится с зарегистрированными в системе SID, можно позвать крошечную консольную утилиту "whoami" (Who Am I, кто я?), или универсальный тяжеловес WMI (Windows Management Instrumentation). Соответствие имени пользователя и его SID можно отследить и в ключе реестра: [HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList]:
Полезную смысловую нагрузку несут в себе три поля идентификатора – это Authority, Sub-Authority, и последний RID. На платформе Win всех мастей ревизия всегда одинакова и равна
• Поле "Authority" имеет фиксированную длину и определяет класс учётной записи:
• Поле "Sub-Authority" имеет переменную длину и идентифицирует учётную запись внутри указанного класса. Это поле может иметь значение нуль, тогда SID определяет класс или группу учётных записей.
• Относительные идентификаторы "RID" одинаковы для всех компьютеров. Например RID учётки "Админ" всегда имеет значение 500, а RID гостя равен 501. Когда учётную запись создаёт админ, идентификатор нового пользователя начинается со значения 1000 и увеличивается на 1 для каждой следующей учётки. Кроме того, в системе имеется несколько широко-известных (Well-Known) учётных записей. К ним относятся группы "Все", "Интерактивные" и т.д. С исчерпывающим списком SID и их описанием можно ознакомится на сайте MSDN по
В системе существует специальный SID для обозначения идентификатора сессии "LogonSID" – он тоже используется в записях ACЕ и назначается юзерам, которые вошли в систему аутентифицировано (указав свой логин и пароль) и работают в ней на текущий момент. SID сессии одноразовый и существует, пока пользователь не вышел из системы – при каждом последующем входе генерируется новый LogonSID для того-же пользователя. К примеру SID моей текущей сессии имеет значение
4. Под занавес..
Таким образом идеология ограничения прав доступа к объектам сводится к тому, что пользователь имеет маркер доступа к объекту "AccessToken", а объект имеет свой дескриптор безопасности "SecurityDescriptor" со-списком DACL, и записями АСЕ в нём. Когда процесс хочет получить доступ к объекту, монитор безопасности SRM берёт токен процесса, и сопоставляет его с записью АСЕ запрошенного объекта. Состав, структуру и назначение токена я оставил для следующей части статьи, чтобы была возможность переварить теорию выше. До скорого, пока!
На повестке дня – подсистема безопасности Win. Рассмотрим такие понятия как: системные объекты, права пользователей и их привилегии, токены и дескрипторы безопасности, списки контроля доступа DACL/SACL и записи в них ACE, проведём экскурсию в процесс Lsass.exe, познакомимся с диспетчером объектов, системным монитором SRM и узнаем, каким образом утилите "runas.exe" удаётся запускать процессы с админскими правами. Одним словом попытаемся бросить камень в стеклянную форточку Win и посмотрим, что из этого выйдет.
Содержание:
1. Знакомство с объектами
2. Дескриптор безопасности объекта
2.1. Структура дескриптора SD
2.2. Списки DACL и записи в них АСЕ
2.3. Эксперимент в отладчике WinDbg
3. Формат идентификатора SID4. Под занавес..
-------------------------------------------------------
1. Знакомство с объектами
Известно, что Win построена на объектной модели. Это значит, что если в операционных системах класса Linux всё является файлом, то в глазах большого Билли мир существует в виде объектов. К числу объектов можно отнести всё, с чем нам приходится иметь дело в процессе программирования – это армия всевозможных физ.устройств Device, драйверы и контролёры, процессы и потоки, файлы и папки, таймеры, семафоры, мьютексы, рабочий стол и многое другое. В программе М.Руссиновича "Object Explorer", на своей семёрке я насчитал 42 именованных типов объектов, и это только доступные нам как смертным. Помимо них, в нёдрах оси имеются ещё и безымянные ресурсы, так-что здесь есть где развернуться.
Чтобы хоть немного навести порядок в этой кухне, все объекты система сортирует по типам – так в литературе появился термин "Объект типа". На программном уровне, каждый объект описывается своей структурой "OBJECT_HEADER", за которой тут-же располагается непосредственно и сам объект. В заголовке Header, тушке объекта присвоено имя "Body", и это самое последнее поле структуры (см.рис.ниже). При таких раскладах ясно, что системе требуется некий руководящий орган, который управлял-бы всем этим сообществом. Протекторат возложил эту задачу на плечи диспетчера объектов – службе уровня ядра.
На рисунке ниже скрин программы, куда я вставил прототип структуры Header из ядерного отладчика WinDbg. Если выбрать в левом окне просмотрщика раздел "ObjectTypes", можно ознакомиться с полным списком именованных объектов ядра (правый клик запрашивает свойства):
Каждый из объектов имеет свой заголовок Header, его структура одинакова для всех. В данном случае, в заголовке видим число указателей на объект(18), счётчик открытых дескрипторов(4), состояние, тип объекта(7), маску, флаги, информацию о создании, и по смещению 14h – наиболее интересное для нас поле "SecurityDescriptor" SD, что в привычном нам понимании означает дескриптор безопасности объекта. Если мыслить глобально, то именно на этом крохотном дескрипторе держится вся подсистема контроля доступа к объектам Win, в чём мы убедимся позже.
Отметим, что дескриптор SD могут иметь только разделяемые именованные объекты системы, поскольку безымянные как-правило тусуются в самом ядре и по определению не нуждаются в защите. В эту-же корзину отправляется и понятие "разделяемый" – если объект используется кем-то одним, то защищаться ему не от кого. Таким образом, объекты бывают защищаемые (с активным дескриптором безопасности) и незащищаемые, когда дескриптор присутствует, но забит нулями.
2. Дескриптор безопасности объекта
Дескриптор SD содержит информацию о владельце объекта в виде его SID (Security Identifier), а так-же включает в себя следующие два списка контроля-доступа, которые назвали Access-Contol List [ACL]:
• Discretionary Access-Control List [DACL] – список управления избирательным доступом, в котором перечислены пользователи и группы, с выданными им правами на доступ к объекту. В списке DACL хранится информация о доверенных лицах в системе, которым разрешён/запрещён доступ к данному объекту.
• System Access-Control List [SACL] – системный список управления доступом, который позволяет админам регистрировать в журнале событий неправомерные попытки доступа к защищённым объектам. Системный список SACL пока нам не интересен – мы остановимся лишь на пользовательском DACL.
В свою очередь, в списке DACL имеются несколько записей Access-Control Entry [ACE], по одной на каждую группу пользователей в системе. Например если зарегистрированных юзеров с одинаковыми правами двое, они собираются в свою группу и для этой группы в списке DACL будет выделена одна запись АСЕ. Вторая запись может хранить параметры доступа к объектам учёткой System, а третью – диспетчер объектов может пожертвовать группе (или пользователю) с правами админа. Нужно понимать, что записи АСЕ в списках DACL являются последними элементами в цепочке контроля доступа к объектам, и вся вселенная вращается вокруг именно АСЕ.
Когда наш процесс пытается получить доступ к защищённому ресурсу, диспетчер объектов либо предоставляет, либо запрещает доступ. К примеру, это может быть попытка открыть системный файл функцией CreateFile(), с запросом на запись или удаление. Если у файлового объекта нет списка управления доступом DACL (такие объекты называют ещё Null-Dacl), диспетчер не задумываясь предоставит нам доступ. В противном случае, служебные процедуры диспетчера начнут поиск принадлежащей нам записи ACE в списке DACL объекта, с последующей проверкой наших прав. Как показала практика, такая схема работает отменно и никогда не даёт сбоев, если только не выдать себя за другое лицо.
2.1. Структура дескриптора SD
На физическом уровне, в заголовке OBJECT_HEADER объекта прописан лишь указатель на дескриптор безопасности SD, а сам дескриптор может находиться где угодно в пространстве ядра. Он оформлен в виде одноимённой структуры с таким прототипом:
C-подобный:
struct SECURITY_DESCRIPTOR
Revision db 0 ;// версия дескриптора
Sbz1 db 0 ;// **(байт выравнивания версии на 16-бит границу)
Control dw 0 ;// флаги настроек дескриптора
Owner dd 0 ;// указатель на SID владельца
Group dd 0 ;// указатель на SID группы
Sacl dd 0 ;// указатель на "SACL_HEADER"
Dacl dd 0 ;// указатель на "DACL_HEADER"
ends
Здесь, ревизия интересна только самой системе. Следующий байт так-же не несёт полезной нагрузки и вставлен для выравнивания структуры в памяти, на границу слова Word. А вот дальше уже интересней.. В поле "Control" лежит 16-битный набор флагов, которые определяют, действительный или нет в объекте список DACL. Если объект защищённый, то в поле Control обязательно должен быть установлен бит
"SE_DACL_PRESENT=4"
, иначе даже при физическом наличии списка он будет проигнорирован диспетчером объектов. Остальные флаги комбинируются с основным, и могут определять авто-наследуемость дескриптора потомками, защиту от модификации и прочее. Типичное значение флагов = 0x8814
. Раскройте спойлер ниже, и соберите из констант это значение – должно получиться как минимум DACL/SACL_PRESENT
.
C-подобный:
;//--- Флаги SECURITY_DESCRIPTOR_CONTROL
;//--- https://docs.microsoft.com/en-us/windows/win32/secauthz/security-descriptor-control
SE_OWNER_DEFAULTED = 0x0001
SE_GROUP_DEFAULTED = 0x0002
SE_DACL_PRESENT = 0x0004
SE_DACL_DEFAULTED = 0x0008
SE_DACL_AUTO_INHERIT_REQ = 0x0100
SE_DACL_AUTO_INHERITED = 0x0400
SE_DACL_PROTECTED = 0x1000
SE_RM_CONTROL_VALID = 0x4000
SE_SACL_DEFAULTED = 0x0008
SE_SACL_PRESENT = 0x0010
SE_SACL_AUTO_INHERIT_REQ = 0x0200
SE_SACL_AUTO_INHERITED = 0x0800
SE_SACL_PROTECTED = 0x2000
SE_SELF_RELATIVE = 0x8000
В следующих полях "Owner/Group" дескриптора, прописаны линки на идентификатор SID владельца, и группы в которую он входит. Ну и в хвосте свои позиции заняли два указателя на столь важные для нас (и в первую очередь для самого объекта) списки DACL и SACL соответственно. Рассмотрим их подробней..
2.2. Списки DACL и записи в них АСЕ
Список контроля-доступа к объекту DACL можно воспринимать как таблицу с произвольным кол-вом строк, и 32 столбцами "AccessMask" (dword). В каждой из строк таблицы имеется запись АСЕ, которая закреплена за конкретной группой пользователей в системе. В столбцах-же, хранятся уже непосредственно права на доступ к объекту в виде логической единицы да/нет. Всю таблицу в лице списка DACL описывает структура DACL_HEADER, а строки с записями в ней – структура ACE_HEADER.
C-подобный:
struct DACL_HEADER ;//<------ Заголовок списка DACL
AclRevision db 0 ;// ревизия = 2
Sbz1 db 0 ;// **(байт выравнивания на 16-бит)
AclSize dw 0 ;// размер этой структуры + всех ACE
AceCount dw 0 ;// кол-во записей ACE в списке DACL объекта
Sbz2 dw 0 ;// **(слово выравнивания структуры на 32-бита)
ends
Здесь видно, что первые два поля опять отводятся под версию и байт выравнивания, а следом идёт размер всей таблицы DACL, и общее число записей в ней. Обратите внимание на слово-выравнивания структуры в памяти в последнем поле "Sbz2". Дело в том, что если не подправить эту структуру на границу параграфа (16,32,48,64 и т.д. байт), то следующая за ней структура окажется по невыровненным адресам в памяти, и для её чтения процессору придётся делать два обращения к ОЗУ. То-есть в данном случае структура заботится не о себе, а о своём гипотетическом (со)брате, который может тесно примкнуть к ней в пространстве виртуальной памяти.
Ну вот мы и добрались до иглы Кащея – это непосредственно запись "Access-Control Entry" ограничевающая доступ к объекту. Кол-во таких записей хранится в поле "AceCount" заголовка DACL, а структура ниже описывает только одну из них. Здесь имеем тип записи, какие-то левые флаги (всегда сброшены в нуль), размер данной записи, и принципиально важные для нас: 32-битная маска с атрибутами доступа + идентификатор пользователя SID, которому принадлежит данная запись. Забегая вперёд авансом скажу, что SID в записях АСЕ представляет собой уязвимость, поскольку достаточно подменить его на админский (или System), чтобы получить полный доступ к защищаемому объекту:
C-подобный:
struct ACE_HEADER
AceType db 0 ;// тип записи
AceFlags db 0 ;// флаги (всегда нуль)
AceSize dw 0 ;// размер данной записи АСЕ
Mask dd 0 ;// маска доступа к объекту
SidStart dd 0 ;// идентификатор пользователя
ends
Что такое SID и от куда он берётся мы рассмотрим позже, а пока обратим внимание на "AccessMask".
Известно, что объектов в системе вагон и целая тележка, и каждый со-своим нравом. Они могут иметь специфичные права, под которые отводятся младшие 16-бит маски [15:0]. Следующий байт маски в диапазоне [23:16] занят под стандартные права доступа к объекту, где бит "Write_DAC" разрешает модификацию списка DACL. Обычно такими полномочиями обладает учётная запись Admin или System. Если взведён красный бит(S), перед нами запись АСЕ, а самая старшая тетрада выделена под универсальные права, которые мы пытаемся получить при вызовах функций типа CreateFile(), CreateProcess() и других, которые имеют параметр с атрибутами доступа в своём прототипе.
C-подобный:
;//---- Права доступа "Rights" -----------
DELETE = 0x00010000
READ_CONTROL = 0x00020000
WRITE_DAC = 0x00040000
WRITE_OWNER = 0x00080000
SYNCHRONIZE = 0x00100000
STANDARD_RIGHTS_REQUIRED = 0x000F0000
STANDARD_RIGHTS_ALL = 0x001F0000
SPECIFIC_RIGHTS_ALL = 0x0000FFFF
STANDARD_RIGHTS_READ = READ_CONTROL
STANDARD_RIGHTS_WRITE = READ_CONTROL
STANDARD_RIGHTS_EXECUTE = READ_CONTROL
Если запустить программу "Object Explorer" (в том числе обычный проводник Win) и в свойствах какого-либо объекта перейти на вкладку "Безопасность", можно будет обнаружить список всех пользователей и назначенные им права на доступ к выбранному объекту – это окно называют ещё графическим редактором ACL. В штатной поставке Win идёт и консольная утилита для этих целей под названием "icacls.exe":
2.3. Эксперимент в отладчике WinDbg
Теперь посмотрим, как выглядят списки DACL и записи АСЕ в реальном приложении.
В самом простом варианте, можно запустить отладчик WinDbg в режиме ядра Kernel, после чего дать команду
!process 0 0
на отображение всех активных процессов. Среди длинной портянки лога появится и наш процесс, который в моём случае имеет имя TOKEN.EXE:
C-подобный:
lkd> !process 0 0
..........
PROCESS 891df030 SessionId: 1 Cid: 127c
Peb: 7ffdf000 ParentCid: 00e8
DirBase: 5f59cf80 ObjectTable: ab871bb8
HandleCount: 33.
Image: TOKEN.EXE
Так мы получили адрес окружения нашего процесса в ядерной памяти
0x891df030
, и теперь передаём его команде !object
, в надежде получить указатель на заголовок объекта OBJECT_HEADER (см.первый скрин). Здесь отладчик вернул адрес 0x891df018
(у вас он будет другим), и сразу-же командой dt
я погружаюсь в его детали:
C-подобный:
lkd> !object 891df030
Object: 891df030 Type: (847bfeb0) Process ;//<----- объект типа "Process"
ObjectHeader: 891df018 (new version) ;//<----- адрес заголовка!
HandleCount: 4 PointerCount: 21
lkd> dt _object_header 891df018 ;//<----- просмотреть содержимое заголовка
nt!_OBJECT_HEADER
+0x000 PointerCount : 18
+0x004 HandleCount : 4
+0x004 NextToFree : 0x00000004 Void
+0x008 Lock : _EX_PUSH_LOCK
+0x00c TypeIndex : 0x7 ''
+0x00d TraceFlags : 0 ''
+0x00e InfoMask : 0x8 ''
+0x00f Flags : 0 ''
+0x010 ObjectCreateInfo : 0x866f6040 _OBJECT_CREATE_INFORMATION
+0x010 QuotaBlockCharged : 0x866f6040 Void
+0x014 SecurityDescriptor : 0x94840e76 Void ;//<----- виновник торждества!
+0x018 Body : _QUAD
Уже лучше, и в поле по смещению
0x14
наблюдаем указатель на дескриптор безопасности процесса 0x94840e76
. Обратите внимание, что линк указывает на невыровненную область памяти, т.к. младший байт имеет значение 0x76
, а должен быть или 0x70
, или 0x78
. Поэтому сбрасываем 3-младших бита адреса, и получаем "правильный" линк 0x94840e70
. Теперь остаётся позвать на помощь расширение отладчика с говорящим за-себя названием !sd
, как перед нами возникнет содержимое списков DACL/SACL, со-всей своей подноготной в виде записей АСЕ:
C-подобный:
lkd> !sd 94840e70 ;//<---- обнулить мл.3 бита от "SecurityDescriptor"
->Revision: 0x1
->Sbz1 : 0x0
->Control : 0x8814
SE_DACL_PRESENT ;//<------ активный DACL
SE_SACL_PRESENT
SE_SACL_AUTO_INHERITED
SE_SELF_RELATIVE
->Owner : S-1-5-32-544 ;//<---- SID владелеца объекта
->Group : S-1-5-21-1365493694-2245328239-4151685940-513 ;//<---- SID группы, в которую входит владелец
->Dacl : //<------------------- Заголовок списка DACL_HEADER
->Dacl : ->AclRevision: 0x2
->Dacl : ->Sbz1 : 0x0
->Dacl : ->AclSize : 0x50
->Dacl : ->AceCount : 0x3 ;//<------- Всего записей АСЕ в листе DACL
->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
->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
->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-132935
->Sacl : //<------------------ Заголовок списка SACL_HEADER
->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
Разберём на атомы последнюю запись Аce[2] списка DACL..
Значит она стандартного типа "ACCESS_ALLOWED_ACE_TYPE" размером 0x1C=28 байт, имеет маску доступа к объекту
0x00121411
, и принадлежит пользователю с идентификатором входа в систему S-1-5-5-0-132935
. У остальных записей SID уже другой, причём двух одинаковых быть не может. Кроме того делаем вывод, что DACL данного объекта "Process" действителен, т.к. в самом начале поле "Control" имеет взведённый флаг "SE_DACL_PRESENT". Попробуйте перевести маску из Hex в Bin, и наложить её на скрин выше. Так вы узнаете доступные юзеру S-1-5-5-0-132935
права, в отношении подопытного процесса.3. Формат идентификатора SID
Идентификатор безопасности Security-Identifier (SID) – двоичная структура, которая однозначно идентифицирует учётную запись пользователя, группы, службы, домена или компьютера. SID выдаётся подсистемой Lsass (Local Security Authority Sub-System) в момент установки операционной системы, а так-же при регистрации новых пользователей. Системе не удобно оперировать привычными нам именами в строковом виде, поэтому она идентифицирует пользователя при помощи SID. Соответственно в проверке доступа к защищённым объектам и списках DACL участвуют так-же только SID'ы, что продемонстрировано в эксперименте с отладчиком выше.
Структура SID хорошо документирована и состоит из нескольких частей, которые указаны на следующем рисунке. Чтобы ознакомится с зарегистрированными в системе SID, можно позвать крошечную консольную утилиту "whoami" (Who Am I, кто я?), или универсальный тяжеловес WMI (Windows Management Instrumentation). Соответствие имени пользователя и его SID можно отследить и в ключе реестра: [HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList]:
Полезную смысловую нагрузку несут в себе три поля идентификатора – это Authority, Sub-Authority, и последний RID. На платформе Win всех мастей ревизия всегда одинакова и равна
S-1
, а 96-битное значение компьютера (или домена), генерируется системой от фонаря рандомно.• Поле "Authority" имеет фиксированную длину и определяет класс учётной записи:
C-подобный:
;//--- Типы SID_AUTHORITY --------
SECURITY_NULL_SID_AUTHORITY = 0
SECURITY_WORLD_SID_AUTHORITY = 1
SECURITY_LOCAL_SID_AUTHORITY = 2
SECURITY_CREATOR_SID_AUTHORITY = 3
SECURITY_NON_UNIQUE_AUTHORITY = 4
SECURITY_NT_AUTHORITY = 5
• Поле "Sub-Authority" имеет переменную длину и идентифицирует учётную запись внутри указанного класса. Это поле может иметь значение нуль, тогда SID определяет класс или группу учётных записей.
• Относительные идентификаторы "RID" одинаковы для всех компьютеров. Например RID учётки "Админ" всегда имеет значение 500, а RID гостя равен 501. Когда учётную запись создаёт админ, идентификатор нового пользователя начинается со значения 1000 и увеличивается на 1 для каждой следующей учётки. Кроме того, в системе имеется несколько широко-известных (Well-Known) учётных записей. К ним относятся группы "Все", "Интерактивные" и т.д. С исчерпывающим списком SID и их описанием можно ознакомится на сайте MSDN по
Ссылка скрыта от гостей
.В системе существует специальный SID для обозначения идентификатора сессии "LogonSID" – он тоже используется в записях ACЕ и назначается юзерам, которые вошли в систему аутентифицировано (указав свой логин и пароль) и работают в ней на текущий момент. SID сессии одноразовый и существует, пока пользователь не вышел из системы – при каждом последующем входе генерируется новый LogonSID для того-же пользователя. К примеру SID моей текущей сессии имеет значение
S-1-5-5-0-132935
, хотя постоянный SID моей-же учётной записи равен S-1-5-21-1365493694-xxxx-4151685940-1000
.4. Под занавес..
Таким образом идеология ограничения прав доступа к объектам сводится к тому, что пользователь имеет маркер доступа к объекту "AccessToken", а объект имеет свой дескриптор безопасности "SecurityDescriptor" со-списком DACL, и записями АСЕ в нём. Когда процесс хочет получить доступ к объекту, монитор безопасности SRM берёт токен процесса, и сопоставляет его с записью АСЕ запрошенного объекта. Состав, структуру и назначение токена я оставил для следующей части статьи, чтобы была возможность переварить теорию выше. До скорого, пока!
Последнее редактирование: