• Курсы Академии Кодебай, стартующие в мае - июне, от команды The Codeby

    1. Цифровая криминалистика и реагирование на инциденты
    2. ОС Linux (DFIR) Старт: 16 мая
    3. Анализ фишинговых атак Старт: 16 мая Устройства для тестирования на проникновение Старт: 16 мая

    Скидки до 10%

    Полный список ближайших курсов ...

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

Marylin

Mod.Assembler
Red Team
05.06.2019
305
1 378
BIT
323
Всем привет!
На повестке дня – подсистема безопасности 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
7
Спасибо за статью, было бы интересно почитать про обзор твоей рабочей станции. Система, настройки софт. Думаю многим было бы интересно.
 

Crazy Jack

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

Вложения

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

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