• XSS.stack #1 – первый литературный журнал от юзеров форума

Techniques Пишем ядерные LPE-эксплойты по горячим следам

varwar

El Diff
Забанен
Регистрация
12.11.2020
Сообщения
1 383
Решения
5
Реакции
1 537
Пожалуйста, обратите внимание, что пользователь заблокирован
Статью рекомендуется читать на темной теме или pdf из-за того, что схемы на светлой плохо читаются.
Ссылка на pdf - http://**************************************************************/b/DPYGPuhUGduurEWaZf7K2
(отдельное спасибо ordinaria1 за создание pdf-файла)


Введение
В этой небольшой статье я бы хотел поделиться опытом эксплуатации 1-day уязвимостей в нативных компонентах Windows - ее драйверах. За основу были взяты 4 уязвимости в appid.sys, csc.sys, afd.sys, ks.sys. Для CVE-2024-26229, CVE-2024-35250, CVE-2024-21338 мною были опубликованы PoC, которые добавлены или находятся в очереди на добавление во фреймворки постэксплуатации, такие как Cobalt Strike, Brute Ratel, Metasploit Framework и другие инструменты. Для CVE-2023-21768 PoC был опубликован другими исследователями, но я решил включить эту уязвимость в доклад из-за различий в подходах к эксплуатации. На ее примере будут описаны три техники LPE, которые будут использоваться в дальнейшем. Следующие типы уязвимостей будут представлены.
  • CWE-822: Untrusted Pointer Dereference
  • CWE-781: Improper Address Validation in IOCTL with METHOD_NEITHER I/O Control Code
Несмотря на то, что каждая уязвимость была ранее описана другими исследователями в статьях или на конференциях, интересные и важные детали были ими опущены по каким-либо причинам.

Для чего разрабатывать 1-day эксплойты?
По сравнению с поиском 0-day разработка 1-day эксплойтов менее трудозатратна и позволяет атакующим расширить свой арсенал, повысив тем самым эффективность.
Работая с 1-day уязвимостями, исследователь совершенствует свои навыки статического, динамического анализа, а также навыки программирования.
В случае с разработкой проактивных решений для эксплойта исследователь также совершенствует техники обхода зищитных систем, что позволяет минимизировать риски обнаружения и повысить эффективность эксплойта. При анализе 1-day не исключена возможность обхода патча или обнаружения 0-day.

Согласно недавнему отчету Mandiant 30% эксплуатируемых уязвимостей in-the-wild - это 1-day уязвимости, успешная эксплуатация которых может происходить даже спустя полгода после выхода исправлений.
tte-2023-fig3.gif

Рисунок 1 - Окно эксплуатации N-Day уязвимостей по данным Mandiant

Таким уязвимостям подвержены также End-Of-Life системы, информации о потенциальном количестве которых не много. Например, согласно информации из блога Государственного Университета Портлэнда40% их систем к октябрю 2025 года перестанут получать обновления для Windows 10 из-за устаревшего железа.

CVE-2023-21768
0-day для этой уязвимости использовался в дикой природе, а сама уязвимость была подробно описана в блоге IBM X-Force. PoC также был опубликован на GitHub. В оригинале авторы эксплуатировали подсистему I/O Ring для получения примитивов чтения и записи. Мы же доработали эксплойт и применили два других метода, один из которых был обнаружен совсем недавно и отлично вписывается в контекст уязвимости.
Начнем с того, что уязвимость позволяет осуществлять запись DWORD по произвольному указателю при удалении из очереди пакета для объекта I/O Completion Port.
Происходит это в цепочке вызовов AfdNotifySock->AfdNotifyRemoveIoCompletion->IoRemoveIoCompletion.
C:
NTSTATUS __fastcall AfdNotifySock(
        _FILE_OBJECT *FileObject,
        __int64 a2,
        KPROCESSOR_MODE PreviousMode,
        AFD_NOTIFISOCK_DATA *user_UnkStruct, // 1. Attacker controlled input data
        int nInBufferSize,
        __int64 lpOutBuffer,
        int nOutBufferSize)
    // Skipped
    status = AfdNotifyRemoveIoCompletion(PreviousMode, CompletionObject, user_UnkStruct);
    // Skipped
}

NTSTATUS __fastcall AfdNotifyRemoveIoCompletion(
        KPROCESSOR_MODE PreviousMode,
        _KQUEUE *IoCompletion,
        AFD_NOTIFISOCK_DATA *user_UnkStruct) // 2. Attacker controlled input data
{
    nCompletionPck = 0; // 3. Initialization of nCompletionPck

    // Skipped
    RemovePackets:
        status = IoRemoveIoCompletion(
                     IoCompletionObject,
                     OutBuf,
                     Pool,
                     dwLen,
                     &nCompletionPck, // 4. Saving value to the nCompletionPck
                     PreviousMode,
                     Timeout,
                     0);
    // Skipped
        *(_DWORD *)user_UnkStruct->pAcidPtr = nCompletionPck; // 5. Writing nCompletionPck to the attacker controlled pointer
    // Skipped
}
Листинг 1 - Цепочка передачи пользовательских данных
На листинге выше мы можем видеть запись значения переменной nCompletionPck по указателю в структуре user_unkStruct, которую мы передаем драйверу. Откуда берется значение nCompletionPck?
C:
NTSTATUS __fastcall IoRemoveIoCompletion(
        struct _KQUEUE *IoCompletionObject,
        __int64 OutBuf,
        PLIST_ENTRY *EntryArray,
        ULONG dwLen,
        ULONG *nCompletionPck,
        KPROCESSOR_MODE PreviousMode,
        LARGE_INTEGER *Timeout,
        BOOLEAN somebool)
{
    // Skipped
    retVal = KeRemoveQueueEx(IoCompletionObject, PreviousMode, somebool, Timeout, EntryArray, dwLen);
    // Skipped
    *nCompletionPck = retVal;
    //
Листинг 2 - Получение значения nCompletionPck
Значение nCompletionPck является результатом выполнения функции KeRemoveQueueEx и возвращает кол-во пакетов, убранных из очереди.
Для вызова уязвимости необходимо:
  • Cоздать объект I/O Completion и ассоциированный с ним IPv4 TCP сокет.
  • Добавить в очередь N пакетов при помощи PostQueuedCompletionStatus
  • Инициировать входящий буфер AFD_NOTIFYSOCK_DATA
  • Отправить запрос драйверу с IOCTL_AFD_NOTIFY_SOCK
Описатель сокета для общения с драйвером мы получаем в обертке функции NtCreateFile.
socket.png

Листинг 3 - Получение описателя для TCP сокета
Объект I/O Completion Port cоздается при помощи функции CreateIoCompletionPort или NtCreateIoCompletion.
На Рисунке 2 показано как происходит добавление пакетов в очередь.
add_packet1.png

Рисунок 2 - Добавление пакетов в очередь объекта I/O Completion
В оригинальном исследовании авторы производили запись значения 0x1, т.е. в очередь добавлялся всего один пакет. На первый взгляд сложно представить как можно такой ограниченный примитив "докрутить" до LPE, но данный подход был уже апробирован и отлично накладывается на технику с использование I/O Ring. Нам же изначально было любопытно возможно ли переписать PreviousMode на значение 0x0 и получить простой, мощный примитив на чтение и запись ядерной памяти. Для этого пришлось отредактировать код, добавив цикл с PostQueuedCompletionStatus, чтобы получить произвольный инкремент.
loop.png

Листинг 4 - Цикл для добавления пакетов в очередь
Добавив цикл в оригинальный код мы можем приступить к эксплуатации.
arbitrarykernelwrite.png

Листинг 5 - Реализация интерфейса ArbitraryKernelWrite
Что нам дает перезапись PreviousMode? При вызове Nt* функций ядро производит валидацию входных параметров, поступающих из режима пользователя, основываясь на значении PreviousMode для текущего потока описываемого структурой KTHREAD. У этого поля есть два значения - KernelMode и UserMode которые равны 0 и 1 соответственно. Если значение PreviousMode == KernelMode, то проверки пользовательских указателей опускаются. В эксплойтах чаще всего используются функции NtReadVirtualMemory, NtWriteVirtualMemory для произвольнго чтения/записи виртуальной памяти. Возможно использование и других интересных функций, например, получение описателя устройства \Device\PhysicalMemory и вызов NtOpenSection, NtMapViewOfSection для чтения и записи физической памяти.
Чтобы обнулить PreviousMode мы должны записать значение 0x100 по смещению 0x232 структуры KTHREAD для текущего потока. Следовательно, мы должны добавить 0x100 пакетов в очередь.

aw1.png


Рисунок 3 - перезапись KTHREAD.PreviousMode
Стоит отметить, что это было не так очевидно на первый взгляд, т.к. не удалось найти примеров использования функции NtSetIoCompletion. С задачей помог справиться ChatGPT, который написал цикл с оберткой PostQueuedCompletionStatus и PoC сработал как от него и ожидалось.
Для повышения привилегий мы будем использовать технику DKOM и чтобы завершить эксплойт нам потребуются ядерные адреса объектов EPROCESS для системного и текущего процессов, KTHREAD для текущего потока. Получить их мы можем при помощи системной функции NtQuerySystemInformation с идентификатором SystemHandleInformation. Данная техника давно известна и широко применяется, поэтому не будем на ней акцентировать внимание.
prevmode.png

Листинг 6 - Получение ядерных адресов объектов EPROCESS, KTHREAD
GetObjPtr в данном случае - это просто обертка над NtQuerySystemInformation, которая возвращает адрес объекта по идентификатору (PID) и описателю.
prevmode2.png

Листинг 7 - Перезапись значения токена текущего процесса на системный
Перезапись производится при помощи обертки для NtWriteVirtualMemory. В обязательном порядке следует восстановить значение PreviousMode, в противном случае при создании нового процесса произойдет краш системы. Восстановление значения BasePriority в данном случае необязательно и оно ни на что не влияет.
write64.png

Листинг 8 - Реализация обертки Write64

На Рисунке 4 схематически изображен процесс подмены токена.

tokenswap.png

Рисунок 4 - Схема подмены токена
test1.png

Рисунок 5 - Результат выполнения эксплойта
Еще два метода построены вокруг манипуляций с привилегиями токена. Рассмотрим их по порядку. В структуре токена есть поле Privileges, которое описывается другой структурой - _SEP_TOKEN_PRIVILEGES и имеет вид.
C:
typedef struct _SEP_TOKEN_PRIVILEGES
{
     UINT64 Present;
     UINT64 Enabled;
     UINT64 EnabledByDefault;
} SEP_TOKEN_PRIVILEGES, *PSEP_TOKEN_PRIVILEGES;
Листинг 9 - Структура _SEP_TOKEN_PRIVILEGES
Зачастую для повышения привилегий используют добавление токену привилегии SeDebugPrivilege, т.к. эта привилегия позволяет атакующему [обойти любую проверку доступа при открытии объекта процесса или потока.](Windows Security Internals (James Forshaw)) Для успешной эксплуатации мы должны установить двадцатый бит полей Present и Enabled т.к. именно он ответственен за эту привилегию.

septokenprivileges2.png

Рисунок 6 - Схема перезаписи полей Present/Enabled для текущего токена
septokenprivileges.png

Листинг 10 - Повышение привилегий при помощи перезаписи полей Present/Enabled
Следующая техника была изобретена недавно специалистами из DEVCORE и все также основана на получении привилегий SeDebugPrivilege. Как было уже показано в публикации DEVCORE SeDebugPrivilege - это константа типа LUID. Хранится она в секции ядра PAGEDATA, имеющей права на запись.
sedebugprivilege.png

Рисунок 7 - Вывод декомпилятора функции SepVariableInitialization
Т.к. стандартный пользователь в системе имеет права SeChangeNotifyPrivilege (см. Изображение 4), то мы можем изменить значение SeDebugPrivilege с 0x14 на 0x17, тем самым пройдя проверку при открытии описателя привилегированного процесса и аналогично предыдущему способу получить шелл с правами nt authority/system.
1.png

Листинг 11 - Повышение привилегий при помощи перезаписи константы SeDebugPrivilege
Немаловажным фактором при использовании этого метода является то, что мы можем многократно вызывать ArbitraryKernelWrite без BSOD или других явных внешних признаков вмешательства, что обеспечивает эксплойту стабильность и возможность его модернизации.

CVE-2024-21338 & CVE-2024-38041
В феврале 2024 года вышла статья от Avast, которая описывала техники Lazarus и в частности 0-day уязвимость, которую использовал руткит. Уязвимость довольно простая, хотя все еще требует реверс-инжиниринга и обхода ряда проверок. Основной сложностью было найти подходящий kCFG-гаджет для эксплуатации.

kcfg2.png

Листинг 12 - Arbitrary Pointer Dereference в драйвере appid.sys

kCFG позволяет отслеживать, верифицировать и прерывать непрямые вызовы.

kcfg3.png

Рисунок 7 - Схема kCFG
Когда указатель не проходит проверку kCFG в рантайме, то Windows завершает выполнение программы, таким образом прерывая попытки эксплуатации непрямых вызовов.

kcfg4.png

Рисунок 9 - Вывод секции GFID в утилите PEAnatomist

Параллельно несколько человек уже разрабатывало PoC и чтобы не повторяться было решено использовать не kCFG гаджет.

kcfg1.jpg

Рисунок 10 - Твит в одной запрещенной социальной сети
Критерии для гаджета были следующие:
  • Размер функции < 100 байт
  • В функции должен быть вызов memmove
После применения фильтров плагин вернул 46 кандидатов. Единственным подходящий гаджет - это PopEtDataSectionCopyData и здесь нужно остановиться для более детального рассмотрения уязвимости.
kcfg5.png

Рисунок 10 - Интерфейс плагина FindFunc
kcfg6.png

Листинг 13 - Псевдокод функции PopEtDataSectionCopyData при наложении данных для эксплуатации
В Листинге 13 указатель vuln_callback_addr принимает два аргумента, где controlled_arg - это указатель на копию пользовательского буфера в пространстве ядра - SMART_HASH_IMAGE_FILE, который мы передаем драйверу через интерфейс IoDeviceControl. Во втором аргументе находится указатель переменной some_val, значение которой равно 0x0. Это состояние мы и будем эксплуатировать.
C:
//
// Main exploit structures definition
//
typedef struct _VULN_CALLBACK_STRUCT
{
    PVOID vuln_callback_addr;
}VULN_CALLBACK_STRUCT;

typedef struct  _SMART_HASH_IMAGE_FILE
{
    int64_t field0;                    // 0x00
    unsigned __int64 dummy_f_obj;    // 0x8 should containt a valid FILE_OBJECT pointer to avoid BSOD
    VULN_CALLBACK_STRUCT *ptr;        // 0x10 points to the struct which contain malicious callback function pointer
    void *prev_mode;                // 0x18 KTHREAD->PreviousMode address
}SMART_HASH_IMAGE_FILE;
Листинг 14 - Объявление структуры SMART_HASH_IMAGE_FILE
Чтобы успешно проэксплуатировать этот гаджет, мы должны уточнить состояние третьего аргумена на момент вызова коллбека, т.е. регистра r8, который равен 0x1.
Получается, что мы можем точно обнулить PreviousMode.
  • Data->prev_mode содержит указатель на KTHREAD.PreviousMode текущего потока
  • По указателю InBuf находится значение 0x0
  • InBufSize = 0x1
Чтобы избежать возможного неопределенного поведения при прохождении проверки можно выделить память под указатель в куче по адресу 0x100000000 и таким образом старший DWORD будет равен 0x1. В сумме с размером InBufSize значение всегда будет меньше младшего DWORD ядерного объекта.
kcfg8.png

Листинг 15 - Инициализация входящего буфера и вызов уязвимости

kcfg7.png

Рисунок 12 - Результат выполнения эксплойта
Недостатком этого гаджета помимо того, что эксплойт не будет работать с включенным HVCI является то, что код применим только на Windows 11, т.к. в Windows 10 размер структуры меньше и мы не можем контролировать адрес по смещению 0x18.

CVE-2024-26229
Уязвимость изначально обнаружена Эриком Эгсгардом и описана в докладе на конференции OffensiveCon. Microsoft в бюллетени безопасности пометил уязвимость следующим образом.
Оба утверждения неверны. Во-первых, уязвимость не имеет никакого отношения к переполнения пула, хотя есть догадки почему могло произойти недопонимание, но сначала мы должны рассмотреть ошибку подробнее. Во-вторых, эксплуатация уязвимости тривиальна, несмотря на то, что PoC был продемонстрирован с ошибкой.
Из самого доклада становится ясно, что уязвимость - это CWE-781: Improper Address Validation in IOCTL with METHOD_NEITHER I/O Control Code. При этом методе передачи данных входящий адрес не валидируется I/O Manager'ом. Сделано это в целях повышения производительности, а использование этого метода можно встретить, например, в сетевых драйверах.

На изображении ниже PoC, который продемонстрировал Эрик. При компиляции и запуске кода вызвать уязвимость не получится из-за ошибки в коде.

26229_3.png

Рисунок 13 - Листинг с PoC от Эрика
Передача пользовательских аргументов происходит через цепочку драйверов и структуру RX_CONTEXT. Валидация входящих аргументов происходит в драйвере rdbss.sys и показана на рисунке ниже.
26229_2.png

Рисунок 14 - Схема передачи пользовательских данных и триггер уязвимости
Чтобы обойти проверку необходимо при вызове NtFsControlFile указать следующие аргументы.
  • InputBufferLength != 0
  • InputBuffer != 0
  • OutputBufferLength = 0
  • UnkVal = 0
При удовлетворении этих условий удастся вызвать уязвимость. В Рисунке 14 умышленно был указан неверный размер OutputBufferLength. После этого эксплуатация становится тривиальной. Из Рисунка 14 следует, что мы можем записать 0 по произвольному адресу. Наиболее очевидным решением для эксплуатации уязвимости является обнуление PreviousMode.
26229_4.png

Рисунок 15 - Повышение привилегий при помощи обнуления PreviousMode

26229_5.png

Рисунок 16 - Результат выполнения эксплойта
CVE-2024-35250
Уязвимость была обнаружена Angelboy из DEVCORE, продемонстрирована на Pwn2Own Vancouver 2024 и описана в блогекомпании в рамках одного большого исследования, посвященного логическим уявимостям в Kernel Streaming. Из данного источника об уязвимости было известно следующее.
  • Уязвимость CWE-822: Untrusted Pointer Dereference
  • Работа ограничена в среде Hyper-V
  • Уязвимая функция UnserializePropertySet
  • Непрямой вызов происходит в функции CKSThunkDevice::CheckIrpForStackAdjustmentNative
Несмотря на кажущуюся простоту, написание эксплойта потребовало дополнительного реверс-инжиниринга и чтения документации для технологии Kernel Streaming.

Код:
1: kd> k
 # Child-SP          RetAddr               Call Site
00 ffffea85`d3bdb1e8 fffff806`8b711f74     ksthunk!CKSThunkDevice::CheckIrpForStackAdjustmentNative
01 ffffea85`d3bdb1f0 fffff806`8b7113c6     ksthunk!CKSThunkDevice::DispatchIoctl+0xc4
02 ffffea85`d3bdb230 fffff806`8b711133     ksthunk!CKernelFilterDevice::DispatchIrp+0xa6
03 ffffea85`d3bdb290 fffff806`802cb875     ksthunk!CKernelFilterDevice::DispatchIrpBridge+0x13
04 ffffea85`d3bdb2c0 fffff806`8bf2a15a     nt!IofCallDriver+0x55
05 ffffea85`d3bdb300 fffff806`8bf04a41     ks!KsSynchronousIoControlDevice+0x11a
06 ffffea85`d3bdb3a0 fffff806`8bf1e32b     ks!UnserializePropertySet+0x165
07 ffffea85`d3bdb420 fffff806`8bf1d8de     ks!KspPropertyHandler+0x6db
08 ffffea85`d3bdb490 fffff806`8bf1d0f7     ks!KspHandleAutomationIoControl+0xce
09 ffffea85`d3bdb530 fffff806`8bed5fca     ks!KsDispatchIrp+0xf7
0a ffffea85`d3bdb5f0 fffff806`802cb875     ks!CKsDevice::PassThroughIrp+0x6a
0b ffffea85`d3bdb630 fffff806`8b711415     nt!IofCallDriver+0x55
0c ffffea85`d3bdb670 fffff806`8b711133     ksthunk!CKernelFilterDevice::DispatchIrp+0xf5
0d ffffea85`d3bdb6d0 fffff806`802cb875     ksthunk!CKernelFilterDevice::DispatchIrpBridge+0x13
0e ffffea85`d3bdb700 fffff806`806c2c70     nt!IofCallDriver+0x55
0f ffffea85`d3bdb740 fffff806`806c123c     nt!IopSynchronousServiceTail+0x1d0
10 ffffea85`d3bdb7f0 fffff806`806bf516     nt!IopXxxControlFile+0x72c
11 ffffea85`d3bdba00 fffff806`8043d1e5     nt!NtDeviceIoControlFile+0x56
12 ffffea85`d3bdba70 00007ffb`afe2eee4     nt!KiSystemServiceCopyEnd+0x25
13 00000001`9b7cfb08 00007ffb`ad1ebc5b     ntdll!NtDeviceIoControlFile+0x14
14 00000001`9b7cfb10 00007ffb`aefc27f1     KERNELBASE!DeviceIoControl+0x6b
15 00000001`9b7cfb80 00007ff6`e70e1b0a     KERNEL32!DeviceIoControlImplementation+0x81
16 00000001`9b7cfbd0 000001a6`5f3dde70     CVE_2024_35250+0x1b0a
Листинг 16 - Цепочка вызова уязвимости
На рисунке ниже изображен псевдокод функции UnserializePropertySet, который отчасти дает представление о данных, которые мы должны передать драйверу для вызова уязвимости.
35250_1.png

Рисунок 17 - Фрагмент псевдокода функции UnserializePropertyset
Логическая уязвимость заключается в некорректном установлении значения RequestorMode для Irp при вызове функции KsSynchronousIoControlDevice.
35250_2.png

Рисунок 17 - Фрагмент псевдокода функции KsSynchronousIoControlDevice
При инциализации Irp в IoBuildDeviceIoControlRequest поле RequestorMode по умолчанию устанавливается как KernelMode.
Далее оно еще раз инициализируется как KernelMode и это проблема, т.к. в дальнейшем при вызове IofCallDriver драйвер будет оперировать недоверенными указателями, а проверки на RequestorMode будут опущены.
Код:
3: kd> dt nt!_IRP @rax
   +0x000 Type             : 0n6
   +0x002 Size             : 0x358
   +0x004 AllocationProcessorNumber : 3
   +0x006 Reserved         : 0
   +0x008 MdlAddress       : (null)
   +0x010 Flags            : 0x60000
   +0x018 AssociatedIrp    : <unnamed-tag>
   +0x020 ThreadListEntry  : _LIST_ENTRY [ 0xffff800b`ea7cd3b0 - 0xffff800b`e970e580 ]
   +0x030 IoStatus         : _IO_STATUS_BLOCK
   +0x040 RequestorMode    : 0 ''
Листинг 17 - Поле RequestorMode установлено как 0 после вызова IoBuildDeviceIoControlRequest
После вызова IofCallDriver мы попадаем в обработчик IOCTL CKSThunkDevice::DispatchIoctl, где происходит валидация запроса, IOCTL из входящих параметров IRP и вызов обработчика CKSThunkDevice::CheckIrpForStackAdjustmentNative.
35250_4.png

Рисунок 18 - Псевдокод функции CKSThunkDevice::DispatchIoctl
Здесь цепочка уязвимости завершается. И снова, все проверки на RequestorMode опускаются и происходит непрямой вызов по недоверенному указателю, который мы контролируем во входящем буфере, также как и первый аргумент при условии, что InputBufferLenth >= 0x18.
35250_3.png

Рисунок 19 - Псевдокод функции CKSThunkDevice::CheckIrpForStackAdjustmentNative
Немаловажной деталью здесь является проверка с KSPROPSETID_DrmAudioStream. Устройство, которому мы будем посылать запрос должно поддерживать этот набор свойств. Как узнать описатель какого устройства мы должны получить? Есть два неочевидных способа.
  • Программно через функцию KsOpenDefaultDevice c KSCATEGORY_DRM_DESCRAMBLE
  • Найти полное имя устройства через утилиту KsStudio
35250_8.png

Рисунок 20 - Получение описателя устройства с набором свойств KSPROPSETID_DrmAudioStream
KsStudio имеет графический интерфейс и позволяет получить большое количество информации об устройствах, логгировать IRP и т.д. Категорий устройств, содержащих в своем названии DRM было не так много - всего одно.
35250_5.png

Рисунок 21 - Интерфейс утилиты KsStudio
35250_6.png

Рисунок 22 - Интерфейс утилиты KsStudio
На рисунке ниже утилита показывает какой набор свойств поддерживает устройство. Это устройство и будет целью при эксплуатации.
35250_7.png

Рисунок 23 - Интерфейс утилиты KsStudio
Полное имя устройства, которому необходимо передать запрос - \\?\root#system#0000#{ffbb6e3f-ccfe-4d84-90d9-421418b03a8e}\{eec12db6-ad9c-4168-8658-b03daef417fe}&{abd61e00-9350-47e2-a632-4438b90c6641}. Это можно сделать напрямую через CreateFileW или, как упоминалось ранее, через KsOpenDefaultDevice.
Вернемся к псевдокоду фукнции UnserializePropertySet. В SystemBuffer хранятся данные не из входящего буфера, как это бывает зачастую. Перед вызовом UnserializePropertySet данные в SystemBuffer копируются из исходящего буфера.
35250_9.png

Рисунок 24 - Фрагмент псевдокода функции KspPropertyHandler
Для запроса с флагом свойства KSPROPERTY_TYPE_UNSERIALIZESET валидный исходящий и входящий буферы должны выглядеть следующим образом.
35250_11.png

Рисунок 25 - Стукртура буферов UnserializePropertySetRequest и InBuffer
Структура буфера сериализации.
  • Заголовок KSPROPERTY_SERIALHDR
  • Набор свойств для сериализации KSPROPERTY_SERIAL
  • Какие-то данные
35250_10.png

Рисунок 26 - Инициализация UnserializePropertySetRequest
Структура входящего буфера.
  • Структура KSPROPERTY
  • Какие-то данные
35250_12.png

Рисунок 27 - Инициализация InBuffer
То, как будут инициализироваться поля структур EXPLOIT_DATA1, EXPLOIT_DATA2 во входящем и исходящем буфере зависит от того, какую технику для повышения привилегий мы будем использовать.
Нам снова необходимо найти гаджеты для обхода kCFG. В оригинальном исследовании Angelboy использовал гаджет RtlSetAllBits для перезаписи SEP_TOKEN_PRIVILEGES для текущего токена.
Для разнообразия была добавлена поддержка перезаписи PreviousMode и гаджет RtlClearAllBits.

35250_13.png

Рисунок 28 - Инициализация FakeBitmap и ptr_ArbitraryFunCall
Поле FakeBitmap описывается структурой RTL_BITMAP и содержит два поля - SizeOfBitMap и указатель Buffer. Т.к. мы полностью контролируем содержимое структуры, то можем осуществить запись по произвольному адресу в Buffer.
C:
typedef struct _RTL_BITMAP
{
    DWORD SizeOfBitMap;
    PVOID Buffer;
}RTL_BITMAP, *PRTL_BITMAP;
Листинг 18 - Структура RTL_BITMAP
Код повышения привилегий шаблонный и похож на то, что мы уже проделывали ранее.
35250_14.png

Рисунок 29 - Повышение привилегий двумя техниками
По итогу при корректном вызове DeviceIoControl суммарно 10 проверок должно быть удовлетворено, не считая указания валидных гаджетов. Это был самый сложный 1-day, который мне приходилось реверсить из-за отсутствия примеров кода, запутанной работы самой технологии, сложностях при динамическом анализе. Например, мне так и не удалось поймать в системе вызовы обработчика UnserializePropertySet, чтобы подсмотреть стек вызовов и проанализировать аргументы. Основную часть работы пришлось делать в статике.
 
Последнее редактирование:
5.jpg


This is a well known phenomena that devs patch the symptom: they see that a certain param crashes in the heap allocator, so they clamp a len or do partial bounding. But they may not fix the actual root cause that the ptr is being used w/o proper ProbeForRead/Write. You’ll sometimes see 1day exploits that bypass a “patch” because the patch addresess a len mismatch / an uninit ptr but not the underlying improper mode or trust check

This is why analyzing 1day vulns is so fruitful. Even after “fixes”, it’s not unusual to find that the new code path is still missing one or two checks.
 
http://xssforum7mmh3n56inuf2h73hvhnzobi7h2ytb3gvklrfqm7ut3xdnyd.onion/attachments/104761/
1.jpg


No chatgpt cameo needed: just drop the credits, please lol.
 
Пожалуйста, обратите внимание, что пользователь заблокирован
No chatgpt cameo needed: just drop the credits, please lol.
In those situation chatgpt did the thing cause never used i/o completion and don't even knew what is it lol. Mission accomplished. Which credits should I drop?

shrekushka it took couple of minutes to fix code and got LPE, I thought that it was a magic. The AI shit just became popular in the field and I was like.

woah-neo-2096105213.gif
 
1.jpg

We got 10 years to make our millions and get in line for a neuralink.
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх