• 15 апреля стартует «Курс «SQL-injection Master» ©» от команды The Codeby

    За 3 месяца вы пройдете путь от начальных навыков работы с SQL-запросами к базам данных до продвинутых техник. Научитесь находить уязвимости связанные с базами данных, и внедрять произвольный SQL-код в уязвимые приложения.

    На последнюю неделю приходится экзамен, где нужно будет показать свои навыки, взломав ряд уязвимых учебных сайтов, и добыть флаги. Успешно сдавшие экзамен получат сертификат.

    Запись на курс до 25 апреля. Получить промодоступ ...

Статья ASM – Безопасность Win. [1]. Разграничение прав доступа к объектам

Marylin

Mod.Assembler
Red Team
05.06.2019
295
1 310
BIT
111
Всем привет!
На повестке дня – подсистема безопасности Win. Рассмотрим такие понятия как: системные объекты, права пользователей и их привилегии, токены и дескрипторы безопасности, списки контроля доступа DACL/SACL и записи в них ACE, проведём экскурсию в процесс Lsass.exe, познакомимся с диспетчером объектов, системным монитором SRM и узнаем, каким образом утилите "runas.exe" удаётся запускать процессы с админскими правами. Одним словом попытаемся бросить камень в стеклянную форточку Win и посмотрим, что из этого выйдет.

Содержание:

1. Знакомство с объектами
2. Дескриптор безопасности объекта

2.1. Структура дескриптора SD
2.2. Списки DACL и записи в них АСЕ
2.3. Эксперимент в отладчике WinDbg
3. Формат идентификатора SID
4. Под занавес..
-------------------------------------------------------


1. Знакомство с объектами

Известно, что Win построена на объектной модели. Это значит, что если в операционных системах класса Linux всё является файлом, то в глазах большого Билли мир существует в виде объектов. К числу объектов можно отнести всё, с чем нам приходится иметь дело в процессе программирования – это армия всевозможных физ.устройств Device, драйверы и контролёры, процессы и потоки, файлы и папки, таймеры, семафоры, мьютексы, рабочий стол и многое другое. В программе М.Руссиновича "Object Explorer", на своей семёрке я насчитал 42 именованных типов объектов, и это только доступные нам как смертным. Помимо них, в нёдрах оси имеются ещё и безымянные ресурсы, так-что здесь есть где развернуться.

Чтобы хоть немного навести порядок в этой кухне, все объекты система сортирует по типам – так в литературе появился термин "Объект типа". На программном уровне, каждый объект описывается своей структурой "OBJECT_HEADER", за которой тут-же располагается непосредственно и сам объект. В заголовке Header, тушке объекта присвоено имя "Body", и это самое последнее поле структуры (см.рис.ниже). При таких раскладах ясно, что системе требуется некий руководящий орган, который управлял-бы всем этим сообществом. Протекторат возложил эту задачу на плечи диспетчера объектов – службе уровня ядра.

На рисунке ниже скрин программы, куда я вставил прототип структуры Header из ядерного отладчика WinDbg. Если выбрать в левом окне просмотрщика раздел "ObjectTypes", можно ознакомиться с полным списком именованных объектов ядра (правый клик запрашивает свойства):


WinObj.png


Каждый из объектов имеет свой заголовок 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

SD_Mask.png


Если запустить программу "Object Explorer" (в том числе обычный проводник Win) и в свойствах какого-либо объекта перейти на вкладку "Безопасность", можно будет обнаружить список всех пользователей и назначенные им права на доступ к выбранному объекту – это окно называют ещё графическим редактором ACL. В штатной поставке Win идёт и консольная утилита для этих целей под названием "icacls.exe":

Obj_Mask.png



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]:


Sid_CMD.png


Полезную смысловую нагрузку несут в себе три поля идентификатора – это 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 берёт токен процесса, и сопоставляет его с записью АСЕ запрошенного объекта. Состав, структуру и назначение токена я оставил для следующей части статьи, чтобы была возможность переварить теорию выше. До скорого, пока!
 
Последнее редактирование:

MLNK

Mod. Ethical Hacking
Red Team
23.01.2018
560
706
BIT
1
Спасибо за статью, было бы интересно почитать про обзор твоей рабочей станции. Система, настройки софт. Думаю многим было бы интересно.
 

Crazy Jack

Grey Team
08.07.2017
573
89
BIT
35
Спасибо за статью. Коротко и ясно.
P.S.Может кому понадобится полное описание идентификаторов безопасности (SID), но там на месяц разбираться :D.
 

Вложения

  • Identity and access management.pdf
    4 МБ · Просмотры: 631
  • Нравится
Реакции: Mikl___ и Marylin
Мы в соцсетях:

Обучение наступательной кибербезопасности в игровой форме. Начать игру!