Ques/Help/Req Ломаем дескрипторы! Как злоупотреблять хендлами в Windows

XakeR

Member
Регистрация
13.05.2006
Сообщения
1 912
Реакции
0
Баллы
16
Местоположение
Ukraine
В Windows все взаимодействие программного кода с системой построено на хендлах. Можно атаковать компоненты системы, ковырять ядро, но что, если атаковать сами дескрипторы? В этой статье я разобрал несколько вариантов атак на них.

Хендлы (они же дескрипторы) в Windows — это один из важных камней в фундаменте системы. На хендлах построено и основывается все взаимодействие из программного кода с компонентами Windows. Именно благодаря дескрипторам ядро понимает, к какому объекту мы хотим обратиться и с каким уровнем доступа. Хендл обычно можно получить с помощью стандартных функций. На файл — через CreateFile(), на LSA — через LsaOpenPolicy(). В Windows очень много разных объектов, с которыми можно взаимодействовать. Ты можешь открыть список WinAPI, ткнуть на любой интересующий компонент и убедиться, что самой основной и важной апихой будет API для получения хендла на нужный объект. Не получил хендл — не смог взаимодействовать с системой.

Знаешь ли ты, что есть атаки и на сами хендлы?

warning​


Статья имеет ознакомительный характер и предназначена для специалистов по безопасности, проводящих тестирование в рамках контракта. Автор и редакция не несут ответственности за любой вред, причиненный с применением изложенной информации. Распространение вредоносных программ, нарушение работы систем и нарушение тайны переписки преследуются по закону.



Интересные хендлы​


Во‑первых, следует определиться с тем, что мы ищем. Нас интересуют далеко не все хендлы. Конечно, приоритетнее всего процессы и потоки. Взаимодействие с ними позволит запустить шелл‑код. Впрочем, тут опять не все так гладко. Полученный хендл должен иметь хотя бы право PROCESS_VM_WRITE (для процесса) или THREAD_SET_CONTEXT (для потока). Кстати, нам подойдут и хендлы на секции памяти и — в исключительных случаях — на конкретные файлы или ключи реестра.



Изучение хендлов процесса​


Перед тем как приступить, можем глянуть хендлы конкретных процессов в системе. Это позволит наметить дальнейший вектор развития атаки. Удобнее всего, как мне кажется, использовать утилиту Process Hacker.

Изучение хендлов процесса

Есть также System Explorer и Sysinternals, но я ими пользовался не очень много.

Вывод инструмента Handle из пакета Sysinternals

Handle Duplicating​


Это самая первая атака на хендлы. Основана она по большей части на функции DuplicateHandle() и ее более низкоуровневых аналогах вроде NtDuplicateObject().

Взглянем на прототип функции.

BOOL DuplicateHandle( [in] HANDLE hSourceProcessHandle, [in] HANDLE hSourceHandle, [in] HANDLE hTargetProcessHandle, [out] LPHANDLE lpTargetHandle, [in] DWORD dwDesiredAccess, [in] BOOL bInheritHandle, [in] DWORD dwOptions);

  • hSourceProcessHandle — хендл процесса, из которого требуется скопировать хендл. Должен иметь маску доступа PROCESS_DUP_HANDLE;
  • hSourceHandle — хендл, который нужно скопировать;
  • hTargetProcessHandle — процесс, в который нужно скопировать хендл;
  • lpTargetHandle — куда класть скопированный хендл;
  • dwDesiredAccess — флаги доступа;
  • bInheritHandle — наследуемый ли дескриптор, то есть можно ли его передавать в дочерние процессы;
  • dwOptions — дополнительные параметры.

Подробнее каждый аргумент можно изучить в официальной документации.

Итак, вся атака заключается в том, что мы находим процессы, на которые можем запросить хендл с маской PROCESS_DUP_HANDLE, затем копируем их хендлы, изучаем, на что они указывают, после чего эксплуатируем! Например, на процесс victim.exe из нашего процесса target.exe можно успешно получить хендл с нужной маской. После копирования дескрипторов из целевого процесса обнаруживаем, что у нас есть дескриптор процесса lsass.exe! Как следствие, успешно дампим процесс.

Конечно, в целевом процессе может быть указана плохая маска доступа, которая не подходит для дампа процесса. Тем не менее, если у нас есть хендл, мы можем распарсить его и извлечь маску доступа, используемую для получения этого хендла.

Здесь нам поможет функция NtQueryObject() и передача структуры PUBLIC_OBJECT_BASIC_INFORMATION.

#include <iostream>#include <windows.h>#include <winternl.h>#pragma comment(lib, «ntdll.lib»)void ParseAccessMask(ACCESS_MASK accessMask) { if ((accessMask & GENERIC_READ) == GENERIC_READ) std::cout << «GENERIC_READ «; if ((accessMask & GENERIC_WRITE) == GENERIC_WRITE) std::cout << «GENERIC_WRITE «; if ((accessMask & GENERIC_EXECUTE) == GENERIC_EXECUTE) std::cout << «GENERIC_EXECUTE «; if ((accessMask & GENERIC_ALL) == GENERIC_ALL) std::cout << «GENERIC_ALL «; if ((accessMask & PROCESS_CREATE_PROCESS) == PROCESS_CREATE_PROCESS) std::cout << «PROCESS_CREATE_PROCESS «; if ((accessMask & PROCESS_TERMINATE) == PROCESS_TERMINATE) std::cout << «PROCESS_TERMINATE «; if ((accessMask & PROCESS_SUSPEND_RESUME) == PROCESS_SUSPEND_RESUME) std::cout << «PROCESS_SUSPEND_RESUME «; if ((accessMask & PROCESS_QUERY_INFORMATION) == PROCESS_QUERY_INFORMATION) std::cout << «PROCESS_QUERY_INFORMATION «; if ((accessMask & PROCESS_SET_INFORMATION) == PROCESS_SET_INFORMATION) std::cout << «PROCESS_SET_INFORMATION «; std::cout << std::endl;}int main() { HANDLE hProcess = GetCurrentProcess(); ULONG returnLength = 0; PUBLIC_OBJECT_BASIC_INFORMATION obi; NTSTATUS status = NtQueryObject( hProcess, ObjectBasicInformation, &obi, sizeof(obi), &returnLength); if (NT_SUCCESS(status)) { std::cout << «Granted Access: « << std::hex << obi.GrantedAccess << std::endl; ParseAccessMask(obi.GrantedAccess); } else { std::cerr << «NtQueryObject failed with status: « << std::hex << status << std::endl; } CloseHandle(hProcess); return 0;}Пример вывода

Есть проблема: мы парсим маски, совершенно забывая о том, что маски доступа могут иметь перекрывающиеся значения. Например, у процесса есть PROCESS_VM_READ, у потока — THREAD_SET_CONTEXT, а эти оба значения указывают на 0x0010, поэтому нашу функцию парсинга маски доступа нужно модифицировать, научив распознавать переданный хендл.

Далеко ходить не надо — используем ту же функцию NtQueryObject(), только будем передавать ей структуру PUBLIC_OBJECT_TYPE_INFORMATION.

typedef struct __PUBLIC_OBJECT_TYPE_INFORMATION { UNICODE_STRING TypeName; ULONG Reserved[22];} PUBLIC_OBJECT_TYPE_INFORMATION, *PPUBLIC_OBJECT_TYPE_INFORMATION;

Здесь будем отталкиваться только от поля TypeName. Именно там содержится тип объекта, на который указывает хендл.

Материалы из последних выпусков становятся доступны по отдельности только через два месяца после публикации. Чтобы продолжить чтение, необходимо стать участником сообщества «Xakep.ru».

Присоединяйся к сообществу «Xakep.ru»!​


Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее

-60%

1 год​


9990 рублей 4000 р.


[TD]

1 месяц​


920 р.
[/TD]

Я уже участник «Xakep.ru»
 
198 114Темы
635 085Сообщения
3 618 401Пользователи
EeOneНовый пользователь
Верх